0x00 前言
在glibc2.34或更高版本,没有了hook用来劫持,所以一般会用IO之类的路子来劫持。如果条件允许,还可以考虑劫持劫持libc的got表来getshell。libc一般是partial relro,所以got表可写。但准确来说,接下来的PoC,其实针对的是劫持.got.plt段执行one gadget而设计的。劫持libcgot的技术还可以结合context的gadget实现rop,后面再深入研究。
在glibc2.34或更高版本,没有了hook用来劫持,所以一般会用IO之类的路子来劫持。如果条件允许,还可以考虑劫持劫持libc的got表来getshell。libc一般是partial relro,所以got表可写。但准确来说,接下来的PoC,其实针对的是劫持.got.plt段执行one gadget而设计的。劫持libcgot的技术还可以结合context的gadget实现rop,后面再深入研究。
最近给战队出了一道pwn题,第一次体验完整的出题流程,涉及到docker的使用。因为是第一次使用docker来封装题目环境,所以遇到了很多问题。网上有一些关于出pwn题的使用方法,但是有些问题没有提到,所以这里记录一下我遇到的问题,还有解决方法。docker的安装网上教程很多这里不多赘述。
顺带一提,我的操作环境是wsl2的Ubuntu 22.04.3 LTS,若有因环境不同而引起的操作不同,具体请查询其他资料。
首先每个Large Bin中存在63个bin链表,在binmap中的index是64-126。每个bin中存的是一定范围内大小的chunk,而不是像tcachebin那样的一个bin一个大小。举个栗子,index64的bin中存的是0x400到0x430的chunk。每个bin能存取的范围一般是0x30。
Large Bin既不是FIFO也不是LIFO,它的排序是根据chunk大小来进行的,并且结构更加复杂。large chunk被释放的时候不仅会被写入fd和bk,还有fd_nextsize和bk_nextsize两个指针来维护bin的结构。fd和bk用来链接bin中相同大小的chunk,而nextsize则用来链接bin中不同大小的chunk。更具体地说,fd
指向比自己晚释放的相同大小的chunk,bk
则指向比自己晚释放的相同大小的chunk,fd_nextsize
用来指向比自己大的chunk,bk_nextsize
则指向比自己小的chunk。在相同大小的chunkbin中只有首堆块会有nextsize的指针。bin中首尾chunk的nextsize会指向另一端,首堆块的fd会指向对应index的bin头地址,尾堆块的bk会指向对应index的bin头地址。
虽然说是新生赛,但是五道堆题估计真新生都被吓傻了。实际上题目限制非常宽松,也正好可以拿来总结各个常见版本glibc的基本特点。因为题目除了2.39之外都一样所以就先分析题目,再来看不同版本下的做法。
1 | from pwn import * |
线程退出的时候,需要释放掉这个线程所占有的资源,并且停止内核和CPU对其的调度,才算是结束了这个线程。换句话说,线程退出需要用户层面和内核层面的共同工作。内核层面,glibc通过_exit()系统调用来终止线程,用户层面则通过exit()函数。在pwn中,系统调用很难被利用,我们通常重点关注exit函数。
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
main函数开头要我们绕过一个随机数检查才有输入点。gift函数会打印puts函数的libc地址,libc地址不请自来。接着read函数溢出14个字节,只能覆盖六个字节到ret地址,所以想要在这里写one gadget是不可能的了,因为libc地址占七个字节。
x86架构的ret2text,非常简单,程序有栈溢出,没有canary保护,所以只要溢出覆盖ebp后,将ret地址覆盖为ret2win函数的地址即可。
1 | from pwn import * |