Recently I saw Timothy Morgan (@ecbftw) presentation on OWASP AppSec USA’13 (Video) where he explained a clever trick to exploit a XXE or SSRF vulnerability fooling the server to fetch a file for us using the jar:// protocol. The trick is to serve the file but keep the connection opened, so our file is effectively uploaded to the victim server and stored on a temporary location until we close the connection.
In Level 19 we are given the source code of a vulnerable program:
#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.
After reading this great post by Dan Rosenberg, I learned about using LD_PRELOAD to pre-populate uninitializaed variables with arbitrary contents. The details are explained in the article, I just wanted to show how it can be used to solve challange 11.
Ok, so we are going to try to fill the uninitialized buffer used in the process function with a string containing the commands to be be run:
level11@nebula:/home/flag11$ export LD_PRELOAD=`python -c 'print("\x0a/bin/getflag"*80)'` Now we can go and execute our binary and see if it works:
In Level 18 we are given the code of a vulnerable program:
#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) !
In Level 17 we are given a vulnerable python server:
#!/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) The only part of the application that processes our data is:
In [Level 16]() we are given the following perl CGI:
#!/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?
In Level 15 we are given the following description:
strace the binary at /home/flag15/flag15 and see if you spot anything out of the ordinary. You may wish to review how to “compile a shared library in linux” and how the libraries are loaded and processed by reviewing the dlopen manpage in depth. Clean up after yourself :)
As suggested by the challange, we execute strace:
level15@nebula:/home/flag15$ strace ./flag15 execve(".
In Level14 we are given an encrypted token: 857:g67?5ABBo:BtDA?tIvLDKL{MQPSRQWW. and the cipher.
We can try to reverse the cipher but lets play with it and see if we can find out the encryption routine:
level14@nebula:/home/flag14$ ./flag14 -e aaaaaaaaaaaaaaaaa abcdefghijklmnopq level14@nebula:/home/flag14$ ./flag14 -e abcdefg acegikm Ok, so it looks pretty simple, we shift a given characters a number of positions in the ASCII table where the key is the position of the character to encrypt.
In Level13 we are given the following code:
#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); } Well the code is missing the token but it is clear that is reading the user UID anc comparing it with 1000, so the only way to get our token is to fake that our UID is 1000.
In Level12 we are given the following code:
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 We have a command injection as the password variable can be controlled by the user and it is used to create a command that will be run in the system.