0x01 前情提要
北京時(shí)間11點(diǎn)42分,正準(zhǔn)備劃 20 分鐘水去吃午飯,園長突然跟我說過火線的 IAST 突然開源了,原名靈芝IAST更名為洞態(tài)IAST,真是OMG。
IAST 也是同樣使用 agent 技術(shù)。同樣一種技術(shù),在不同人的手里用法也不同,同一種思路在不同人手里的實(shí)現(xiàn)方式也可能存在差異。那么既然他開源了,那就來看一看學(xué)習(xí)一下他的思路和實(shí)現(xiàn),也順便測試一下產(chǎn)品,取其精華。
能有這樣的產(chǎn)品開源供廣大安全從業(yè)者測試和學(xué)習(xí)真的是一件非常好的事,真的是要撒花慶祝,感謝老板。由于明確項(xiàng)目定位是 IAST 產(chǎn)品,因此主要關(guān)注的是檢測能力的實(shí)現(xiàn)和 sink/source/hook 點(diǎn)的選取。
項(xiàng)目地址:[https://github.com/HXSecurity/DongTai-agent-java][0]
文檔地址:https://github.com/HXSecurity/DongTaiDoc
0x02 項(xiàng)目結(jié)構(gòu)
首先看一下整個(gè)的代碼邏輯,項(xiàng)目總共分為 3 個(gè)模塊,分別是 iast-agent、iast-core、iast-inject。
iast-agent
入口類是 com.secnium.iast.agent.Agent,與任何一家使用 java agent 技術(shù)的產(chǎn)品一樣,洞態(tài)也是使用了 Sun JVM Attach API 將 agent 附加到指定的 Java 進(jìn)程上。

com.secnium.iast.agent.IASTProperties 是 agent 的單例配置類,從 src/main/resources/iast.properties 中讀取配置。
com.secnium.iast.agent.IASTClassLoader 是 agent 自定義的 ClassLoader,繼承自 URLClassLoader,這個(gè)類的代碼參考自 jvm-sandbox,其中需要注意的是,在卸載 agent 時(shí)需要關(guān)閉 ClassLoader,如果不能通過 ClassLoader 的 close() 方法進(jìn)行關(guān)閉,則需要尋找已經(jīng)打開的 jar 文件并釋放文件句柄。
com.secnium.iast.agent.UpdateUtils 是由客戶端主動向云端發(fā)送請求,用來檢查版本更新和發(fā)送更新狀態(tài),其中靜態(tài)方法 sendRequest() 可以向外發(fā)送 Http 請求。也可以看到發(fā)送請求使用了 ua: SecniumIast Java Agent,這部分其實(shí)可以做為一個(gè)特征。
com.secnium.iast.agent.AgentLauncher 是在應(yīng)用程序指定了 javaagent 參數(shù)情況下的入口類,這個(gè)類中的 premain 和 agentmain 方法均調(diào)用了共同的 install() 方法安裝 agent。這個(gè)方法則是調(diào)用 com.secnium.iast.agent.manager.EngineManager 對整個(gè)流程進(jìn)行管理。

接下來我們看一下 EngineManager 這個(gè)類,這個(gè)類是 IAST 引擎管理器,并且使用單例對象,首先執(zhí)行的是 updateEnginePackage() ,更新 IAST 引擎需要的 jar 包,從云端進(jìn)行下載。
然后調(diào)用 install() 方法,首先將 iast-inject.jar 注冊到 BootstrapClassLoader 中,然后使用自定義的 IASTClassLoader 加載檢測引擎 iast-core.jar ,并反射調(diào)用里面的 com.secnium.iast.core.AgentEngine 的 install() 進(jìn)行檢測邏輯的初始化和加載動作。

進(jìn)一步加載檢測引擎中的多個(gè)引擎:

然后反射調(diào)用com.secnium.iast.core.AgentEngine 的 start() 方法,更新在檢測引擎中的一個(gè)全局標(biāo)識位。

檢測引擎啟動之后,agent 端還會啟動幾個(gè)守護(hù)線程:
PerformanceMonitor:負(fù)責(zé)監(jiān)控jvm性能狀態(tài),如果達(dá)到停止閾值(80%),則停止檢測引擎;如果達(dá)到卸載閾值(90%),則卸載引擎。
UpdateMonitor:監(jiān)控云端,判斷檢測引擎是否需要更新。
EngineMonitor:監(jiān)控配置文件 engine.status 配置項(xiàng)的更改。

這個(gè)模塊就是起到一個(gè) agent 入口的作用,無需多言。
iast-inject
這個(gè)模塊只有一個(gè)類 java.lang.iast.inject.Injecter,難道不應(yīng)該是 Injector 嗎,害。
這個(gè)類定義了一些回調(diào)方法鉤子:

并圍繞這些鉤子定義了若干方法,這些放在將會使用 ASM 時(shí)插入指定類的類字節(jié)碼中。

這個(gè)類實(shí)際上是定義中間處理邏輯以及定義供 ASM 調(diào)用的方法。代碼太長了,不想細(xì)看。
iast-core
這個(gè)模塊如其名字,是整個(gè) agent 的核心,我們先來看一下整個(gè)項(xiàng)目的目錄結(jié)構(gòu):
- engines:支撐整個(gè)模塊調(diào)度的幾個(gè)引擎,包括:ConfigEngine(通過云端返回結(jié)果構(gòu)建IAST的模型規(guī)則),LoggerEngine(日志記錄引擎),SandboxEngine(創(chuàng)建 EngineManager 實(shí)例),ServiceFactory(服務(wù)引擎,使用定時(shí)任務(wù)實(shí)現(xiàn)心跳、報(bào)告發(fā)送以及 http 請求重放機(jī)制),SpyEngine(初始化 SpyUtils),TransformEngine(創(chuàng)建自定義 ClassFileTransformer,使用 retransform 方法處理類字節(jié)碼)
- enhance:如其名——增強(qiáng),包括了自定義 ClassVisitor、自定義 AdviceAdapter、sink/source 點(diǎn)的選取、對一些框架和中間件的適配、SCA 檢查功能的實(shí)現(xiàn)等等。
- handler:針對漏洞的檢測邏輯、策略模型的建立、污點(diǎn)圖的構(gòu)造、對整個(gè)流程的處理。
- middlewarerecognition:用來檢測當(dāng)前的中間件
- replay:HTTP 請求重放
- report:一些報(bào)告和日志的實(shí)現(xiàn)
- threadlocalpool:一些需要在線程之間傳遞的類
- util:工具類
- 其他:一些負(fù)責(zé)管理和調(diào)用的類
另外,在這個(gè)項(xiàng)目的 resources 中,還有一些基礎(chǔ)的 txt 以及 xml 的配置,這部分等調(diào)用到此的時(shí)候再說。
首先來繼續(xù)之前的調(diào)用流程,iast-agent 通過反射調(diào)用com.secnium.iast.core.AgentEngine 的 install() 方法,調(diào)用各個(gè)引擎的 start() 方法。此處關(guān)注其中幾個(gè)引擎:
ConfigEngine 通過請求 /api/v1/profiles,并解析其中的結(jié)果,最后創(chuàng)建了一個(gè) IASTHookRuleModel 實(shí)例,這個(gè)實(shí)例就是這個(gè) agent 的處理模型,其中保存了很多處理中用到的規(guī)則。

TransformEngine使用 Instrumentation 接口,進(jìn)行字節(jié)碼的轉(zhuǎn)換,調(diào)用了com.secnium.iast.core.enhance.IASTClassFileTransformer#retransform 方法。

這個(gè)方法使用了 IASTClassHookPointMatcher#findForRetransform() 使用 Instrumentation 對象的 getAllLoadedClasses() 獲取所有已經(jīng)被加載的類,并通過 com.secnium.iast.core.util.matcher.ConfigMatcher#isHookPoint() 方法進(jìn)行篩選判斷,返回了一個(gè)需要修改的類的 List 。

通過以上代碼可知,agent 對以下的類沒有 hook:
- 類名 com/secnium/iast 開頭的類
- 類名 java/lang/iast/ 開頭的類
- 類名包含 CGLIB$$ 的類
- 類名包含 $$Lambda$ 的類
- 類型包含 _$$_jvst 的類
- 在資源文件 com.secnium.iast.resources/blacklist.txt 寫入的類,其中包括了:EDU/oswego/cs/dl/util/concurrent/、net/sf/ehcache/、net/bytebuddy/、com/secnium/、apple/、com/octo/captcha/、Routes$、aQute/service/、ch/qos/logback/、bsh/、antlr/debug/、com/bea/common/*等。
在 blacklist.txt 中寫了多達(dá) 7 萬多行的類名和前后綴,根據(jù)其注釋,這是為了過濾掉 Sandbox 所需要的類,防止 ClassCircularityError 的發(fā)生。
隨后調(diào)用 retransformClasses() 會讓類重新加載,從而使得注冊的類修改器能夠重新修改類的字節(jié)碼,這要就會調(diào)用之前通過 addTransformer() 注冊的 IASTClassFileTransformer 中重寫的 transform() 方法。
方法里首先調(diào)用 com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 通過獲取 jar 包中的 manifest 信息并將其發(fā)送回云端,這部分是 SCA 功能的實(shí)現(xiàn)。
然后二次調(diào)用了 ConfigMatcher.isHookPoint() 判斷 hook 類,感覺這個(gè)判斷寫重了,沒必要。
在 IASTClassAncestorQuery 里緩存了 CodeSource/ClassLoader/ClassName/SuperName/Interfaces。
創(chuàng)建 ClassWriter,依然是使用 COMPUTE_FRAMES 自動計(jì)算幀的大小,并且重寫了getCommonSuperClass() 方法,在計(jì)算兩個(gè)類共同的父類時(shí)指定ClassLoader。
創(chuàng)建 IASTContext 上下文,初始化 PluginRegister,這個(gè)類中包含了一個(gè)全局常量 PLUGINS,里面保存了很多的處理插件,這些類都實(shí)現(xiàn)了 DispatchPlugin 接口,這個(gè)接口包含兩個(gè)方法:
- dispatch():分發(fā)不同的 classVisitor 處理對應(yīng)的類
- isMatch():判斷是否命中當(dāng)前插件

在上圖的類中是 DispatchPlugin 的實(shí)現(xiàn)類,其中包含了 agent 中的一些 sink/source/hook 點(diǎn),在這些類的 dispatch() 方法中,會創(chuàng)建繼承至 AbstractClassVisitor 的各個(gè)ClassVisitor,在 ClassVisitor 中又通過重寫 visitMethod() ,注冊繼承至 AbstractAdviceAdapter 的實(shí)現(xiàn)類,這些類重寫父類的 before()/after() ,實(shí)際上是 AdviceAdapter 的 onMethodEnter()/onMethodExit() 實(shí)現(xiàn)了字節(jié)碼的插入。具體的字節(jié)碼插入部分是 ASM 的 API ,無需多言。
這里可以看到,洞態(tài)為每種不同的 hook點(diǎn)/sink點(diǎn)/source點(diǎn)訂制了不同的 ClassVisitor 和 MethodVisitor,也就是說寫入的字節(jié)碼不一致,那到底寫入了什么呢?通過看 ASM 的 API 比較難以閱讀,還是在字節(jié)碼寫入后把 class dump 出來看比較方便。
SpyEngine 通過調(diào)用 java.lang.iast.inject.Injecter#init 方法將 com.secnium.iast.core.handler.EventListenerHandlers 中定義的全部處理方法存入了 namespaceMethodHookMap 中供全局調(diào)用。而在 EventListenerHandlers 中定義的這個(gè)方法,實(shí)際上又是由 Injecter 通過反射調(diào)用。
而后續(xù)系統(tǒng)的全部功能,都是由 EventListenerHandlers 中定義的這些方法處理和調(diào)度的,這里不再進(jìn)行一一分析。
0x03 功能實(shí)現(xiàn)探究
支持漏洞
洞態(tài) IAST 支持的漏洞類型位于 com.secnium.iast.core.handler.vulscan.VulnType,如下圖:

對應(yīng)配置文件中的 model.xml,通過反向查找調(diào)用就可以查看相關(guān)的處理邏輯,各位看官請自行評測,本文不會對每種漏洞的實(shí)現(xiàn)進(jìn)行一一介紹。
SCA 實(shí)現(xiàn)
一個(gè)優(yōu)秀的 IAST 一定有 SCA 一類的功能,簡單的實(shí)現(xiàn)都是通過收集客戶段組件信息,發(fā)送到云端通過匹配 CPE,并鏈接到對應(yīng)的 CVE/CWE/CNNVD 等,并進(jìn)行展示,先看一下洞態(tài)的云端效果,在組件管理:

看看右邊的數(shù)量和左邊的數(shù)量完全對應(yīng)不上,難道是我對這些數(shù)字的理解有問題?點(diǎn)擊進(jìn)入條目,有該組件對應(yīng)的一些信息的展示

再點(diǎn)擊就有對應(yīng) CVE 的一些漏洞信息描述的信息。

那么這個(gè)功能是如何實(shí)現(xiàn)的呢?在前面提到 SCA 是由com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 所實(shí)現(xiàn),這個(gè)方法有兩個(gè)出口,據(jù)我判斷,應(yīng)該是不會走到下面那個(gè) scan() 方法。

com.secnium.iast.core.enhance.sca.ManifestScaner#parseJarManifest 調(diào)用 getPackgeInfo 獲取 Attributes 中的 Implementation-Version 和 Implementation-Title

最后拼接出了一些對應(yīng)的信息發(fā)送給云端。

云端接收到這些信息處理入庫,并對接自己 CPE/CVE 漏洞信息庫進(jìn)行分析和展示。
這部分實(shí)際上是非常簡單的實(shí)現(xiàn),沒有復(fù)雜的檢查邏輯,這個(gè)功能 OWASP 有開源的,建議參考:https://github.com/jeremylong/DependencyCheck
sink/source/propagator/http
構(gòu)建一個(gè) IAST,重要的就是整個(gè)模型的構(gòu)建,前面分析過,模型的構(gòu)建是通過 buildRemote() 方法獲取遠(yuǎn)端的配置。

由于這個(gè) json 太長了,我沒細(xì)看,在這里就不展示了,這個(gè)配置在本地也有一個(gè) model.xml,以 xml 格式儲存了這些信息,這些信息在處理后會被轉(zhuǎn)為 IASTHookRuleModel 對象。
在這個(gè)配置中我們發(fā)現(xiàn)了一些標(biāo)記,他們都代表什么呢?
- type:這個(gè) hook 點(diǎn)的類型,一個(gè) hook 點(diǎn)會被分類為:1. 傳播節(jié)點(diǎn) 2. source 點(diǎn) 3. filter 點(diǎn) 4. sink 點(diǎn)
- value:方法類型
- details:inherit 是否繼承,value 方法簽名,target 目標(biāo)參數(shù)位置,source 源參數(shù)位置,track 標(biāo)記是否要追蹤
其中 sink 點(diǎn)要標(biāo)記污點(diǎn)所在的參數(shù)位置,傳播節(jié)點(diǎn)要標(biāo)記源位置和目標(biāo)位置。分類處理完這些配置文件后,將會將所有信息保存到 IASTHookRuleModel 中的一些變量中。

在之前的分析中就提到過,對于每一種不同的 hook 點(diǎn),插入的字節(jié)碼是不一樣的。
sink 點(diǎn)插入:

傳播節(jié)點(diǎn):

source節(jié)點(diǎn):

http節(jié)點(diǎn):

這其中的邏輯,總結(jié)起來是這樣的,系統(tǒng)中定義了了一個(gè) com.secnium.iast.core.handler.controller.TrackerHelper,用來作為一個(gè)全局的計(jì)數(shù)器?(追蹤器),當(dāng)進(jìn)入一個(gè)節(jié)點(diǎn)時(shí),對應(yīng)的成員變量會自增 1,而退出時(shí),會 -1,并且判斷當(dāng)前節(jié)點(diǎn)是否為第一層級節(jié)點(diǎn),如果不是,將不會走入后續(xù) spyMethodOnBefore() 方法。這個(gè)類中還定義個(gè)一個(gè) trackCounts,目前還沒有用上。
在 spyMethodOnBefore() 方法中,將會 Hook 點(diǎn)類型的不同分別調(diào)用 HttpImpl.solveHttp、PropagatorImpl.solvePropagator、SourceImpl.solveSource、SinkImpl.solveSink 進(jìn)行不同點(diǎn)的處理。
http 節(jié)點(diǎn)處理
創(chuàng)建一個(gè)自定義的 com.secnium.iast.core.util.http.HttpRequest 對象,儲存相關(guān)內(nèi)容和 httpServletRequest 引用對象,靜態(tài)文件不處理,目前定義的靜態(tài)文件后綴是 .js,.css,.htm,.html,.jpg,.png,.gif,.woff,.woff2,.ico,.maps,.xml,看到了 htm/html 沒有處理,這種情況下偽靜態(tài)的網(wǎng)站可能會漏檢查,根據(jù)正則查看 url 中是否含有 “l(fā)ogin” 字樣,并設(shè)置其是否為登陸 URL,將一些信息初始化和緩存到 EngineManager 中。這類節(jié)點(diǎn)主要負(fù)責(zé)標(biāo)記和預(yù)處理的。
sink 節(jié)點(diǎn)處理
使用程序啟動時(shí)加載的 IASTHookRuleModel,在其中獲取方法簽名對應(yīng)的 sink 方法對象,這里返回的是一個(gè) IASTSinkModel 對象,調(diào)用 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan 進(jìn)行數(shù)據(jù)預(yù)處理,預(yù)處理主要是包括對 unvalidated-redirect 和 sql-over-power 兩種漏洞類型的處理。
預(yù)處理之后,調(diào)用 com.secnium.iast.core.handler.vulscan.ScannerFactory#scan,又分別進(jìn)行動靜態(tài)的掃描:
- 靜態(tài)掃描包括:crypto-weak-randomness、crypto-bad-mac、crypto-bad-ciphers、cookie-flags-missing 四種漏洞類型的支持。
- OverPower 掃描:目前沒有具體實(shí)現(xiàn)。
- 動態(tài)掃描:判斷 sink 方法的污點(diǎn)來源是否命中污點(diǎn)池,將當(dāng)前調(diào)用的污點(diǎn)事件存入 EngineManager.TRACK_MAP 中。
source 節(jié)點(diǎn)處理
將當(dāng)前污點(diǎn)來源事件存入 EngineManager.TRACK_MAP 中,將污點(diǎn)來源的返回結(jié)果放入 EngineManager.TAINT_POOL 污點(diǎn)池中。
propagator 節(jié)點(diǎn)處理
處理傳播節(jié)點(diǎn)的邏輯是最復(fù)雜,這里還是簡單描述:
- 如果污點(diǎn)池為空,證明還沒有經(jīng)過 source 點(diǎn),則不處理傳播節(jié)點(diǎn),否則還是去 IASTHookRuleModel 里找對應(yīng)的 IASTPropagatorModel 對象。
- 如果找到了對應(yīng)的對象,則調(diào)用 auxiliaryPropagator() 對象,根據(jù)傳播節(jié)點(diǎn)的配置將結(jié)果寫入 event.outValue 。并將傳播節(jié)點(diǎn)時(shí)間寫入 EngineManager.TRACK_MAP 中。
- 如果沒找到對應(yīng)的對象,則調(diào)用 TrackUtils.smartEventMatchAndSetTaint() 判斷,判斷太長了沒看,最后還是將傳播節(jié)點(diǎn)寫入 EngineManager.TRACK_MAP 中。
看完了這些節(jié)點(diǎn)的處理方式,我們簡單串一下邏輯:
- 一次請求到達(dá)了應(yīng)用程序,首先進(jìn)入 http 節(jié)點(diǎn)處理邏輯,進(jìn)行標(biāo)記和預(yù)處理。
- 請求進(jìn)入到 source 點(diǎn),將 event 放入 EngineManager.TRACK_MAP 中,將 source 的結(jié)果放入了 EngineManager.TAINT_POOL 污點(diǎn)池中。
- 請求再進(jìn)入 propagator 節(jié)點(diǎn)時(shí),根據(jù)配置判斷傳播節(jié)點(diǎn)的參數(shù)是否存在于污點(diǎn)池中,如果是,則將傳播節(jié)點(diǎn) event 放入 EngineManager.TRACK_MAP 中。
- 應(yīng)用程序走到最后的 sink 點(diǎn)時(shí),根據(jù) sink 點(diǎn)的配置,判斷 sink 點(diǎn)的參數(shù)是不是在 TAINT_POOL 中,如果是,則將 sink 點(diǎn)寫入 EngineManager.TRACK_MAP 中。
- 隨著程序的多次調(diào)用,程序還會再次進(jìn)入多次傳播節(jié)點(diǎn),這些節(jié)點(diǎn)也會被放入 EngineManager.TRACK_MAP 中。
- 在應(yīng)用程序執(zhí)行完,回到 http 節(jié)點(diǎn),最后執(zhí)行到 leaveHttp 時(shí),會調(diào)用 GraphBuilder 構(gòu)造污點(diǎn)調(diào)用圖并發(fā)送至云端。
越權(quán)檢測
在看代碼的時(shí)候,多次看到 over power 一類的字樣,想來想去終于想明白了,這可能是越權(quán)的意思。IAST 能檢測越權(quán)?有點(diǎn)意思,那我們來看看他是如何實(shí)現(xiàn)的。
之前提到過 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan,在這個(gè)位置命中 sink 點(diǎn)后,有兩個(gè)處理邏輯:
- 如果命中了 unvalidated-redirect 的 sink 點(diǎn),并且是方法簽名是 setHeader/addHeader 等,就將其理解為可能是登陸成功后的跳轉(zhuǎn)操作,找到其中 Set-Cookie 的值:
- 如果判斷當(dāng)前為登錄邏輯,則保存到 AuthInfoCache中,
- 否則將更新原有緩存的 cookie 信息,并且向云端發(fā)送報(bào)告
- 將會創(chuàng)建 IJdbc 的實(shí)例, 調(diào)用LoginLogicRecognize.handleLoginLogicRecognize() 方法處理登陸邏輯識別:
- 通過 isLoginSqlQuery() 使用正則匹配 sql 語句,看是否有登錄字樣,識別是否為登錄連接
- 調(diào)用 AuthInfoManager.handleAddCookieAction() 方法將“登陸相關(guān)的sql查詢語句”、“cookie”信息、sink 點(diǎn)的 ClassName發(fā)送至云端,并把 cookie 緩存到 AuthInfoCache 中。
后續(xù)在之前也看到了,對應(yīng)的 OverPowerScanner 的 scan 方法沒有具體實(shí)現(xiàn),那這時(shí)候我們可以猜測一下,作者主要是想通過 cookie 和登陸的 sql 語句進(jìn)行關(guān)聯(lián)。通過檢查 sql 語句是否與污點(diǎn)池有關(guān)、檢查 sink 點(diǎn)的參數(shù)是否與污點(diǎn)池有關(guān)來判斷是否有越權(quán)。這些數(shù)據(jù)都被發(fā)送到了云端,那對于云端來說,如何區(qū)分不同權(quán)限的用戶?如何判斷這個(gè)請求是否應(yīng)該匹配到用戶?
以目前程序里的處理邏輯,還做不到鑒權(quán)的功能,在云端中也沒有展示這個(gè)漏洞類型,等待進(jìn)一步的更新。
其他
其他漏洞的檢測邏輯沒什么要說的,主要是 hook 點(diǎn)的選取。
0x04 測試
在看完代碼邏輯和簡單試用后,我們正式的測試一下這個(gè)產(chǎn)品。
首先編譯一下 agent,我這邊的環(huán)境是:
- mvn 3.2.5
- JDK 1.8.0_131
- Tomcat 8.5.31/7.0.25
- 測試代碼:自寫靶場,由于我的靶場是為了測試 RASP 產(chǎn)品而寫,當(dāng)時(shí)為了測試自家 LingXe 以及 OpenRasp 、云鎖、安全狗等等產(chǎn)品包含的 RASP 功能,里面隱藏了很多可以用來插樁和繞過的點(diǎn),因此用來測試 IAST 正好,園長發(fā)了個(gè)修改版在 [https://github.com/tongasdp/tongasdp-test][0],有興趣的小伙伴也可以用來自測。
通過調(diào)試發(fā)現(xiàn)自己編譯 core 和 inject 沒用,他的 agent 無論如何還是會從官網(wǎng)上自己下載這兩個(gè) jar 包并放到 temp 目錄下,不知道是故意的還是寫出來的 bug,因?yàn)槿绻麖墓倬W(wǎng)上下載,也只是下載 agent,在運(yùn)行時(shí)動態(tài)下載 core 和 inject,應(yīng)該是最開始打算試用,沒打算開源??由于我的目的是學(xué)習(xí)調(diào)試,所以我使用了自己編譯的關(guān)閉了 proguard 混淆的版本,并修改了判斷邏輯,使應(yīng)用程序不去云端判斷,直接加載本地 jar。
為了更直觀的看到 agent 對類字節(jié)碼的更改,需要在配置文件中更改 iast.dump.class.enable 和 iast.dump.class.path 相關(guān)參數(shù)。
功能型測試
通過上一章功能實(shí)現(xiàn)的探究,我們已經(jīng)關(guān)注到了幾個(gè)安全檢測功能的實(shí)現(xiàn),那具體的檢測結(jié)果怎么樣呢?

我在自己的靶場里觸發(fā)了絕大部分的漏洞類型,但很遺憾的是,由于云端的搜索引擎問題,以及 sink 點(diǎn)選取問題,我沒能在云端看到太多的檢出漏洞。云端的漏洞展示是有問題的,看不到前一天的漏洞內(nèi)容,不知道是 django 的問題還是什么,建議修復(fù)一下。
也建議作者出一個(gè)官方漏洞靶場,能對應(yīng)到所有洞態(tài)支持的漏洞類型,也容易理解和說明。
性能測試
以下是使用 wrk 進(jìn)行的壓測:

可以看到洞態(tài)給應(yīng)用程序性能帶來的影響特別大,當(dāng)然 IAST 通常都在測試環(huán)境下使用,所以可能并不是特別關(guān)注性能。
0x05 評價(jià)
出來在之前分析過程中的一些我將從兩個(gè)方面對目前版本洞態(tài) IAST 進(jìn)行評價(jià),首先是使用中的一些想法:
- SCA 漏洞組件管理沒有整理和去重,在我測試的過程中多次重啟項(xiàng)目導(dǎo)致同一個(gè)條目在云端能看到很多次。
- 頁面上表格查詢和相關(guān)排序用起來真的難受,建議招一個(gè)設(shè)計(jì)。
- 搜索功能面對小白非常不友好,可以說如果對 IAST 不了解的情況完全用不了這個(gè)管理后臺。
- 支持的漏洞數(shù)量還是少了點(diǎn),并且沒有對這種漏洞的描述、解決建議什么的信息。
- 既然是 java agent 技術(shù)的產(chǎn)品,那應(yīng)該能夠給到用戶完整的調(diào)用鏈、一些關(guān)鍵調(diào)用點(diǎn)信息如代碼行數(shù)等等,而不是只給到一個(gè) http 請求。
作為一名安全研究員,同時(shí)也是 RASP 產(chǎn)品的參與者,我提出幾點(diǎn)想法:
- 所有的 hook 點(diǎn),全部是寫死的規(guī)則文件,無論是本地的 xml 也好,還是遠(yuǎn)端的 json 也好,都將 hook 點(diǎn)的類,描述符,相關(guān)信息完全寫死,我相信這些規(guī)則是通過某些手段生成出來的,但是一旦在 hook 點(diǎn)選取上沒有選擇使用動態(tài)手段,那就失去了和 0 day 打交道的能力。
- hook 點(diǎn)(這里指 sink )的選取還是層次太淺、規(guī)律性較差。
- 洞態(tài) IAST 檢測了當(dāng)前環(huán)境使用的中間件,并發(fā)送給云端,目前除了信息收集還沒看到有什么樣的具體用途,但是通過hook點(diǎn)來看,對于http請求的點(diǎn)還是使用了適配各個(gè)中間件的方式, OpenRASP 也是采用這種方式,這種方式在功能上沒有什么問題,但是同樣地還是失去了動態(tài)性,不優(yōu)雅,也不能做到通用。
- 應(yīng)用在實(shí)現(xiàn)上使用了太多的字符串比較處理,以及正則,這將對原應(yīng)用性能帶來極大的影響。
- 在調(diào)試過程中,包括污點(diǎn)圖的處理,總是被大量的無效信息占用了過多的時(shí)間,比如 StringBuilder 一類的傳播節(jié)點(diǎn),這其實(shí)不是漏洞調(diào)用的關(guān)鍵節(jié)點(diǎn),個(gè)人認(rèn)為沒有必要處理他。
- agent 每次收到請求,只要不是靜態(tài)的,都要向云端發(fā)送報(bào)告,喪心病狂吧。
- 沒有對 ClassLoader 進(jìn)行相關(guān)處理,無論是前兩年的冰蝎,還是各種反序列化的利用 gadget ,包括我自己的 su18.jsp ,都少不了使用 ClassLoader 加載惡意類的請求,這應(yīng)該目前 Java 安全關(guān)注點(diǎn)比較高的地方,還是建議給 IAST 一個(gè)挖 gadget 的可能。
- 攻擊者目前常用的類似內(nèi)存馬、動態(tài)注冊 Filter 一類的、以及像一些反序列化惡意類找 response 對象回顯的,其實(shí)都可以試著搞一搞。
就這樣吧,也不想說太多了。
0x06 吐槽
通過個(gè)人角度,主要覺得這個(gè)項(xiàng)目有以下槽點(diǎn):
- 錯別拼寫真的不少

- 代碼重復(fù)也不少

- 報(bào)告導(dǎo)出的 word 字體到底是咋回事

- 這個(gè)功能我點(diǎn)了,不好使,我沒看代碼有沒有相關(guān)實(shí)現(xiàn),但這功能賊危險(xiǎn),建議還是不要搞了

- 應(yīng)用漏洞的展示頁面中不會出現(xiàn)重復(fù)漏洞,會進(jìn)行合并,但是可怕的是我觸發(fā)一次SQL注入漏洞,頁面上的出現(xiàn)次數(shù)就漲 5 次,這應(yīng)該是同一條請求調(diào)用鏈上觸發(fā)多個(gè) sink 點(diǎn)導(dǎo)致的?

- 這個(gè)日志記錄提供清空和刪除功能不說,詳細(xì)度也完全不夠,絕對過不了等保。。。

- 搜索顯示完全有問題

- 后端明明回?cái)?shù)據(jù)了,為啥前面不展示呢

- 一個(gè)方法的多個(gè)重載方法,沒必要都寫進(jìn)去,因?yàn)橛凶哉{(diào)用啊

- 區(qū)分應(yīng)用是怎么區(qū)分的呢?

0x07 總結(jié)
對于一款使用 Java agent 技術(shù)開發(fā)的工具/產(chǎn)品,重中之重就是 hook 點(diǎn)的選取,以及處理各項(xiàng)邏輯的具體實(shí)現(xiàn),因?yàn)樾枰獙⒋a運(yùn)行到服務(wù)器端,不影響原有功能、不影響原有性能是首要考慮的目標(biāo)。
這是作為框架考慮的,其次是針對各個(gè)漏洞點(diǎn)的檢測邏輯,這部分是需要對漏洞理解的足夠深入,也就是需要安全研究人員的介入,目前洞態(tài)有一部漏洞的檢測是寫死在代碼里的,一部分漏洞的檢測是依靠配置文件的設(shè)置的。還沒有處理成大家習(xí)慣的框架-插件的模式,所以想在此基礎(chǔ)上二開還是需要花費(fèi)較多時(shí)間理解代碼。