1、基礎知識
1.1、java插樁技術 :javaagent
-
允許在jvm啟動時優先執行agent代碼,通常agent代碼用來添加Transformer,提供了對class在加載時進行字節碼修改的機會。
-
java啟動時增加參數java -javaagent:/Users/didi/Downloads/openrasp-javaagent/boot/target/rasp.jar -jar myapplication.jar
-
jar包構建時設置入口 <Premain-Class>com.baidu.openrasp.Agent</Premain-Class> ,啟動時會進入此類的premain方法執行
public static void premain(String agentArg, Instrumentation inst) {inst.addTransformer(new CustomClassTransformer());}
1.2、java字節碼修改:javaassist
- 其他類似的庫:
- javaassist:openrasp用的這個,學習門檻低,源碼級別,不需要掌握字節碼相關技術。
- asm:字節碼級別,學習門檻高,需要掌握字節碼技術。性能好
- cglib:spring aop用的這個進行字節碼增強
- bcel:apache
2、OpenRasp原理
2.1、openrasp介紹
- 百度開源rasp,提供了完整的rasp + iast的功能,包括以下模塊:
- java + php 的探針,對應用插樁,收集應用運行時數據; (java)
- v8引擎 + rasp檢測插件,根據hook信息判斷是否為攻擊;(javascript)
- 管理控制台,用於接收及查看rasp攻擊事件、iast漏洞信息、配置下發、應用管理等功能;(golang)
- iast fuzz功能,用戶對探針采集的流量增加poc並進行重放 (python);
- rasp支持的攻擊類型:
- iast支持的漏洞類型:
- 命令注入
- 目錄遍歷
- PHP eval代碼執行
- 文件上傳
- 文件包含
- 任意文件讀取
- 任意文件寫入
- SQL注入
- SSRF
- Java XXE
2.2、啟動過程
- a、通過插樁技術,進入openrasp入口com.baidu.openrasp.Agent.premain方法
- b、premain方法執行初始化,並加載引擎模塊,引擎模塊執行后續動作
- c、加載配置:本地yml文件 + 雲端定期拉取,配置比如:每個攻擊類型是否攔截還是只記錄、agent身份認證、郵件&釘釘報警、控制台密碼
loadConfigFromFile(new File(configFilePath), true);
- e、CheckerManager初始化,為不同的攻擊類型,設置不同的檢測checker,大部分攻擊使用V8AttackChecker檢測
public synchronized static void init() throws Exception {for (Type type : Type.values()) {checkers.put(type, type.checker);}}<!-- // js插件檢測--><!--SQL("sql", new V8AttackChecker(), 1),--><!--COMMAND("command", new V8AttackChecker(), 1 << 1),--><!--DIRECTORY("directory", new V8AttackChecker(), 1 << 2),--><!--REQUEST("request", new V8AttackChecker(), 1 << 3),--><!--READFILE("readFile", new V8AttackChecker(), 1 << 5),--><!--WRITEFILE("writeFile", new V8AttackChecker(), 1 << 6),--><!--FILEUPLOAD("fileUpload", new V8AttackChecker(), 1 << 7),--><!--RENAME("rename", new V8AttackChecker(), 1 << 8),--><!--XXE("xxe", new V8AttackChecker(), 1 << 9),--><!--OGNL("ognl", new V8AttackChecker(), 1 << 10),--><!--DESERIALIZATION("deserialization", new V8AttackChecker(), 1 << 11),--><!--WEBDAV("webdav", new V8AttackChecker(), 1 << 12),--><!--INCLUDE("include", new V8AttackChecker(), 1 << 13),--><!--SSRF("ssrf", new V8AttackChecker(), 1 << 14),--><!--SQL_EXCEPTION("sql_exception", new V8AttackChecker(), 1 << 15),--><!--REQUESTEND("requestEnd", new V8AttackChecker(), 1 << 17),--><!--DELETEFILE("deleteFile", new V8AttackChecker(), 1 << 18),--><!--MONGO("mongodb", new V8AttackChecker(), 1 << 19),--><!--LOADLIBRARY("loadLibrary", new V8AttackChecker(), 1 << 20),--><!--SSRF_REDIRECT("ssrfRedirect", new V8AttackChecker(), 1 << 21),--><!--RESPONSE("response", new V8AttackChecker(false), 1 << 23),--><!--LINK("link", new V8AttackChecker(), 1 << 24),--><!--// java本地檢測--><!--XSS_USERINPUT("xss_userinput", new XssChecker(), 1 << 16),--><!--SQL_SLOW_QUERY("sqlSlowQuery", new SqlResultChecker(false), 0),--><!--// 安全基線檢測--><!--POLICY_LOG("log", new LogChecker(false), 1 << 22),--><!--POLICY_MONGO_CONNECTION("mongoConnection", new MongoConnectionChecker(false), 0),--><!--POLICY_SQL_CONNECTION("sqlConnection", new SqlConnectionChecker(false), 0),--><!--POLICY_SERVER_TOMCAT("tomcatServer", new TomcatSecurityChecker(false), 0),--><!--POLICY_SERVER_JBOSS("jbossServer", new JBossSecurityChecker(false), 0),--><!--POLICY_SERVER_JBOSSEAP("jbossEAPServer", new JBossEAPSecurityChecker(false), 0),--><!--POLICY_SERVER_JETTY("jettyServer", new JettySecurityChecker(false), 0),--><!--POLICY_SERVER_RESIN("resinServer", new ResinSecurityChecker(false), 0),--><!--POLICY_SERVER_WEBSPHERE("websphereServer", new WebsphereSecurityChecker(false), 0),--><!--POLICY_SERVER_WEBLOGIC("weblogicServer", new WeblogicSecurityChecker(false), 0),--><!--POLICY_SERVER_WILDFLY("wildflyServer", new WildflySecurityChecker(false), 0),--><!--POLICY_SERVER_TONGWEB("tongwebServer", new TongwebSecurityChecker(false), 0),--><!--POLICY_SERVER_BES("bes", new BESSecurityChecker(false), 0);-->
- f、構造CustomClassTransformer,設置到Instrumentation:關鍵動作如下:
transformer = new CustomClassTransformer(inst);public CustomClassTransformer(Instrumentation inst) {this.inst = inst;inst.addTransformer(this, true);addAnnotationHook();}
- ServerDetectorManager構造,並創建所有服務器檢測類,比如tomcat、springboot、webloigc、jboss、jetty、resin、dubbo、bes、ubdertow等
private ServerDetectorManager serverDetector = ServerDetectorManager.getInstance();
- 對所有com.baidu.openrasp.hook包里的class進行掃描,判斷每個class是否設置了注解HookAnnotation,如果設置了則加入CustomClassTransformer的hooks集合
Set<Class> classesSet = AnnotationScanner.getClassWithAnnotation(SCAN_ANNOTATION_PACKAGE, HookAnnotation.class);
- ServerDetectorManager構造,並創建所有服務器檢測類,比如tomcat、springboot、webloigc、jboss、jetty、resin、dubbo、bes、ubdertow等
- g、對已經加載和后續加載的class進行transform,transform包括以下處理:
-
判斷class所在的文件路徑是否為jar包,如果是則加入 loadedJarPaths ,這樣可以獲取所有加載的jar包,可用於三方組件檢測
-
調用hooks集合每個hook的isClassMatched與每個class進行匹配,判斷是否需要hook,如果需要則進行hook處理
- 主要是利用字節碼技術 javaassist 對相應的字節碼進行修改,插入hook邏輯
// 這個類hook發送網絡請求的類,用於檢測ssrf;這個hook點類似於白盒的sink點@HookAnnotationpublic class URLConnectionHook extends AbstractSSRFHook {@Overridepublic boolean isClassMatched(String className) { //這個方法判斷當前class是否需要hookreturn "sun/net/www/protocol/http/HttpURLConnection".equals(className) ||"weblogic/net/http/HttpURLConnection".equals(className);}@Override //這個方法執行真正的hook邏輯,完成對class的修改protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {String src = getInvokeStaticSrc(URLConnectionHook.class, "checkHttpConnection","$0", URLConnection.class);// 通過javaassit修改class,插入一段源碼srcinsertBefore(ctClass, "getInputStream", "()Ljava/io/InputStream;", src);src = getInvokeStaticSrc(URLConnectionHook.class, "onExit", "$0", Object.class);insertAfter(ctClass, "getInputStream", "()Ljava/io/InputStream;", src, true);}public static void onExit(Object urlConnection) {try {if (isChecking.get() && !isExit.get() && URLConnectionRedirectHook.urlCache.get() != null) {// 以下會繼續調用 getinpustream isExit 避免死循環isExit.set(true);HashMap<String, Object> cache = originCache.get();HashMap<String, Object> redirectCache = getSsrfParamWithURL(URLConnectionRedirectHook.urlCache.get());if (cache != null && redirectCache != null) {AbstractRedirectHook.checkRedirect(cache, redirectCache,((HttpURLConnection) urlConnection).getResponseMessage(), ((HttpURLConnection) urlConnection).getResponseCode());}}} catch (Exception e) {// ignore} finally {isChecking.set(false);originCache.set(null);isExit.set(false);URLConnectionRedirectHook.urlCache.set(null);}}private static HashMap<String, Object> getSsrfParamWithURL(URL url) {try {String host = null;String port = "";if (url != null) {host = url.getHost();int temp = url.getPort();if (temp > 0) {port = temp + "";}}if (url != null && host != null) {return getSsrfParam(url.toString(), host, port, "url_open_connection");}} catch (Exception e) {// ignore}return null;}public static void checkHttpConnection(URLConnection urlConnection) {if (!isChecking.get()) {isChecking.set(true);if (urlConnection != null) {URL url = urlConnection.getURL();HashMap<String, Object> param = getSsrfParamWithURL(url);checkHttpUrl(param);originCache.set(param);}}}}
- 主要是利用字節碼技術 javaassist 對相應的字節碼進行修改,插入hook邏輯
-
調用serverDetector.detectServer,檢測服務器類型,比如是否為springboot、tomcat、weblogic
// springboot 判斷,class是否為 org/apache/catalina/startup/Bootstrappublic boolean isClassMatched(String className) {return "org/apache/catalina/startup/Bootstrap".equals(className);}
-
- h、注冊應用,向控制台注冊應用,並且定期發送心跳、上報漏洞、異常、組件依賴信息等。
2.3、攻擊檢測:
- hook點拿到對應的參數信息后,交給hook指定攻擊類型的checker處理(在啟動時為每個攻擊類型設置過對應的checker),大部分攻擊使用V8AttackChecker檢測
- V8AttackChecker調用v8引擎,通過js插件來進行判斷是否為攻擊、是否進行攔截
- V8AttackChecker -> jni方式調用chrome v8 -> js plugin
function check_ssrf(params, context, is_redirect) {var hostname = params.hostnamevar url = params.urlvar ip = params.ipvar reason = false// 算法1 - 當參數來自用戶輸入,且為內網IP,判定為SSRF攻擊if (algorithmConfig.ssrf_userinput.action != 'ignore'){var retret = check_internal(params, context, is_redirect)// 過濾非HTTP請求(dubbo)var header = context.header || {}if (ret && Object.keys(header).length != 0) {return ret}}// 算法2 - 檢查常見探測域名if (algorithmConfig.ssrf_common.action != 'ignore'){if (is_hostname_dnslog(hostname)){return {action: algorithmConfig.ssrf_common.action,message: _("SSRF - Requesting known DNSLOG address: %1%", [hostname]),confidence: 100,algorithm: 'ssrf_common'}}}<!-- domains: [--><!-- '.vuleye.pw',--><!-- '.ceye.io',--><!-- '.exeye.io',--><!-- '.vcap.me',--><!-- '.xip.name',--><!-- '.xip.io',--><!-- '.sslip.io',--><!-- '.nip.io',--><!-- '.burpcollaborator.net',--><!-- '.tu4.org',--><!-- '.2xss.cc',--><!-- '.bxss.me',--><!-- '.godns.vip',--><!-- '.pipedream.net' // requestbin 新地址--><!--]-->// 算法3 - 檢測 AWS/Aliyun/GoogleCloud 私有地址: 攔截IP訪問、綁定域名訪問兩種方式if (algorithmConfig.ssrf_aws.action != 'ignore'){if (ip == '169.254.169.254' || ip == '100.100.100.200'|| hostname == '169.254.169.254' || hostname == '100.100.100.200' || hostname == 'metadata.google.internal'){return {action: algorithmConfig.ssrf_aws.action,message: _("SSRF - Requesting AWS metadata address"),confidence: 100,algorithm: 'ssrf_aws'}}}// 算法4 - ssrf_obfuscate//// 檢查混淆:// http://2130706433// http://0x7f001//// 以下混淆方式沒有檢測,容易誤報// http://0x7f.0x0.0x0.0x1// http://0x7f.0.0.0if (algorithmConfig.ssrf_obfuscate.action != 'ignore'){var reason = falseif (!isNaN(hostname) && hostname.length != 0){reason = _("SSRF - Requesting numeric IP address: %1%", [hostname])}// else if (hostname.startsWith('0x') && hostname.indexOf('.') === -1)// {// reason = _("SSRF - Requesting hexadecimal IP address: %1%", [hostname])// }if (reason){return {action: algorithmConfig.ssrf_obfuscate.action,message: reason,confidence: 100,algorithm: 'ssrf_obfuscate'}}}// 算法5 - 特殊協議檢查if (algorithmConfig.ssrf_protocol.action != 'ignore'){// 獲取協議var proto = url.split(':')[0].toLowerCase()if (algorithmConfig.ssrf_protocol.protocols.indexOf(proto) != -1){return {action: algorithmConfig.ssrf_protocol.action,message: _("SSRF - Using dangerous protocol: %1%://", [proto]),confidence: 100,algorithm: 'ssrf_protocol'}}}return false}
2.4、iast漏洞檢測:
- 非重放
- 對rasp的攻擊事件數據按照stack合並去重后作為漏洞
- 重放
- 利用javaagent抓取請求信息,然后參數加上poc后重放(fuzz)
- 業界情況
- 大部分公司已經在開發
- 京東、阿里、華為、攜程、58、美團、百度、騰訊、華泰、快手、字節
- 優點:
- java開源的比較成熟,可以快速拿來用,其他語言較少;
- 檢測准確率高、覆蓋率也不錯(如果是測試環境取決於QA的測試覆蓋度);
- 可以拿到完整的調用鏈,類似白盒的數據流但也不太一樣;
- 可以很方便的支持dubbo之類的框架;
- 可以比較方便的拿到所有三方組件依賴信息;
- 不存在白盒那種路徑爆炸、spring依賴注入之類的問題;
- 完整的獲取API接口信息、請求和響應數據,檢測敏感數據的流向、收集、輸出很方便;
- 可以更精准的檢測日志打印敏感數據、數據庫存儲敏感數據等目前黑白盒檢測不了的風險。
- 可以檢測web弱口令;中間件配置類安全問題;
- 缺點:
- 部署成本較高,生產環境對穩定性、性能要求較高
- 每種語言的實現方式不一樣,需要適配多種開發語言,目前開源的方案java相對來說比較成熟,其他語言實踐較少。
- 對業務應用有侵入性、如果存在bug可能導致應用出問題,比如crash;
- 跨線程促發的漏洞檢測調用鏈不完整;
- 大部分公司已經在開發