【逆向安全】-工具(一)AppShark

一、介紹

Appshark 是一個靜態(tài)污點分析工具,用于掃描 Android 應(yīng)用程序中的漏洞。我做了一點改造,增加了應(yīng)用批處理能力。

  • 工具能力:自動掃描apk漏洞,篩選出符合條件的應(yīng)用集合,再對這部分應(yīng)用有針對性的進(jìn)行人工詳細(xì)分析。主要價值在于對廠商海量的系統(tǒng)應(yīng)用能減少人力篩查成本;

  • 工具特點:支持漏洞規(guī)則自定義、多漏洞規(guī)則并行篩查、支持文件批處理;

  • 工具局限性:分析的是污點在變量之間的傳遞關(guān)系,所以無論是source、sink還是sanitizer描述的具體粒度都是變量(但是也已滿足絕大多數(shù)場景)。

改造后項目倉庫地址:晚點補鏈接//todo
開源項目地址:https://github.com/bytedance/appshark
官方文檔:https://github.com/bytedance/appshark/blob/main/doc/zh/overview.md

二、使用

2.1 工具運行及結(jié)果分析

環(huán)境:>= JDK-11
配置:config/config.json
運行:$ python3 appShark.py -s
輸入
① config配置:主要設(shè)置文件路徑及選擇使用何種漏洞分析規(guī)則來處理文件
② rules: 漏洞分析規(guī)則 (后面會介紹規(guī)則編寫)
輸出
① results.json文件(這里對其他非必要文件做了精簡)
② 發(fā)現(xiàn)匹配漏洞規(guī)則的應(yīng)用,且至少有1個導(dǎo)出組件的情況下,會在包名加上_marked后綴。

results.json文件說明:

