趋势科技举办的CTF比赛,题目的分类比较奇怪,而且本次题目中linux binary非常少,很多是windows binary。
这次是跟随队伍一起比赛,贡献了400分,比之前划水的时候多了不少。

照例是先放一些自己搞出来的题。

Analysis/Defensive 100 - vott.zip

这个题比较简单(我指的不是逆向层面而是调试层面),因为一开始文件的这个链接访问不了,后来能访问的时候正好我下了下来。

难得的一个64bit-ELF,运行以后显示’You are not on VMM!’,从elf中判断应该是需要判断是否处于VMM中。不过,这个判断是可以直接绕过去的…….快速地将rip定位到下面的另一个分支,貌似程序启动了另一个进程然后就打印出了flag。

1
2
3
4
5
6
7
8
gdb-peda$ set $rip=0x400cd9
gdb-peda$ c
Continuing.
You are on VMM!
process 8085 is executing new program: /tmp/...,,,...,,
<program name unknown>: /lib64/libcrypto.so.1.0.0: no version information available (required by <main program>)
TMCTF{ce5d8bb4d5efe86d25098bec300d6954}[Inferior 1 (process 8085) exited with code 0377]
/tmp/...,,,...,,: 没有那个文件或目录.

程序退出以后居然自毁了,有点惊悚……

Programming 200 - Calculate it!

这个题比较麻烦,连接到一个服务器然后会给出算式,要求你计算结果。本来纯数字的算式直接eval就可以了,算了几个之后就出现了罗马数字这种,我上网搜了一下,发现还有个roman.py这个库可以处理单个罗马数字,于是直接re.sub来替换算式。

又算了几个开始出现number name,比如zero, forty还有ninety two这种,到后面肯定会是更为奇怪的数字。这要是手写处理肯定花时间,于是又开始了google,搜了一会又发现了可以转换的代码,于是把它们组合一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python

from zio import *
import re
import roman

def text2int(textnum, numwords={}):
if not numwords:
units = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen",
]

tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

scales = ["hundred", "thousand", "million", "billion", "trillion"]

numwords["and"] = (1, 0)
for idx, word in enumerate(units): numwords[word] = (1, idx)
for idx, word in enumerate(tens): numwords[word] = (1, idx * 10)
for idx, word in enumerate(scales): numwords[word] = (10 ** (idx * 3 or 2), 0)

current = result = 0
for word in textnum.split():
if word not in numwords:
raise Exception("Illegal word: " + word)

scale, increment = numwords[word]
current = current * scale + increment
if scale > 100:
result += current
current = 0

return result + current

def handle_question(quest):
quest = quest.replace('=','').replace(',','')
quest = quest.strip()
quest = re.sub('[IXCMVLD]+', lambda s:str(roman.fromRoman(s.group(0))), quest)
quest = re.sub('[a-z][a-z\s]+', lambda s:str(text2int(s.group(0))), quest)
return quest

def main():
target = ('ctfquest.trendmicro.co.jp', 51740)
io = zio(target, print_read=COLORED(REPR, 'red'), print_write=True, timeout=999)
while True:
cmd = io.read_until('=')
cmd = handle_question(cmd)
result = eval(cmd)
io.write(str(result)+'\n')

if __name__ == '__main__':
main()

跑了大概几十个算式,出现了奇奇怪怪的各种大数小数,本来我还以为会出现更奇怪的写法,不过没了,处理了上面的情况以后就能够拿到flag。

Programming 100 - Click different color

因为之前这个100分的题跪着,主办方才放了200分题。我把200分解决之后才修好,回来再看这个,发现是一个网站,每次给出一幅403*403的图片,其中有很多色块其中一个的颜色跟其他的不一样,点击这个不一样的进入下一关,一直点到最后估计就能出现flag。

这个题就是需要用到PIL来识别图片,因为之后的色块越来越小根本不可能用肉眼去看,还有就是要留意其中的白色间隔。我采用直接取出直方图中最高的数值来判断主要颜色,在这之前把255这个坐标给去掉来排除白色。然后遍历图中每个点,如果既不是白色也不是主要颜色就直接返回x,y就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python

import Image
import re
from urllib2 import urlopen

start_url = 'http://ctfquest.trendmicro.co.jp:43210/click_on_the_different_color'
host = 'http://ctfquest.trendmicro.co.jp:43210'

def judge_pic(name):
im = Image.open(name)
w, h = im.size

# get the major color
hist = im.histogram()
r_major = hist[0:255].index(max(hist[0:255]))
g_major = hist[256:511].index(max(hist[256:511]))
b_major = hist[512:767].index(max(hist[512:767]))
major = (r_major, g_major, b_major)

# travel the image
for x in range(w):
for y in range(h):
if im.getpixel((x,y)) != (255, 255, 255) and im.getpixel((x,y)) != major:
return x, y

def crawl():
now_url = start_url
pic_id = 0
while True:
print '[+] open %s' % now_url
html = urlopen(now_url).read()
open('{}.html'.format(pic_id), 'w').write(html)

