During the offline AWD competition in Liaoning Province, there was a Java question:
@PostMapping({"/cmd"})
@ResponseBody
public String cmd(@RequestParam String command) throws Exception {
Base64.getDecoder();
StringBuffer sb = new StringBuffer("");
String[] c = {"/bin/bash", "-c", command};
Process p = Runtime.getRuntime().exec(c);
CopyInputStream(p.getInputStream(), sb);
CopyInputStream(p.getErrorStream(), sb);
return sb.toString();
}
Unfortunately, only three teams were able to solve it. We ranked fourth because we didn't know how to fix it (thankfully, we had jadx-gui on Kali, but why can we only view it and not modify it?!).
After some research, I found out about this thing called Java Agent, which can achieve a similar effect to hooking (although it doesn't seem as good as Xposed). However, there seem to be many pitfalls and environmental issues, so I decided to document it.
First, the least important part of the code was referenced from here, with some modifications for precise searching of overloaded functions (you can also find an introduction to Java Agent here): https://www.cnblogs.com/rickiyang/p/11368932.html
package agent2;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.*;
public class PreMainAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("agentArgs : " + agentArgs);
inst.addTransformer(new DefineTransformer(), true);
}
static class DefineTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (!"com/example/ezjava/controller/EvilController".equals(className)) {
return null;
}
System.out.println("premain load Class:" + className);
try {
System.out.println("trying!");
final ClassPool classPool = ClassPool.getDefault();
final CtClass clazz = classPool.get("com.example.ezjava.controller.EvilController");
final CtClass str_class = classPool.get("java.lang.String");
System.out.println(str_class);
CtMethod cmd = clazz.getDeclaredMethod("cmd", new CtClass[]{str_class});
// Here, the java.util.Date.convertToAbbr() method is modified by adding a print operation before the return statement
String methodBody = "{return \"stupid!\";}";
System.out.println("Setting body!");
cmd.setBody(methodBody);
// Return the bytecode and detach the CtClass object
byte[] byteCode = clazz.toBytecode();
// Detach means removing the Date object that was previously loaded by Javassist from memory. If it is not found in memory next time, Javassist will reload it.
clazz.detach();
System.out.println("returning!");
return byteCode;
} catch (IOException | CannotCompileException | NotFoundException e) {
System.out.println("Error!");
System.out.println(e);
return null;
}
}
}
}
Pitfalls:
Download javassist.jar
.
Then, he used Maven to modify the manifest, but I couldn't find a way to include the dependencies in the package, so I switched to Ant.
I used NetBeans as my IDE. You can directly right-click on Libraries and add the JAR.
To modify the manifest, refer to this link: https://www.javaxt.com/wiki/Tutorials/Netbeans/How_to_Add_Version_Information_to_a_Jar_File_with_Netbeans
However, this part is not quite right:
First, you must update your Netbeans "project.properties" file found in the "nbproject" directory. Add the following line to the file:
manifest.file=manifest.mf
Instead, you should change manifest.file=manifest.mf
in project.properties
to manifest.file=MANIFEST.MF
.
In addition to Premain-Class
and Agent-Class
, two more lines need to be added to the manifest (remember to delete all the messy properties he wrote):
<attribute name="Can-Redefine-Classes" value="true"/>
<attribute name="Can-Retransform-Classes" value="true"/>
Although Ant didn't include the dependencies in the package, he created a lib
directory and added the classpath attribute to the manifest, so it can still run.
The startup command is java -javaagent:'/home/zfn/NetBeansProjects/agent2/dist/agent2.jar' -jar awd.jar
Good.