BSidesSF CTF: Easy to hard Rust reversing challenges

As mentioned in a previous post, I was honoured to once again help run BSidesSF CTF!

This is going to be a quick writeup for three challenges: config-me, rusty1, and rusty2. All three are reversing challenges written in Rust, although the actual amount of reversing required is low for the first two.

config-me

config-me (source) was actually modeled after two different (but identical) vulnerabilities I’ve run into. The premise is a configuration file with opportunistically encrypted values - that is, some values are encrypted, and some aren’t - where you can fool the program into decrypting values for you.

From talking to solvers, I know it was solved in a fairly complex way, but I’m here to go over the simple route only. :)

When you run config-me, it reads config-me.conf and lets you edit it:

$ ./config-me 
Welcome to the Configuration Configurer, the tool for configuring your config files! Now featuring secure encryption!

(This tool is brought to you by the San Francisco Department of Redundancy Department)

Let's start by loading your configuration from ./config-me.conf

------------------------------------

Welcome back, Ron! Your config file currently has 5 entries. What would you like to do?

 [A]dd a key
 [D]elete a key
 [S]ave configuration file
 [L]oad a different configuration file
 [Q]uit

The important bit - that many people may have missed - is the welcome message:

Welcome back, Ron! Your config file currently has 5 entries. What would you like to do?

As you can see, that’s in the configuration file:

$ cat config-me.conf
name: Ron
password: E$0d6b731d24127ad34e76a78133c91e59f13ab12eaa8dc0ad99e10c71
comment: This configuration service is super duper! I'm going to write all my configurations like this!
conference: BSidesSF 2020
flag: E$af7ac775b3716f6d6ae96fdb6080ef41f4918e0b9f2837b82105b5da39

So what we do is change the name value to the flag value:

$ cat config-me.conf
name: E$af7ac775b3716f6d6ae96fdb6080ef41f4918e0b9f2837b82105b5da39
password: E$0d6b731d24127ad34e76a78133c91e59f13ab12eaa8dc0ad99e10c71
comment: This configuration service is super duper! I'm going to write all my configurations like this!
conference: BSidesSF 2020

Then run the program:

$ ./config-me 
Welcome to the Configuration Configurer, the tool for configuring your config files! Now featuring secure encryption!

(This tool is brought to you by the San Francisco Department of Redundancy Department)

Let's start by loading your configuration from ./config-me.conf

------------------------------------

Welcome back, CTF{my_rust_is_rusty}! Your config file currently has 4 entries. What would you like to do?

 [A]dd a key
 [D]elete a key
 [S]ave configuration file
 [L]oad a different configuration file
 [Q]uit

Your choice > 

And there’s your flag!

rusty1

I wanted rusty1 (source - which was provided) to be a gentle introduction to Rust reversing, as well as preparation (and a reference) for rusty2. It’s actually based 95% on game-docker-wrapper, which is a wrapper for running a Terraria server in Docker (with clean shutdown) that I wrote for my boyfriend. I just changed it to run /bin/bash instead of the Terraria server, removed all the flags, and added a bit of obfuscation.

The key lines are in input_task and output_task:

  // Encode
  let mut bytes: Vec<u8> = line.into_bytes().into_iter().map(|b| b - 1).collect();
  // Output
  let mut bytes: Vec<u8> = line.into_bytes().into_iter().map(|b| b - 1).collect();

That shifts each byte of the line by 1, meaning that you have to encode your payloads, and decode responses. cat /home/ctf/flag.txt becomes dbu!0ipnf0dug0gmbh/uyu. If you send that to the server, you get an encoded response:

$ echo 'dbu!0ipnf0dug0gmbh/uyu' | nc rusty1-080e45dc.challenges.bsidessf.net 8832
BSEzxd`g^ats^xnt^g`c^sgd^rntqbd|

If you decode that, it’s the flag (please forgive the ruby code :) ):

irb(main):007:0> 'BSEzxd`g^ats^xnt^g`c^sgd^rntqbd|'.bytes.map {|b| (b+1).chr}.join
=> "CTF{yeah_but_you_had_the_source}"

rusty2

rusty2 (source) is nearly identical to rusty1 with two changes: the encoding is more complex, and I don’t provide source. My goal was for people to compare rusty1 and rusty2 source to eliminate all the same-y stuff, and just focus on the difference. I’d be curious if people did that?

This time, the encoding is Base32 encoded with a non-standard alphabet: Crockford. Normally, it’s used to ensure easier human readability - ambiguous characters are replaced. But in this case, it’s to make it harder to guess. :)

I used this encoder to test, and encoded cat /home/ctf/flag.txt:

$ echo 'CDGQ881FD1QPTS9FCDT6CBV6DHGPEBKMF1T0' | nc rusty2-7c8a2fad.challenges.bsidessf.net 8833
8DA4CYVFDDGQJQV3DXP6YXBJBXPPAQV9DNR74SBKEDJP8Z8

The response decoded into the flag:

CTF{okay_colour_me_impressed}

Comments

Join the conversation on this Mastodon post (replies will appear below)!

    Loading comments...