趋势科技举办的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 process /tmp/ <program /lib64/ TMCTF{ce5d8bb4d5efe86d25098bec300d6954}[Inferior /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 from zio import *import reimport romandef 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 import Imageimport refrom urllib2 import urlopenstart_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 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) 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) next_link = re.findall(r'window\.location\.href=\'([/a-z0-9]+?)\?x=\'' , html)[0 ] img_link = re.findall(r'<img src="([^"]+?)"' , html)[0 ] print '[+] next stage : %s' % next_link print '[+] picture at %s' % img_link 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 3 Y3s!}
小结 可以说这次的收获还是挺大的,不仅拿到了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设备里运行就可以了。