第六届安洵杯网络安全挑战赛(CB PriorityQueue替代+Postgresql JDBC Attack+FreeMarker)
链子调出来的时候已经剩半个小时了,卡在最后模板注入中url编码的问题,最后时间来不及了,很可惜。😭
目标环境通过iptables防火墙设置不出网
iptables -F
iptables -X
iptables -Z
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -m state --state NEW -j DROP
iptables -P OUTPUT DROP
iptables -n -L清空原来配置的规则
添加一个规则到INPUT链,允许TCP协议的目标端口为80和22的数据包通过。
添加一个规则到OUTPUT链,允许与已建立的连接或相关的数据包通过,即允许回应外部请求的数据包通过。
添加一个规则到OUTPUT链,拒绝所有新建立的连接。
将OUTPUT链的默认策略设置为拒绝(DROP)
server.port=80
spring.freemarker.template-loader-path=file:/app/templates/,classpath:/templates/
spring.freemarker.suffix=.ftl
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.expose-spring-macro-helpers=true
spring.freemarker.settings.new_builtin_class_resolver=saferfreemarker的模板路径有两个可选:/app/templates和类路径下的templates目录
模板缓存关了,设置了安全的类解析器,一眼鉴定为模板注入
自定义一个ObjectInputStream,重写了resolveClass,黑名单如下:
环境有两个重要依赖
commons-beanutils和postgresql
CB链可以调用任意getter,但常用的getter利用类JdbcRowSetImpl、com.sun.jndi.ldap.LdapAttribute(JNDI,但目标环境8u312也受限)、TemplatesImpl(加载字节码)、SignedObject(二次反序列化)
之前看到过一篇文章,在JDK17中利用反射访问JDK内部类时,会抛出IllegalAccessException异常,这与JDK9引入的模块隔离机制有关。文中提到了用一些JDBC有关的第三方依赖来代替这些常见的内部类,调用其getConnection,控制连接指定的JDBC URL。联系到这题就很容易想到postgresql的JDBC Attack
回顾一下CB链:
PriorityQueue#readObject
-> #heapify
-> #siftDown
-> #siftDownUsingComparator
-> BeanComparator#compare
-> PropertyUtils#getProperty
-> EvilObject#getter
需要调用到BeanComparator#compare,这题把CB链的入口类PriorityQueue禁了
网上找到了一个TreeBag+TreeMap配合来调用compare,用来代替CC2中的PriorityQueue
但是TreeBag是commons collections里的类,后面才发现commons beanutils居然带了commons collections(哭死)
TreeBag+TreeMap
Bag接口继承自Collection接口,定义了一个集合,该集合会记录对象在集合中出现的次数。
Defines a collection that counts the number of times an object appears in the collection. Suppose you have a Bag that contains {a, a, b, c}. Calling getCount(Object) on a would return 2, while calling uniqueSet() would return {a, b, c}.
它有一个子接口SortedBag,定义了一种可以对其唯一不重复成员排序的Bag类型。
Defines a type of Bag that maintains a sorted order among its unique representative members.
既然用到了排序,那就很有可能会调用到compare。
看看TreeBag的类介绍:
Implements SortedBag, using a TreeMap to provide the data storage. This is the standard implementation of a sorted bag.
这个类的构造器接收一个TreeMap对象,用TreeMap来存储排序的数据。
TreeMap的构造器接收一个Comparator对象,通过这个给定的比较器来排序。
TreeBag反序列化的时候会调用父类的doReadObject来对数据进行恢复,往TreeMap里put数据,TreeMap存储的是对象和它的出现次数。
在往TreeMap里放数据时就会进行比较来排序。
构造TreeBag的时候,调用add也会触发map#put,因此我们通过反射来构造
简单看看TreeMap#put的源码,首先它会判断根节点是否为空,最初没往里面放东西肯定是为空的。
接着主要变动的有三个属性root、size和modCount(modCount不改也没事)
根节点(Entry)的parent为null
如果要设置子节点,再接着设置left和right属性即可
我们这里只需要设置一个根节点就够了,根节点放入TreeMap时会对自身的key自己跟自己进行compare
HashMap+TreeMap
不用那个TreeBag也可以,用原生JDK自带的。
不止TreeMap的put会调用compare,其get也会调用compare
那哪里会调用Map#get呢,答案是AbstractMap.equals(刚好TreeMap没有重写equals方法)
为了判断两个Map对象是否相等,通过遍历Map A的entrySet每个键值对,比较Map B对应键的值(这里就调用了Map.get(key))是否和Map A的相等
HashMap#readObject会调用putVal,putVal当遇到哈希碰撞时就会调用equals
构造如下:
PostgreSQL JDBC Attack
org.postgresql.ds.PGSimpleDataSource继承自BaseDataSource,其getConnection()会发起JDBC连接,通过设置一些连接参数来进行攻击。
socketFactory/socketFactoryArg
这两个参数用于实例化一个类,构造器需要接收一个String类型的参数
直接想到springboot下加载XML来RCE的两个类
org.springframework.context.support.ClassPathXmlApplicationContext
org.springframework.context.support.FileSystemXmlApplicationContext
但目标环境不能出网,弃之。
loggerLevel/loggerFile
这两个参数用来开启JDBC日志,一个用于指定日志记录等级,一个用于指定日志文件位置
本来想利用这个覆盖index.ftl为XML,再让ClassPathXmlApplicationContext去请求这个XML,这样就不用出网了。但是日志还会带上其他信息,而这个类解析XML对格式要求不能含有其他垃圾字符。
模板引擎就允许标签之外有其他垃圾字符。
但是有一个问题,如果直接把要写入的模板放到jdbc url的参数中,会被url编码,导致模板解析不了。
放到其他位置比如ServerNames就不会被url编码了。
EXP
接着访问/拿到flag

Ref
https://tttang.com/archive/1462/
https://mogwailabs.de/en/blog/2023/04/look-mama-no-templatesimpl/
https://mp.weixin.qq.com/s/ClASwg6SH0uij_-IX-GahQ
https://xz.aliyun.com/t/12143
Last updated
Was this helpful?