A custom kernel module was vulnerable to a buffer overflow, with a small ropchain I escalated my privileges to root and with a sys_chmod syscalls I got the flag.
Description
Recon
I’m not a Linux kernel expert, everything might not be 100% correct, but I’ll do my best to summarize what I understood.
We were provided with 3 files, a bzImage, an iniramfs and a custom kernel module. I tried to run everything locally with qemu, but It didn’t work as the provided initramfs was broken, so let’s run the virtual machine on the server and see what happens.
It seems that the flag is located in the root directory, but the XXXX are a bit weird. We can create files and see what append inside the home folder of the current user:
1 2 3 4 5 6 7 8 9 10 11 12 13
~ $ touch ecsc_flag_aaaa
~ $ ls -asl
total 0
0 drwxrwxrwx 2 ctf ctf 0 May 7 15:55 .
0 drwxr-xr-x 3 root root 0 Feb 25 09:30 ..
0 -r-------- 0 root root 0 Jan 0 1900 ecsc_flag_XXXX
~ $ touch ecsc_flag_bbbb
~ $ ls -asl
total 0
0 drwxrwxrwx 2 ctf ctf 0 May 7 15:56 .
0 drwxr-xr-x 3 root root 0 Feb 25 09:30 ..
0 -r-------- 0 root root 0 Jan 0 1900 ecsc_flag_XXXX
0 -r-------- 0 root root 0 Jan 0 1900 ecsc_flag_aaaa
It looks like the permission on files starting by ecsc_flag_ are modified.
Static analysis
Let’s load the kernel module inside Ghidra and search for interesting things. The module is not so big we can quickly spot what’s going on, the init_module function is responsible for the initialization of this custom module.
The module replaces the addresses of the original sys_getdents64, sys_getdents and sys_lstat addresses by custom addresses which are functions inside the module. The module is hooking legit function by a custom one. From the Linux man page we can read:
getdents, getdents64 - get directory entries
When we want to list entries in a directory this syscall is made and instead of the original one the Linux kernel will call the custom one. Here is a part of the custom sys_getdents function:
We can spot a strcpy with only 2 parameters which means a buffer overflow if we can control the second parameter. The second parameter comes from the second parameter of the sys_getdents function. Once again from the Linux man page:
1 2 3 4
int getdents(unsigned int fd, struct linux_dirent *dirp,
unsigned int count);
int getdents64(unsigned int fd, struct linux_dirent64 *dirp,
unsigned int count);
The second parameter is a pointer to a linux_dirent structure. Here is the definition of such a structure:
1 2 3 4 5 6 7 8 9 10 11 12 13
struct linux_dirent {
unsigned long d_ino; /* Inode number */
unsigned long d_off; /* Offset to next linux_dirent */
unsigned short d_reclen; /* Length of this linux_dirent */
char d_name[]; /* Filename (null-terminated) */
/* length is actually (d_reclen - 2 -
offsetof(struct linux_dirent, d_name)) */
/*
char pad; // Zero padding byte
char d_type; // File type (only since Linux
// 2.6.4); offset is (d_reclen - 1)
*/
}
So at the offset 0x12 of a linux_dirent structure we can find the d_name parameter. The little loop before the strcpy checks that the name of the file starts by ecsc_flag_. We can try a buffer overflow with a long filename starting by ecsc_flag_ and see what append:
A kernel stack trace with RIP: 0010:0x6161616161616161, we have full control over RIP we can start building our exploit.
Ropping through hell
I though that it would be easy from here, but I actually spent a lot of time finding a good strategy to execute some privileged actions. My idea was initially to pop a root shell, let’s see the different steps to get there. First I tried to map a userland region as rwx and execute a custom shellcode from there, but I don’t know why It didn’t work. So I decided to build a ropchain.
In order to elevate our privileges, we have to call these functions in kernel-land:
1
commit_creds(prepare_kernel_cred(0));
This will grant us root privileges which means that we will theoretically be able to do whatever we want on the system. To call these functions we need to know their addresses, kaslr is enabled, but we can read /proc/kallsym which holds all the kernel function addresses and their current offset, thus defeating kaslr.
Here is a useful function to get kernel addresses base on the symbol name :
unsigned long get_kernel_sym(char *name) {
FILE *f;
unsigned long addr;
char dummy;
char sname[256];
int ret = 0;
f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
printf("[-] Failed to open /proc/kallsyms\n");
exit(-1);
}
printf("[+] Find %s...\n", name);
while(ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
printf("[+] Found %s at %lx\n", name, addr);
return addr;
}
}
fclose(f);
return 0;
}
We can now compute the addresses of those functions, but we need gadgets, and there are not so many gadgets inside the kernel module. To get more gadgets I extracted the vmlinux from the bzImage with this tool. Then we can launch ROPgadet on the vmlinux and get a lot of them. Here is the beginning of my ropchain:
After those gadget are executed, we will get root privileges. My naive idea was just to go back to a userland function and execute system(“/bin/sh”) but it didn’t work as expected. I triggered segfault and I could only execute one printf then it would failed with a segfault.
I successfully executed a printf which means that I still can execute code but not for long. I tried to execute a custom minimal shellcode to call execve(‘/bin/sh’) but without success. I was probably messing up with the kernel stack so I either had to restore it or find another solution. I tried in vain to restore it and I also tried to make a stack pivot but without sucess.
I also tried to execute a call to exit after the ropchain, it worked but printf and exit are pretty useless in our case. But something is happening, we can still make at least one syscall, what can we do with one sycall ?
One syscall to rule them all
I used the sys_chmod sycall and It worked, no root shell but a root directory with rwx permission for everybody. I also tried to put the suid bit on /bin/sh but it didn’t work.
First a syscall to chmod:
syscall(SYS_chmod, “/”, 0777);
Then I created a file starting by ecsc_flag_ inside the root folder to get the real name of the flag and then we can get the flag:
Final thought
I saw other write-ups where people simply unloaded the kernel module and then printed the flag which is simpler and less painful. I really wanted a root shell but I don’t know why It didn’t work since there is no SMEP or SMAP protection if someone has any idea please send me a message :)
Event Challenge Category Points Solves ecsc Hello Rootkitty pwn 500 24 TL;DR A custom kernel module was vulnerable to a buffer overflow, with a small ropchain I escalated my privileges to root and with a sys_chmod syscalls I got the flag.
Description Recon I’m not a Linux kernel expert, everything might not be 100% correct, but I’ll do my best to summarize what I understood.
Introduction I recently bought a DVID board which is an open source vulnerable designed IoT device. In this post I will try to explain how to solve the third challenge of the DVID project. In this challenge we need to write data to a special characteristic.
Challenge Let’s flash the firmware, enable and setup the usb dongle:
1
2
3
sudo avrdude -c usbasp -p m328p -U flash:w:characteristics2.
Introduction I recently bought a DVID board which is an open source vulnerable designed IoT device. In this post I will try to explain how to solve the second Bluetooth challenge of the DVID project. In this challenge we need to read data from a special characteristic.
Challenge Let’s flash the firmware, enable Bluetooth and setup the usb dongle:
1
2
3
sudo avrdude -c usbasp -p m328p -U flash:w:characteristics.
Introduction I recently bought a DVID board which is an open source vulnerable designed IoT device. In this post I will try to explain how the Bluetooth protocol works and how we can solve the first Bluetooth challenge of the DVID project.
Bluetooth protocol This talk is a good introduction to Bluetooth hacking, what’s following come from this document, but if you want more details you should read it. The next diagram shows how a typical connection between a phone and a Bluetooth device work.
Event Challenge Category Points Solves AperiCtf TMNT web 300 6 TL;DR In this challenge we need to trigger an XSS, first we need to bypass the template engine of the browser to insert custom tags in the page. We can then trigger the XSS with some specific tag and use a DOM-based JavaScript injection vulnerability.
Step 1 This is my first web write-up, I usually prefer popping shell, but this time we will pop some alert boxes !
Event Challenge Category Points Solves AperiCtf PwnRunSee 1 pwn 175 5 AperiCtf PwnRunSee 2 pwn 250 2 TL;DR This challenge was a use after free vulnerability which allow the user to get a shell on the remote docker after a call to execve with some user controlled parameters. Once inside the docker, we can abuse some privileges to mount the host disk inside the container and get the last flag.
Event Challenge Category Points Solves ecsc2019 filereader pwn 1000 20 TL;DR We need to exploit binary which read the content of files listed in an other file. A buffer-overflow is present in one of the function and we can leak the address of libc thanks to /proc/self/map since we can read files. A onegadget is then used to pop a shell.
Event Challenge Category Points Solves inshack-2019 gimme-your-shell pwn 50 67 TL;DR This is a remote buffer overflow challenge, there is no protection on the binary but ASLR is enable on the remote server. I redirected the execution flow to write my shellcode to a controled area, then jump to it and execute it.
Getting informations First I looked at the protections on the binary :
Event Challenge Category Points Solves santhacklausctf mi1 Forensic/Crypto 800 18 TL;DR After downloading the zip file we were faced with a linux memory dump. After building the correct profile for volatility you had to perform a known plain text attack on an encrypted and splited zip file to recover the file flag.txt.
Introduction After downloading the file MI1.zip we had a memdump.
Event Challenge Category Points Solves santhacklausctf mi2 Forensic/Crypto/network 500 22 TL;DR In the second part of the challenge we also had a memory dump of a Debian system and a network capture. When you analyse the network capture you can see that some data were exfiltrated, if you look into the memdup you can see that the tool DET (Data Exfiltration Toolkit), has been used to exfiltrate the data.