Mecha Crabs Going Rogue

// A very specific set of circumstances (mainly, implementing Deref, and having a procedural macro and a Debug derivation in external crates) caused an internal compiler error (ICE) when trying to use rustdoc. This test reproduces the exact circumstances which caused the bug and checks that it does not happen again.

Source

One gets dizzy imagining the contributors back then, like masterful chemists, isolating and purifying the united causes of this obscure error, until at last illumination strikes, resulting in the deletion of a single line of code, and the creation of a run-make test easily 10 times larger than the fix itself.

This week, running through review comments and implementing requested suggestions and patches proved steady and rhythmic. Another fresh batch was on its way to getting shipped to the Rust compiler - packaged, wrapped up, with a nice r+ sticker on it. We just needed a quick test on the Microsoft Visual C++ compiler for compatibility...

Which caused the Bors bot to go rogue and... seemingly try to merge without running the compatibility test. I mean, really now? The bot's profile picture on Github is literally HAL 9000's eye. What could possibly go wrong giving the bot managing a programming language designed for robustness (excellent for tasks critical to humanity, such as space exploration, the creation of drones to save orphans from blazing buildings, and the blockchain generation for non-fungible tokens) the physical appearance of a fictional robot not known for its valuing of human life?

The runaway machine was subdued, and the pull request is safe... for now.

Merged this week

Open

Oxidizing My Toolkit

So, I've been a VSCodium (the no-telemetry version of VSCode) plebeian for a long time, but my techiest friend insisted on how COOL and PRODUCTIVE it is to have a modal editor. But not ANY modal editor. It had to be Helix. Which is like Neovim, but written in Rust (this is the only argument I needed to be convinced).

I also found for myself a terminal multiplexer, Zellij, which is like Tmux but written in Rust (this is also the only argument I needed to be convinced).

Before someone posts me on r/programmingcirclejerk, I am jesting and I found myself enjoying Helix not because of "blazing speed" or any other crab-related meme, but really just because:

  • It shows me a lot of keybinds when I press space or g, and what they do
  • It doesn't require me to install any extensions

I quickly learned the basics - i to insert, e and b to move through words, I and A to move to the start and end of a line, and most amusingly, pressing C to duplicate my cursor. I always feel like a masterful illusionist when doing it.

I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool. I am actually typing this on Helix right now. And yes, I just suddenly felt the need to uselessly summon a bunch of clones of myself because it's just so cool.

After just one week, I really do feel more productive in it than I was in VSCodium. I think the most useful part is easily the file picker, as it gives previews of each file while browsing, unlike VSCodium's Ctrl-P. It's great for run-make tests, as I simply write "Makefile" and start browsing tests quickly and isolating which ones are doable with the current state of the support library with little effort.

Another neat touch to make programming even more fun :3

Ketamine In The DNA, Average Day

I am freshly returning from Northsec, a large size cybersecurity Capture The Flag competition in my province - Québec, Canada - and while I spent an unreasonable amount of time on a cryptography challenge way above my skill level ("Olfactive Memory System") just because I saw it was written in Rust and I got excited, I still snuck in a solve of this fun challenge among my efforts.

(3 Points) Genetic System

Mr Wellington wants to ensure that he can easily repair his inner organs by generating stem cells at will. We’ve been severly warned to not do anything bad with this code, but we have to give it a try. To help generate genetic construction, we’ve been granted a copy of the advanced “game” needed to generate a 4-letters sequence and provide the expected value. It’s named the Stem Cell Generator advanced game, Boy/Girl edition. So for this, find a way to understand how to run that weird game, and then you’ll have to reverse engineer it. Let us know when you found the right answer.

Upon opening the .gba (Game Boy Advance - yes, Evie, I wrote it "Game Boy" and not "Gameboy", I remember!) file, we are met with this ultra-sophisticated UI, which demands an input of 4 letters:

The challenge window, showing the sequence AAAA

"The challenge is called Genetic System. Of course the code will be composed of AGCT. 4x4x4x4 combinations, for a total of 256, this will be easy to run trial and error on!" - blissfully unaware me

I flick the first letter.

The challenge window, showing the sequence BAAA

Well. 26x26x26x26 = 456 976 combinations. This will be fun.

