Welcome to my series of writeups for n00bzunit3d 2024 capture-the-flag competition. In this post, we look at the crypto/Random challenge.

The challenge takes an input string and “randomly” shuffles it. If the shuffled string is at least 10 characters long and in ascending order, the challenge gives us the flag. Let us look at the challenge code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<chrono>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string>
#include<fstream>
#include<thread>
#include<map>
using namespace std;

bool amazingcustomsortingalgorithm(string s) {
    int n = s.size();
    for (int i = 0; i < 69; i++) {
        cout << s << endl;
        bool good = true;
        for (int i = 0; i < n - 1; i++)
            good &= s[i] <= s[i + 1];
        
        if (good)
            return true;

        random_shuffle(s.begin(), s.end());

        this_thread::sleep_for(chrono::milliseconds(500));
    }

    return false;
}

int main() {
    string s;
    getline(cin, s);

    map<char, int> counts;
    for (char c : s) {
        if (counts[c]) {
            cout << "no repeating letters allowed passed this machine" << endl;
            return 1;
        }
        counts[c]++;
    }

    if (s.size() < 10) {
        cout << "this machine will only process worthy strings" << endl;
        return 1;
    }

    if (s.size() == 69) {
        cout << "a very worthy string" << endl;
        cout << "i'll give you a clue'" << endl;
        cout << "just because something says it's random mean it actually is" << endl;
        return 69;
    }

    random_shuffle(s.begin(), s.end());
    
    if (amazingcustomsortingalgorithm(s)) {
        ifstream fin("flag.txt");
        string flag;
        fin >> flag;
        cout << flag << endl;
    }
    else {
        cout << "UNWORTHY USER DETECTED" << endl;
    }
}

The challenge uses random_shuffle() to shuffle the string 69 times. It then checks if the shuffled string is in ascending order according to their ASCII values.

We start with entering input 0123456789, which lets me see how to characters have been shuffled.

Output:

.
.
.
9360274158
9380765124
0123456789

Is the the output we get from the shuffling. The challenge hints that the shuffling may not be truly random. In addition, we know C++ random functions use a seed to determine their randomness. We do not see a seed being set each time so maybe the results are reproducable.

We run the challenge again and give the same input 0123456789. And we get the same output.

.
.
.
9360274158
9380765124
0123456789

So the order of shuffling always remains the same between each runs. We can use this and work backwards to enter a string that gets shuffled to an ascending order string.

We wrote a simple script to get the right sequence. Which was 4759106328. Running the challenge with the input gives

9360274158
9380765124
0123456789
n00bz{REDACTED}