unlink
原理:
在执行_int_free()函数的时候会执行unlink:
1 |
|
chunk1 free进入unsortedbin 的时候,如果相邻地址的chunk的chunk2处于空闲状态:
unlink会将chunk1和chunk2进行合并操作,而chunk2的fd和bk位置对应的地址就会进行unlink也就是断链操作。
1 |
|
全部free完成后的情况:


注:0x440是chunk2 0x669是chunk1 0x9a2是chunk3 0x4d0是chunk4

这里在进行free(Chunk4)的时候 由于chunk2与chunk4相邻就会合并chunk2 而chunk1和chunk3的bk 和 fd就会进行Unlink:

但是还需要绕过一下三个检测:

检查1:检查与被释放chunk相邻高地址的chunk的prevsize的值是否等于被释放chunk的size大小
可以看左图绿色框中的内容,上面绿色框中的内容是second_chunk的size大小,下面绿色框中的内容是hollk5的prev_size,这两个绿色框中的数值是需要相等的(忽略P标志位)
检查2:检查与被释放chunk相邻高地址的chunk的size的P标志位是否为0
可以看左图蓝色框中的内容,这里是hollk5的size,hollk5的size的P标志位为0,代表着它前一个chunk(second_chunk)为空闲状态
检查3:检查前后被释放chunk的fd和bk
可以看左图红色框中的内容,这里是second_chunk的fd和bk。首先看fd,它指向的位置就是前一个被释放的块first_chunk,这里需要检查的是first_chunk的bk是否指向second_chunk的地址。再看second_chunk的bk,它指向的是后一个被释放的块third_chunk,这里需要检查的是third_chunk的fd是否指向second_chunk的地址。
stkof
creat:

delete:

edit:


创立的chunk 只需要管后两个堆块

存储heap的数组n
下面讲解一下unlink的部分:
1 | from pwn import * |
unlink的目的是控制数组n 实现任意地址写:
利用edit伪造了一个已经free掉进入unsortedbin中的chunk 然后free(chunk3)的时候触发unlink 实现了修改chunk3 fd的目的。

fake chunk:


可以看到绕过了上述的检测。
这里看一下伪造的fd 和 bk 也就是chunk3和chunk2:
这两个伪造的堆块位于数组n

38的bk和40的fd都指向我们伪造的fake chunk
然后free chunk3(与fake chunk地址相邻)后:

① fake_chunk被摘除之后首先执行的就是first_bk = third_addr,也就是说first_chunk的bk由原来指向fake_chunk地址更改成指向third_chunk地址:

② 接下来执行third_fd = first_addr,即third_chunk的fd由由原来指向fake_chunk地址更改成first_chunk地址:

这里需要注意的是third_chunk的fd与first_chunk的bk更改的其实是一个位置,但是由于third_fd = first_addr后执行,所以此处内容会从0x602140被覆盖成0x602138。

总结一下:最终利用的是修改third chunk(fake chunk)的fd指向数组n。
后续通过数组n实现任意地址写:
1 | pay2 = p64(0) + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi']) |

1 | payload = p64(hollkelf.plt['puts']) |
触发system(binsh)
1 | libc_base = puts_addr - libc.symbols['puts'] |