Iris CTF - 2025 Chall

KittyCrypt

Information

Screenshot 2025-01-06 063917.png

Diberikan kittycrypt.tar.gz yang isinya example_input.txt, example_output.txt, flag_output.txt dan main.go

-- example_input.txt
You fools! You will never get my catnip!!!!!!!

-- example output.txt
πŸ±β€πŸ’»πŸ˜ΈπŸ˜ΏπŸ˜ΌπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ˜ΏπŸ™€πŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜ΉπŸ˜ΏπŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ±πŸ±β€πŸ‘€πŸ˜ΉπŸ˜ΏπŸ˜ΊπŸ±β€πŸ‘“πŸ˜ΉπŸ™€πŸ˜ΏπŸ˜ΎπŸ±β€πŸπŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΎπŸ±β€πŸš€πŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ˜ΏπŸ˜½πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ±β€πŸ‘€πŸ™€πŸ˜»πŸ±β€πŸ‘“πŸ˜ΈπŸ™€πŸ˜ΌπŸ±β€πŸ‘€πŸˆπŸ±β€πŸ‘“πŸ˜ΊπŸ±β€πŸ‘€πŸ˜ΌπŸ˜ΏπŸ˜ΎπŸ±β€πŸ‘“πŸˆπŸ˜ΏπŸ˜½πŸ™€πŸ±β€πŸš€πŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΎπŸ˜ΉπŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ˜ΉπŸ™€πŸ±πŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸ‘“πŸ±πŸ™€πŸ˜½πŸ˜ΏπŸ˜»πŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ±πŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸˆπŸ˜ΎπŸ˜½πŸ˜ΏπŸ±β€πŸ’»πŸ±β€πŸ‘“πŸˆπŸ™€πŸ±β€πŸ‘“πŸ˜ΏπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸπŸ±β€πŸ‘“πŸˆπŸ±β€πŸ‘€πŸ±πŸ˜ΏπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ˜ΈπŸ˜ΎπŸ˜ΏπŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸ’»πŸ±β€πŸπŸ˜ΏπŸ™€πŸ±β€πŸ‘“πŸ˜ΈπŸ˜ΎπŸ™€πŸ™€πŸ±πŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ™€πŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ±πŸ™€πŸ™€πŸ˜ΎπŸ˜ΊπŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ˜½πŸ˜ΏπŸ˜ΈπŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ˜ΎπŸ™€πŸˆπŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ˜ΌπŸ™€πŸ˜ΌπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜ΏπŸ˜ΏπŸ˜ΏπŸ±β€πŸπŸ˜ΏπŸ˜ΎπŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜½πŸ˜ΏπŸ±β€πŸπŸ±β€πŸ‘“πŸˆπŸ˜ΎπŸˆπŸ˜ΏπŸ˜ΉπŸ±β€πŸ’»πŸ˜ΈπŸ±β€πŸ‘€πŸ˜ΉπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸ‘“πŸ™€πŸ±πŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸπŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ˜ΈπŸ™€πŸ±β€πŸπŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ˜ΌπŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸ‘“πŸ±πŸ™€πŸˆπŸ±β€πŸ‘€πŸˆπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ˜½πŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸˆπŸ±β€πŸ‘€πŸ±β€πŸš€πŸ±β€πŸ‘“πŸ˜ΊπŸ±β€πŸ‘€πŸ˜½πŸ™€πŸ˜ΏπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸ’»πŸ™€πŸ˜ΏπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ±β€πŸ‘€πŸ˜ΏπŸ±β€πŸš€πŸ±β€πŸ‘“πŸ˜ΈπŸ™€πŸ±β€πŸπŸ±β€πŸ‘€πŸ˜»πŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ±β€πŸ’»πŸ±β€πŸ‘€πŸ˜ΎπŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΏπŸ˜»πŸ™€πŸ±β€πŸš€πŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΏπŸ˜ΏπŸ˜ΏπŸ˜Ώ