{
    "AppInfo": { // App信息
        "AppName": "NFC Service",
        "PackageName": "com.android.nfc",
        "min_sdk": 34,
        "target_sdk": 34,
        "versionCode": 34,
        "versionName": "14",
        "classCount": 1254,
        "methodCount": 8466,
        "appsharkTakeTime": 8571
    },
    "ManifestRisk": { // AndroidManifest安全信息
        "debuggable": false, // 是否允許調(diào)試
        "allowBackup": false, // 是否允許對應(yīng)用數(shù)據(jù)的備份和恢復(fù)
        "usesCleartextTraffic": false //是否使用明文流量(非加密的Http)
    },
    "SecurityInfo": { // 匹配自定義靜態(tài)掃描規(guī)則生成的安全結(jié)果,這里以全局掃描是否有setWifiEnabled調(diào)用為例
        "camille": {
            "setWifiEnabled": {
                "category": "camille",
                "detail": "setWifiEnabled",
                "name": "setWifiEnabled",
                "vulners": [
                    {
                        "details": {
                            "url": "/Users/XX/appshark-main/out/vulnerability/0-setWifiEnabled.html",
                            "position": "<com.android.nfc.ConfirmConnectToWifiNetworkActivity: void onClick(android.view.View)>",
                            "target": [ // 調(diào)用鏈
                                "<com.android.nfc.ConfirmConnectToWifiNetworkActivity: void onClick(android.view.View)>",
                                "virtualinvoke $r3.<android.net.wifi.WifiManager: boolean setWifiEnabled(boolean)>(1)"
                            ]
                        },
                        "hash": "eb84e25e4fd117ff421ff2a2c81ef92671f087c7",
                        "old_hash": "68064390a20df5d492e9525b7c43acf8096e8c83"
                    },
                    ...
                ],
                "deobfApk": ""
            }
        }
    },
    "DeepLinkInfo": {
    },
    "HTTP_API": [
    ],
    "JsBridgeInfo": [
    ],
    "BasicInfo": { // 組件基本信息,這里主要是呈現(xiàn)組件是否導(dǎo)出及其相關(guān)信息
        "ComponentsInfo": {
            "exportedReceivers": {
                "com.android.nfc.NfcReaderDetector$1": {
                    "exported": true,
                    "DynamicBroadcastReceiver": "com.android.nfc.NfcReaderDetector$1",
                    "RegisteredMethod": "<com.android.nfc.NfcReaderDetector: void <init>(android.content.Context)>",
                    "RegisteredStmt": "virtualinvoke $r1_1.<android.content.Context: android.content.Intent registerReceiver(android.content.BroadcastReceiver,android.content.IntentFilter,int)>($r3, $r7, 2)"
                },
                "com.android.nfc.NfcBootCompletedReceiver": {
                    "exported": true,
                    "<receiver exported=true name=com.android.nfc.NfcBootCompletedReceiver>": [
                        {
                            "<intent-filter>": [
                                {
                                    "content": "<action name=android.intent.action.BOOT_COMPLETED>",
                                    "isString": true
                                }
                            ]
                        }
                    ]
                },
                ...
            },
            "unExportedActivities": {
                "com.android.nfc.TechListChooserActivity": {
                    "exported": false,
                    "<activity process=:com.android.nfc.chooser finishOnCloseSystemDialogs=true name=com.android.nfc.TechListChooserActivity launchMode=3 multiprocess=false theme=16974850 excludeFromRecents=true>": [
                    ]
                },
                "com.android.nfc.cardemulation.AppChooserActivity": {
                    "exported": false,
                    "<activity clearTaskOnLaunch=true finishOnCloseSystemDialogs=true name=com.android.nfc.cardemulation.AppChooserActivity multiprocess=true theme=2131886364 excludeFromRecents=true>": [
                    ]
                },
                ...
            },
            "unExportedProviders": {
                "androidx.startup.InitializationProvider": {
                    "exported": false,
                    "<provider exported=false name=androidx.startup.InitializationProvider authorities=com.android.nfc.androidx-startup>": [
                        {
                            "<meta-data name=androidx.lifecycle.ProcessLifecycleInitializer value=androidx.startup>": [
                            ]
                        }
                    ]
                },
                ...
            },
            "unExportedServices": {
                "com.android.nfc.handover.PeripheralHandoverService": {
                    "exported": false,
                    "<service name=com.android.nfc.handover.PeripheralHandoverService>": [
                    ]
                },
                ...
            },
            "unExportedReceivers": {
                "androidx.profileinstaller.ProfileInstallReceiver": {
                    "exported": false,
                    "<receiver exported=true name=androidx.profileinstaller.ProfileInstallReceiver permission=android.permission.DUMP enabled=true directBootAware=false>": [
                        {
                            "<intent-filter>": [
                                {
                                    "content": "<action name=androidx.profileinstaller.action.INSTALL_PROFILE>",
                                    "isString": true
                                }
                            ]
                        },
                        ...
                    ]
                }
            },
            "exportedActivities": {
                "com.android.nfc.BeamShareActivity": {
                    "exported": true,
                    "<activity exported=true noHistory=true finishOnCloseSystemDialogs=true icon=2131230958 name=com.android.nfc.BeamShareActivity theme=16973839 excludeFromRecents=true label=2131820586>": [
                        {
                            "<intent-filter>": [
                                {
                                    "content": "<action name=android.intent.action.SEND>",
                                    "isString": true
                                },
                                {
                                    "content": "<category name=android.intent.category.DEFAULT>",
                                    "isString": true
                                },
                                {
                                    "content": "<data mimeType=*/*>",
                                    "isString": true
                                }
                            ]
                        },
                        ...
                    ]
                }
            }
        },
        "JSNativeInterface": [
        ]
    },
    "UsePermissions": [ // 申請的權(quán)限
        "android.permission.BLUETOOTH_PRIVILEGED",
      ...
    ],
    "DefinePermissions": { // 自定義權(quán)限
        "com.miui.nfc.permission.SEND_HCI_EVENT": "signatureOrSystem",
       ...
    },
    "Profile": "/Users/XX/appshark-main/out/vulnerability/2-profiler.json"
}

2.2 配置文件設(shè)置

