Eureka just retired on HackTheBox, and it was a good “Hard” Linux machine that made me learn a few new tricks. This box was a fun challenge, needing good enumeration, a bit of knowledge about Java Spring Boot problems, and a smart way to look at how services find each other to get the user flag. The path to root then moved to a cool Bash script vulnerability, showing that even simple-looking scripts can lead to privesc. In the end Eureka was a well-made box and a great way to learn, especially about common Spring issues and tricky script exploits. Definitely one to try if you like a challenge with a few steps!
Tl;Dr: To get the user flag: first, find an exposed Spring Boot /actuator/heapdump endpoint. Analyze the heapdump to extract initial user credentials (oscar190). Further analysis of the same heapdump reveals credentials for a Spring Eureka service. Exploit Spring Eureka by registering a malicious service instance to hijack traffic intended for the USER-MANAGEMENT-SERVICE, capturing credentials this way for miranda-wise and gaining access to the user flag. For the root flag: identify a cron job executing a Bash script (/opt/log_analyse.sh) as root, which processes application logs. miranda-wise user can write to one of these log files due to developers group membership. Exploit a command injection vulnerability within the Bash script’s arithmetic evaluation context ([[ ... -eq ... ]]) by crafting a malicious log entry, allowing arbitrary command execution as root to set up an SUID binary for a root shell.
Alright! Let’s get into the details now!
First things first, let’s add the box IP to our /etc/hosts file:
Let’s begin with the classic nmap scan to see which ports are open on the box:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[hg8@archbook ~]$ nmap -sV -sT -sC eureka.htb Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-23 11:51 UTC Nmap scan report for eureka.htb (10.10.11.66) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 d6:b2:10:42:32:35:4d:c9:ae:bd:3f:1f:58:65:ce:49 (RSA) | 256 90:11:9d:67:b6:f6:64:d4:df:7f:ed:4a:90:2e:6d:7b (ECDSA) |_ 256 94:37:d3:42:95:5d:ad:f7:79:73:a6:37:94:45:ad:47 (ED25519) 80/tcp open http nginx 1.18.0 (Ubuntu) |_http-server-header: nginx/1.18.0 (Ubuntu) |_http-title: Did not follow redirect to http://furni.htb/ Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 8.63 seconds
We have a typical web app running on port 80 and the SSH port 22 open. We can see that port 80 redirects to a new hostname. Let’s add it to our hosts file as well:
The Spring Boot Actuator heapdump endpoint is designed to capture the current state of the Java heap, making it a valuable tool for diagnosing memory issues. However, if credentials such as passwords, tokens, cloud keys, or other secrets are loaded into the memory of a Java application’s JVM during its runtime, these might be included in the heap dump.
Let’s download it and search through it; maybe we can find passwords, credentials, or other secrets:
1
curl http://furni.htb/actuator/heapdump -O
strings and grep are enough to find what we are looking for.
We now have credentials for a user named oscar190. Let’s see if Oscar reuses these credentials for SSH:
1 2 3 4 5
[hg8@archbook ~]$ ssh oscar190@eureka.htb
Last login: Wed Jun 4 14:00:18 2025 from 10.10.14.93 oscar190@eureka:~$ ls oscar190@eureka:~$
We gain access to Oscar’s account on the server. No flag here yet. Looking at /etc/passwd and /home/, it seems like we might need to pivot to the user named miranda-wise:
1 2 3 4
oscar190@eureka:~$ ls -l /home total 8 drwxr-x--- 8 miranda-wise miranda-wise 4096 Mar 21 13:26 miranda-wise drwxr-x--- 5 oscar190 oscar190 4096 Apr 1 12:57 oscar190
With that in mind, let’s see what ports are listening on the server:
Most ports seem uninteresting for now, except for port 8761. This is the default port for Spring Eureka. Considering the box name (eureka), this seems like a promising lead. Let’s try to access it:
It’s protected with Basic Auth, and the credentials we retrieved earlier (oscar190:0sc@r190_S0l!dP@sswd) do not work.
Perhaps we should revisit the heapdump. Given its size, it might contain more interesting information, specifically about this Spring Eureka instance running on port 8761.
We indeed find something very interesting: an HTTP query log showing access to localhost:8761, which includes Basic Auth credentials:
This page looks a bit confusing at first. We need to understand what Spring Eureka is and what it’s used for. According to the documentation, Spring Eureka simply provides integrations for Spring Boot apps with “Netflix Eureka”. The Netflix Eureka documentation describes it as:
Eureka is a RESTful service that is primarily used in the cloud for the purpose of discovery, load balancing and failover of middle-tier servers.
To keep it simple: it’s a service discovery tool. It helps microservices find and communicate with each other without hardcoding hostnames or IPs.
Given that we have access to this Eureka instance, the first thing that comes to mind is: Can we register a malicious application? And what access or impact could we gain by doing so?
We notice an interesting instance already registered: USER-MANAGEMENT-SERVICE. Let’s keep that in mind to see if we can find a way to exploit this instance.
Traffic Hijacking via Malicious Instance Registration
While researching this “malicious instance” registration idea, I stumbled upon a very interesting blog article which dives deep into a few exploitation scenarios. The one that caught my attention was Traffic Hijacking. The idea here is to register a new instance of USER-MANAGEMENT-SERVICE and point it to a malicious server we control. By doing so, the APP-GATEWAY should begin to route HTTP traffic intended for USER-MANAGEMENT-SERVICE to our malicious server instead. Given the name USER-MANAGEMENT-SERVICE, it’s likely that credentials or sensitive user data will be present in this traffic.
Let’s see if we can pull this off!
First, we need to find a way to register a new instance. According to the documentation, we can leverage the REST API to do so. The documentation itself is outdated and frankly terrible, but after a bit of trial and error, we were able to craft a POST request to add a new instance to the USER-MANAGEMENT-SERVICE application. Our malicious instance is quite simple here: we will have netcat listening on port 8585 to retrieve all the traffic forwarded by APP-GATEWAY that was meant for USER-MANAGEMENT-SERVICE.
Final Request (the vipAddress field gave me a lot of pain):
I quickly noticed what looks like a homemade script to analyze the logs of the main application. We don’t have permission to read /opt/scripts/log_cleanup.sh; however, we can read /opt/log_analyse.sh. Let’s take a look.
# Main execution analyze_logins analyze_http_statuses display_results | tee"$OUTPUT_FILE" analyze_log_errors | tee -a "$OUTPUT_FILE" echo -e "\n${GREEN}Analysis completed. Results saved to $OUTPUT_FILE${RESET}"
This one was tricky and took me a while to find. I had already searched the rest of the server for other privilege escalation vectors but hadn’t found anything besides this interesting configuration:
1 2 3 4 5 6 7 8 9
miranda-wise@eureka:/tmp$ ls -la /var/www/web/cloud-gateway/log/ drwxrwxr-x 2 www-data developers 4096 Jun 4 18:34 . drwxrwxr-x 6 www-data developers 4096 Mar 18 21:17 .. -rw-r--r-- 1 www-data www-data 21254 Jun 4 18:42 application.log -rw-rw-r-- 1 www-data www-data 5702 Apr 23 07:37 application.log.2025-04-22.0.gz -rw-rw-r-- 1 www-data www-data 5956 Jun 4 17:01 application.log.2025-04-23.0.gz
miranda-wise@eureka:/tmp$ id uid=1001(miranda-wise) gid=1002(miranda-wise) groups=1002(miranda-wise),1003(developers)
So, while we don’t have permission to edit application.log directly, we are part of the developers group. This means we can delete and recreate the file with any content we want. We know that this log file is being read by the root-owned script: /opt/log_analyse.sh /var/www/web/cloud-gateway/log/application.log.
Let’s review the script once more, keeping in mind that we can potentially inject content into the log file it processes (and, as a reminder, the script runs as root).
Arithmetic Evaluation Code Injection
This function is interesting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
analyze_http_statuses() { # Process HTTP status codes while IFS= read -r line; do code=$(echo"$line" | grep -oP 'Status: \K.*') found=0 # Check if code exists in STATUS_CODES array for i in"${!STATUS_CODES[@]}"; do existing_entry="${STATUS_CODES[$i]}" existing_code=$(echo"$existing_entry" | cut -d':' -f1) existing_count=$(echo"$existing_entry" | cut -d':' -f2) if [[ "$existing_code" -eq "$code" ]]; then new_count=$((existing_count + 1)) STATUS_CODES[$i]="${existing_code}:${new_count}" break fi done done < <(grep "HTTP.*Status: ""$LOG_FILE") }
I first noticed the regex used by grep (grep -oP 'Status: \K.*') looks problematic, as it doesn’t have an end delimiter. If we create a line in the log like “Status: 200 Hello World!”, then the $code variable will contain 200 Hello World!. By itself, this isn’t necessarily a problem. However, the vulnerability appears later in the comparison: if [[ "$existing_code" -eq "$code" ]]; then. It took me a bit of research to realize that in Bash’s [[ ]] conditional construct, the -eq operator forces arithmetic evaluation of its operands. So, if $code contains something like 1+1 or, more dangerously, an arithmetic expression containing a command substitution like 0[$(malicious_command)], Bash will evaluate it.
We can craft our command injection using array subscript syntax in Bash arithmetic (0[$(cmd)]). The if condition will then be evaluated somewhat like this (conceptually):
1
[[ 5 -eq 0[$(touch /tmp/test)] ]]
Given that we can inject code that will be run as root, let’s go for the classic SUID bit trick on Bash: