日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
在Linux中使用C語(yǔ)言實(shí)現(xiàn)控制流保護(hù)(CFG)

一、前言

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比虎林網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式虎林網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋虎林地區(qū)。費(fèi)用合理售后完善,十載實(shí)體公司更值得信賴。

最近版本的Windows有一個(gè)新的緩解措施叫做控制流保護(hù)(CFG)。在一個(gè)非直接調(diào)用之前——例如,函數(shù)指針和虛函數(shù)——針對(duì)有效調(diào)用地址的表檢查目標(biāo)地址。如果地址不是一個(gè)已知函數(shù)的入口,程序?qū)?huì)終止運(yùn)行。

如果一個(gè)程序有一個(gè)緩沖區(qū)溢出漏洞,攻擊者可以利用它覆蓋一個(gè)函數(shù)地址,并且通過調(diào)用那個(gè)指針來(lái)控制程序執(zhí)行流。這是ROP攻擊的一種方法,攻擊者構(gòu)建一系列配件地址鏈,一個(gè)配件是一組包含ret指令的指令序列,這些指令都是原始程序中的,可以用來(lái)作為非直接調(diào)用的起點(diǎn)。執(zhí)行過程會(huì)從一個(gè)配件跳到另一個(gè)配件中以便做攻擊者想做的事,卻不需要攻擊這提供任何代碼。

兩種非常廣的緩解ROP攻擊的技術(shù)是地址空間布局隨機(jī)化(ALSR)和棧保護(hù)。前者是隨機(jī)化模塊的加載基址以便達(dá)到不可預(yù)料的結(jié)果。在ROP攻擊中的地址依賴實(shí)時(shí)內(nèi)存布局,因此攻擊者必須找到并利用信息泄漏來(lái)繞過ASLR。

關(guān)于棧保護(hù),編譯器在其他棧分配之上分配一個(gè)值,并設(shè)置為每個(gè)線程的隨機(jī)值。如果過緩沖區(qū)溢出覆蓋了函數(shù)返回地址,這個(gè)值將也被覆蓋。在函數(shù)返回前,將校驗(yàn)這個(gè)值。如果不能與已知值匹配,程序?qū)⒔K止運(yùn)行。

CFG原理類似,在將控制傳送到指針地址前做一個(gè)校驗(yàn),只是不是校驗(yàn)一個(gè)值,而是校驗(yàn)?zāi)繕?biāo)地址本身。這個(gè)非常復(fù)雜,不像棧保護(hù),需要平臺(tái)協(xié)調(diào)。這個(gè)校驗(yàn)必須在所有的可靠的調(diào)用目標(biāo)中被通知,不管是來(lái)自主程序還是動(dòng)態(tài)庫(kù)。

雖然沒有廣泛部署,但是值得一提的是Clang’s SafeStack。每個(gè)線程有兩個(gè)棧:一個(gè)“安全?!庇脕?lái)保存返回指針和其他可安全訪問的值,另一個(gè)“非安全?!北4鎎uffer之類的數(shù)據(jù)。緩沖區(qū)溢出將破環(huán)其他緩沖區(qū),但是不會(huì)覆蓋返回地址,這樣限制了破環(huán)的影響。

二、利用例子

使用一個(gè)小的C程序,demo.c:

 
 
 
 
  1. int 
  2.    main(void) 
  3.    { 
  4.        char name[8]; 
  5.        gets(name); 
  6.        printf("Hello, %s.\n", name); 
  7.        return 0; 
  8.    } 

它讀取一個(gè)名字存到緩沖區(qū)中,并且以換行結(jié)尾打印出來(lái)。麻雀雖小五臟俱全。原生調(diào)用gets()不會(huì)校驗(yàn)緩沖區(qū)的邊界,可以用來(lái)緩沖區(qū)溢出漏洞利用。很明顯編譯器和鏈接器都會(huì)拋出警告。

簡(jiǎn)單起見,假設(shè)程序包含危險(xiǎn)函數(shù)。

 
 
 
 
  1. void     
  2.    self_destruct(void) 
  3.    { 
  4.        puts("**** GO BOOM! ****"); 
  5.    } 

攻擊者用緩沖區(qū)溢出來(lái)調(diào)用這個(gè)危險(xiǎn)函數(shù)。

為了使攻擊簡(jiǎn)單,假設(shè)程序不使用ASLR(例如,在GCC/Clang中不使用-fpie和-pie編譯選項(xiàng))。首先,找到self_destruct()函數(shù)地址。

 
 
 
 
  1. $ readelf -a demo | grep self_destruct     
  2.     46: 00000000004005c5  10 FUNC  GLOBAL DEFAULT 13 self_destruct 

因?yàn)樵?4位系統(tǒng)上面,所以是64位的地址。Name緩沖區(qū)的大小事8字節(jié),在匯編我看到一個(gè)額外的8字節(jié)分配上面,所以有16個(gè)字節(jié)填充,然后8字節(jié)覆蓋self_destruct的返回指針。

 
 
 
 
  1. $ echo -ne 'xxxxxxxxyyyyyyyy\xc5\x05\x40\x00\x00\x00\x00\x00' > boom     
  2.     $ ./demo < boom 
  3.     Hello, xxxxxxxxyyyyyyyy?@. 
  4.     **** GO BOOM! **** 
  5.     Segmentation fault 

使用這個(gè)輸入我已經(jīng)成功利用了緩沖區(qū)溢出來(lái)控制了執(zhí)行。當(dāng)main試圖回到libc時(shí),它將會(huì)跳轉(zhuǎn)到威脅代碼,然后崩潰。打開堆棧保護(hù)可以阻止這種利用。

 
 
 
 
  1. $ gcc -Os -fstack-protector -o demo demo.c     
  2.    $ ./demo < boom 
  3.    Hello, xxxxxxxxaaaaaaaa?@. 
  4.    *** stack smashing detected ***: ./demo terminated 
  5.    ======= Backtrace: ========= 
  6.    ... lots of backtrace stuff ... 

棧保護(hù)成功阻止了利用。為了繞過過這個(gè),我將不得不猜canary值或者發(fā)現(xiàn)可以利用的信息泄漏。

棧保護(hù)轉(zhuǎn)化為程序看起來(lái)就是如下這樣:

 
 
 
 
  1. int     
  2.    main(void) 
  3.    { 
  4.        long __canary = __get_thread_canary(); 
  5.        char name[8]; 
  6.        gets(name); 
  7.        printf("Hello, %s.\n", name); 
  8.        if (__canary != __get_thread_canary()) 
  9.            abort(); 
  10.        return 0; 
  11.    } 

然而,實(shí)際上不可能在C中實(shí)現(xiàn)堆棧保護(hù),緩沖區(qū)溢出是不確定行為,并且canary僅對(duì)緩沖區(qū)溢出有效,還允許編譯器優(yōu)化它。

三、函數(shù)指針和虛函數(shù)

在攻擊者成功上述利用后,上層管理加入了密碼保護(hù)措施??雌饋?lái)如下:

 
 
 
 
  1. void     
  2.     self_destruct(char *password) 
  3.     { 
  4.         if (strcmp(password, "12345") == 0) 
  5.             puts("**** GO BOOM! ****"); 
  6.     } 

這個(gè)密碼是硬編碼的,它是比較愚蠢,但是假設(shè)它不為攻擊者所知。上層管理已經(jīng)要求堆棧保護(hù),因此假設(shè)已經(jīng)開啟。

另外,程序也做一點(diǎn)改變,現(xiàn)在用一個(gè)函數(shù)指針實(shí)現(xiàn)多態(tài)。

 
 
 
 
  1. struct greeter {     
  2.         char name[8]; 
  3.         void (*greet)(struct greeter *); 
  4.     }; 
  5.       
  6.     void 
  7.     greet_hello(struct greeter *g) 
  8.     { 
  9.         printf("Hello, %s.\n", g->name); 
  10.     } 
  11.       
  12.     void 
  13.     greet_aloha(struct greeter *g) 
  14.     { 
  15.         printf("Aloha, %s.\n", g->name); 
  16.     } 

