This page looks best with JavaScript enabled

Pwnable: uaf

 ·  ☕ 4 min read

Introduction

We are given the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <fcntl.h>
#include <iostream> 
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;

class Human{
private:
	virtual void give_shell(){
		system("/bin/sh");
	}
protected:
	int age;
	string name;
public:
	virtual void introduce(){
		cout << "My name is " << name << endl;
		cout << "I am " << age << " years old" << endl;
	}
};

class Man: public Human{
public:
	Man(string name, int age){
		this->name = name;
		this->age = age;
        }
        virtual void introduce(){
		Human::introduce();
                cout << "I am a nice guy!" << endl;
        }
};

class Woman: public Human{
public:
        Woman(string name, int age){
                this->name = name;
                this->age = age;
        }
        virtual void introduce(){
                Human::introduce();
                cout << "I am a cute girl!" << endl;
        }
};

int main(int argc, char* argv[]){
	Human* m = new Man("Jack", 25);
	Human* w = new Woman("Jill", 21);

	size_t len;
	char* data;
	unsigned int op;
	while(1){
		cout << "1. use\n2. after\n3. free\n";
		cin >> op;

		switch(op){
			case 1:
				m->introduce();
				w->introduce();
				break;
			case 2:
				len = atoi(argv[1]);
				data = new char[len];
				read(open(argv[2], O_RDONLY), data, len);
				cout << "your data is allocated" << endl;
				break;
			case 3:
				delete m;
				delete w;
				break;
			default:
				break;
		}
	}

	return 0;	
}

Let’s see what flags it is compiled with:

Security flags

So far we can’t execute shellcode on the stack. However it doesn’t have ASLR enabled so we can reference segments of code without need of leaking addresses.

Solution

Before anything else, since we have the source code I compiled the binary with debugging symbols to make the task easier:

c++ -no-pie -g -O0 -fno-inline -fno-eliminate-unused-debug-types -o pie_debug_uaf uaf.cpp

The challenge makes references to uaf vulnerability, which is caused by using a pointer to memory after it was freed.

When calling new it allocates the initialized object in the heap.

Check how the heap chunk looks like when storing the instance:
New chunks

It stores a pointer to the text section where the class is located:
Heap pointer

When m->introduce() get’s compiled in the binary, it dereferences the pointer stored in the heap chunk and adds 0x8 to it. Afterwards it dereference it one more time and calls that function.

m->introduce()

Within the menu we can use the third option to free the variables. Notice how the first 8 bytes where the pointer to the class is now just filled with null bytes. Therefore calling again to m-introduce() leads to segmentation fault because it can’t dereference the address.
Heap after free

Finally the second menu option allows us to store the amount of bytes and data we want. So if we free the instance we can then write a chunk of the same size, since there is no other dynamic memory allocation going on we are guaranteed that our data is going to overwrite the chunk previously used by the class instance.

Therefore we can overwrite the pointer at the beginning of the chunk decreasing it’s value by 0x8 (0x401568). Doing this when executing m->introduce() it’s going to call give_shell.

Only first 3 bytes are being used so you could just overwrite the pointer instead of overwriting the whole instance structure.

Side notes

After solving the challenge I thought that it may be possible to read the file just from the heap. Tried locally and voilá:
Heap flag

However when I tried to read on the remote server my dreams got crushed. You can only read when you execute give_shell because bash uses setuid. Meanwhile open get’s called with 1029(uaf) as effective uid so you don’t have permissions to open it.

Here are the registers after attempting to open the flag:
open registers

Basically we don’t have permissions to open it, from the docs:
EPERM

Share on

ITasahobby
WRITTEN BY
ITasahobby
InTernet lover