Writeup Aria
01_General01_Encoding

Encoding Challenge

Desc

Setelah memahami ASCII, hex, Base64, ROT13, dan integer besar, challenge ini meminta kita mengotomatisasi proses decoding. Server akan mengirim 100 data acak dengan tipe encoding berbeda, lalu kita harus mengirim balik hasil decode yang benar.

Jika semua 100 level berhasil dijawab, server akan mengirim flag.

File 13377.py adalah source code logic server. File pwntools_example.py adalah template client untuk connect ke server dan mengirim response JSON.

Untuk informasi lebih lanjut tentang cara terhubung ke challenge interaktif, lihat FAQ CryptoHack. Jika ingin mengetes secara lokal, perlu modul utils.listener dari CryptoHack.

Connect at socket.cryptohack.org 13377

Attachment

#!/usr/bin/env python3

from Crypto.Util.number import bytes_to_long, long_to_bytes
from utils import listener # this is cryptohack's server-side module and not part of python
import base64
import codecs
import random

FLAG = "crypto{????????????????????}"
ENCODINGS = [
    "base64",
    "hex",
    "rot13",
    "bigint",
    "utf-8",
]
with open('/usr/share/dict/words') as f:
    WORDS = [line.strip().replace("'", "") for line in f.readlines()]

class Challenge():
    def __init__(self):
        self.no_prompt = True # Immediately send data from the server without waiting for user input
        self.challenge_words = ""
        self.stage = 0

    def create_level(self):
        self.stage += 1
        self.challenge_words = "_".join(random.choices(WORDS, k=3))
        encoding = random.choice(ENCODINGS)

        if encoding == "base64":
            encoded = base64.b64encode(self.challenge_words.encode()).decode() # wow so encode
        elif encoding == "hex":
            encoded = self.challenge_words.encode().hex()
        elif encoding == "rot13":
            encoded = codecs.encode(self.challenge_words, 'rot_13')
        elif encoding == "bigint":
            encoded = hex(bytes_to_long(self.challenge_words.encode()))
        elif encoding == "utf-8":
            encoded = [ord(b) for b in self.challenge_words]

        return {"type": encoding, "encoded": encoded}

    #
    # This challenge function is called on your input, which must be JSON
    # encoded
    #
    def challenge(self, your_input):
        if self.stage == 0:
            return self.create_level()
        elif self.stage == 100:
            self.exit = True
            return {"flag": FLAG}

        if self.challenge_words == your_input["decoded"]:
            return self.create_level()

        return {"error": "Decoding fail"}

import builtins; builtins.Challenge = Challenge # hack to enable challenge to be run locally, see https://cryptohack.org/faq/#listener
listener.start_server(port=13377)
from pwn import * # pip install pwntools
import json

r = remote('socket.cryptohack.org', 13377, level = 'debug')

def json_recv():
    line = r.recvline()
    return json.loads(line.decode())

def json_send(hsh):
    request = json.dumps(hsh).encode()
    r.sendline(request)

received = json_recv()

print("Received type: ")
print(received["type"])
print("Received encoded value: ")
print(received["encoded"])

to_send = {
    "decoded": "changeme"
}
json_send(to_send)

json_recv()

Solution

Contoh manual dengan nc hanya membuktikan format komunikasinya: server mengirim JSON berisi type dan encoded, lalu client membalas JSON berisi decoded.

wget https://cryptohack.org/static/challenges/13377_86793614535c47dea371d2f0e406dbd9.py -O 13377.py
wget https://cryptohack.org/static/challenges/pwntools_example_f93ca6ccef2def755aa8f6d9aa6e9c5b.py -O pwntools_example.py

nc socket.cryptohack.org 13377
# {"type": "bigint", "encoded": "0x726561636865735f61746c61735f6d61726b657273"}
# {"decoded": "reaches_atlas_markers"}
# {"type": "bigint", "encoded": "0x70726f66696c655f656469746f72735f636f6e74696e75697479"}

Untuk menyelesaikannya, kita buat fungsi decode() yang menangani semua tipe encoding yang mungkin dikirim server.

from pwn import *
import json

from Crypto.Util.number import long_to_bytes
import base64
import codecs

def decode(encoded, encoding_type):
    if encoding_type == "base64":
        return base64.b64decode(encoded).decode()
    if encoding_type == "hex":
        return bytes.fromhex(encoded).decode()
    if encoding_type == "rot13":
        return codecs.decode(encoded, "rot_13")
    if encoding_type == "bigint":
        return long_to_bytes(int(encoded, 16)).decode()
    if encoding_type == "utf-8":
        return "".join(chr(b) for b in encoded)
    raise ValueError(f"Unknown encoding: {encoding_type}")

r = remote("socket.cryptohack.org", 13377, level="debug")

def json_recv():
    line = r.recvline()
    return json.loads(line.decode())

def json_send(hsh):
    request = json.dumps(hsh).encode()
    r.sendline(request)

received = json_recv()

while True:
    decoded_value = decode(received["encoded"], received["type"])
    print(f"Decoded value: {decoded_value}")

    json_send({"decoded": decoded_value})
    received = json_recv()

    if "flag" in received:
        print(f"Flag: {received['flag']}")
        break

    print("Received type:")
    print(received["type"])
    print("Received encoded value:")
    print(received["encoded"])

Penjelasan fungsi decoding:

  • base64: base64.b64decode(encoded).decode() mengubah Base64 menjadi bytes, lalu bytes menjadi string.
  • hex: bytes.fromhex(encoded).decode() mengubah hex menjadi bytes, lalu bytes menjadi string.
  • rot13: codecs.decode(encoded, "rot_13") menggeser huruf ROT13 kembali ke teks asli.
  • bigint: int(encoded, 16) membaca integer hex dari server, lalu long_to_bytes() mengubahnya menjadi bytes.
  • utf-8: server mengirim list angka ordinal, jadi chr(b) mengubah tiap angka menjadi karakter.

Loop utama akan terus menerima challenge baru, decode sesuai type, mengirim {"decoded": decoded_value}, lalu berhenti ketika response server berisi key flag.

Summary

Challenge ini melatih otomasi decoding dan komunikasi JSON. Library yang dipakai adalah pwntools untuk socket, json untuk encode/decode request, base64 untuk Base64, codecs untuk ROT13, dan Crypto.Util.number.long_to_bytes untuk integer besar.

Flag

crypto{3nc0d3_d3c0d3_3nc0d3}

On this page