現(xiàn)在有一個(gè)greeter對(duì)象和函數(shù)指針來(lái)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)。把他想想為手寫的C的虛函數(shù)。下面是新的main函數(shù):

 
 
 
 
  1. int     
  2.     main(void) 
  3.     { 
  4.         struct greeter greeter = {.greet = greet_hello}; 
  5.         gets(greeter.name); 
  6.         greeter.greet(&greeter); 
  7.         return 0; 
  8.     } 

(在真實(shí)的程序中,其他東西會(huì)提供greeter并挑選它自己的函數(shù)指針)

而不是覆蓋返回指針,攻擊者有機(jī)會(huì)覆蓋結(jié)構(gòu)中的函數(shù)指針。讓我們重新像之前一樣利用。

 
 
 
 
  1. $ readelf -a demo | grep self_destruct     
  2.     54: 00000000004006a5  10 FUNC  GLOBAL DEFAULT  13 self_destruct 

我們不知道密碼,但是我們確實(shí)知道密碼校驗(yàn)是16字節(jié)。攻擊應(yīng)該跳過16字節(jié),即跳過校驗(yàn)(0x4006a5+16=0x4006b5)。

 
 
 
 
  1. $ echo -ne 'xxxxxxxx\xb5\x06\x40\x00\x00\x00\x00\x00' > boom     
  2.    $ ./demo < boom 
  3.    **** GO BOOM! **** 

不管堆棧保護(hù)還是密碼保護(hù)都么有幫助。堆棧保護(hù)僅僅保護(hù)返回指針,而不保護(hù)結(jié)構(gòu)中的函數(shù)指針。

這就是CFG起作用的地方。開啟了CFG,編譯器會(huì)在調(diào)用greet()之前插入一個(gè)校驗(yàn)。它必須指向一個(gè)已知函數(shù)的開頭,否則將想堆棧保護(hù)一樣終止程序運(yùn)行。因?yàn)閟elf_destruct()不是函數(shù)的開頭,但是利用后程序還是會(huì)終止。

然而,linux還沒有CFG機(jī)制。因此我打算自己實(shí)現(xiàn)它。

四、函數(shù)地址表

正如文中頂端PDF鏈接中描述的,Windows上面的CFG使用bitmap實(shí)現(xiàn)。每個(gè)位代表8字節(jié)內(nèi)存。如果過8字節(jié)包含了函數(shù)開頭,這個(gè)位設(shè)置為1。校驗(yàn)一個(gè)指針意味著校驗(yàn)在bitmap中它關(guān)聯(lián)的位。

關(guān)于我的CFG,我決定保持相同的8字節(jié)解決方案:目標(biāo)地址的低3位將舍棄。其余24位用來(lái)作為bitmap的索引。所有指針中的其他位被忽略。24位的索引意味著bitmap最大只能是2MB。

24位對(duì)于32位系統(tǒng)已經(jīng)足夠了,但是在64位系統(tǒng)上面是不夠的:一些地址不能代表函數(shù)的開頭,但是設(shè)置他們的位為1.這是可以接受的,尤其是只有已知函數(shù)作為非直接調(diào)用的目標(biāo),降低了不利因素。

注意:根據(jù)指針轉(zhuǎn)化為整數(shù)的位是未指定的且不可移植,但是這個(gè)實(shí)現(xiàn)不管在哪里都能工作良好。

下面是CFG的參數(shù)。我將他們封裝為宏以便編譯是方便。這個(gè)cfg_bits是支持bitmap數(shù)組的整數(shù)類型。CFG_RESOLUTION是舍棄的位數(shù),一次“3”是8字節(jié)的一個(gè)粒度。

 
 
 
 
  1. typedef unsigned long cfg_bits;     
  2.     #define CFG_RESOLUTION  3 
  3.     #define CFG_BITS        24 

