Last active
December 2, 2022 09:15
-
-
Save 00xc/f10a2f99e22eb3f85f29ab35efd80de9 to your computer and use it in GitHub Desktop.
Proof of concept for a use-after-free bug in libbus.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* uaf.c | |
* This program serves as a proof of concept for a dangling pointer bug in libbus. | |
* It displays a message if a client context gets corrupted due to it being freed. | |
* Compile with: gcc uaf.c libbus.a -O3 -Wall -Wextra -o uaf -I<path_to_libbus>/src/ -latomic -pthread | |
* https://github.com/00xc/libbus | |
* https://scavengersecurity.com/posts/libbus/ | |
* https://scavengersecurity.com/posts/libbus-uaf/ | |
*/ | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <err.h> | |
#include <pthread.h> | |
#include <unistd.h> | |
#include "bus.h" | |
#define NUM_THREADS 2 | |
/* Data passed to each thread */ | |
typedef struct { | |
Bus* bus; | |
unsigned int id; | |
} ThreadData; | |
/* Function to be called by the bus for each new message */ | |
void bus_callback(void* _ctx, void* _msg) { | |
(void)_msg; | |
sleep(1); | |
int ctx = *(int*) _ctx; | |
/* If the context got corrupted, print a message */ | |
if (ctx != 1) | |
printf("Data corrupted! %d\n", ctx); | |
else | |
printf("Data OK\n"); | |
} | |
/* This funcion will be spawned `NUM_THREADS` times as a separate thread. */ | |
void* thread_fn(void* _data) { | |
ThreadData* data = (ThreadData*) _data; | |
if (data->id == 0) { | |
int* ctx = malloc(sizeof(int)); | |
*ctx = 1; | |
if (!bus_register(data->bus, data->id, &bus_callback, ctx)) { | |
fprintf(stderr, "Error registering client %d\n", data->id); | |
return NULL; | |
} | |
printf("[Thread %d] Registered callback\n", data->id); | |
sleep(2); | |
if (!bus_unregister(data->bus, data->id)){ | |
fprintf(stderr, "Error unregistering client %d\n", data->id); | |
} | |
printf("[Thread %d] Unregistered callback\n", data->id); | |
free(ctx); | |
} else { | |
int sent = 0; | |
while (1) { | |
if (!bus_send(data->bus, 0, NULL, 0)) { | |
if (sent > 0) { | |
printf("[Thread %d] Failed to send message to Thread 0 after %d messages (client got unregistered), stopping\n", data->id, sent); | |
break; | |
} | |
} else { | |
++sent; | |
printf("[Thread %d] Sent message to Thread 0 (%d)\n", data->id, sent); | |
} | |
} | |
} | |
return NULL; | |
} | |
int main() { | |
unsigned int i; | |
Bus* bus; | |
pthread_t threads[NUM_THREADS]; | |
ThreadData contexts[NUM_THREADS]; | |
srand(time(NULL)); | |
if (!bus_new(&bus, 0)) | |
errx(EXIT_FAILURE, "error @ %s:%d: bus_new", __FILE__, __LINE__); | |
/* Launch threads, each with their own context containing a reference to the bus and their ID */ | |
for (i=0; i<NUM_THREADS; ++i) { | |
contexts[i].bus = bus; | |
contexts[i].id = i; | |
if (pthread_create(&threads[i], NULL, thread_fn, &contexts[i])){ | |
fprintf(stderr, "Error creating thread %d\n", i); | |
} | |
} | |
/* Wait until completion */ | |
for (i=0; i<NUM_THREADS; ++i) { | |
if (pthread_join(threads[i], NULL) != 0) { | |
fprintf(stderr, "Error joining thread %d\n", i); | |
} | |
} | |
bus_free(bus); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment