uaf
HITCON-training - lab 10 hacknote
存在uaf漏洞
note的结构是,有两个属性put和content
puts函数存放的是print_note_content函数指针,输出的时候会调用这个函数,content属性是存放内容的堆指针。
存在后门函数magic
所以,只要修改puts属性为magic属性,那么在show的时候,就会调用magic函数,get flag。
申请一个堆块,会建立两个堆,一个存放输出信息,一个存放内容
这里,0x11的是notelist结构题,存放的是puts和content两个指针。
如果我们把这两个堆块都释放了,那么他们都进入了fastbin
如果此时我们申请一个和notelist长度一样的堆块,那么它会把原先的两个0x11给我们,一个作为notelist一个作为content,但是content我们是可控的,放入magic,再输出,即调用magic函数。
exp
1 | from pwn import * |
以前写的:https://blog.csdn.net/qq_43935969/article/details/104730157
unlink
2014_hitcon_stkof
libc
如果题目中给出了libc文件,用ida打开,搜索字符串,搜索version
如果题目没有给出libc文件,需要一个个尝试
堆的话试试UAF啥的
要么2.23、要么2.27、2.31就喷他
栈溢出那就leak一下就行了
got表和plt表
- .got
GOT(Global Offset Table)全局偏移表。这是「链接器」为「外部符号」填充的实际偏移表。
- .plt
PLT(Procedure Linkage Table)程序链接表。它有两个功能,要么在 .got.plt
节中拿到地址,并跳转。要么当 .got.plt
没有所需地址的时,触发「链接器」去找到所需地址
- .got.plt
这个是 GOT 专门为 PLT 专门准备的节。说白了,.got.plt 中的值是 GOT 的一部分。它包含上述 PLT 表所需地址(已经找到的和需要去触发的)
我们说的覆写plt表指的是.got.plt,通过调试得出
第一个add
没有setbuf,把输入输出缓冲区申请好。
globals(head)
所有chunk存储的位置
chunk开始的地方
v2是分配的数据指针,而v2是存储在bss段上的,没有开启pie,所以是不变的。
堆溢出
edit函数中,因为修改的长度是自己输入的,那我想改多少改多少,存在堆溢出
unlink的具体操作
1 | alloc(0x30) # idx 2 |
分配两个chunk,对第一个chunk进行修改,其中bk和fd放成target - 12
和target - 8
20是因为-12和-8是伪造一个堆块,大小为0x10,被释放了,所以,检查机制会看下一个堆块的prev_size
30和90是下一个chunk的头,也就是上chunk3的上一个chunk在chunk2,90表示被释放了
unlink的结果
所以,接下来往chunk2写入数据就会从0x602138开始。。。
注意的是:0x602140仍旧是global[0]的起始地址,那么,覆盖40-58的地址,用global[0\1\2]就可以调用到。
执行结果如下:
got表
所以,现在edit(0),就会往第一个地址里面写入数据。
如果第一个是某个got表地址,那么写入的话,got表里面的内容就可能被替换掉了。
free(1)
因为这里,第一个指针地址是puts的got表地址(参数),free的got表也被替换为puts的plt表(原来的执行plt实际上就是执行.got.plt,所以一样),所以执行free函数的话,执行的是put.plt,参数是puts的got表地址
edit(2, len(payload), payload)
这里修改的2就直接是88,atoi的got表里面的数据,然后继续执行的时候,会让输入,把binsh给他们就行。
exp
1 | from pwn import * |
2016 ZCTF note2
对于程序自己重写read等功能,要看一下,因为很可能就是做了一定的变动,导致可能存在某个漏洞。
本题中主要是ReadStr这个函数,由于是i是无符号数,所以在比较len - 1 > i
时,会把它转换为无符号数,如果len = 0,那么长度就变成了0xfffffffff,如果ReadStr(note, size, 10);
就可以往note里面写入很多的数据,造成堆溢出。
漏洞分析
本题的主要漏洞就在于这个点,利用堆溢出,可以实现unlink功能。
堆块的指针存在于ptr,地址为0x00602120
首先申请一个块,由于有大小限制,就申请最大的0x80,在里面伪造块
1 | ptr = 0x00602120 |
因为只有大小为0的才能栈溢出,所以我们申请一个大小为0的块作为中介。
但是 glibc 的要求 chunk 块至少可以存储 4 个必要的字段 (prev_size,size,fd,bk),所以会分配 0x20 的空间。
最后再申请一个正常的堆块,0x80。
这时,堆布局如下:
因为大小为0的堆块只有在add的时候,读入才堆溢出。所以我们先把它释放,再重新申请,因为大小一样,会把释放掉的给我们重新分配回来。
这时候,就可以把下一个堆块的头给覆盖了。
1 | free(1) |
因为是释放chunk3,却要让它unlink chunk1,所以prev_size要是和chunk1的距离,这样,chunk3会根据自己的地址 - prev_size找到前一个chunk是chunk1。
然后free2,就成功unlink了。
然后,就可以正常了。
覆盖指针的地址为atoi_got表地址,show泄漏他的地址,找到libc基址,找到system的地址,然后覆盖原来的atoi_got的地址,然后执行的atoi函数的时候,发送‘/bin/sh’即可,这里可以是它的地址也可以是字符串。
exp
1 | from pwn import * |
unlink总结
- 要伪造堆块
- 可以溢出覆盖下一个堆块的头
- unlink是函数指针
unlink先到这里,,等有空再做剩下几题。。。
花式栈溢出技巧
stack pivoting(栈迁移)
第一题是直接用jmp esp进行栈迁移的
题目很简单,一个栈溢出,但是溢出字节不是很多,只有50 - 0x20-0x4(ebp) = 14个字节,难以利用。
因为这里是存在栈上而不是bss段上,所以不能用ret2shellcode(不知道输入的具体地址)
所以
fastbin
2017 0ctf babyheap
64位的程序,保护全开
填充内容的时候,长度是重新输入的,可以填充任意长度,造成栈溢出
没有uaf
思路
主要的漏洞:任意长度堆溢出
利用unsortedbin地址泄漏libc基地址
利用fastbin attack将chunk分配到malloc_hook附近
具体过程
要利用unsortedbin泄漏,所以,要让两个块同时指向unsortedbin地址。
第一部分:
所以,一开始,首先将大小为0x80的块同时被认为是chunk2。方法是释放chunk2和chunk1,修改1的fd(原本指向chunk2),现在修改为chunk4,那么申请回来的时候chunk2实际是chunk4的内容。这里为了绕过检查,要让chunk4的大小为0x10。
接着,让chunk4的地址变回0x80,为了防止和top chunk合并,多申请一个chunk5,接着释放chunk4进入unsortedbin(chunk被释放后,如果大小不再fastbin内,会先放到unsortedbin中),chunk4里面的指针指向unsortedbin的链表头,用它可以计算出main_arena和libc的地址
第二部分:
因为malloc_hook附近有0x7f可用,找到一个合适的地方,申请堆块到这里,然后覆盖malloc_hook为one_gadget地址,再malloc任意值即可得到shell。
1.前期申请
5个chunk
1 | add(0x10)#0 |
1 | pwndbg> x/40gx 0x55b474ad1000 |
2.令另一个chunk分配到chunk4
1 | free(2) |
链表:
1 | fastbins |
本来chunk1是指向chunk2的,修改最低8位,(因为开了pie,但是最低3字节不变),这样chunk1就指向chunk4了。
1 | payload = p64(0) * 3 + p64(0x21) + p8(0x80) |
1 | pwndbg> bin |
接着修改chunk4的大小,方便绕过检查
1 | payload = p64(0) * 3 + p64(0x21) + p64(0x21) |
这样接着申请两个0x10的堆块时候,一个是原来的chunk1不变,chunk2变成了chunk4
1 | add(0x10)#1 |
以后,使用chunk2,实际的操作在chunk4里面
3.将chunk4放入到unsortbin中
只要修改大小为0x80,然后释放掉即可。
这里为了防止与top chunk合并,所以在释放申请前,再申请一个chunk5
1 | payload = p64(0) * 3 + p64(0x91) |
现在,chunk2和chunk4指向相同的地址,而chunk4是unsortedbin中唯一的chunk,fd指针指向的是unsortedbin的链表,用chunk2就可以打印出它
1 | dump(2) |
由此计算出main_arena和libc基地址(0x58和0x3c4b20都是固定的libc-2.23.so)
main_arena_offset可以使用工具:https://github.com/Coldwave96/LibcOffset
1 | main_arena = unsortedbin_addr - offset_unsortedbin_main_arena |
4. 伪造堆块
main_arena上面就是malloc_hook,并且那些地址的开头都是0x7f,所以需要chunk块大小为0x60。
我们申请0x60的块即可,因为小于原来的0x80,会自动分割为两个:0x60和0x10(头,,),
有三个0x7f,但是第一个太近了,无法完全覆盖malloc_hook,第二个,前面没有7个0x00,不符合要求,所以是第三个,地址是main_arena-0x33
1 | add(0x60)#4 |
5.申请伪造的堆块
接着申请两个chunk
第一次申请的是chunk4,第二次申请的是chunk4的fd指针指向的地址,也就是我们伪造的堆块
1 | add(0x60)#4 |
6.在malloc的地址填入one_gadget
one_gadget libc文件(本地在/lib/x86_64-linux-gnu/libc-2.23.so
),有四个一个个试呗,第二个就行。
1 | one_gadget = libc_base + 0x4526a |
完整exp
1 | from pwn import * |