CTF之mobile入门
2023-4-14 00:3:10 Author: 白帽子(查看原文) 阅读量:14 收藏

本着入门安卓逆向的目的,由于对安卓逆向没有过多的了解故准备从CTF中的安卓题目入手,在安卓逆向入门阶段对一些安卓基础进行学习。选择CTF安卓逆向入门的主要目的是在安卓逆向中熟练掌握逆向工具的使用,本文章不涉及深入的安卓逆向代码分析。

  • Android Killer

  • JEB

  • jd-gui

工具获取:链接:https://pan.baidu.com/s/1_yahr6Z7zZIA3uOXAHkHBQ?pwd=6vsq提取码:6vsq

通过攻防世界中的6个例题,整理了解题思路。

easyjni

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5091&page=1

下载题目安装入模拟器,运行软件进行测试,发现输入任意文字提交提示“You are wrong! bye~”。

将apk丢入Android Killer进行反编译。

反编译完成后搜索“You are wrong! bye~”字符串,发现位于MainActivity

使用jd-gui工具直接查看反编译后的java代码

在MainActivity中首先导入了名为“native”的so文件,然后在“You are wrong! bye~”处,程序首先将输入框得到的字符串与MainActivity.this属性作为参数,调用MainActivity中的a方法返回的布尔值作为是否通关条件的。

在这里参数是经过“com/a/easyjni/a”这个包里的a类new出一个实例,然后跟进a。

根据特征发现a类是替换了编码表的base64加密

在native方法,程序开头已经加载了库,所以,将apk解压,然后IDA载入libnative.so文件

找到Java_com_a_easyjni_MainActivity_ncheck函数,F5查看伪代码

长度32的字符串,前16个和后16个交换。

从字符串首位开始,每2个交换位置。

比较

  1. 从字符串首位开始,每2位交换位置。

  2. 前16和后16进行交换。

  3. 变异base64解密。

import base64#每2位交换位置str1 = list("MbT3sQgX039i3g==AQOoMQFPskB1Bsc7")str1_result = ''for i in range(0, len(str1), 2):    str1_result += str1[i+1] + str1[i]#前16和后16交换位置str2 = list(str1_result)str2_result = ''.join(str2[i] for i in range(16, 32)) + ''.join(str2[j] for j in range(0, 16))#变异base64解密base_now = ['i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v',            '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g',            'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h',            'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N']base_now_str = ''.join(i for i in base_now)base_original_str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'flag = base64.b64decode(str2_result.translate(str.maketrans(base_now_str, base_original_str)))print(flag)

## flag{just_ANot#[email protected]}

Ph0en1x-100

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5093&page=1

下载题目安装入模拟器,测试发现提示“Failed”将apk丢入Android Killer进行反编译。

反编译完成后搜索“Failed”定位到MainActivity.samli

使用jd-gui工具直接查看反编译后的java代码

程序中在if判断中前后都调用getSecret方法,通过判断参数相等即可。也就是说getFlag()=encrypt(paraView)其中paraView是我们的输入的字符串

在MainActivity中首先导入了名为“phcm”的so文件,接着使用两个native方法

解压apk包使用IDA加载libphcm.so库,(干扰项:此步骤中lib中有三个so库文件,分别对应文件夹armeabi、armeabi-v7a和x86,三个不同so库对应不同处理器架构,此处armeabi、armeabi-v7a下的so库文件为干扰项,通过加载后分析getFlag和encrypt函数可知),分别查看

“Java_com_ph0en1x_android_1crackme_MainActivity_encrypt”函数

“Java_com_ph0en1x_android_1crackme_MainActivity_getFlag”函数

encrypt:

对字符串每个字符ASCII值减一

getFlag比较复杂如下图

根据思路 getflag的值=我们输入的字符串每个字符ASCII减一,也就是说getflag字符串每个字符ASCII+1就是正确答案。

getflag值得获得:

  1. 动态调试

Java_com_ph0en1x_android_1crackme_MainActivity_getFlag

2.修改smali源码,让app显示getFlag方法的执行结果