config.json5主要的設(shè)置項:
"apkPath": // apk文件路徑,必要參數(shù)
"out": "", // 結(jié)果輸出路徑
"maxThread": 1, // 控制內(nèi)部進(jìn)行指針分析等操作時的并行度,默認(rèn)數(shù)量為2
"rules": "wifiEnable.json", // 自定義規(guī)則配置
"rulePath": "config/rules", //specifies the rule's parent directory, default is ./config/rules
"logLevel": 1, //debug 0;info 1;warn 2;error 3
"javaSource": true, //是否在最終的漏洞詳情中展示源碼. 該源碼是通過jadx反編譯得到.
"supportFragment":true , //是否對處理Fragment的lifeCycle函數(shù). 類似于處理Activity的onCreate等函數(shù)
"wholeProcessMode": //是否進(jìn)行全程序分析,默認(rèn)為false. 全行程分析主要是影響分析的范圍和性能表現(xiàn)

2.3 自定義規(guī)則撰寫

  1. 規(guī)則模塊介紹:
    entry : 分析入口,一般是個函數(shù)
    source:污染源
    sink:污染利用點
    sanitizer: 過濾source到sink的無效鏈路,消除誤報。

注:appshark分析的是污點在變量之間的傳遞關(guān)系,所以無論是source,還是sink,還是sanitizer描述的具體粒度都是變量。

  1. 各模塊規(guī)則撰寫實現(xiàn):
    規(guī)則JSON編寫框架
{
  "unZipSlip": {
    "SliceMode": true, // 1 分析模式入口
    "desc": {
      "category": "FileRisk", // 2 分類
    },
    "entry": { // 3 分析入口
    },
    "source": { // 4 污染源定義
    },
    "sanitizer": { // 6 過濾無效鏈路
    },
    "sink": { // 5 污染利用點
    }
  }
}

① 分析入口模式(mode)
DirectMode: 固定分析入口。需要明確指明分析的入口,即 : entry;
SliceMode: 不固定分析入口。和DirectMode的區(qū)別是它的分析入口不是固定的,而是根據(jù)具體的source,sink計算得到的;
ConstStringMode: 以常量字符串所在的函數(shù)作為分析入口。不受traceDepth的約束;
ConstNumberMode: 以常量數(shù)值所在的函數(shù)作為分析入口。不受traceDepth的約束;
APIMode: APIMode和前面的幾種mode都不一樣,他并不是一個數(shù)據(jù)流分析的規(guī)則,而是一個簡單的查找指定api的規(guī)則。

② 分類(category)
這里就是封裝安全信息的key,這里key已經(jīng)優(yōu)化了配置:保持名稱與分類一致即可

"setWifiEnabled": {
     "desc": {     
        "category": "setWifiEnabled",
      }
}

③ 分析入口(entry)

"entry": { 
     "methods": [  "<net.bytedance.security.app.ruleprocessor.testdata.ZipSlip: void UnZipFolder(java.lang.String,java.lang.String)>" ] 
}

分析入口一般是一個函數(shù)。按jimple規(guī)則:<類名>:<返回值> <函數(shù)> (<函數(shù)參數(shù)>...) 為模板設(shè)置,entry只有在DirectMode下需要明確指定,其他三個模式下,都無需明確指明分析入口。

④ 污染源定義(source)

  • 常量字符串
  • 函數(shù)返回值
  • 某對象的field
  • 某個函數(shù)的參數(shù)
  • 某個對象的創(chuàng)建

I 常量字符串

"source": { 
    "ConstString": ["path1"]
 }

對應(yīng):
String s="path1";
f(12,"path1");
s將成為source. 函數(shù)f的參數(shù)1將成為source

II 函數(shù)返回值

"source": { 
      "Return": ["<java.util.zip.ZipEntry: java.lang.String getName()>" ]
    }

也就是getName的返回值將會是source, 那么:
ZipEntry e=getEntry();
String name=e.getName();
name將成為source點。

III 某對象的field

"source": {
    "Field": [  "<android.provider.CalendarContract: android.net.Uri CONTENT_URI>", ] 
}

Uri uri=CalendarContract.CONTENT_URI;
uri將會成為source點. 注意不區(qū)分該field是靜態(tài)field還是非靜態(tài)field。

IV 某個函數(shù)的參數(shù)

"source": { 
          "Param": { 
              "<android.webkit.WebViewClient: android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest)>": [ "p1" ]
           } 
}

p0是第一個參數(shù),p1是第二個參數(shù),這里p1對應(yīng)的是WebResourceRequest,它才是source。

V 某個對象的創(chuàng)建

"source": {
      "NewInstance": ["android.content.Intent"] 
}