-- flag_output.txt
πŸ±β€πŸ’»πŸ˜ΈπŸ™€πŸ˜ΌπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ˜ΏπŸ±β€πŸ‘€πŸ±πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜ΉπŸ˜ΏπŸˆπŸ±β€πŸ‘“πŸ±πŸ±β€πŸ‘€πŸ˜ΊπŸ™€πŸ˜½πŸ±β€πŸ‘“πŸ˜ΉπŸ™€πŸ˜ΏπŸ˜ΎπŸ˜ΏπŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΎπŸ±β€πŸš€πŸ±β€πŸ‘€πŸ±β€πŸ’»πŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ˜ΎπŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ˜ΊπŸ±β€πŸ‘“πŸ˜ΈπŸ™€πŸ˜ΌπŸ±β€πŸ‘€πŸˆπŸ±β€πŸ‘“πŸ˜ΊπŸ±β€πŸ‘€πŸ˜ΌπŸ™€πŸ˜½πŸ±β€πŸ‘“πŸˆπŸ˜ΏπŸ˜ΎπŸ±β€πŸ‘€πŸ±β€πŸπŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΎπŸ˜ΉπŸ˜ΏπŸ˜»πŸ±β€πŸ‘“πŸ˜ΉπŸ™€πŸ±πŸ˜ΎπŸ±πŸ±β€πŸ‘“πŸ±πŸ™€πŸ˜ΌπŸ˜ΏπŸˆπŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ˜ΈπŸ˜ΎπŸ˜ΎπŸ±β€πŸ‘“πŸˆπŸ˜ΎπŸ˜ΌπŸ˜ΏπŸ˜ΏπŸ±β€πŸ‘“πŸˆπŸ™€πŸ±β€πŸ‘“πŸ™€πŸ˜»πŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ™€πŸ±β€πŸ‘€πŸ±β€πŸš€πŸ±β€πŸ‘“πŸˆπŸ±β€πŸ‘€πŸ±πŸ˜ΏπŸˆπŸ±β€πŸ‘“πŸ˜ΈπŸ˜ΎπŸ™€πŸ±β€πŸ‘€πŸˆπŸ±β€πŸ’»πŸ±β€πŸ‘€πŸ™€πŸ˜ΉπŸ±β€πŸ‘“πŸ˜ΈπŸ˜ΎπŸ˜ΏπŸ™€πŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ±β€πŸ’»πŸ™€πŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ±πŸ™€πŸ˜ΏπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ˜ΌπŸ˜ΏπŸ˜ΊπŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ˜ΏπŸ±β€πŸ‘€πŸ˜ΉπŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ˜»πŸ±β€πŸ‘€πŸ˜ΈπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜ΏπŸ™€πŸ˜ΈπŸ±β€πŸπŸ˜ΎπŸ˜ΏπŸˆπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ˜ΎπŸ˜ΏπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸˆπŸ˜ΎπŸˆπŸ˜ΏπŸ±β€πŸ’»πŸ±β€πŸ’»πŸ˜ΈπŸ™€πŸ˜ΈπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸ‘“πŸ±β€πŸ‘€πŸ˜ΊπŸ±β€πŸ‘“πŸ˜ΊπŸ™€πŸ™€πŸ™€πŸ±πŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ˜ΈπŸ™€πŸ™€πŸ±β€πŸ‘“πŸ˜ΉπŸ±β€πŸ‘€πŸ˜ΌπŸ±β€πŸ‘€πŸ±β€πŸ’»πŸ±β€πŸ‘“πŸ±πŸ™€πŸ±πŸ±β€πŸ‘€πŸ˜ΉπŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸπŸ˜ΎπŸ˜ΉπŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸˆπŸ™€πŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΊπŸ±β€πŸ‘€πŸ˜½πŸ±β€πŸ‘€πŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΏπŸ±β€πŸš€πŸ˜ΎπŸ±πŸ±β€πŸ‘“πŸ˜ΊπŸ˜ΎπŸ±β€πŸπŸ™€πŸ±β€πŸ‘“πŸ±β€πŸ‘“πŸ˜ΈπŸ™€πŸ±β€πŸ’»πŸ˜ΎπŸ˜½πŸ±β€πŸ‘“πŸ˜ΈπŸ±β€πŸ‘€πŸ±β€πŸ‘“πŸ±β€πŸ‘€πŸ™€πŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΏπŸ˜ΌπŸ˜ΎπŸ˜»πŸ±β€πŸ‘“πŸ˜ΉπŸ˜ΏπŸ™€πŸ±β€πŸ‘€πŸ˜»
-- main.go

package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math/big"
	"os"
	"strings"
)

var CharSet = map[rune]string{
	'0': "🐱", '1': "🐈", '2': "😸", '3': "😹",
	'4': "😺", '5': "😻", '6': "😼", '7': "😽",
	'8': "😾", '9': "😿", 'A': "πŸ™€", 'B': "πŸ±β€πŸ‘€",
	'C': "πŸ±β€πŸ", 'D': "πŸ±β€πŸ’»", 'E': "πŸ±β€πŸ‘“", 'F': "πŸ±β€πŸš€",
}

func catify(input string, keys []int) string {
	var keyedText string
	var result string

	for i, char := range input {
		keyedText += string(rune(int(char) + keys[i]))
	}
	fmt.Printf("I2Keyed: %s\\n", keyedText)

	hexEncoded := strings.ToUpper(hex.EncodeToString([]byte(keyedText)))
	fmt.Printf("K2Hex: %s\\n", hexEncoded)

	for _, rune := range hexEncoded {
		result += CharSet[rune]
	}

	return result
}

