Heap 2
Write-up for: https://exploit.education/phoenix/heap-two/.
The vulnerability
The goal of this challenge is trick the program into thinking that the user is authenticated. This requires two preconditions to be true:
auth && auth->auth
auth
needs to be a valid pointer and the value of auth->auth
needs to be non-zero.
auth
can be set by entering the string auth
. This leads to allocation of memory on
the heap for the auth
struct. The memory is zero-initialized and the string after the
auth command is copied to the name
field (length-checked). Every time, the auth
command
is issued again, new memory is allocated and assigned to the auth
pointer variable.
Issuing the reset
command will free the memory pointed to by the auth
pointer.
There is a memory leak issue in the program. If the auth
command is given multiple
times without a corresponding reset
call,
the allocated memory is leaked since the program has no way of freeing the memory.
However, the security issue here is more subtle. The auth
pointer is never set to NULL
.
This means, even if the memory pointed to by auth
is freed, it still points to the memory.
Thus, the check if auth
is not NULL
will succeed. Then, the auth
pointer is dereferenced
to access the auth->auth
field. This is undefined behavior. Since this memory was freed,
it can be re-used by subsequent allocations.
When we issue the service
command, the strdup
function will also allocate memory. In
the used malloc implementation, the memory previously pointed to by auth
will be reused,
but auth
still points there. This way, via the service
pointer, we can manipulate the
heap in a way that tricks the program in thinking auth->auth != 0
. Let’s see this in action:
user@phoenix-amd64:/opt/phoenix/amd64$ ./heap-two
Welcome to phoenix/heap-two, brought to you by https://exploit.education
[ auth = 0, service = 0 ]
auth AAA
[ auth = 0x600e40, service = 0 ]
reset
[ auth = 0x600e40, service = 0 ]
service A
[ auth = 0x600e40, service = 0x600e40 ]
login
you have logged in already!
[ auth = 0x600e40, service = 0x600e40 ]
First, auth
and service
are initialized to NULL
. auth AAA
allocates memory at 0x600e40
which is
freed by the reset
call. However, auth
still points to the memory. This memory is re-used in the subsequent
service A
call. As you can see, service
points to the same memory location!
The final call to login
will now follow the stale auth
pointer and we see the success message. Done!
We could confirm all this in GDB but since the program provides nice output we’ll skip this exercise this time :).