那么:
android.content.Intent i=new android.content.Intent();
這時候變量i將成為source點。

⑤ 污染源利用(sink)
key:

  • LibraryOnly 默認(rèn)值為false,如果設(shè)置為true,那么就要求匹配到的函數(shù)簽名必須是EngineConfig.json5中指定的Library
  • TaintParamType 參數(shù)類型限制
  • TaintCheck 檢查規(guī)則

限制條件(目前sink點只能是函數(shù)的周邊)

  • this指針 @this "TaintCheck": [ "@this"]
  • 函數(shù)的某個參數(shù) p0,p1,p2,所有參數(shù)p* "TaintCheck": [ "p*" ]
  • 函數(shù)的返回值 return "TaintCheck": [ "return" ]

多條件設(shè)置寫法:"TaintCheck": [ "@this","return" ]
舉例:

"sink": {     
     "<*: * startActivit*(*)>": {          
                  "LibraryOnly": true,          
                  "TaintParamType": [                  
                                 "android.content.Intent",                  
                                 "android.content.Intent[]"           
                  ],            
                  "TaintCheck": ["p*"]          
                 }    
 }

這里定startActivit相關(guān)泛函數(shù)的入?yún)?Intent/.Intent[]為漏洞利用點,即sink,appshark會檢查能否找到從source到這些變量的一個污點傳播路徑。

⑥ 過濾無效鏈路規(guī)則(sanitizer)
sanitizer目的是消除誤報. 發(fā)現(xiàn)了一條從source到sink的完整傳播路徑, 該路徑經(jīng)由sanitizer規(guī)則過濾,如果滿足條件就刪掉這條路徑,否則保留。

以上面unZipSlip舉例:

"rule1": {
        "<java.io.File: java.lang.String getCanonicalPath()>": {
          "TaintCheck": ["@this" ]
        }
      },

      "containsDotDot": {
        "<java.lang.String: boolean contains(java.lang.CharSequence)>": {
          "TaintCheck": [ "@this"],
          "p0": ["..*"]
        }
      },

      "indexDotDot": {
        "<java.lang.String: boolean indexOf(java.lang.String)>": {
          "TaintCheck": ["@this"],
          "p0": [ "..*"]
        }
      }

頂層規(guī)則是或的關(guān)系:也就是說,你可以自定義多個一級key,這里就是指rule1、containsDotDot、indexDotDot,過濾規(guī)則滿足他們3個中的1個就過濾掉;

二層規(guī)則間是與的關(guān)系:containsDotDot下的TaintCheck和p0規(guī)則是與的關(guān)系,即:
if(path.contains("../")){
return false
}

"TaintCheck": ["@this"]:從source出發(fā),傳播到的所有變量中,是否污染到了<java.lang.String: boolean contains(java.lang.CharSequence)>這個函數(shù)的this指針,即path

"p0": [ ".."] :常量字符串..污染到contains的參數(shù)0

因此這個規(guī)則意思就是看代碼里是否有"../"相關(guān)的判斷語句,如果有就認(rèn)為是對這種漏洞有預(yù)防,就不用統(tǒng)計了。

更詳細(xì)撰寫規(guī)則說明參考官方文檔:https://github.com/bytedance/appshark/blob/main/doc/zh/how_to_write_rules.md

了解到這,結(jié)合官方詳細(xì)文檔,自己就可以編寫簡單規(guī)則試試看了,項目中也提供了一些寫好的規(guī)則模板,可以借鑒學(xué)習(xí)。

三、實現(xiàn)原理分析

① 配置文件解析 :Json解析config.json;
② apk解析:使用jadx對apk反編譯成java; 解析manifest和resource文件,提權(quán)清單文件信息,包括:應(yīng)用、組件信息、權(quán)限信息等;
③ 代碼預(yù)處理:通過soot框架構(gòu)建call graph, 即各函數(shù)的可達(dá)路徑圖;
④ 用戶自定義規(guī)則解析:解析規(guī)則,查找source、sink;
⑤ 數(shù)據(jù)流分析:基于call graph和漏洞規(guī)則,尋找source到sink調(diào)用鏈;
⑥ 輸出報告:封裝數(shù)據(jù),輸出報告文件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容