0x00 简介
Android应用通常使用PF_UNIX、PF_INET、PF_NETLINK
等不同domain的socket来进行本地IPC或者远程网络通信,这些暴露的socket代表了潜在的本地或远程攻击面,历史上也出现过不少利用socket进行拒绝服务、root提权或者远程命令执行的案例。特别是PF_INET
类型的网络socket,可以通过网络与Android应用通信,其原本用于linux环境下开放网络服务,由于缺乏对网络调用者身份或者本地调用者pid、permission等细粒度的安全检查机制,在实现不当的情况下,可以突破Android的沙箱限制,以被攻击应用的权限执行命令,通常出现比较严重的漏洞。作为Android安全研究的新手,笔者带着传统服务器渗透寻找开放socket端口的思路,竟然也刷了不少漏洞,下面就对这种漏洞的发现、案例及影响进行归纳。
0x01 Android开放端口应用定位
简单地利用命令netstat就可以发现Android开放了许多socket端口,如图。但这些开放端口本后的应用却不得而知。
此时可以通过三步定位法进行寻找(感谢@瘦蛟舞的帖子),支持非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,尽管我们还不能分辨出开放该端口的准确进程,但仍然为进一步的漏洞挖掘打下基础。
写一个简单的脚本来自动化的完成此项工作.
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。
0x02 漏洞挖掘实例
得知某个应用开放某个端口以后,接下就可以在该应用的逆向代码中搜索端口号(通常是端口号的16进制表示),重点关注ServerSocket(tcp)、DatagramSocket(udp)等类,定位到关键代码,进一步探索潜在的攻击面,下面列举一些漏洞实例。
1、敏感信息泄露、控制手机
WooYun-2015-94537:某service打开udp的65502端口监听,接收特定的命令字后可返回手机的敏感信息,包括手机助手远程管理手机的SecretKey,进而未授权的攻击者可通过网络完全管理手机。
CVE-2014-8757, LG On-Screen Phone预装App认证绕过漏洞。
2、命令执行
这类漏洞比较常见,通常通过开放socket端口传入启动android应用组件的intent,然后以被攻击应用的权限执行启动activity、发送广播等操作。由于通过socket传入的intent,无法对发送者的身份和权限进行细粒度检查,绕过了Android提供的对应用组件的权限保护,能够启动未导出的和受权限保护的应用组件,对安全造成影响。
如果监听的端口是在本地,那么可能造成本地命令执行和权限提升,而如果监听的端口是任意地址,则可能造成比较严重的远程命令执行。
3、本地命令执行:
用前面端口应用定位的方法,发现某流行应用实现了一个小型的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请求后执行命令的代码如下:
... 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。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/55316.html