> For the complete documentation index, see [llms.txt](https://eephillip.gitbook.io/zw2020-ctf-writeup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://eephillip.gitbook.io/zw2020-ctf-writeup/writeup/fly-off-the-handle.md).

# Fly Off The Handle

{% hint style="info" %}
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.
{% endhint %}

```c
@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.

```c
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.

```c
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

```c
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.&#x20;

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

{% hint style="success" %}
The solution!

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

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

{% endhint %}
