deleteTask
select and index or ID to be free, also will remove it from the linked list
void deleteTask(void)
{
long in_FS_OFFSET;
int id;
custom_data *curr;
custom_data *before;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
if (head == (custom_data *)0x0) {
puts(&DAT_00102200);
}
else {
printf(&DAT_00102238);
__isoc99_scanf("%d",&id);
before = (custom_data *)0x0;
for (curr = head; (curr != (custom_data *)0x0 && (curr->ID != id)); curr = curr->next) {
before = curr;
}
if (curr == (custom_data *)0x0) {
puts(&DAT_00102267);
}
else {
if (before == (custom_data *)0x0) {
head = curr->next;
}
else {
before->next = curr->next;
}
free(curr);
curr = (custom_data *)0x0;
puts(&DAT_00102280);
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
modifyTask
edit a chunk within the linked list
void modifyTask(void)
{
long in_FS_OFFSET;
int local_1c;
custom_data *curr;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
if (head == (custom_data *)0x0) {
puts(&DAT_001022a0);
}
else {
printf(&DAT_001022c8);
__isoc99_scanf("%d",&local_1c);
for (curr = head; (curr != (custom_data *)0x0 && (curr->ID != local_1c)); curr = curr->next) {
}
if (curr == (custom_data *)0x0) {
puts(&DAT_00102267);
}
else {
printf(&DAT_001022f8);
getchar();
__isoc99_scanf("%80s",curr);
printf(&DAT_00102320);
__isoc99_scanf("%d",&curr->priority);
puts(&DAT_00102348);
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
Vulnerability
The vulnerability lies in the Add and Modify functions, where the program uses scanf to input 80 characters or 0x50 characters into the desc field. Although it initially seems harmless, it becomes problematic due to the nature of strings themselves, which are:
a collection/array of characters terminated by a null byte.
This characteristic is also reflected in the use of scanf with the format specifier %s. This means the result of scanf is an input of up to 0x50 characters plus one null byte at the end, making the actual memory input 81 bytes. This vulnerability is commonly referred to as an "off-by-one" error.
Exploitation
with the null byte overwrite, we can perform the operation ptr & 0xff on the next pointer of a chunk and corrupt the linked list. this enables us to create chunk overlaps, allowing the modification of chunk metadata as follows
from this chunk overlap, we can overwrite the size of a chunk or construct a fake chunk with a large size and then free it (as it is linked to the list) inserting it to the unsorted bin and leak the libc address.
using the same chunk overlap, we can also overwrite a next pointer to an arbitrary address, enabling arbitrary read and write operations.
with libc, we can perform an arbitrary read to leak the stack address via environ. Then, using arbitrary write, we can overwrite the saved stack address from the main stack frame to execute Return-Oriented Programming (ROP).
here's the final exploit being ran againts the remote server
└──╼ [★]$ sudo seccomp-tools dump ./chall
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x03 0x00 0x00000142 if (A == execveat) goto 0005
0002: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0005
0003: 0x15 0x01 0x00 0x00000039 if (A == fork) goto 0005
0004: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0005: 0x06 0x00 0x00 0x00000000 return KILL
Vulnerability
although the off-by-one issue no longer exists, during modifyTask, the size edit is determined by the length of the data inside the chunk.
If the data fully fills the chunk, strlen will also count the bytes beyond the data and as the mangled pointer of the chunk is located immediately afterwards it without a NULL byte as a separator, it will also count it as the length that is able to be edited.
As a result, it is possible to hijack the next pointer of the chunk.
Note that this vulnerability only exists in the modifyTask, as the addTask inputs data with a hard-coded size.
Exploitation
first, to bypass mangling, since the key is symmetric, both demangling and mangling use the same key and the same mathematical operations.
moreover, it is not necessary to recover the entire key to recreate the operation. consider the following operation that is used:
chunk_ptr ^ key ^ (key << 7 | key >> 0x39);
this can be simplified as:
chunk_ptr ^ (obfuscated_key);
if the chunk is the first chunk and also the tail of the linked list (i.e., next is NULL), then:
NULL ^ (obfuscated_key) = obfuscated_key
thus, we can obtain the value of the obfuscated key, which can then be used for both mangling and demangling operations, as demonstrated in the following test script.
next, we can hijack the next pointer to create overlapping chunks and UAF.
the rest of the exploitation process is similar to the previous one, except that the ROP chain is used not to spawn a shell but to perform an ORW as seccomp exists
here's the exploit being ran againts the remote server