Web
Nmap discovered a web server on the target port 80
The running service is nginx 1.18.0
Webroot
It appears to provide a web hosting service
Wappalyzer identified technologies involved
The passive crawler from Burp Suite picked up an interesting endpoint;
login
/login
Several methods of authentication bypass were attempted with no avail
Fuzzing
┌──(kali㉿kali)-[~/archive/htb/labs/cozyhosting]
└─$ ffuf -c -w /usr/share/wordlists/seclists/discovery/web-content/directory-list-2.3-medium.txt -u http://cozyhosting.htb/FUZZ -ic
________________________________________________
:: Method : GET
:: URL : http://cozyhosting.htb/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
[status: 200, Size: 4431, Words: 1718, Lines: 97, Duration: 95ms]
* fuzz: login
[status: 200, Size: 12706, Words: 4263, Lines: 285, Duration: 139ms]
* fuzz: index
[status: 401, Size: 97, Words: 1, Lines: 1, Duration: 59ms]
* fuzz: admin
[status: 204, Size: 0, Words: 1, Lines: 1, Duration: 32ms]
* fuzz: logout
[status: 500, Size: 73, Words: 1, Lines: 1, Duration: 36ms]
* fuzz: error
:: Progress: [220547/220547] :: Job [1/1] :: 307 req/sec :: Duration: [0:14:19] :: Errors: 0 ::
Found /admin
┌──(kali㉿kali)-[~/archive/htb/labs/cozyhosting]
└─$ ffuf -c -w /usr/share/wordlists/seclists/discovery/web-content/spring-boot.txt -u http://cozyhosting.htb/FUZZ -ic
________________________________________________
:: Method : GET
:: URL : http://cozyhosting.htb/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/spring-boot.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
[status: 200, Size: 634, Words: 1, Lines: 1, Duration: 103ms]
* fuzz: actuator
[status: 200, Size: 148, Words: 1, Lines: 1, Duration: 167ms]
* fuzz: actuator/sessions
[status: 200, Size: 487, Words: 13, Lines: 1, Duration: 231ms]
* fuzz: actuator/env/lang
[status: 200, Size: 487, Words: 13, Lines: 1, Duration: 211ms]
* fuzz: actuator/env/path
[status: 200, Size: 487, Words: 13, Lines: 1, Duration: 284ms]
* fuzz: actuator/env/home
[status: 200, Size: 4957, Words: 120, Lines: 1, Duration: 295ms]
* fuzz: actuator/env
[status: 200, Size: 15, Words: 1, Lines: 1, Duration: 232ms]
* fuzz: actuator/health
[status: 200, Size: 9938, Words: 108, Lines: 1, Duration: 276ms]
* fuzz: actuator/mappings
[status: 200, Size: 127224, Words: 542, Lines: 1, Duration: 325ms]
* fuzz: actuator/beans
:: Progress: [112/112] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
Performing additional fuzzing with a different wordlist returned a directory; /actuator
the
/actuator
directory is mostly associated with spring boot framework, which is used in Spring web application
In a Spring Boot web application, an actuator is a set of production-ready features that provide valuable insights and management capabilities. These features include endpoints that expose information about the application’s health, metrics, environment, and more. Actuators are crucial for monitoring and managing the application in a production environment, as they enable administrators to check its status, diagnose issues, and even modify certain aspects of the application without stopping it. Spring Boot Actuators can be easily integrated into an application by adding the necessary dependencies and configuring them to expose the desired endpoints, making it a powerful tool for ensuring the reliability and performance of Spring Boot applications.
/actuator
Heading over to the
/actuator
page reveals a series of API endpoints
Location reveals that the web application itself it hosted over localhost:8080
and likely proxied through nginx
/actuator/health
The
/actuator/health
endpoint shows the status of the web application
/actuator/env
The
/actuator/env
endpoint in a Spring Boot application’s Actuator module provides information about the application’s environment properties and configuration. It exposes a snapshot of the application’s runtime environment, including properties from configuration files, environment variables, and other sources. Developers and administrators can use this endpoint to inspect the current configuration, which can be valuable for troubleshooting and monitoring purposes in real-time.
/actuator/mappings
┌──(kali㉿kali)-[~/archive/htb/labs/cozyhosting]
└─$ curl http://cozyhosting.htb/actuator/mappings
{"contexts":{"application":{"mappings":{"dispatcherServlets":{"dispatcherServlet":[{"handler":"Actuator web endpoint 'env'","predicate":"{GET [/actuator/env], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/env"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'env-toMatch'","predicate":"{GET [/actuator/env/{toMatch}], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/env/{toMatch}"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'sessions'","predicate":"{GET [/actuator/sessions], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/sessions"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'health'","predicate":"{GET [/actuator/health], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/health"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'health-path'","predicate":"{GET [/actuator/health/**], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/health/**"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'mappings'","predicate":"{GET [/actuator/mappings], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/mappings"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator web endpoint 'beans'","predicate":"{GET [/actuator/beans], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler","name":"handle","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator/beans"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"Actuator root web endpoint","predicate":"{GET [/actuator], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}","details":{"handlerMethod":{"className":"org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.WebMvcLinksHandler","name":"links","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)Ljava/util/Map;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["GET"],"params":[],"patterns":["/actuator"],"produces":[{"mediaType":"application/vnd.spring-boot.actuator.v3+json","negated":false},{"mediaType":"application/vnd.spring-boot.actuator.v2+json","negated":false},{"mediaType":"application/json","negated":false}]}}},{"handler":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)","predicate":"{ [/error]}","details":{"handlerMethod":{"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController","name":"error","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;)Lorg/springframework/http/ResponseEntity;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":[],"params":[],"patterns":["/error"],"produces":[]}}},{"handler":"htb.cloudhosting.compliance.ComplianceService#executeOverSsh(String, String, HttpServletResponse)","predicate":"{POST [/executessh]}","details":{"handlerMethod":{"className":"htb.cloudhosting.compliance.ComplianceService","name":"executeOverSsh","descriptor":"(Ljava/lang/String;Ljava/lang/String;Ljakarta/servlet/http/HttpServletResponse;)V"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":["POST"],"params":[],"patterns":["/executessh"],"produces":[]}}},{"handler":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)","predicate":"{ [/error], produces [text/html]}","details":{"handlerMethod":{"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController","name":"errorHtml","descriptor":"(Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)Lorg/springframework/web/servlet/ModelAndView;"},"requestMappingConditions":{"consumes":[],"headers":[],"methods":[],"params":[],"patterns":["/error"],"produces":[{"mediaType":"text/html","negated":false}]}}},{"handler":"ParameterizableViewController [view=\"admin\"]","predicate":"/admin"},{"handler":"ParameterizableViewController [view=\"addhost\"]","predicate":"/addhost"},{"handler":"ParameterizableViewController [view=\"index\"]","predicate":"/index"},{"handler":"ParameterizableViewController [view=\"login\"]","predicate":"/login"},{"handler":"ResourceHttpRequestHandler [classpath [META-INF/resources/webjars/]]","predicate":"/webjars/**"},{"handler":"ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]","predicate":"/**"}]},"servletFilters":[{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"requestContextFilter","className":"org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter"},{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"Tomcat WebSocket (JSR356) Filter","className":"org.apache.tomcat.websocket.server.WsFilter"},{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"serverHttpObservationFilter","className":"org.springframework.web.filter.ServerHttpObservationFilter"},{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"characterEncodingFilter","className":"org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter"},{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"springSecurityFilterChain","className":"org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1"},{"servletNameMappings":[],"urlPatternMappings":["/*"],"name":"formContentFilter","className":"org.springframework.boot.web.servlet.filter.OrderedFormContentFilter"}],"servlets":[{"mappings":["/"],"name":"dispatcherServlet","className":"org.springframework.web.servlet.DispatcherServlet"}]}}}}
the /actuator/mappings
endpoint in a spring boot application’s actuator module provides a comprehensive list of all the available HTTP request mappings in the application. It includes details about the mapping patterns, HTTP methods, controller methods, and more. This endpoint is useful for developers and administrators to understand how incoming requests are mapped to specific controller methods, making it easier to debug and analyze the application’s request handling process.
/actuator/sessions
According to the official documentation, The
sessions
endpoint provides information about the application’s HTTP sessions that are managed by Spring Session
The /actuator/sessions
endpoint reveals 2 active sessions with what appears to be session IDs
While the UNAUTHORIZED
user is likely mine, the kanderson
user appears to be valid
I will grab the session ID of the kanderson
user and attempt to hijack it
Session Hijacking
Authenticated to the
/admin
page
the
/admin
page reveals that the web application is cozy cloud
cozy is a personal cloud platform free, and self-hostable, written in Go (the former version, v2, was written in Node.js instead).
Automatic Patching
This part in particular seems to be the way in as it appears to interact with SSH service
While it says automatic patching, it is unclear what exactly it does to achieve that
I will test it out to clarify the feature
It sends out a POST request to the
/executessh
endpoint
The web app returned an error that the host was not added
Additionally, there is the familiar SSH error, which suggests that it might actually be performing OS command to SSH to the requested host
While it is clear that I cannot SSH to Kali since the SSH file isn’t included in the target’s
.ssh/authorized_keys
file, I may be able to perform OS command injection