team-logo
Published on

1753CTF 2025 - REV - Ancient Memory

Authors

Challenge

Challenge description

Solution

import torch
import torch.nn as nn
import random
import string

FLAG_LENGTH = 26
CHARSET = string.printable[:95]  # Bez znaków kontrolnych

CORRECT_FLAG = "1753c{xxxxxxx_xx_xx_xxxxx}"  # używane do paddingu, nie do porównania

# Ładowanie modelu
class FlagSimilarityModel(nn.Module):
    def __init__(self):
        super(FlagSimilarityModel, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(FLAG_LENGTH * 128, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.model(x)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = FlagSimilarityModel().to(device)
model.load_state_dict(torch.load("model.pt", map_location=device)['model_state_dict'])
model.eval()

# Zamiana flagi na one-hot tensor
def flag_to_tensor(flag):
    padded_flag = flag[:FLAG_LENGTH].ljust(FLAG_LENGTH, 'x')
    X = torch.zeros(FLAG_LENGTH, 128)
    for i, char in enumerate(padded_flag):
        X[i, ord(char)] = 1
    return X.flatten().to(device)

# Predykcja similarity
def predict_flag_score(flag):
    with torch.no_grad():
        input_tensor = flag_to_tensor(flag)
        score = model(input_tensor.unsqueeze(0)).item()
    return score

# Mutacja flagi
def mutate_flag(flag):
    idx = random.randint(0, FLAG_LENGTH - 1)
    new_char = random.choice(CHARSET)
    return flag[:idx] + new_char + flag[idx + 1:]

# Główna pętla ewolucyjna
def evolve_flag(iterations=10000):
    best_flag = ''.join(random.choices(CHARSET, k=FLAG_LENGTH))
    best_score = predict_flag_score(best_flag)

    print(f"Start: {best_flag} | Score: {best_score:.6f}")

    for i in range(iterations):
        new_flag = mutate_flag(best_flag)
        new_score = predict_flag_score(new_flag)

        if new_score > best_score:
            best_flag = new_flag
            best_score = new_score
            print(f"[{i}] New best: {best_flag} | Score: {best_score:.6f}")
            if best_score > 0.999:
                break

    print(f"\nFinal guess: {best_flag} | Score: {best_score:.6f}")

if __name__ == "__main__":
    evolve_flag()

End lines of the result:

[4468] New best: <753c[Yr1tt3n_1njm#_brt1n} | Score: 0.934130
[4768] New best: <753c{Yr1tt3n_1njm#_brt1n} | Score: 0.937444
[4798] New best: <753c{wr1tt3n_1njm#_brt1n} | Score: 0.943044
[4915] New best: <753c{wr1tt3n_1njmd_brt1n} | Score: 0.943385
[5300] New best: 1753c{wr1tt3n_1njmd_brt1n} | Score: 0.946521
[5719] New best: 1753c{wr1tt3n_1njmd_bra1n} | Score: 0.952286
[6546] New best: 1753c{wr1tt3n_1n_md_bra1n} | Score: 0.957028
[7304] New best: 1753c{wr1tt3n_1n_my_bra1n} | Score: 0.961432

Final guess: 1753c{wr1tt3n_1n_my_bra1n} | Score: 0.961432

But it wasn't a flag, little leetspeak and flag is 1753c{wr1tt3n_1n_my_br41n}.