Cracking open this program with a reverse engineering utility (in my case, Binary Ninja) reveals its inner workings... starting with this function, directing the user's ability to change the 4 letter sequence:

if (key_hit(0x20) != 0)
{
    cursor = (cursor - 1);
    if (cursor < 0)
    {
        cursor = 0;
    }
}
if (key_hit(0x10) != 0)
{
    cursor = (cursor + 1);
    if (cursor > 3)
    {
        cursor = 0;
    }
}

The left and right keys let us move the cursor across all four letters...

if (key_hit(0x40) != 0)
{
    *(uint8_t*)((cursor * 0xc) + 0x3000244) = ((int8_t)(((((uint32_t)*(uint8_t*)((cursor * 0xc) + 0x3000244)) + 1) << 0x18) >> 0x18));
    if (((uint32_t)*(uint8_t*)((cursor * 0xc) + 0x3000244)) > 0x5a)
    {
        *(uint8_t*)((cursor * 0xc) + 0x3000244) = 0x41;
    }
}
if (key_hit(0x80) != 0)
{
    *(uint8_t*)((cursor * 0xc) + 0x3000244) = ((int8_t)(((((uint32_t)*(uint8_t*)((cursor * 0xc) + 0x3000244)) - 1) << 0x18) >> 0x18));
    if (((uint32_t)*(uint8_t*)((cursor * 0xc) + 0x3000244)) <= 0x40)
    {
        *(uint8_t*)((cursor * 0xc) + 0x3000244) = 0x5a;
    }
}

As for the up and down keys, they are editing variables located at the memory address 50332228 (0x3000244), plus 12 (0xc) for each cursor shift. As for the nested if statements with 0x41 and 0x5a, they reset the counter back to A or Z if trying to find some mysterious 27th or 0th letter of the alphabet, respectively.

What are 0x41 and 0x5a? The codes for A and Z in hexadecimal, respectively.

Let us see what lives at those addresses:

03000244  char key_chars = 0x0
03000250  char data_3000250 = 0x0
0300025c  char data_300025c = 0x0
03000268  char data_3000268 = 0x0

Hmm, yes, data_300250, what a poetic way to say "second letter in the secret combination". Now, let us see what we need to print out the flag...

if (evaluation_routine() == 1) {
  sprintf(&var_38, "FLAG-%s%s%s%s", &var_15c);
}
while (true)
{
    tte_write("FAILURE");
}

We require a function named evaluation_routine to equal 1, or else, we enter an infinite loop where all our efforts to escape are rendered futile (until the big RESET button in my emulator is pressed, naturally).

That function is where the secret code is revealed to us... in a slightly indirect fashion.

if (((uint32_t)(key_chars & 0xf0)) != 0x40)
{
    r3 = 0;
}
else if (((uint32_t)(key_chars & 0xf)) != 5)
{
    r3 = 0;
}
else
{
    char r2_6 = data_3000250;
    if (((((uint32_t)r2_6) << 0x18) >> 0x18) < 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 0x40) == 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 0x20) != 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 0x10) != 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 8) != 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 4) == 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 2) == 0)
    {
        r3 = 0;
    }
    else if ((((uint32_t)r2_6) & 1) == 0)
    {
        r3 = 0;
    }
    else if ((((((uint32_t)r2_6) ^ ((uint32_t)data_300025c)) << 0x18) >> 0x18) != 0x16)
    {
        r3 = 0;
    }
    else
    {
        if ((((((uint32_t)(data_3000268 & 0xf0)) & 0x40) << 0x18) >> 0x18) != 0x40)
        {
            r3 = 0;
        }
        else
        {
            if (((uint32_t)(data_3000268 & 0xf)) == 0xb)
            {
                r3 = 1;
            }
            else
            {
                r3 = 0;
            }
        }
    }
}
return r3;

Wew. Good thing this is just a 4-letter code, or else, the Tentacular Mass of Writhing If Statements would grow as large as a star and would devour the universe as we know it.

We already know which variable corresponds to each letter in the combination (here is a reminder):

char key_chars // Letter 1
char data_3000250 // Letter 2
char data_300025c // Letter 3
char data_3000268 // Letter 4

We need to find the letters that will pass through all of those bitwise checks without ever tripping one, and landing on the dreaded r3 = 0 pitfalls, which will place us in the FAILURE eternal loop.

