Freeing a memory in prorgram, different bins have different ways they approach re-allocation
Fast Bins
Since Fast Bins follow LIFO (Last In First Out), the allocator will return the most recently freed chunk from the head of the fastbin (considering that the size is the same)
// Example
char *a = malloc(20); // 0x55bd0a0ac670
char *b = malloc(20); // 0x55bd0a0ac690
char *c = malloc(20); // 0x55bd0a0ac6b0
free(a);
free(b);
free(c);
b = malloc(20); // c - 0x55bd0a0ac6b0
c = malloc(20); // b - 0x55bd0a0ac690
d = malloc(20); // a - 0x55bd0a0ac670
On older versions of libc, malloc does not clear the memory that has been freed, we can use this to exploit the fact that the new allocation and freed size are the same.
Example 1 - PicoCTF AreYouRoot
int main(int argc, char **argv){
char buf[512];
char *arg;
uint32_t level;
struct user *user;
setbuf(stdout, NULL);
menu();
user = NULL;
while(1){
puts("\nEnter your command:");
putchar('>'); putchar(' ');
if(fgets(buf, 512, stdin) == NULL)
break;
if (!strncmp(buf, "show", 4)){
if(user == NULL){
puts("Not logged in.");
}else{
printf("Logged in as %s [%u]\n", user->name, user->level);
}
}else if (!strncmp(buf, "login", 5)){
if (user != NULL){
puts("Already logged in. Reset first.");
continue;
}
arg = strtok(&buf[6], "\n");
if (arg == NULL){
puts("Invalid command");
continue;
}
user = (struct user *)malloc(sizeof(struct user));
if (user == NULL) {
puts("malloc() returned NULL. Out of Memory\n");
exit(-1);
}
user->name = strdup(arg);
printf("Logged in as \"%s\"\n", arg);
}else if(!strncmp(buf, "set-auth", 8)){
if(user == NULL){
puts("Login first.");
continue;
}
arg = strtok(&buf[9], "\n");
if (arg == NULL){
puts("Invalid command");
continue;
}
level = strtoul(arg, NULL, 10);
if (level >= 5){
puts("Can only set authorization level below 5");
continue;
}
user->level = level;
printf("Set authorization level to \"%u\"\n", level);
}else if(!strncmp(buf, "get-flag", 8)){
if (user == NULL){
puts("Login first!");
continue;
}
if (user->level != 5){
puts("Must have authorization level 5.");
continue;
}
give_flag();
}else if(!strncmp(buf, "reset", 5)){
if (user == NULL){
puts("Not logged in!");
continue;
}
free(user->name);
user = NULL;
puts("Logged out!");
}else if(!strncmp(buf, "quit", 4)){
return 0;
}else{
puts("Invalid option");
menu();
}
}
}
// User Structure
struct user {
char *username;
double authorization_level;
}
Solve
We need authorization level of 5 to get the flag but we can only set it to 4 using the legitimate method
The chances of the allocator reallocating the user buffer after freeing is high as the user struct is not freed upon reset
We can just send a specially crafted username to see if the user struct will be reused upon reset
We can see that the username pointer is pointing to 0x603460 - 0x73616c6f6863696e - "nicholas" and our input has overridden the authorization level 0x3837363534333231
Since they only compare the last 4 bytes of the authorization level 0x34333231, we can simply adjust our payload to <username>\x05 and send it giving us the flag
Example 2 - Swamp19 Heap Golf
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-1BCh]
int i; // [rsp+8h] [rbp-1B8h]
int v6; // [rsp+Ch] [rbp-1B4h]
_DWORD *v7; // [rsp+10h] [rbp-1B0h]
_DWORD *v8; // [rsp+18h] [rbp-1A8h]
void *ptr[50]; // [rsp+20h] [rbp-1A0h]
char buf[8]; // [rsp+1B0h] [rbp-10h] BYREF
unsigned __int64 v11; // [rsp+1B8h] [rbp-8h]
v11 = __readfsqword(0x28u);
v7 = malloc(0x20uLL);
write(0, "target green provisioned.\n", 0x1AuLL);
ptr[0] = v7;
v4 = 1;
write(0, "enter -1 to exit simulation, -2 to free course.\n", 0x30uLL);
while ( 1 )
{
write(0, "Size of green to provision: ", 0x1CuLL);
read(1, buf, 4uLL);
v6 = atoi(buf);
if ( v6 == -1 )
break;
if ( v6 == -2 )
{
for ( i = 0; i < v4; ++i )
free(ptr[i]);
ptr[0] = malloc(0x20uLL);
write(0, "target green provisioned.\n", 0x1AuLL);
v4 = 1;
}
else
{
v8 = malloc(v6);
*v8 = v4;
ptr[v4++] = v8;
if ( v4 == 48 )
{
write(0, "You're too far under par.", 0x19uLL);
return 0;
}
}
if ( *v7 == 4 )
win_func();
}
return 0;
}
Solve
Allocate 4 buffers of the same size (32), which the fourth buffer will be equivalent to 4