#!/usr/bin/python | |
from scapy.all import * | |
import random | |
# number of times to inject probe for one bit (combat packet loss) | |
ATTEMPTS_PER_BIT = 3 | |
# time to wait for ACK in seconds | |
SNIFFTIME = 0.1 | |
def randmac(): | |
mac = [0] * 6 | |
for i in xrange(6): | |
mac[i] = random.randint(0, 256) | |
return ":".join([format(byte, '02x') for byte in mac]) | |
def parsemac(macstr): | |
parts = macstr.replace("-", ":").split(":") | |
if len(parts) != 6: | |
raise ValueError("MAC does not consist of 6 parts (separated by : or -)") | |
return [int(byte, 16) for byte in parts] | |
def is_ack(p): | |
return Dot11 in p and p.type == 1 and p.subtype == 13 | |
def find_fixed_bits(s, mac): | |
# eventually contains the real MAC address | |
orgmac = [0] * 6 | |
# random MAC address, used as sender, to which the target will send an ACK | |
srcmac = randmac() | |
# for all the bits - FIXME: Don't consider H.O. bit of first MAC byte | |
for i in range(6): | |
for bit in range(8): | |
# flip the bit at current position | |
currbit = mac[i] & (1 << bit) | |
mac[i] ^= (1 << bit) | |
# convert modified mac to string | |
strmac = ":".join([format(byte, '02x') for byte in mac]) | |
print "Probing", strmac, "...", | |
replied = False | |
for attempt in range(ATTEMPTS_PER_BIT): | |
# inject data packet to modified MAC address | |
packet = Dot11(type="Data", subtype=4, FCfield="from-DS", | |
addr1=strmac, addr2=srcmac, addr3=strmac) | |
s.send(RadioTap()/packet) | |
# Sniff air for ACK to modified MAC | |
l = sniff(lfilter=lambda p: is_ack(p) and p.addr1 == srcmac, count=1, | |
timeout=SNIFFTIME, opened_socket=s) | |
# We we got an ACK, don't need to try again | |
if len(l) == 1: | |
replied = True | |
break | |
print replied | |
# If client replied, original bit is different from the one currently set, | |
# otherwise it's equal to original bit. | |
if replied: | |
orgmac[i] |= (~currbit) & (1 << bit) | |
else: | |
orgmac[i] |= currbit | |
# flip bit back to original value | |
mac[i] ^= (1 << bit) | |
# Done, return original MAC | |
return orgmac | |
if __name__ == "__main__": | |
if len(sys.argv) != 3: | |
print "Usage:", sys.argv[0], "interface macaddr" | |
quit(1) | |
try: | |
mac = parsemac(sys.argv[2]) | |
conf.iface = sys.argv[1] | |
random.seed() | |
# Open up read/write socket so we don't miss the ACK | |
L2socket = conf.L2socket | |
s = L2socket(type=ETH_P_ALL, iface=conf.iface) | |
# Now find the MAC | |
orgmac = find_fixed_bits(s, mac) | |
s.close() | |
print "\nReal MAC address:", ":".join(format(byte, "02x") for byte in orgmac), "\n" | |
except ValueError, e: | |
print "Invalid MAC address:", e | |
except socket.error, e: | |
print "Error with provided interface:", e | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment