Fly Off The Handle

I ran zeek fly.zeek to generate all the files in tmp/.

However, I then deleted the flag.zeek file. See if you can recover the flag string.

@load ./lookup
@load ./flag

global GLOBAL::n: count = 0;
global GLOBAL::fh: file;
global GLOBAL::pain_level: count = 1000;

srand(double_to_count(time_to_double(current_time())));
local winner = rand(pain_level);
print winner;

while (n < pain_level) {
  if (mkdir("tmp/")) {
    fh = open(fmt("tmp/%s", n));
    if (n == winner) {
      for (char in flag) {
        write_file(fh, fmt("%s.", ascii_map[char] + n));
      }
    } else {
      for (char in flag) {
        write_file(fh, fmt("%s.", rand(93) + 32 + n));
      }
    }
    close(fh);
  } else {
    ;
  }
  n += 1;
}

Hmm so we don't have the flag string. We have some 1000 files in directories which contain numbers nicely delimited by decimals. So some string was used to compute a file that wasn't created using a randomizing function, the rest use the lookup table and a random offset we can't reverse.

Ok lets reverse every file back into strings on the assumption that is was the winner one.

So lets read the file in using the input framework.

Input::add_event([$source=fmt("tmp/%s", n),
                          $name=fmt("tmp_%s", n),
                          $reader=Input::READER_RAW,
                          $want_record=F,
                          $fields=FileLine,
                          $ev=file_line]);

We get an event from we can hook into that will gives us the line in the file. Brute force some string manipulations and reverse the look table.

event file_line(description: Input::EventDescription, tpe: Input::Event, s: string) {
    local parts = split_string(s, /\./)[:-1];
    local nn = to_count(split_string1(description$name, /_/)[1]);
    local msg: string="";

    for(el in parts) {
        local idx = to_count(parts[el]) - nn;
        if(idx in rev_ascii_map) {
            msg += rev_ascii_map[idx];
        }
    }
}

Putting it all together

module fly;

@load base/frameworks/input
@load ./lookup
# @load ./flag

redef exit_only_after_terminate = F;
global rev_ascii_map: table[count] of string = {};

type FileLine: record {
    s: string;
};

event file_line(description: Input::EventDescription, tpe: Input::Event, s: string) {
    local parts = split_string(s, /\./)[:-1];
    local nn = to_count(split_string1(description$name, /_/)[1]);
    local msg: string="";

    for(el in parts) {
        local idx = to_count(parts[el]) - nn;
        if(idx in rev_ascii_map) {
            msg += rev_ascii_map[idx];
        }
    }

    if (/flag/i in msg) {
        print fmt("n=%s flag=%s", nn, msg);
    }
}

function run() {

    local n: count = 0;
    local pain_level: count = 1000;

    while (n < pain_level) {
        Input::add_event([$source=fmt("tmp/%s", n),
                          $name=fmt("tmp_%s", n),
                          $reader=Input::READER_RAW,
                          $want_record=F,
                          $fields=FileLine,
                          $ev=file_line]);

        n += 1;
    }
}

event zeek_init() {
    # Probably a better way but I just brute forced reversed the lookup table
    for(el in ascii_map){
        rev_ascii_map[ascii_map[el]] = el;
    }
    run();
}

You end up getting a fair amount of string output, but this is a CTF and your looking for flags right. So a quick case insensitive search for flag and voila.

n=705 flag=3s..AWildFlagAppeared..3Sx[ylmnop::GLOBAL::

The solution!

The ::GLOBAL:: being in the string threw me off

3s..AWildFlagAppeared..3Sx[ylmnop::GLOBAL::

Last updated