unlink原理及利用

unlink是利用glibc malloc 的内存回收机制造成攻击的,核心就在于当两个free的堆块在物理上相邻时,会将他们合并,并将原来free的堆块在原来的链表中解链,加入新的链表中,但这样的合并是有条件的,向前或向后合并。但这里的前和后都是指在物理内存中的位置,而不是fd和bk链表所指向的堆块。
以当前的chunk为基准,将preivous free chunk合并到当前chunk称为向后合并,将后面的free chunk合并到当前chunk就称为向前合并。

向后合并

/* consolidate backward */
    if (!prev_inuse(p)) {
      prevsize = p->prev_size;
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      unlink(p, bck, fwd);
    }
#define chunk_at_offset(p, s)  ((mchunkptr)(((char*)(p)) + (s)))

unlink的定义如下:

#define unlink(P, BK, FD) {                                            \
  FD = P->fd;                                                          \
  BK = P->bk;                                                          \
  if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \
    malloc_printerr (check_action, "corrupted double-linked list", P); \
  else {                                                               \
    FD->bk = BK;                                                       \
    BK->fd = FD;                                                       \
    if (!in_smallbin_range (P->size)                       \
    && __builtin_expect (P->fd_nextsize != NULL, 0)) {         \
      assert (P->fd_nextsize->bk_nextsize == P);               \
      assert (P->bk_nextsize->fd_nextsize == P);               \
      if (FD->fd_nextsize == NULL) {                       \
    if (P->fd_nextsize == P)                       \
      FD->fd_nextsize = FD->bk_nextsize = FD;              \
    else {                                 \
      FD->fd_nextsize = P->fd_nextsize;                \
      FD->bk_nextsize = P->bk_nextsize;                \
      P->fd_nextsize->bk_nextsize = FD;                \
      P->bk_nextsize->fd_nextsize = FD;                \
    }                                  \
      } else {                                 \
    P->fd_nextsize->bk_nextsize = P->bk_nextsize;              \
    P->bk_nextsize->fd_nextsize = P->fd_nextsize;              \
      }                                    \
    }                                      \
  }                                                                    \
}

其中有很多检测是之前没有的,首先把单纯的unlink弄清楚。

#define unlink(P,BK,FD){
FD=P->fd;
BK=P->bk;
FD->bk=BK;
BK->fd=FD;
...
}

unlink可以简单看作上面的代码部分,将链表中的P脱链,把之前P的下一个chunk与P的上一个chunk连接,使P离开链表。
首先检测前一个chunk是否为free状态,通过检测当前free chunk的PREV_INUSE(P)标志位,如果为0表示free状态,但内存中第一个申请的chunk的前一个chunk一般都被认为在使用中,所以不会发生向后合并。
如果不是内存中的第一个chunk且它的前一个chunk标记为free状态时,发生向后合并:
首先修改chunk的size位大小为两个chunk size之和
再将指针移动到前一个chunk处
最后调用unlink将前一个chunk从它所在的链表中移除。

向前合并

if (nextchunk != av->top) {
      /* get and clear inuse bit */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

      /* consolidate forward */
      if (!nextinuse) {
    unlink(nextchunk, bck, fwd);
    size += nextsize;
      } else
    clear_inuse_bit_at_offset(nextchunk, 0);

#define clear_inuse_bit_at_offset(p, s)\
 (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE))

向前合并不修正P的指针,只增加size大小。
在合并后glbc malloc会将合并后新的chunk加入unsorted bin中,加入第一个可用的chunk之前,更改自己的size字段将前一个chunk标记为已用,再将后一个chunk的previous size改为当前chunk的大小。

利用unlink改写got表执行shellcode的第一种姿势(理解来自 阿里@走位)

如果两个相邻的chunk在第一个chunk写入数据时发生了溢出,覆盖了后一个chunk的数据为特殊含义的数据,那么就有可能使程序执行特定的代码,从而控制程序流程达到相应的目的。
当我们通过溢出改写数据如下时会满足特别的条件:

previous size => 一个偶数
size =>-4
fd => free@got addr-12
bk =>shellcode addr

当覆盖数据后,因为改写的是下一个chunk的数据,当free当前第一个chunk时,先考虑会不会向后合并,这时因为第一个chunk 的前一个总是占用的,即使他根本不存在,所以当第向后chunk被free后不会发生向后合并,再判断向前合并,
首先去检测下一个chunk是否处于free状态
需要通过next->next chunk的size标志位检测,当我们设置next chunk的size为-4时,next chunk的previous size字段会被看作next->next chunk的size字段,此时又因为next chunk的previous size 字段为偶数,即next->next chunk->size为偶数,P标志位为0,表示next chunk为free状态,满足向后合并的条件,触发unlink。
当满足unlink的条件时,内存的变化
利用两个临时变量FD、BK将后一个chunk从原来的free链表中解链


首先FD=P->fd;BK=P->bk;这时FD的值为free@got-12,BK为shellcode地址
再次FD->bk=BK;BK->fd=FD;因为这时FD,BK是强制被看作两个chunk的,所以它的bk与fd相对的地址与一个正常chunk是一样的,FD->bk与FD的地址相差12,而FD为free@got-12,那么FD->bk的位置就是free@got的位置,被赋值了BK=shellcode,这时当执行free函数时转而执行shellcode,达成了通过unlink修改free@got表的目的。


利用unlink的第二种姿势

将一个大的chunk伪造成两个较小的堆块,在填充数据时通过第二个伪造堆块的size标志位P使前一个伪chunk处于free状态,这时因为这个大的堆块本来就处于inuse状态,所以第二个伪堆块的下一个堆块size位P=1,表示前一个chunk处于使用状态,当free后面的伪堆块时会将前一个伪堆unlink,

伪堆构造

图中红色边框为一个大的申请的堆块,黄色为第一个伪堆块,蓝色为第二个伪堆块,通过数据填充构造上图的内存布局,因为ptr为申请内存时返回的指针,所以ptr一开始就指向fake_prev,要绕过unsafe_unlink,将第一个伪堆块的fd=ptr-0x18,bk=ptr-0x10,这样当检查FD->bk=p的时候成功绕过了检测,满足了unlink可以实现后续的利用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 参考文章: 关于heap overflow的一些笔记 by ETenal [CTF]Heap vuln -- u...
    BJChangAn阅读 2,658评论 2 5
  • pwntools简单语法 作为最好用的pwn工具,简单记一下用法: 连接:本地process()、远程remote...
    SueLyon阅读 24,110评论 3 38
  • 不知不觉间 一年又一年 记忆似雪花 一片又一片 曾经的时间和地点 浮现你的欢笑和容颜 过往浮华如烟飘散 在昨天仿佛...
    阿长的沙鸥阅读 274评论 0 0
  • 重要的事情有时候会迟一步再来,虽然等的辛苦,但是不会被辜负。❤️ 有一次跟同事小佳聊天的时候,她突然评价我说听起来...
    刘二姑娘阅读 132评论 0 0
  • (连载)送我一城(第五十八章 官升一级) 第五十九章 煮石头汤 懒人不想走得那么辛苦,就发明了汽车;懒人不想爬楼梯...
    云问雨阅读 322评论 2 5