Retr0 的注册

这是一篇关于在Llama.cpp中利用堆溢出实现远程代码执行(RCE)的详细研究文章,主要内容总结如下:

  • 研究背景与目标:作者是开发爱好者,花费约 30 小时利用堆溢出实现 RCE,此前已花 2 周研究Llama.cpp的 RPC 和内存实现。Llama.cpp有特殊的堆管理系统,其特性使传统的ptmalloc利用方法失效,作者旨在探索新的利用向量。
  • 前期工作与漏洞发现Llama.cpp的 RPC 服务器在开发初期存在低级别内存安全漏洞,主要利用Tensor内存相关操作。Llama.cpp实现了自己的内存管理机制,基于 glibc 的基本 malloc 和经典的ptmalloc管理方法,并添加了优化Tensor相关处理操作的功能。通过alloc_buffer命令分配内存,返回包含实际分配内存的buffer结构。
  • 过去的补丁与缓解措施:针对早期报告的漏洞,Llama.cpp实施了大量的glibc级别的内存检查,包括在deserialize_tensor()RPC方法调用包装器、内部实现以及buffer->iface实现等四个阶段进行检查,以防止各种内存越界和数据损坏问题。
  • 分析与利用过程

    • 维度到破坏:cpy_tensor 和 ggml_nbytes:发现ggml_nbytes方法可用于计算Tensor对象的维度大小,其计算结果受Tensor的形状和步长影响。在ggml_backend_cpu_buffer_cpy_tensor方法中,memcpy的大小由src Tensor的维度大小计算得出,而此方法未对srcdst Tensorggml_nbytes大小进行比较,导致可通过构造特定的Tensor来实现堆溢出。
    • 利用悖论的世界:溢出的悖论:虽然可以控制 RPC 服务器的执行流到任意地址,但由于缺乏可利用的地址,无法单纯依靠buffer进行利用。修改buffercontextget_base成员会导致其他成员指针损坏,而获取ggmlbase基地址需要绕过边界缓解措施,陷入了“溢出悖论”。
    • 部分写入:现实生活中的部分写入?:介绍了在经典glibcCTF 利用中的部分写入技术,即在不损坏映射基的情况下部分溢出指针,以访问动态链接库中的特定方法。但在当前的利用中,部分写入buffer->iface指针非常困难,因为需要控制ggml_nbytes的计算,且目标方法的范围有限。
    • 解决悖论:当经典 ptmalloc 不起作用时:通过研究ggml_backend_buffer_free方法中的NullPointerNoException检查,发现可以将iface->free_buffer设置为已知地址NULL以避免程序崩溃,从而能够部分写入其他buffer->iface成员,为解决“溢出悖论”提供了可能。
    • 构造泄漏:逐层构建:目标是构造一个泄漏,通过部分写入buffer->iface->get_base成员,找到合适的操纵地址和参数,成功泄漏了ggml_backend_buffer_get_type的地址,进而计算出libggml-base.so的基地址。然后利用类似方法泄漏了libc.so.6的基地址。
    • 远程代码执行:新的悖论,面向结构的编程?:尽管绕过了write-what-wheresread-what-wheres的缓解措施,但在当前的Llama.cpp版本中实现远程代码执行并不容易。通过结构导向编程,利用ggml_backend_tggml_backend_dev_t结构中的嵌套调用链,伪造backenddevice结构,最终实现了在 RPC 服务器中执行远程指定代码。
  • 最终的利用脚本 exp.py:详细描述了利用过程的 Python 脚本实现,包括内存分配、部分写入、伪造结构等步骤,最终通过构造特定的bufferbackend结构,实现了在Llama.cpp中的远程代码执行,并通过监听127.0.0.1:1337接收反向 shell 连接。

总的来说,作者通过深入研究Llama.cpp的内存管理和 RPC 机制,克服了重重困难,成功实现了堆溢出利用并达到远程代码执行的目的,展示了在复杂的内存管理系统中进行漏洞利用的技巧和挑战。

阅读 7
0 条评论