Understanding what the bitwise checks actually do is not required to solve the challenge - the 4 letter sequence can now be brute-forced by testing every single letter of the alphabet against each check.

Here is my quick Rust script to get the job done:

fn main() {
    for letter in b'A'..=b'Z' {
        let letter_32 = letter as u32;

        if (letter_32 & 0xf0) == 0x40 && (letter_32 & 0xf) == 5 {
            println!("Character '{}' meets conditions.", char::from_u32(letter_32).unwrap());
        }
    }

    for letter in b'A'..=b'Z' {
        let letter_32 = letter as u32;

        if ((letter_32 << 24) >> 24) >= 0 &&
           (letter_32 & 0x40)!= 0 &&
           (letter_32 & 0x20) == 0 &&
           (letter_32 & 0x10) == 0 &&
           (letter_32 & 8) == 0 &&
           (letter_32 & 4)!= 0 &&
           (letter_32 & 2)!= 0 &&
           (letter_32 & 1)!= 0 {
            println!("Character '{}' meets conditions.", char::from_u32(letter_32).unwrap());
        }        
    }

    for letter in b'A'..=b'Z' {
        let letter_32 = letter as u32;

        if (((b'G' as u32 ^ letter_32) << 0x18) >> 0x18) == 0x16{
            println!("Character '{}' meets conditions.", char::from_u32(letter_32).unwrap());
        }
    }

    
    for letter in b'A'..=b'Z' {
        let letter_32 = letter as u32;

        if ((((letter_32 & 0xf0) & 0x40) << 0x18) >> 0x18) == 0x40 && letter_32 & 0xf == 0xb{
            println!("Character '{}' meets conditions.", char::from_u32(letter_32).unwrap());
        }
    }
}

Note the b'A', converting from uppercase letters to their binary equivalents (an array of 8 unsigned 8-bit integers). Another amusing detail is the bitwise operation on 'G' in the third letter loop - indeed, the original code uses the variable tied to the second character (data_3000250) to check for the validity of the third character. As I was writing my code letter-by-letter, I was able to find out that the second loop always returns G as the only viable character.

For the curious, here is what is happening in the first loop:

  1. E, rendered in binary, is 01000101.
  2. 01000101 & 0xf fetches the last 4 bits, which becomes 00000101, which is equal to 5, or 00000101 in binary.
  3. 01000101 & 0xf0 only fetches the first 4 bits, which becomes 01000000, and this is indeed equal to 0x40, or 01000000 in binary.
  4. No other letter allows both of these checks to be true at once.
  5. All other loops do the same thing, with simply more numerous or varied checks.

And so, running this script returns the sequence: EGQK. Not much of a genetic code, now is it? There's guanine in there, sure, but what else? Ethanol? Quinine?? KETAMINE??? This Mr. Wellington's sure is a peculiar gentleman.

Regardless of any esoteric DNA composition, the code is indeed correct, and fetches us the flag!

The challenge window, showing the sequence EGQK and the revealed flag!

Technology Sprints and Marathons

I enjoyed my time at Northsec, but I have to say, the conditions are... dangerous. Over 16 hours of continued CTF solving on Saturday? Pizza served at 23:00? An waterfall of coffee perpetually flowing?

I think the CSGames were a little more reasonable in that aspect by granting 3 hour time limits after the initial revelation of each challenge. I don't know how much sleep the leading teams sacrificed, but closing challenges at 3:00 in the morning feels a bit like an encouragement of crunch culture.

Technology is a long and iterative process. Keeping a service secure is a matter of months of maintenance and investment in stable, robust protocols. Solutions cooked up in single day will crash and burn - even the so-called perfect ones, once you leave and the caretaking of your precious baby is entrusted to a successor with... less attachment to the guarantees of quality you pushed forwards.

Of course, it's a CTF. It's all in good fun. Work remains work, play remains play. Just don't make overwork a habit under the pressure of time...

Or else, once one can't take it anymore and is forced to quit, one will soon have time. Lots, lots of time.

If you enjoyed this writeup, feel free to contact me!

  • Discord: oneirical
  • Reddit: https://old.reddit.com/user/oneirical/
  • Zulip: https://rust-lang.zulipchat.com/#user/693959
  • Email: julien-robert@videotron.ca