显然此题考查的为安卓逆向技能,使用第二种方法,修改smali源码,让原来页面提示“Failed”的地方输出getFlag结果。

flag = "ek`[email protected]^x/t^fn0mF^6/^rb`qanqntfg^E`hq|"result = ''for i in flag:    result += chr(ord(i) + 1)print(result)

## flag{Ar3_y0u_go1nG_70_scarborough_Fair}

app1

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5085&page=1

下载题目安装入模拟器,测试发现提示“再接再厉,加油~”将apk丢入Android Killer进行反编译。

搜索“再接再厉,加油~”Unicode编码定位到字符串位置。

使用jd-gui工具直接查看反编译后的java代码

程序的实现逻辑为:

获取版本名和版本号,获取输入

遍历输入的字符串,当输入的字符与版本名与版本号的异或不一致时,输出“再接再厉,加油~”一致时输出“恭喜开启闯关之门!”

在BuildConfig配置文件可获取到版本号和版本名信息

将版本名与版本号异或得到答案

s="X<cP[?PHNB<P?aj"flag=""for i in s:    flag+=chr(ord(i)^15)print (flag)
## flag{W3l_T0_GAM3_0ne}

app2

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5086&page=1

下载题目首先安装入模拟器运行一下,没有找到有用信息。直接使用Android Killer进行逆向

找到程序入口MainActivity,使用jd-gui进行反编查看源码

对源码进行分析可看出在onClick()类中变量c、d获取了文本框中的输入保存为ili传入了SecondActivity。跟进SecondActivity,在onCreate中取出了ili字符串并对其进行了加密与另一加密字符串进行对比

跟进加密,可以看出代码调用了“JNIEncrypt”so文件

使用压缩包解压安装包将lib下的so文件导入IDA进行分析doRawData函数

分析代码

,AES_128_ECB_PKCS5Padding_Encrypt()对传入的数据做了AES加密。

可以看到v14-v10这里组成了个数组,我们将其取出。秘钥为“thisisatestkey==”,

对“VEIzd/V2UPYNdn/bxH3Xig==”进行解密结果为“aimagetencent”

在线解密:http://tool.chacuo.net/cryptaes

提交flag后不正确,在FileDataActivity中有一串密文,对其解密后为正确flag

app3

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5087&page=1

下载附件后发现文件为.ab格式,修改后缀为apk尝试安装,安装失败。经查阅后发现.ab文件是android应用的备份文件,需要使用android-backup-extractor工具进行数据提取,工具地址:https://github.com/nelenkov/android-backup-extractor

java -jar abe.jar unpack app3.ab app3.tar


使用该工具从app3.ab提取出app3.tar,解压app3.tar,得到一个base.apk和Demo.db、Encryto.db

在模拟器中安装apk查看功能

使用JEB2进行逆向进行逆向找到入口程序MainActivity反编译查看源码

程序中为button设置了一个监听器,button点击后传递数据到AnotherActivity

跟进AnotherActivity传递过来的数据没有做操作,简单输出了一段话

回到MainActivity分析下面的代码,引用了多个父类

a、b两个类中程序的逻辑为将Stranger和123456两个字符串取前四个,并拼接到一起,得到Stra1234将Stra1234进行md5加密后用base16加密,得到的字符串前面加上Stra1234,在拼接上字符串yaphetshan后用SHA-1加密,在用base16加密,最后取得到字符串的前七位

解密代码:

import java.security.MessageDigest;
public class b {
public static String a(String str) { byte[] digest; char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { byte[] bytes = str.getBytes(); MessageDigest instance = MessageDigest.getInstance("MD5"); instance.update(bytes);
char[] cArr2 = new char[(16 * 2)]; int i = 0; for (byte b : instance.digest()) { int i2 = i + 1; cArr2[i] = cArr[(b >>> 4) & 15]; i = i2 + 1; cArr2[i2] = cArr[b & 15]; } return new String(cArr2); } catch (Exception e) { return null; } } public static final String b(String str) { byte[] digest; char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { byte[] bytes = str.getBytes(); MessageDigest instance = MessageDigest.getInstance("SHA-1"); instance.update(bytes); char[] cArr2 = new char[(32 * 2)]; int i = 0; for (byte b : instance.digest()) { int i2 = i + 1; cArr2[i] = cArr[(b >>> 4) & 15]; i = i2 + 1; cArr2[i2] = cArr[b & 15]; } return new String(cArr2); } catch (Exception e) { return null; } } public static void main(String[] args){ String two = ("Stra1234"+a("Stra1234")+"yaphetshan"); System.out.println(b(two).substring(0,7)); }}

