Skip to content

Instantly share code, notes, and snippets.

@00xc
Last active December 2, 2022 09:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 00xc/f10a2f99e22eb3f85f29ab35efd80de9 to your computer and use it in GitHub Desktop.
Save 00xc/f10a2f99e22eb3f85f29ab35efd80de9 to your computer and use it in GitHub Desktop.
Proof of concept for a use-after-free bug in libbus.
/*
* 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