【安全攻防综合实验4】利用堆溢出修改文件

Posted by 慕念 on October 7, 2021

【实验目的】

堆溢出漏洞利用

  • 假定heap.c 编译成为heap可执行程序后,属主为root ,具备suid标志位。
  • 以hacker登录,利用此程序的堆溢出漏洞,获取root权限(得到具备root权限的shell)

【实验步骤】

一、heap

1、首先分析代码,找到危险代码,确定破解思路:

image2

2、在自己的虚拟机上用gdb调试一下,看堆结构:

image3

image4

找到userinput和outputfile对应的地址后,查看内存:

image5

用命令vis_heap_chunk展示的更加清楚,其中紫色块和绿色块代表malloc的两个堆块,这里都分配了0x20个chunk,显示为0x21是因为,由于分配时总是按照16字节对齐,最后一位就可以用来表示previous_inuse,即表示这一个chunk相邻的前一个chunk是否是在使用的状态,如果是就为1,否则为0。

image6

一开始随便输入测试一下,验证一下思路。所以要使输入覆盖到outputfile,首先需要先输入32个字节的填充第一个chunk和第二个chunk的头部,再输入就可以修改文件名。

image7

image8

3、分析实验要求,要求以hacker登录,利用堆溢出漏洞,获取root权限,即得到具备root权限的shell,提示中说可以利用/etc/passwd。

查阅了一下关于/etc/passwd的资料,其中每一项都以root:x:0:0:root:/root:/bin/bash的形式记录。

image9

可以通过在/etc/passwd中新增一个有root权限的用户,再通过switch user获取root权限。其中x为空代表不需要密码,UID为0代表有root权限,同时总字符共需32字节,比如:123456789test::0:0:test12345678:/etc/passwd

4、在CentOs上进行验证,实验完成:

image10

二、heap3

首先随便输入,看堆结构,可以看到每一个分配的堆块size字段都是0x29,表示当前块大小是40,并且上一块正在使用。

image11

Free三块结束后再看堆结构,和预想中的free不一样,对于空闲块只设置了fd没有设置bk,标志上一块是否正在使用的标志位也没有置于0。查了一下,当分配的缓冲区较小(默认情况,当块小于64字节时),malloc将使用简化的数据结构(fastbin),并将忽略prev_size,bk和size的最低位。

image12

所以对于这道题目,需要在free的时候把块伪造成常规块,而不是fastbin块(用到unlink)。Unlink的原理如图所示,意味着可以把P->bk的值写入地址(P->fd)+ 12的内存中,把P->fd的值写入地址(P->bk)+ 8的内存中。

image13

为了触发unlink,把C块的prev_size和size都构造为-4,即\xfc\xff\xff\xff,这时分配器会认为分配的内存区域很大,就不会采取fastbin,将堆块看作常规块处理。并且由于0xfffffffc最后一位为0,意味着前一个chunk没有被使用,符合调用unlink的条件,且分配器会认为前一个相邻chunk的地址是当前块的首地址减去-4。

image14

由于puts函数最后是通过通过PLT跳转到GOT表,根据DWORDSHOOT的原理,如果覆盖puts的GOT表表项地址0x0804b128,可以实现控制程序的目的。

需要把调用winner函数的shellcode放在A块中,即push 0x08048864 ret

image15

根据功略中给出的答案,A块由10个NOP和winner的shellcode组成;B块为32个’A‘(只需要填充完B块,其中的内容不影响);C块则是特地构造的prev_size和size的两段\xfc\xff\xff\xff,fd不影响,随便填写4个字符,bk是puts的GOT表-12,data段位shellcode地址+F。

结果如下:(如果在自己的虚拟机里跑会报错,所以放在了centos里跑)

image16

最后在执行puts的之后跳转到了构造的地址0x0804c004

image17

下面是各步骤中堆块的结构:

Free前:

image18

Free(c)

image19

Free(b)

image20

Free(a)

image21

三、思考

Q1、为何“shellcode adress”不直接写winner的地址,而是跳到0x0804c004?在这个地方再执行'\x68\x64\x88\x04\x08\xc3'

A1:不能直接写winner的地址,因为BK->fd在unlink的过程中会进行更新,但是代码段是不可写的。Heap段是具有写权限的,所以先把shellcode写入heap中,然后将shellcode的地址通过unlink写入到GOT表中。

Q2、为何攻略里最后是跳转到0x0804c004?难道不是0x0804c008吗?试一试,为何不行?

A2:尝试了一下,如果改成跳转到0x0804c008,free(c)时A块中0x804c010改为puts@got-12。之后puts跳转到0x0804c008时可以发现之后执行的并不是我们构造的shellcode,产生了错误编译。

image22

image23

正确的跳转到0x0804c004时,A块中0x804c0c的位置改为puts@got-12。当执行到puts时,跳转到0x0804c004,先执行了几句无意义的汇编,再执行NOP,最后成功执行winner的shellcode。

image24

image25

Q3、最后那个F,是否可以长一点,如FFFF?

A3:尝试了一下最多可以输入三个字符,F或者FF或者FFF都可以,但是FFFF不行。

因为输入的数据会放在C块的data段,覆盖掉fd的位置(命令行构造的时候输入了4个C),可以看到输入3个F正好把fd全部覆盖,如果输入4个字符,则会把bk构造的puts@got-12地址覆盖,导致最后跳转失败。

image26

image27

最后,愿天堂没有堆溢出

image-20220703192949200