ae56f99 是解数据库的密码

使用DB Browser for SQLite打开加密的数据库文件Encryto.db,浏览数据发现base64字符串,解密后获得flag

工具下载地址:http://www.xitongzhijia.net/soft/198516.html


easy-apk

题目链接:https://adworld.xctf.org.cn/task/answer?type=mobile&number=6&grade=0&id=5088&page=1

下载安装包后首先安装测试,发现如下,对输入的内容提示验证失败,猜测输入正确的flag验证成功

将安装包丢入Android Killer进行逆向,逆向完毕找到入口函数进行反编译查看源代码


从代码中可以看出逻辑为,对输入的内容调入Base64New类进行加密,加密后的结果与“5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=”进行对比,相同则提示验证通过,不同则验证失败

跟进Base64New类发现程序使用了自己的base64顺序表进行加密,将“5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=”按照代码中的base64表进行还原为正常base64加密再对其进行解密

加解密脚本(可变换码表):

# coding:utf-8
# s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"s = "vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/"
def My_base64_encode(inputs): # 将字符串转化为2进制 bin_str = [] for i in inputs: x = str(bin(ord(i))).replace('0b', '') bin_str.append('{:0>8}'.format(x)) #print(bin_str) # 输出的字符串 outputs = "" # 不够三倍数,需补齐的次数 nums = 0 while bin_str: #每次取三个字符的二进制 temp_list = bin_str[:3] if(len(temp_list) != 3): nums = 3 - len(temp_list) while len(temp_list) < 3: temp_list += ['0' * 8] temp_str = "".join(temp_list) #print(temp_str) # 将三个8字节的二进制转换为4个十进制 temp_str_list = [] for i in range(0,4): temp_str_list.append(int(temp_str[i*6:(i+1)*6],2)) #print(temp_str_list) if nums: temp_str_list = temp_str_list[0:4 - nums] for i in temp_str_list: outputs += s[i] bin_str = bin_str[3:] outputs += nums * '=' print("Encrypted String:\n%s "%outputs) def My_base64_decode(inputs): # 将字符串转化为2进制 bin_str = [] for i in inputs: if i != '=': x = str(bin(s.index(i))).replace('0b', '') bin_str.append('{:0>6}'.format(x)) #print(bin_str) # 输出的字符串 outputs = "" nums = inputs.count('=') while bin_str: temp_list = bin_str[:4] temp_str = "".join(temp_list) #print(temp_str) # 补足8位字节 if(len(temp_str) % 8 != 0): temp_str = temp_str[0:-1 * nums * 2] # 将四个6字节的二进制转换为三个字符 for i in range(0,int(len(temp_str) / 8)): outputs += chr(int(temp_str[i*8:(i+1)*8],2)) bin_str = bin_str[4:] print("Decrypted String:\n%s "%outputs) print()print(" *************************************")print(" * (1)encode (2)decode *") print(" *************************************")print()

num = input("Please select the operation you want to perform:\n")if(num == "1"): input_str = input("Please enter a string that needs to be encrypted: \n") My_base64_encode(input_str)else: input_str = input("Please enter a string that needs to be decrypted: \n") My_base64_decode(input_str)

E

N

D

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章400余篇,自研平台达到31个,目有18个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246632&idx=2&sn=b8a42a1ad69dd7c014debf0f02d0a9b6&chksm=82ea5641b59ddf57b1c0fa0557c6d94aaa3fe48dadea7384d8c461c264980e8c4a1c3bb0e609#rd
如有侵权请联系:admin#unsafe.sh