PWN堆house of spirit-1
- 参考博客:好好说话之Fastbin Attack(2):House Of Spirit_fastbin attack house of spirit-CSDN博客
- 参考书籍:杨超的《CTF竞赛权威指南——pwn篇》
- 用到的仓库:github的how2heap仓库
前言
-
学习了
UAF
、double_free
、unlink
、off-by-one
,之后基本上对堆稍微有点了解。 -
个人感觉,堆漏洞的成因就是
UAF
和堆溢出
,而栈的漏洞的成因基本上就是栈溢出
,其他的就是格式化字符串漏洞
,漏洞的成因感觉就是这几种。但是利用方式很多,所以学来学去都是在学被人挖掘出来的利用方法。在学这些利用方法的时候会不由感慨,那些开创漏洞利用的人脑洞真大。所以怎么感觉pwn到最后变misc
那些脑洞题了… -
初步了解了一些漏洞成因和漏洞利用后,就可以开始学习
house of
系列的利用方法了。目前打算主线直接学习house of
系列,按照Glibc堆利用之house of系列总结 - roderick - record and learn!这篇文章的顺序学习,学到哪些利用,之前没涉及到的再分出支线去学习。 -
堆的漏洞利用可以进行以下三种分类:这些分类相互有交集,并不互相独立,目前我做的是使用
house of
系列进行堆利用的分类,等house of
系列学习完之后,有时间再进行其他的分类glibc
版本对应的漏洞利用,具体可以看how2heap
这个github仓库shellphish/how2heap: A repository for learning various heap exploitation techniques.house of
分类的漏洞利用bins_attack
以及其他的attack
分类利用
house-of-spirit介绍
house-of-spirit
是堆利用house-of
系列的一种方法。这种方法目前只了解到是针对fast_bin
的attack和tcache_bin
的攻击,所以这个利用方法将被分成两个部分,第一个部分是介绍glibc2.26
之前的还没有引入tcache_bin
的攻击。第二部分是介绍glibc2.26
之后引入了tcache_bin
后针对tcache_bin
的攻击- 在学习
house-of-spirit
之前首先要对Linux
下的堆管理器即ptmalloc2
有一定的了解,还要知道UAF
漏洞、double_free
漏洞以及off-by
的两种漏洞,能简单利用UAF
和double
、这个两个漏洞对fast_bin
进行攻击和利用。这样就可以进一步学习house-of-spirit
。 - 对于
house-of-spirit
这个漏洞,与对堆管理器中unlink
的攻击关系其实不大。可以根据自己的节奏去选择学习顺序。 - 本次介绍的是对
glibc2.26
之前的版本还没引入tcache_bin
,针对的是fast_bin
的攻击。
利用方式
- 先来比较直观和简单地介绍一下
house-of-spirit
的利用方式。这个利用方式是double_free
和UAF
漏洞的进一步利用。 - 首先
house-of-spirit
的堆利用方式,是一种用于获得某块内存区域控制权的技术(即你可以实现对该块内存任意写入数据)。 house-of-spirit
是通过伪造一个堆块fake_chunk
,并且有一个指针ptr
指向该fake_chunk
,加上我们可以使用free()
函数,将指向这个fake_chunk
的ptr
指针给free
掉,这时该fake_chunk
的就会被放入fast_bin
对应大小堆块的链表头部。当我们再次使用malloc函数使用一个堆块,这时fake_chunk
就会被申请出来,被用户使用,这样我们就可以对该fake_chunk
进行写入或者修改数据。- 接下来通过图片来直观感受一下
house-of-spirit
的这个利用方式。 - 如图
fast_bin
链表这边有两个空闲的真chunk
,并且都放在了fast_bin
中。
- 这时我们在某个内存空间中(可能是
.bss
段、栈上
或者其他内存空间)伪造了一个chunk,称为fake_chunk
,并且有一个指针ptr
指向该fake_chunk
- 恰好这个程序中的
free
函数能释放ptr
这个指针。所以当我们释放ptr
这个指针后,就会将这个fake_chunk
放入fast_bin
这个链表中(该链表采用头插法)
- 当我们再次使用
malooc
函数申请适当大小的内存,就可以将这个fake_chunk
申请给用户写入数据。
- 以上就是
house_of_spirit
的利用过程,现在来总结一下house_of_spirit
的利用方式,以及house_of_spirit
与fast bin double free
的区别houser_of_spirit
是通过伪造一个堆块fake_chunk
(该堆块并不是通过malloc申请回来的),利用free
的机制将该fake_chunk
放入fast_bin
链表之中,再次通过malloc
将该fake_chunk
申请,从而实现任意内存读写- 而
double freee
是将本来通过malloc申请的堆块chunk1
(该堆块是合法的),通过两次释放chunk1
,将同一个堆块即chunk1
放入两次到fast_bin
中,这样我们第一次申请chunk1
,就可以修改chunk1
中的fd
指针,使其指向任意内存地址,当我们再次申请chunk1
时,这个任意内存就会被放入我们的fast_bin
链表之中,再次使用malloc
申请,我们就可以申请到任意内存并对该内存进行读写。 - 区别:
house_of_spirit
是构造fake_chunk
想办法放入bin
中链表,通过再次申请达到任意地址读写。而double
是通过合法chunk的两次释放和两次申请,从而在第三次申请chunk时能申请到任意地址,对任意地址读写
伪造条件
-
由于
free
的时候会对堆块进行检查,即验证堆块的合法性,所以我们在伪造堆块的时候就要满足一定的条件,这样才能绕过检查,继续进行house_of_spirit
的利用。 -
接下来说明一下伪造需要满足的条件:
fake_chunk
标志位、fake_chunk
的内存地址对齐、fake_chunk
的size大小、fake_chunk
的next_chunk
的大小、fake_chunk
的连续释放(即double_free的错误) -
ptmalloc
所申请的堆块,数据结构如下:
fake_chunk的标志位:
- 在伪造一个
fake_chunk
的时候,由于堆块结构中的size
部分的最低三位是标志位,所以这些标志位要满足一下条件:P
标志位设置为1
:该标志位表示的是该堆块物理地址上相邻的前一个堆块是否处于空闲状态,1
表示处于使用状态。当标志位设置为0
的时候就会在free
的时候会触发unlink
机制,导致合并一个不存在的地址空间,会引发程序的崩溃。有些题目好像不满足也可以,对P标志位要求没那么严格M
标志位设置为0
:M
标志位表示的是IS_MAPPED
,记录当前chunk是否由mmap分配。当这个位置的标记设置为1
,就表示这个堆块内存时调用mmap
分配的,在处理这个chunk的时候就会单独处理。N
标志位设置为0
:一般攻击的都是主线程下的堆(目前还没有打过多线程的堆题),所以该标志位应该被设置为0
fake_chunk的内存对齐:
- 堆内存在申请的时候都是8字节对齐或者16字节对齐的,也就是说
fake_chunk
的prev_size
的最低位地址、P
标志位对应的地址应该为0xXXXXXXX0
。也就是需要16字节对齐。(32位程序下是0xXXXXXXX8
,也就是8字节对齐) - 在
free
的时候会对内存是否对齐进行检查,如果检查到所free的堆块内存没有对齐就会出现报错,或者是程序无法运行下去。
fake_chunk的size大小:
fake_chunk
是要放入fast_bin
中的,所以我们的size
大小就要满足挂入fast_bin
链表的要求,即size
的大小要小于0x80
字节- 如果要放入
tcache_bin
中就要满足放入tcache_bin
中的size
大小 - 还需要注意一点就是
size
的值也必须是16字节对齐
,即满足0x10
的整数倍(32位程序要满足0x8
字节对齐)
fake_chunk的next_chunk的大小:
- 所谓
fake_chunk
的next_chunk
指的就是与fake_chunk
物理地址上相邻的且地址高于fake_chunk
的一段内存空间(因为是伪造的堆块,所以该段内存空间其实不是堆块) - 当我们释放
fake_chunk
的时候,会有一个检查机制,ptmalloc2
会通过,如下计算确定next_chunk
的地址,并对其size
进行检查
1 | next_chunk_address = fake_chunk_address + fake_chunk->size |
- 所以
next_chunk
的size
大小应该为满足以下两个条件:size
要满足0x10
字节对齐,即必须为16字节的整数倍(32位系统则是0x8
字节对齐)size
还要满足小于128kb
- 这样设置的目的就是在
chunk
连续释放的时候,能够保证伪造的fake_chunk
在释放后能够挂在fast_bin
中的main_arena
的前面。这样我们再次使用malloc
申请适当大小的堆块时,就会直接申请到fake_chunk
fake_chunk的连续释放:
- 在
double_free
中如果直接连续free
相同的堆块,程序就会因为检测到double_free
而无法运行,所以我们在进行double_free
利用的时候就释放一次目标堆块后,要释放另一个堆块,之后才能再次释放目标堆块。这就是需要我们绕过double_free
的检查机制 - 所以
fake_chunk
在释放完一次后,不能马上再进行释放,这样就会导致double_free
检查不通过,导致程序无法运行。
总结:所以我们要构造的fake_chunk
要满足如下条件
实验
- 本实验的代码时在
how2heap
中glibc2.23
版本的house-of-spirit
,并通过阅读程序,模拟出攻击流程 - 我将其源码进行汉化,源码如下:
源码
1 |
|
1 |
|
- 该程序先调用了
malloc(1)
开辟了一个堆块内存,初始化了一下堆块内存。如果没有malloc(1)
在gdb动态调试的时候就会出现堆块没有初始化的提示。
- 使用
malloc
后在栈上创建了一个多的数据,进行堆块的伪造。
- 然后根据输出提示,设置了伪造堆块的相关数据。
- 使用gdb进行动态调试后就会看到栈上的数据是这样分布的,并且现在
fast_bin
是空的
- 之后我们通过
free(a)
即free(&fake_chuns[1])
,就把伪造的堆块放入到了fastbin链中。这样就完成了house of spirit
的利用。
题目
house_of_spirit_level_1
- 题目来源:lctf2016_pwn200,在buuctf上有相应的环境,附件也可以从那边下载。这边我使用patch的方法,使得我在
wsl
的环境下使用glibc2.23
进行动态调试,这样就可以避免glibc
版本过高的原因从而导致出现tcache_bin
的问题
level_1分析1
- 现在先使用
IDA
对该程序进行静态分析,在静态分析的时候运行程序,这样能更好的理清楚程序的运行逻辑。先check
一下该程序的保护机制。发现保护机制开的很少。
- 然后使用IDA对该程序进行逆向分析。先来先查看
main
函数,这里的main
函数比较简单先是对输入输出进行初始化,然后再调用一个func
函数。
- 之后查看
func
函数,结合程序运行的结果看,这个函数会先输出提示who are u
,然后逐个字节的读取用户的输入,知道遇到换行符。还会将用户输入打印出来,并且提示输入id
,之后就是调用两个目前还不知道什么功能的函数。
- 之后将
sub_4007DF
这个函数重新命名为func2
,将sub_400A29
这个函数重新命名为func3
,再进入func2
进行查看函数的具体操作。- 该函数先是逐个字节读取用户的输入,一共读取4个字节或者遇到换行符结束读取。
- 在读取的时候还会对读入
id
进行检查,检查该id
,如果输入的id
不在十进制的数字里面,那么就不会执行接下去的代码。 - 之后还会使用
atoi
函数,该函数将我们输入的id
原本是字符的形式,将其转换为整型的形式,并返回给v2,还会将该值作为返回值返回。
- 之后再来查看
func3
的函数,执行的具体功能。该函数实现以下功能:- 开辟一个
0x40
大小的堆内存,将malloc
返回的地址给dest
变量 - 输入提示字符串
give me money~
,提示我们输入0x40
字节大小的数据,这里并不存在栈溢出 - 之后我们会把输入的数据原来存储在
buf
中,现在复制到dest
这边,即复制到malloc
开辟的堆内存这边。 - 最后将
dest
的值(即堆块的地址)赋值给ptr
这个指针,注意ptr
这个指针是存储在.bss
段上的全局变量这个可能有用 - 之后会调用
sub_4009C4
这个函数,将该函数命名为func4
- 开辟一个
- 接下来查看
func4
函数里面的运行过程,发现该函数就是一个经典菜单的形式。- 先会调用
func2
这个功能,实现用户对选项的选择。 - 输入
1
的时候就调用check_in
这个函数 - 输入
2
的时候就调用check_out
这个函数 - 输入
3
就退出该程序
- 先会调用
- 接下来先查看
check_out
这个函数,该函数实现的功能是释放掉全局变量指针ptr
所指向的内存地址,并将该ptr
指针置零,从而避免UAF
的漏洞利用。
- 接下来查看
check_in
函数- 该函数会先检查
ptr
是已经指向一个指针,如果已经指向一个指针,那么程序就不会执行后续的语句 - 通过
func2
读取数字,并判断读取的数字是否在0-128
范围内 - 之后使用
malloc
函数申请一个前面所读取数字大小的堆块。 - 最后可以向所申请的堆块写入数据。
- 该函数会先检查
level_1分析2
- 现在对该程序进行动态调试,查看该程序是否有溢出点或者其他的漏洞。
- 对于
func
函数这边,存在一个off-by-one
的利用,可以通过将v2
这个数组填满48个字节,然后就可以避免换行符,这样这边就没有\x00
对进行截断,这时printf
就会将rbp
所指向的栈地址的存储的数据泄露出来,从而知道了栈的地址。
- 接下来看
func3
这个函数,这个函数在会使用read
函数在栈上写入0x40
个数据,但是buf
只有56
字节(即0x38)的栈上存储空间,这时最后的0x8
字节就会把dest
这个指针给覆盖掉。- 所以
func3
存在一个溢出,可以修改dest
的值,并且之后会把dest
赋值给ptr
这个全局变量的指针。 - 在分析1中可以知道
free()
函数free
的是ptr
指针所指向的地址。
- 所以
level_1分析3
- 这个时候我们即泄露了栈地址,又能通过溢出修改
dest
这个指针,从而间接修改ptr
指针。现在思路就比较明显,既然可以泄露栈地址,那么就可以利用栈伪造一个fake_chunk
。通过house-of-spirit
这个对利用技术,将栈上的堆放入到fast_bin
中,在申请回来这样就可以对比较大块的栈地址进行写入。一般就可以修改返回地址。接下来要查看一下如何伪造这个堆块。栈上有什么数据可以提供我们伪造堆块。 - 对于
func()
函数这个输入点进行分析,发现并不能使用func()
函数这个输入点进行堆块的伪造,这是因为在堆块的伪造过程中必然会出现\x00
这个截断操作。这就导致我们在使用printf
函数进行格式化输出的时候栈上的地址泄露不出来。
- 对于
func2
可写入栈上的数据有限,也没办法完成对堆块的伪造,所以先来分析一下func3
是否能在修改指针的时候对堆块进行伪造,发现这是可以行的,这时我们在栈上进行堆块的伪造,并不会影响我们ptr
指针的修改
- 所以我们选择该此处对堆块的伪造,然后我们再来分析一下栈上高地址处是否可以有
0x10
整数倍的,并且小于128kb
大小的,还要满足prev_size
的最低位为0xXXXX0
这样我们就可以伪造出fake_chunk
并且成功的把堆块给链到fastbin
上。- 查看栈后发现这个地方明显是可以被控制的,而控制这个栈数据的也就只能是
func
或者func2
的执行过程。 - 我们发现这个数据是存储在
func
函数输入的更低地址,所以应该是在func
这个函数控制的。
- 查看栈后发现这个地方明显是可以被控制的,而控制这个栈数据的也就只能是
- 我们查看
func
的变量时,发现逐字节输入,控制循环的变量i
最终的结果为0x30
(存储位置是位于更低地址的栈那边),但是这个0x30
我们希望用来伪造size
,可是这个栈地址是0xXXXXX0
开始的,这并不能满足堆块内存对齐的问题
- 这时还有一个
0x30
是怎么来的,对于反编译的代码是没有体现出来的。我们需要去看func
的汇编代码,这时我们发现调用完func2
后,存储返回值的寄存器rax
会有一个操作,也就是将func2
的返回值复制到栈上。 - 而
func2
返回的也就是我们输入数字对应的整数。所以我们要伪造该堆块,在func
调用func2
时,就要像这边输入48
即0x30(或者其他符合要求的数据。)
- 这样我们就满足了伪造堆块的条件,现在我们要通过栈地址计算我们所申请堆块的
size
大小,和我们要伪造堆块的size
- 还有我们要修改的指针是指向
0x7ffcddf7d710
,但是我们泄露的指针是泄露到更高地址。所以我们还要计算偏移,所以我们最后会将指针修改为leak_addr-0x110+0x60
- 通过伪造堆块我们就可以释放该堆块,将该堆块放到
fast_bin
链上,这时编写部分exp
进行动态调试查看fake_chunk
是否被添加到fast_bin
链表上
1 | from pwn import * |
- 这时我们就发现我们伪造的
fake_chunk
被加到了fastbin
上
- 这时我们再使用
malloc
申请回来,我们就可以对该栈进行写,并且还可以修改返回地址。之前在查看保护机制时,有注意到栈是具有可执行权限的,所以我们就向我们申请的堆块写入shellcode
,并修改返回地址到栈上我们写入shellcode
的开头。这样就可以getshell
了
level_1_exp
1 | from pwn import * |
house_of_spirit_level_2
- 题目来源:2014 hack.lu oreo,再次挑战该题,被这题薄纱了俩次QAQ
- 题目在该链接的github仓库有:https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2014_hack.lu_oreo
- 这题主要之前没打出来还是因为在Docker环境中这题不知道为什么在
exp
中gdb.attach()
动态调试不了,不能边打边动调,就发现不了原因。然后之前的house_of_spirit
也没理解到位。今天就不用Docker环境,直接在ubuntu22.04
做了一下,发现也可以打tcachebin
对这题没啥影响,所以就使用ubuntu22.04
的环境进行动态试和打了。 - 又打了一下发现还是不行,还是要去Docker环境打,下次如果遇到Docker环境不能动调的,要去试试一下
patchelf
了
level_2分析1
- 先来查看一下保护机制,发现是一个
32
位的程序,并且开启了canary
保护,但是没有开启PIE
和RELRO
保护。所以可以修改got
表
- 然后使用IDA对该二进制文件进行逆向分析。先来查看
main
函数。main
函数的逻辑如下:- 先初始化了三个全局变量,其中
dword_804A2A4
和dword_804A2A0
先初始化为0
dword_804A2A7
指向的是unk_804A2C0
的这个位置- 之后输出字符串,之后执行一个函数,为了方便代码审计,将该函数命名为
func()
- 这边还要注意一个问题,这个程序并没有进行初始化输入输出,所以该程序并不是
0
缓冲。程序一开始会申请一个比较大的堆块作为缓冲区
- 先初始化了三个全局变量,其中
- 现在来查看一下
func()
这个函数中的内部。发现是一个堆菜单的和选项。switch()
函数这边调用了一个函数,用于输入选项,这里将其重新命名为choose()
,其作用就是让用户选择操作对应的选项。- 之后选项
1
就是add
操作,所以将选项1
所执行的函数命名为add()
。选项2
是show
操作,所以将选项2
所执行的函数命名为show()
- 选项
3
对应的是order
操作,将选项3执行的函数命名为order()
- 选项
4
对应的是编辑一个message
,所以将选项4执行的函数命名为edit_message()
- 选项
5
对应的操作就是展示add
和order
的状态。所以将选项5执行的函数命名为stats()
重命名的结果如下 choose()
函数的功能在下图
- 之后来查看一下
add
函数的具体功能- 首先有一个指针变量(全局变量存储在
.bss
)段,将该变量重新命名为ptr
。该ptr
会先把之前的值存储在v1
中,然后该ptr
会重新指向一个新申请的堆块(该堆块申请大小为0x38
,实际上该堆块的size
为0x40
)。 - 申请成功后会像该堆块的
0x3c-0x3f
(算上chunk头)字节处存储存储着v1
变量的值,即前一个申请堆块的地址。 - 之后会从该堆块的
0x21
(算上chunk头)字节处写入数据,作为Rifle name:
,这里会存在一个溢出。会进行一个操作,将换行符变成\x00
这个字符(这个是通过sub_80485EC()
进行的操作) - 之后会从该堆块的
0x8
(算上chunk头)字节处写入数据,作为description:
,这里貌似也存在溢出,也可以修改指向前一个堆块的指针(不过用前面的就足够了)。写入后也会将末尾的换行符替换成\x00
。 - 最后有一个全局变量会自增,方便查看程序,就将该全局变量重新命名为
Rifle_count
。
- 首先有一个指针变量(全局变量存储在
- 从
add()
函数中就可以了解到我们所申请堆块的结构是向这样的。
- 接下来查看
show()
,这个show()
函数会按照新申请的堆块到旧申请的堆块这样的一个顺序,逐条打印出Rifle name
和description
- 之后查看
order()
函数,该函数的执行流程如下:- 会将
ptr
指针的值赋值给v1
,检查Rifle_count
是否存在 - 当
Rifle_count
有的时候就会按照从新申请的堆块到旧申请的堆块这个顺序,逐个释放指针 - 之后
order_count
这个全局变量就会自增。
- 会将
- 查看一下
edit_message
函数,这个函数就会向message_ptr
指向的地址读入128
字节,最后还会调用sub_80485EC()
将末尾的换行符修改为\x00
- 之后查看一下
stats()
:- 这个函数就是输出
rifles
的状态,如果有message_ptr
,也会输出该message_ptr
所指向的值。
- 这个函数就是输出
- 逆向分析一遍程序后,我们再来查看
main
函数的4个全局变量,现在就比较清晰了
level_2分析2
- 静态分析结束后,我们来进行动态调试。既然是
house_of_spirit
,所以我们一定是要通过溢出来修改我们的指针,使其指向我们伪造的堆块,这样就可以将伪造的堆块释放到这fastbin
链表上面去。 - 由于这个函数没有后门函数,所以我们肯定是要去打
libc
。所以我们要先泄露一下libc
的地址。我们可以先申请一个堆块,通过溢出修改char_ptr
的值,使其指向printf_got
表,这样我们就可以泄露libc
的地址。在我们打印第二个堆块的name
和des
的时候我们接收到的des
开头的几个字节就是printf
函数的地址
-
这样我们就把
libc
的地址泄露出来了,接下来就需要伪造一个堆块,然后将该堆块释放,使其能加入fastbin
链表,之后我们就可以申请该堆块,对该堆块的内容进行任意修改。 -
这题伪造堆块的地方有两处。这俩处都在
.bss
段上,但是有一处伪造的堆块,我们之后申请该堆块是对我们后续没有用的。 -
这两处所在地方如下图所示。
- 这两处其中
第一处
伪造比较麻烦,第二处刚好是我们message
存储的地方,我们可以对该地方进行任意写的操作所以比较好伪造。 - 但是对后面的利用有用的其实要伪造
第一处
,因为第一处我们伪造释放后,重新申请回来,我们可以修改message_ptr
的值,使其指向got
表的地址,然后我们可以劫持某个函数的got
表,将其修改为system
函数的地址。这样就可以执行system("/bin/sh")
从而getshell
。而第二处
- 这两处其中
- 所以我们就需要对
第一处
进行堆块的伪造,这样之后才能利用我们所申请的堆块。由于堆块的内存对齐机制,我们要从0x804A2A0
开始伪造prev_size
字段,这样才能满足对齐的要求。而我们申请的堆块size
位都是0x40
大小,所以我们要让我们伪造的堆块size
位为0x40
(这里最好是0x40
)如果是0x41
的话之后我们再申请一个堆块size
位可能会变成0x42
可能会造成程序的一些问题。
- 这时我们在泄露
libc
的地址后还要申请0x3F
个堆块,我们在申请0x3F
个堆块的时候就要通过溢出修改该char_ptr
的值,使其指向我们伪造堆块的用户使用的地址
,也就是0x804A2A8
,我们要将我们伪造堆块中char_ptr
的值要置为0
。同时我们还要伪造与我们伪造堆块物理地址相邻且更高的堆块的size
位。(这两个地方都比较好修改,因为都在message
这个全局变量里面)
- 这样就将堆块伪造好了,这样我们释放的时候就可以绕过
free
的检查机制,将我们所伪造的堆块放入fastbin
链表中,接下来我们将堆块释放后看看。(由于环境原因之后的动态调试调试不了,没办法贴图片了。)
level_2分析3
- 接下来总结一下本题的利用思路:
- 先通过溢出,修改指针,将指针修改为
printf_got
表的地址,然后通过show()
将printf
函数的地址给输出出来。从而达到泄露libc地址的作用 - 然后通过伪造堆块将
.bss
段的一部分作为fake_chunk
,链到fastbin
链表上。 - 申请该
fake_chunk
通过对申请到的fake_chunk
写入数据,达到修改message_ptr
的值。 - 最后通过
edit_message()
劫持scanf_got
表(即将该其got表的值修改为system
的地址),之后我们再发送/bin/sh\x00
从而达到system("/bin/sh")
的目的。这样我们就getshell
了
- 先通过溢出,修改指针,将指针修改为
level_2_exp
- exp如下:
1 | from pwn import * |
总结
- 在遇到
house_of_spirit
的时候要注意- 是否能通过溢出控制要free的地址
- 注意题目中的计数器
- 如果有多个地方可以伪造,注意伪造到哪个地方对后续有用。
- 注意伪造堆块的
size
位和next_size
位。 - 还要注意程序逻辑,如果当程序释放完
fake_chunk
后还要再继续释放,可能就会出现问题,这时就要在fake_chunk
中写入适当的数据,绕过程序逻辑。