# extract next stage link
next_link = re.findall(r'window\.location\.href=\'([/a-z0-9]+?)\?x=\'', html)[0]
# extract image link
img_link = re.findall(r'<img src="([^"]+?)"', html)[0]
print '[+] next stage : %s' % next_link
print '[+] picture at %s' % img_link

# download the image
img = urlopen(host+img_link).read()
open('{}.png'.format(pic_id), 'w').write(img).close()

x,y = judge_pic('{}.png'.format(pic_id))
now_url = host + next_link + '?x={}&y={}'.format(x, y)
pic_id += 1

def main():
crawl()

if __name__ == '__main__':
main()

一共搞了78关,最后无法从网页中抓出下关的url时就意味着flag出现了。

1
2
Congraturations!!
The flag is TMCTF{U must have R0807 3Y3s!}

小结

可以说这次的收获还是挺大的,不仅拿到了3个flag,还学习了PIL的搞法。虽然自己的能力依旧非常弱,确实需要的磨练还很多,不管是工具使用方面还是脑洞方面,都希望能跟上队里大牛的步伐,以后能够帮上更多的忙就好了。(感觉自己要成Progamming选手了)

其他题目

Analysis/Defensive 300 - Posion Ivy

给出一段流量net.pcap,从中找出flag,hint是posion ivy / admin

这个题与其说是流量分析,还不如说是工具安装题,光从流量中基本看不出什么。posion ivy是一种木马的类型,后来看其他人的writeup发现使用chopshop这个工具可以直接从其中分析出flag,所以关键就在于配好这个chopshop。

自己配了一下,果然比较坑……因为Poison Ivy使用了camellia加密算法,所以其中的模块需要camcrypt这个python lib,但是这个lib封装的不好连pip装上去的都没法用,只能自己折腾……

chopshop项目地址:https://github.com/MITRECND/chopshop,要使用其中的poisonivy_23x.py模块,而此模块又要使用camcrypt。

camcrypt项目地址:https://github.com/knowmalware/camcrypt,然而说明中的camcrypt.py项目里都没有,需要从旧版本中下载camcrypt.py

好,make出一个camcrypt.so以后拷贝到/usr/lib64中,然后把camcrypt.py拷贝到chopshop下面的ext_libs文件夹,这个工具差不多就能工作了,然后运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ ./chopshop -f net.pcap -s ./ "poisonivy_23x -c -w admin"
Warning Legacy Module poisonivy_23x!
Starting ChopShop (Created by MITRE)
Initializing Modules ...
Initializing module 'poisonivy_23x'
Running Modules ...
[2015-09-04 04:43:44 EDT] Poison Ivy Version: 2.32
[2015-09-04 04:43:44 EDT] *** Host Information ***
PI profile ID: ctf
IP address: 192.168.0.100
Hostname: ADMIN-PC
Windows User: Administrator
Windows Version: Windows XP
Windows Build: 2600
Service Pack: Service Pack 3
[2015-09-04 04:43:58 EDT] *** Directory Listing Initiated ***
Directory: C:\WINDOWS\
[2015-09-04 04:43:58 EDT] *** Directory Listing Sent ***
[2015-09-04 04:44:57 EDT] *** Service Listing Sent ***
[2015-09-04 04:45:06 EDT] *** Screen Capture Sent ***
PI-extracted-file-1-screenshot.bmp saved..
Shutting Down Modules ...
Shutting Down poisonivy_23x
Module Shutdown Complete ...
ChopShop Complete

接着在文件夹下就能够发现一个bmp里面存着flag

Analysis/Offensive 200 - VirusClick

这个题是apk的逆向,之前比赛的时候不是自己做的,结束以后自己又看了一下,熟悉了apk repack的一个基本流程

之前提到的jeb是可以用的,不过好像不能repack改过的代码。还是使用apktool比较好。

安卓逆向的话需要对Dalvik虚拟机的这种smali代码有一些了解,不过也可以通过smali的方式编译java代码。

首先apktool解出apk里面的代码

1
2
3
4
5
6
7
8
9
10
11
12
$ apktool d VirusClick.apk
I: Using Apktool 2.0.1 on VirusClicker.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/xxxx/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

其中的d表示decode的意思,接着在相同的位置就可以找到文件夹。

这时候另一方面可以通过jeb来反编译成java分析一下程序行为,这个VirusClick需要点击10M下然后打印出flag,稍微研究一下发现处理点击的activity是c,最后会切换到CongratulationsActivity。而在这其中还会有几次的校验,每次校验都会补上几个字符,最后将整个字符串base64 decode得出flag。通过纯逆向来拼凑最后的字符串比较麻烦,这里可以直接修改代码,将点击和校验的数字改小,并且去掉一些不会影响字符串的校验。直接修改smali文件,然后执行apktool重打包

1
2
3
4
5
6
7
$ apktool b VirusClick
I: Using Apktool 2.0.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...

这里的b表示build,注意后面接的是之前生成的目录名,pack好的apk放在VirusClick/dist文件夹中。

这时需要对这个apk做个签名,我使用auto-sigh这个工具进行,其他类似的工具还有很多比如jarsinger等。

接着把apk放到模拟器或者真实的android设备里运行就可以了。