給一個(gè)函數(shù)指針f,下面的宏導(dǎo)出bitmap的索引。

 
 
 
 
  1. #define CFG_INDEX(f) \     
  2.       (((uintptr_t)f >> CFG_RESOLUTION) & ((1UL << CFG_BITS) - 1)) 

CFG bitmap只是一個(gè)整形數(shù)組。初始值為0。

 
 
 
 
  1. struct cfg {     
  2.         cfg_bits bitmap[(1UL << CFG_BITS) / (sizeof(cfg_bits) * CHAR_BIT)]; 
  3.     }; 

使用cfg_register()在bitmap中手動(dòng)注冊(cè)函數(shù)。

 
 
 
 
  1. void     
  2.    cfg_register(struct cfg *cfg, void *f) 
  3.    { 
  4.        unsigned long i = CFG_INDEX(f); 
  5.        size_t z = sizeof(cfg_bits) * CHAR_BIT; 
  6.        cfg->bitmap[i / z] |= 1UL << (i % z); 
  7.    } 

因?yàn)樵谶\(yùn)行時(shí)注冊(cè)函數(shù),需要與ASLR一致。如果ASLR開啟了,bitmap每次運(yùn)行都會(huì)不同。將bitmap的每個(gè)元素與一個(gè)隨機(jī)數(shù)異或是值得的,加大攻擊者的難度。在完成注冊(cè)后,bitmap也需要調(diào)整為只讀權(quán)限(mprotect())。

最后,校驗(yàn)函數(shù)被用于非直接調(diào)用之前。它確保了f先被傳遞給cfg_register()。因?yàn)樗{(diào)用頻繁,所以需要盡量快和簡(jiǎn)單。

 
 
 
 
  1. void     
  2.    cfg_check(struct cfg *cfg, void *f) 
  3.    { 
  4.        unsigned long i = CFG_INDEX(f); 
  5.        size_t z = sizeof(cfg_bits) * CHAR_BIT; 
  6.        if (!((cfg->bitmap[i / z] >> (i % z)) & 1)) 
  7.            abort(); 
  8.    } 

完成了,現(xiàn)在在main中使用它:

 
 
 
 
  1. struct cfg cfg;     
  2.     
  3.   int 
  4.   main(void) 
  5.   { 
  6.       cfg_register(&cfg, self_destruct);  // to prove this works 
  7.       cfg_register(&cfg, greet_hello); 
  8.       cfg_register(&cfg, greet_aloha); 
  9.     
  10.       struct greeter greeter = {.greet = greet_hello}; 
  11.       gets(greeter.name); 
  12.       cfg_check(&cfg, greeter.greet); 
  13.       greeter.greet(&greeter); 
  14.       return 0; 
  15.   } 

現(xiàn)在再次利用:

 
 
 
 
  1. $ ./demo < boom     
  2.     Aborted 

正常情況下self_destruct()不會(huì)被注冊(cè),因?yàn)樗皇且粋€(gè)非直接調(diào)用的合法目標(biāo),但是利用依然不能起作用是因?yàn)樗趕elf_destruct()中間被調(diào)用,在bitmap中它不是一個(gè)可靠的地址。校驗(yàn)將在利用前終止程序。

在真實(shí)的應(yīng)用程序中,我將使用一個(gè)全局的CFG bitmap,在頭文件中使用inline函數(shù)定義cfg_check()。

盡管不使用工具直接在C中實(shí)現(xiàn)是可能的,但是這將變得更加繁瑣和意出錯(cuò)。正確的是該在編譯器中實(shí)現(xiàn)CFG。


當(dāng)前標(biāo)題:在Linux中使用C語(yǔ)言實(shí)現(xiàn)控制流保護(hù)(CFG)
文章位置:http://www.5511xx.com/article/cdecedi.html