func savePair(name, input, output string) {
	inputFile, err := os.OpenFile(name+"_input.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer inputFile.Close()

	outputFile, err := os.OpenFile(name+"_output.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer outputFile.Close()

	if _, err := inputFile.Write([]byte(input)); err != nil {
		fmt.Println(err)
		return
	}
	if _, err := outputFile.Write([]byte(output)); err != nil {
		fmt.Println(err)
		return
	}
}

func getKeys(length int) []int {
	var keys = []int{}
	keyFileName := fmt.Sprintf("keys_%d.json", length)

	file, err := os.Open(keyFileName)
	if err != nil {

		for i := 0; i < length; i++ {
			num, _ := rand.Int(rand.Reader, big.NewInt(60000))

			keys = append(keys, int(num.Int64()))
		}

		keyFile, err := os.OpenFile(keyFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			fmt.Println(err)
			return []int{}
		}
		defer keyFile.Close()

		encoded, _ := json.Marshal(keys)
		keyFile.Write(encoded)

		return keys
	}

	json.NewDecoder(file).Decode(&keys)

	return keys
}

func main() {
	input := "You fools! You will never get my catnip!!!!!!!"

	keys := getKeys(len(input))

	encoded := catify(input, keys)

	savePair("example", input, encoded)
}

Tapi karena saya skill issue, saya convert ke python dengan bantuan gpt.

#main.py

import os
import json
import random
import string
from binascii import hexlify

# Karakter emoji untuk mapping
CHAR_SET = {
    '0': "🐱", '1': "🐈", '2': "😸", '3': "😹",
    '4': "😺", '5': "😻", '6': "😼", '7': "😽",
    '8': "😾", '9': "😿", 'A': "πŸ™€", 'B': "πŸ±β€πŸ‘€",
    'C': "πŸ±β€πŸ", 'D': "πŸ±β€πŸ’»", 'E': "πŸ±β€πŸ‘“", 'F': "πŸ±β€πŸš€"
}

def catify(input_text, keys):
    # Tambahkan key ke setiap karakter input
    keyed_text = ''.join(chr(ord(char) + keys[i]) for i, char in enumerate(input_text))
    print(f"I2Keyed: {keyed_text}")

    # Ubah menjadi representasi hex
    hex_encoded = hexlify(keyed_text.encode()).decode().upper()
    print(f"K2Hex: {hex_encoded}")

    # Konversi hex menjadi emoji menggunakan CHAR_SET
    result = ''.join(CHAR_SET[char] for char in hex_encoded)
    return result

def save_pair(name, input_text, output_text):
    # Simpan input ke file
    with open(f"{name}_input.txt", "w") as input_file:
        input_file.write(input_text)

    # Simpan output ke file
    with open(f"{name}_output.txt", "w") as output_file:
        output_file.write(output_text)

def get_keys(length):
    key_file_name = f"keys_{length}.json"

    # Jika file key ada, baca isinya
    if os.path.exists(key_file_name):
        with open(key_file_name, "r") as file:
            keys = json.load(file)
        return keys

    # Jika tidak ada, buat key baru
    keys = [random.randint(0, 59999) for _ in range(length)]

    # Simpan key ke file
    with open(key_file_name, "w") as file:
        json.dump(keys, file)

    return keys

def main():
    input_text = "You fools! You will never get my catnip!!!!!!!"

    # Dapatkan kunci dengan panjang input
    keys = get_keys(len(input_text))

    # Encode input menjadi catified string
    encoded = catify(input_text, keys)

    # Simpan hasil encoding dan input ke file
    save_pair("example", input_text, encoded)

if __name__ == "__main__":
    main()

Source Code Analysis

kita bisa bagi jadi tiga bagian, yakni catify, save_pair, dan get_keys

def get_keys(length):
    key_file_name = f"keys_{length}.json"

    # Jika file key ada, baca isinya
    if os.path.exists(key_file_name):
        with open(key_file_name, "r") as file:
            keys = json.load(file)
        return keys

    # Jika tidak ada, buat key baru
    keys = [random.randint(0, 59999) for _ in range(length)]

    # Simpan key ke file
    with open(key_file_name, "w") as file:
        json.dump(keys, file)

    return keys

Singkat saja, fungsi ini men-generate random key sesuai panjang pesan yang diterima dengan key ada di interval [0, 59999].

def catify(input_text, keys):
    # Tambahkan key ke setiap karakter input
    keyed_text = ''.join(chr(ord(char) + keys[i]) for i, char in enumerate(input_text))
    print(f"I2Keyed: {keyed_text}")

    # Ubah menjadi representasi hex
    hex_encoded = hexlify(keyed_text.encode()).decode().upper()
    print(f"K2Hex: {hex_encoded}")

    # Konversi hex menjadi emoji menggunakan CHAR_SET
    result = ''.join(CHAR_SET[char] for char in hex_encoded)
    return result

Sementara, fungsi catify memetakan setiap character nya ke character lainnya sesuai key nya masing-masing, di encode ke hex, lalu setiap char hex nya di encode ke emoji.

def save_pair(name, input_text, output_text):
    # Simpan input ke file
    with open(f"{name}_input.txt", "w") as input_file:
        input_file.write(input_text)

    # Simpan output ke file
    with open(f"{name}_output.txt", "w") as output_file:
        output_file.write(output_text)

Jelas bahwa yang kita dapat dari save_pair adalah input_text nya dan output_text nya, namun kita cuma punya flag_output nya, nah dari probset dikasih tau kalau example_output dan flag_output menggunakan keys yang sama, jadi jelas len(flag_input) == len(text_input).

Solution