-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathlibagfs.py
1656 lines (1577 loc) · 79.1 KB
/
libagfs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python3
__author__ = "Ehab Hussein"
__version__ = "2.3"
__status__ = "Alpha"
__twitter__ = "@0xRaindrop"
##################### Imports
import xmltodict
import platform
import binascii
from sys import exit, stdout
from os import geteuid, urandom, path, makedirs
from sqlalchemy import MetaData, create_engine, String, Integer, Table, Column, inspect
import pprint
from time import time, sleep
import threading
import paramiko
import random
import EDAP
from termcolor import cprint
import inspect
import itertools
import re
import glob
import requests
from bs4 import BeautifulSoup
import functools
import json
###################### Pre-Checks
if int(platform.python_version()[0]) < 3:
print("Python 2.x or older are not supported. Make sure you are using python3\n")
exit(-1)
if geteuid() != 0:
print("Don't forget that this needs root privileges!")
exit(-1)
try:
import usb
except:
print("Seems like you dont have pyusb installed.\n[-]install it via pip:\n\t[-]pip3 install pyusb")
exit(-1)
try:
import pika
import pika.exceptions
except:
print("Man in the middle for USB will not work. install pika")
# check that folders are created or exist
if not path.exists('binariesdb'):
makedirs('binariesdb')
if not path.exists('clones'):
makedirs('clones')
if not path.exists('ctrltrnsfdb'):
makedirs('ctrltrnsfdb')
if not path.exists('databases'):
makedirs('databases')
if not path.exists('devEnumCT'):
makedirs('devEnumCT')
if not path.exists('gadgetscripts'):
makedirs('gadgetscripts')
if not path.exists('devfuzzresults'):
makedirs('devfuzzresults')
if not path.exists('payloads'):
makedirs('payloads')
############auto gadgetFS class
class agfs:
def __init__(self):
self.showMessage("AutoGadgetFS: USB testing made easy", color="green")
self.fuzzdevice = 0
self.fuzzhost = 0
self.ingui = 0
self.itshid = 0
self.hostsave = None
self.edap = EDAP.Probability()
self.SelectedDevice = None
self.mitmcounter = 0
self.chksimchrPrev = ""
self.chksimchrNow = ""
self.chksimchrForm = ""
self.diffmap = 0
self.diffmapPTS = ""
self.devsave = 0
with open('agfsSettings.json') as config_file:
agfsSettings = json.load(config_file)
self.rabbitmqserver = agfsSettings['RabbitMQ-IP']
self.pihost = agfsSettings['PiZeroIP']
self.piport = agfsSettings['PiZeroSSHPort']
self.piuser = agfsSettings['PiZeroUser']
self.pipass = agfsSettings['PiZeroPass']
self.mitmstarted = 0
self.newProject()
def valueerror_as_result(func):
"""Patch for Pyusb langID"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ValueError as error:
return error.args[0]
return wrapper
usb.util.get_string = valueerror_as_result(usb.util.get_string)
def createctrltrsnfDB(self):
"""
creates a SQLite database containing values that were enumerated from control transfer enumeration
devEnumCtrltrnsf(self,fuzz="fast")
:return: db and table
"""
try:
try:
if path.exists(f"devEnumCT/{self.SelectedDevice}.db"):
enumdbname = f"devEnumCT/{self.SelectedDevice}{random.randint(1, 999)}.db"
else:
enumdbname = f"devEnumCT/{self.SelectedDevice}.db"
except Exception as e:
print(e)
print("--------------------------------------***")
meta = MetaData()
db = create_engine(f"sqlite:///{enumdbname}")
db.echo = False
self.devECT = Table(
self.SelectedDevice,
meta,
Column('bmRequest', String),
Column('bRequest', String),
Column('wValue', String),
Column('wIndex', String),
Column('Data_length', String),
Column('Data_returned', String),
Column('Data_returned_Ascii', String))
meta.create_all(db)
return db, self.devECT
except Exception as e:
print(e)
print("[Error] cannot create db\n")
def createdb(self, name):
"""
create the sqlite table and columns from usblyzer captures
:param name: this receives a name for the database name to be created
"""
try:
meta = MetaData()
db = create_engine('sqlite:///%s.db' % (name.strip()))
db.echo = False
self.usblyzerdb = Table(
name.strip(),
meta,
Column('Type', String),
Column('seq', Integer),
Column('io', String),
Column('cie', String),
Column('Duration', String),
Column('DevObjAddr', String),
Column('irpaddr', String),
Column('RawDataSize', Integer),
Column('RawData', String),
Column('RawBinary', String),
Column('replyfrom', Integer))
meta.create_all(db)
return db, self.usblyzerdb
except:
print("[Error] cannot create db\n")
def makeChannel(self, ipaddress):
"""creates a channel to RabbitMQ
:param: ipaddress : ip address of the rabbitmq server
:return: a channel
"""
qcreds = pika.PlainCredentials('autogfs', 'usb4ever')
qpikaparams = pika.ConnectionParameters(ipaddress, 5672, '/', qcreds, heartbeat=60)
qconnect = pika.BlockingConnection(qpikaparams)
return qconnect.channel(), qconnect
def devDfuDump(self, vendorID=None, productID=None):
"""
This method would be changed in the future ......
This method allows you to pull firmware from a device in DFU mode
:param vendorID: Vendor ID of the device
:param productID: Product ID of the device
:return: None
"""
pass
#TODO
def releasedev(self):
"""releases the device and re-attaches the kernel driver"""
for configurations in self.device:
print("Releasing interfaces :\n\t%s" % configurations.bNumInterfaces)
print("[-] Attaching the kernel driver")
for inter in range(configurations.bNumInterfaces + 1):
try:
usb.util.release_interface(self.device, inter)
print("Releasing interface: %d" % inter)
self.device.attach_kernel_driver(inter)
print("Interface reattached")
except:
pass
print("[-] Device released!")
def deviceInfo(self):
"""gets the complete info only for any usb connected to the host"""
getusbs = usb.core.find(find_all=True)
devices = dict(
enumerate(str(dev.manufacturer) + ":" + str(dev.idProduct) + ":" + str(dev.idVendor) for dev in getusbs))
for key, value in devices.items():
print(key, ":", value)
hook = input("---> Select a device: ")
idProd, idVen = devices[int(hook)].split(':')[1:]
device = usb.core.find(idVendor=int(idVen), idProduct=int(idProd))
print(device)
def autoDiscover(self):
"""This method will try to discover which interfaces/endpoints communicate"""
#TODO
pass
def deviceInterfaces(self):
"""get all interfaces and endpoints on the device"""
self.device = usb.core.find(idVendor=self.device.idVendor, idProduct=self.device.idProduct)
self.leninterfaces = 0
self.allendpoints = {}
for cfg in self.device:
for intf in cfg:
if intf.bInterfaceClass == 3:
self.itshid = 1
self.leninterfaces += 1
for c,d in enumerate(self.device):
cprint(f"Configuration {c+1}",color='green')
for q,i in enumerate(d.interfaces()):
for j in str(i).split("\n"):
if "INTERFACE" in j:
intf = j.split("=")[0]
antfalt = re.search("INTERFACE (.+):", intf).group(1) if "," in re.search("INTERFACE (.+):", intf).group(1) else f'{re.search("INTERFACE (.+):", intf).group(1)}, 0'
self.allendpoints[antfalt] = []
cprint(intf,color="blue")
elif "ENDPOINT" in j:
endp = j.split("=")[0]
cprint(endp, color="green")
if " IN" in endp:
self.allendpoints[antfalt].append(f"IN:{hex(int(re.search('0x[aA-fF0-9]+',endp).group(0),16))}")
elif " OUT" in endp:
self.allendpoints[antfalt].append(f"OUT:{hex(int(re.search('0x[aA-fF0-9]+', endp).group(0), 16))}")
def newProject(self):
""" creates a new project name if you were testing something else"""
try:
self.releasedev()
except:
pass
self.SelectedDevice = None
self.findSelect()
def chgIntrfs(self):
"""This method allows you to change and select another interface"""
self.findSelect(chgint=1)
cprint("Configuration change succeeded", color="blue", attrs=['blink'])
def findSelect(self, chgint=None):
"""This method enumerates all USB devices connected and allows you to select it as a target device as well as its endpoints"""
if self.SelectedDevice is None and chgint is None:
self.projname = self.SelectedDevice if self.SelectedDevice else input("Give your project a name?!: ")
self.getusbs = usb.core.find(find_all=True)
self.devices = dict(enumerate("{0}:{1}:{2}".format(str(dev.manufacturer), str(dev.idProduct), str(dev.idVendor)) for dev in self.getusbs))
for key, value in self.devices.items():
print(key, ":", value)
self.hook = input("---> Select a device: ")
try:
self.idProd, self.idVen = self.devices[int(self.hook)].split(':')[1:]
except ValueError:
return cprint("Invalid Selection. No device Selected", color="red", attrs=['blink'])
self.device = usb.core.find(idVendor=int(self.idVen), idProduct=int(self.idProd))
if self.SelectedDevice is None and chgint is None:
print(str(self.device))
detachKernel = str(input("do you want to detach the device from it's current system driver: [y/n] "))
else:
detachKernel = 'y'
if 'y' == detachKernel.lower():
self.device.reset()
try:
"""https://stackoverflow.com/questions/23203563/pyusb-device-claimed-detach-kernel-driver-return-entity-not-found"""
confer = 1
for configurations in self.device:
print("Disabling Interfaces on configuration: %d" % confer)
print("Disabling interfaces :\n\t%s" % configurations.bNumInterfaces)
for inter in range(configurations.bNumInterfaces + 1):
try:
if self.device.is_kernel_driver_active(inter):
self.device.detach_kernel_driver(inter)
print("Disabled interface: %d" % inter)
except:
pass
if self.SelectedDevice is None and chgint is None:
print("[-] Kernel driver detached")
self.device.set_configuration()
except Exception as e:
print("Failed to detach the kernel driver from the interfaces.", e)
self.deviceInterfaces()
if input("Do you want to reset the device? [y/n]: ").lower() == 'y':
self.device.reset()
try:
self.precfg = int(input("which Configuration would you like to use: "))
self.device.set_configuration(self.precfg)
self.devcfg = self.device.get_active_configuration()
self.interfaces = self.devcfg[(0, 0)]
self.killthread = 0
print("Checking if device supports DFU mode based on USB DFU R1.1")
'''based on USB Device Firmware Upgrade Specification, Revision 1.1'''
dfu = 0
for i, configurations in enumerate(self.device):
for j, interface in enumerate(configurations.interfaces()):
if interface.bInterfaceClass == 0xFF:
print(f"Configuration #{i + 1} on interface #{j} needs vendor specific Drivers")
if interface.bInterfaceClass == 0xFE and interface.bInterfaceSubClass == 0x01:
print(f"This Device supports DFU mode on configuration {i + 1}, interface {j}")
dfu += 1
if dfu == 0:
self.showMessage("This Device isnt in DFU mode", color="green")
except Exception as e:
print(e)
self.showMessage("Couldn't get device configuration!", color="red", blink='y')
for i in range(self.devcfg.bNumInterfaces):
try:
cprint(f"[+] Claiming interface {i}", color='green')
usb.util.claim_interface(self.device, i)
cprint(f"\t[-]Successfully claimed interface {i}", color='blue')
except:
cprint(f"[+]Failed while claiming interface {i}. Maybe its on a non active configuration", color='red')
if self.itshid == 1:
cprint("Checking HID report retrieval", color="green")
try:
self.device_hidrep = []
"""Thanks https://wuffs.org/blog/mouse-adventures-part-5
https://docs.google.com/viewer?a=v&pid=sites&srcid=bWlkYXNsYWIubmV0fGluc3RydW1lbnRhdGlvbl9ncm91cHxneDo2NjBhNWUwNDdjZGE1NWE1
"""
for i in range(0, self.leninterfaces + 1):
try:
# we read the max possible size of a hid report incase the device leaks some data .. it does happen.
response = binascii.hexlify(self.device.ctrl_transfer(0x81, 0x6, 0x2200, i, 0xfff))
self.device_hidrep.append(response)
except usb.core.USBError:
pass
if self.device_hidrep:
for i, j in enumerate(self.device_hidrep):
if len(j) > 0:
cprint(f"Hid report [{i}]: {j.decode('utf-8')}", color="blue")
dpayload, checkr = self.decodePacketAscii(payload=binascii.unhexlify(j))
cprint(f"\tdecoded: {dpayload}", color="blue")
if binascii.unhexlify(self.device_hidrep[0])[-1] != 192 and len(self.device_hidrep) > 0:
self.showMessage("Possible data leakage detected in HID report!", color='blue', blink='y')
else:
self.device_hidrep = []
except Exception as e:
print(e)
self.device_hidrep = []
self.showMessage("Couldn't get a hid report but we have claimed the device.", color='red', blink='y')
elif self.itshid == 0:
self.device_hidrep = []
self.itshid = 0
if type(self.device.manufacturer) is type(None):
self.manufacturer = "UnkManufacturer"
else:
self.manufacturer = self.device.manufacturer
self.SelectedDevice = self.manufacturer + "-" + str(self.device.idVendor) + "-" + str(
self.device.idProduct) + "-" + str(time())
self.SelectedDevice = self.projname + "-" + self.SelectedDevice.replace(" ", '')
self.clonedev()
def monInterfaceChng(self, ven, prod):
"""Method in charge of monitoring interfaces for changes this is called from def startMonInterfaceChng(self)
don't call this method directly use startMonInterfaceChng(self) instead
:param ven: receives the vendorID of the device
:param prod: receives the productID of the device
:return: None
"""
temp = str(self.device)
while True:
try:
if self.monIntKill == 1:
break
device = usb.core.find(idVendor=ven, idProduct=prod)
if temp != str(device):
temp = str(device)
self.showMessage("Device Interfaces have changed!", color='blue', blink='y')
sleep(10)
except Exception as e:
print(e)
def startMonInterfaceChng(self):
"""This method Allows you to monitor a device every 10 seconds in case it suddenly changes its interface configuration.
Like when switching and Android phone from MTP to PTP . you'll get a notification so you can check
your interfaces and adapt to that change using changeintf() method
"""
self.showMessage("Interface monitoring thread has started.", color='green')
self.monIntKill = 0
self.monIntThread = threading.Thread(target=self.monInterfaceChng,
args=(self.device.idVendor, self.device.idProduct,))
self.monIntThread.start()
def stopMonInterfaceChang(self):
"""Stops the interface monitor thread"""
self.monIntKill = 1
self.monIntThread.join()
self.showMessage("Monitoring of interface changes has stopped", color='green')
def stopSniffing(self):
"""Kills the sniffing thread strted by startsniff()"""
self.killthread = 1
self.readerThread.join()
try:
self.bintransfered.close()
except:
pass
try:
self.devsaveF.close()
except:
pass
if self.frompts == 0:
try:
self.qchannel3.stop_consuming()
except:
pass
self.qconnect3.close()
self.showMessage("Sniffing has stopped successfully!", color='green')
self.killthread = 0
def startsniff(self, epin=None, pts=None, queue=None, timeout=0, devsave=0):
""" This is a thread to continuously read the replies from the device and dependent on what you pass to the method either pts or queue
:param endpoint: endpoint address you want to read from
:param pts: if you want to read the device without queues and send output to a specific tty
:param queue: if you will use the queues for a full proxy between target and host
:param channel: this is automatically passed if you use the self.startMITM()
:param hosthstsave: fill in ********************
:param devsave: fill in ********************
:return: None
"""
try:
mypts = None
self.killthread = 0
if queue is not None:
self.frompts = 0
self.qcreds3 = pika.PlainCredentials('autogfs', 'usb4ever')
self.qpikaparams3 = pika.ConnectionParameters(self.rabbitmqserver, 5672, '/', self.qcreds3, heartbeat=60)
self.qconnect3 = pika.BlockingConnection(self.qpikaparams3)
self.qchannel3 = self.qconnect3.channel()
if pts is not None:
self.frompts = 1
mypts = input("Open a new terminal and type 'tty' and input the pts number: (/dev/pts/X) ")
input("Press Enter when ready..on %s" % mypts)
self.readerThread = threading.Thread(target=self.sniffdevice, args=(epin, mypts, queue, timeout, devsave))
self.readerThread.start()
except pika.exceptions.AMQPConnectionError:
return cprint("Error: Make sure that RabbitMQ is running", color='red')
except Exception as e:
print(e)
def sniffdevice(self, endpoint, pts, queue, timeout, devsave):
""" read the communication between the device to hosts
you can either choose set pts or queue but not both.s
:param endpoint: endpoint IN address you want to read from
:param pts: if you want to read the device without queues and send output to a specific tty
:param queue: is you will use the queues for a full proxy between target and host
:param channel: rabbitmq channel
:param devsave: write sniffed packets to a file
:return: None
"""
if devsave == 1:
self.devsaveF = open(f'binariesdb/{self.SelectedDevice}-device.bin', 'wb')
if queue and pts is None:
self.showMessage("Sniffing the device started, messages sent to host queue!", color="green")
while True:
if self.killthread == 1:
queue = None
self.showMessage("Thread Terminated Successfully", color='green')
break
try:
packet = self.device.read(endpoint, self.device.bMaxPacketSize0)
try:
if self.fuzzhost == 1:
s = memoryview(binascii.unhexlify(binascii.hexlify(packet))).tolist()
random.shuffle(s)
packet = binascii.unhexlify(''.join(format(x, '02x') for x in s))
if devsave == 1:
self.devsaveF.write(binascii.hexlify(packet))
self.devsaveF.write(b'\r\n')
except Exception as e:
print(e)
self.qchannel3.basic_publish(exchange='agfs', routing_key='tohst',
body=packet)
# self.qchannel3.basic_publish(exchange='agfs', routing_key='tohst',
# body=f"{endpoint}-{packet}")
except usb.core.USBError as e:
if e.args == ('Operation timed out\r\n',):
self.showMessage("Operation timed out cannot read from device", color='red', blink='y')
except Exception as e:
print(e)
self.showMessage("Error read from device possibly crashed, trying to reconnect", color='red')
self.reconnectdevice(fromMITM=1)
continue
self.qchannel3.basic_publish(exchange='agfs', routing_key='tonull', body="heartbeats")
elif pts and queue is None:
# cprint(f"|\t Received:{body}", color="blue")
if self.fuzzdevice == 1:
packet = memoryview(binascii.unhexlify(body)).tolist()
random.shuffle(packet)
body = ''.join(format(x, '02x') for x in packet)
cprint(f"|-\t\t manipulation:{body}", color="green")
with open('%s' % (pts.strip()), 'w') as ptsx:
while True:
if self.killthread == 1:
pts = None
ptsx.write("Thread Terminated Successfully")
break
try:
packet = binascii.hexlify(self.device.read(endpoint, self.device.bMaxPacketSize0))
ps, p1 = self.decodePacketAscii(payload=binascii.unhexlify(packet), rec=1)
ptsx.write(f"|-Outgoing Packet to Host{'-' * 70}\r\n")
ptsx.write(f"|\t Sent:{packet}\r\n")
ptsx.write(f"|\t Diff: {p1}\r\n")
ptsx.write(f"|\t\t Decoded:{ps}\r\n")
ptsx.write(f"|{'-' * 90}\r\n")
ptsx.flush()
if devsave == 1:
self.devsaveF.write(packet)
self.devsaveF.write(b'\r\n')
except usb.core.USBError as e:
if e.args == ('Operation timed out! Cannot read from device\n',):
ptsx.write("Operation timed out! Cannot read from device\n")
ptsx.flush()
else:
self.showMessage("either pass to a queue or to a tty", color='red', blink='y')
def startMITM(self, epin=None, epout=None, hostsave=None, devsave=0):
""" Starts a thread to monitor the USB target Device
:param epin: the IN endpoint of the device which is from the device to the PC
:param epout: the OUT endpoint of the device which from PC to device
:param hostsave: if you would like the packets from the host to be saved to a binary file
:param: devsave: save packets from device to file
:return: None
"""
self.mitmin = epin
self.mitmout = epout
if self.mitmstarted == 1:
return self.showMessage("You cannot mitm more than one interface at a time in this current release.",
color='red', blink='y')
self.mitmstarted = 1
if hostsave:
self.hostsave = 1
if devsave:
self.devsave = 1
self.killthread = 0
self.nlpthresh = 0
self.startMITMProxyThread = threading.Thread(target=self.MITMproxy, args=(epin, epout, hostsave, devsave,))
self.startMITMProxyThread.start()
def stopMITM(self):
''' Stops the man in the middle thread between the host and the device'''
self.mitmcounter = 0
self.mitmstarted = 0
try:
if self.hostsave:
self.bintransfered.close()
except:
pass
self.stopSniffing()
self.hostsave = None
self.killthread = 1
try:
self.qchannel.stop_consuming()
self.qconnect.close()
except:
pass
self.startMITMProxyThread.join()
self.showMessage("MITM Proxy has now been terminated!", color='green')
def MITMproxyRQueues(self, ch, method, properties, body, epout=None):
"""
This method reads from the queue todev and sends the request to the device its self.
:param ch: rabbitMQ channel
:param method: methods
:param properties: properties
:param body: Payload
:return None
"""
self.mitmcounter += 1
rec, diff = self.decodePacketAscii(payload=binascii.unhexlify(body), rec=1)
cprint(f"|-[From Host]->Write packet->[To Device][Pkt# {self.mitmcounter}]{'-' * 70}", color="green")
cprint(f"|\t Received:{body}", color="blue")
cprint(f"|\t Diff:{diff}", color="blue")
cprint(f"|\t\t Decoded:{rec}", color="green")
if self.fuzzdevice == 1:
packet = memoryview(binascii.unhexlify(bytearray(body))).tolist()
random.shuffle(packet)
body = ''.join(format(x, '02x') for x in packet)
cprint(f"|-\t\t manipulation:{body}", color="grey")
try:
self.device.write(epout, binascii.unhexlify(body))
except usb.core.USBError as e:
if e.args[0] == 19 or e.args[0] == 2 or e.args[0] == 5:
cprint("\nDevice unavailable it might be disconnected waiting for reconnect",color='red')
self.reconnectdevice()
self.device.write(epout, binascii.unhexlify(body))
try:
if self.hostsave:
self.bintransfered.write(body)
self.bintransfered.write(b'\r\n')
except Exception as e:
print(e)
cprint(f"|{'-' * 90}[Pkt #{self.mitmcounter}]", color="green")
ch.basic_ack(delivery_tag=method.delivery_tag)
def MITMproxy(self, epin, epout, hostsave, devsave):
"""
This method creates a connection to the RabbitMQ and listen on received messages on the todev queue
:param epin: Endpoint IN
:param epout: Endpoint OUT
:param hostsave: if you would like the packets from the host to be saved to a binary file
:param: devsave: save packets from device to file
:return: None
"""
try:
try:
if devsave:
self.devsave = 1
if hostsave:
self.hostsave = 1
self.bintransfered = open(f"binariesdb/{self.SelectedDevice}-Host.bin", 'wb')
except Exception as e:
print(e)
self.hostsave = None
self.qcreds = pika.PlainCredentials('autogfs', 'usb4ever')
self.qpikaparams = pika.ConnectionParameters(self.rabbitmqserver, 5672, '/', self.qcreds)
self.qconnect = pika.BlockingConnection(self.qpikaparams)
self.qchannel = self.qconnect.channel()
#self.qchannel.basic_qos(prefetch_count=1)
self.qchannel.basic_consume(on_message_callback=functools.partial(self.MITMproxyRQueues, epout=epout),
queue='todevice')
self.startsniff(epin=epin, queue=1, devsave=devsave)
print("Connected to RabbitMQ, starting consumption!")
print("Connected to exchange, we can send to host!")
self.qchannel.start_consuming()
self.showMessage("MITM Proxy stopped!", color="green")
except Exception as e:
print(e)
pass
def devWrite(self, endpoint, payload):
"""To use this with a method you would write make sure to run the startsniff(self,endpoint=None, pts=None, queue=None,channel=None)
method first so you can monitor responses
:param endpoint: endpoint address you want to write method
:param payload: the message to be sent to the devices
:return: None
"""
self.device.write(endpoint, payload)
def devctrltrnsf(self, bmRequestType, bRequest, wValue, wIndex, wLength):
""" This method allows you to send ctrl transfer requests to the target device
Usually you'll find the parameters for this method in the vendor's data sheet.
https://www.beyondlogic.org/usbnutshell/usb6.shtml
:param bmRequestType: direction of the request
:param bmRequest: determines the request being made
:param wValue: parameters to be passed with the request
:param wIndex: parameters to be passed with the request
:param wLength: Number of bytes to transfer if there is a data phase
"""
print(binascii.hexlify(self.device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, wLength)))
def startQueuewrite(self):
"""initiates a connection to the queue to communicate with the host"""
self.hbkill = 0
self.qcreds3 = pika.PlainCredentials('autogfs', 'usb4ever')
self.qpikaparams3 = pika.ConnectionParameters(self.rabbitmqserver, 5672, '/', self.qcreds3, heartbeat=60)
self.qconnect3 = pika.BlockingConnection(self.qpikaparams3)
self.qchannel3 = self.qconnect3.channel()
# self.showMessage("Queues to host are yours!",color='blue')
def stopQueuewrite(self):
""" stop the thread incharge of communicating with the host machine"""
# self.qchannel3.stop_consuming()
self.qconnect3.close()
def clearqueues(self):
"""this method clears all the queues on the rabbitMQ queues that are set up"""
try:
cprint("Clearing all queues",color='blue')
self.qcreds4 = pika.PlainCredentials('autogfs', 'usb4ever')
self.qpikaparams4 = pika.ConnectionParameters(self.rabbitmqserver, 5672, '/', self.qcreds4, heartbeat=60)
self.qconnect4 = pika.BlockingConnection(self.qpikaparams4)
self.qchannel4 = self.qconnect4.channel()
self.qchannel4.queue_purge('todevice')
self.qchannel4.queue_purge('tohost')
self.qchannel4.queue_purge('tonull')
self.qchannel4.queue_purge('edapdev')
self.qchannel4.queue_purge('edaphst')
self.qchannel4.queue_purge('gdtfz')
sleep(5)
cprint("Purged all queues", color="blue")
self.qconnect4.close()
except:
cprint("Error: Ensure that rabbitMQ is running!",color='red')
def hostwrite(self, payload, isfuzz=0):
""" This method writes packets to the host either targeting a software or a driver in control of the device
use this when you want to send payloads to a device driver on the host.
:param payload: the message to be sent to the host example: "0102AAFFCC"
:param isfuzz: is the payload coming from the fuzzer ?
start the pizeroRouter.py with argv[2] set to anything so we can send the host messages to a null Queue
"""
self.qchannel3.basic_publish(exchange='agfs', routing_key='tohst',
body=binascii.unhexlify(payload) if isfuzz == 0 else payload)
def hstrandfuzz(self, howmany=None, size=None, min=None, max=None, timeout=0.5,mybyte=None):
"""
this method allows you to create fixed or random size packets created using urandom and send them to the host queue
:param howmany: how many packets to be sent to the device`
:param size: fixed size packet length
size = 10 to generate a length 10 packet
:param min minimum size value to generate a packet
:param max maximum size value to generate a packet
:param timeout: timeOUT !
:param mybyte: if you want to fuzz with a specific byte 'AA'
:return: None
"""
self.startQueuewrite()
sleep(1)
i = 0
while True:
if howmany:
if i == howmany:
self.showMessage("Host fuzzing stopped successfully!")
break
try:
if size:
s = urandom(size) if mybyte is None else binascii.unhexlify(mybyte * size)
sdec, checker = self.decodePacketAscii(payload=s)
cprint(f"|-Packet[{i}]{'-' * 80}", color="green")
cprint(f"|\t Bytes:", color="blue")
cprint(f"|\t\tSent: {binascii.hexlify(s)}", color="green")
cprint(f"|\t Decoded:", color="blue")
cprint(f"|\t\t Sent: {sdec}", color="green")
cprint(f"|{'_' * 90}[{i}]", color="green")
self.hostwrite(s, isfuzz=1)
elif min is not None and max is not None:
s = urandom(random.randint(int(min), int(max)))
sdec, check = self.decodePacketAscii(payload=s)
cprint(f"|-Packet[{i}]{'-' * 80}", color="green")
cprint(f"|\t Bytes:", color="blue")
cprint(f"|\t\tSent: {binascii.hexlify(s)}", color="green")
cprint(f"|\t Decoded:", color="blue")
cprint(f"|\t\t Sent: {sdec}", color="green")
cprint(f"|{'_' * 90}[{i}]", color="green")
self.hostwrite(s, isfuzz=1)
sleep(timeout)
except KeyboardInterrupt:
self.showMessage("Host fuzzing stopped successfully!")
break
except Exception as e:
print(e)
self.showMessage("Error -->sending packet\n", color='red', blink='y')
pass
i += 1
self.stopQueuewrite()
def devrandfuzz(self, epin=None, epout=None, size='fixed', min=0, timeout=0, Cmatch=None, reset=None, Rmatch=None):
"""
this method allows you to create fixed or random size packets created using urandom
Ctrl-C to stop
:param epin: endpoint in
:param epout: endpoint out
:param size: string value whether its fixed or random size
:param timeout: timeOUT !
:param Rmatch: checks the reponse for an non-match of your string and will pause
:param Cmatch: checks the response for a match your string and will pause
:return: None
"""
i = 0
while True:
try:
if size == 'fixed':
s = urandom(self.device.bMaxPacketSize0)
else:
s = urandom(random.randint(min, self.device.bMaxPacketSize0))
self.device.write(epout, s)
r = self.device.read(epin, self.device.bMaxPacketSize0)
sdec, checks = self.decodePacketAscii(payload=s)
rdec, checkr = self.decodePacketAscii(payload=r, rec=1)
cprint(f"|-Packet[{i}]{'-' * 80}", color="green")
cprint(f"|\t Bytes:", color="blue")
cprint(
f"|\t\tSent: {binascii.hexlify(s)}\n|\t\t |____Received: {binascii.hexlify(r)}\n|\t\t\t|_______Diff:{checkr}",
color="green")
cprint(f"|\t Decoded:", color="blue")
cprint(f"|\t\t Sent: {sdec}\n|\t\t |____Received: {rdec}", color="green")
cprint(f"|{'_' * 90}[{i}]", color="green")
if Cmatch:
if Cmatch not in rdec:
# self.fuzzchange = (sdec,rdec)
input("Received data has changed!. Press Enter to continue fuzzing!")
if Rmatch:
if Rmatch in rdec.lower():
input("Received data has matched!. Press Enter to continue fuzzing!")
sleep(timeout)
except usb.core.USBError as e:
if e.args[0] == 19 or e.args[0] == 2 or e.args[0] == 5:
cprint("[+]Device seems to have been disconnected\n\t[-]Sleeping 5 seconds for reconnect",color='blue')
sleep(5)
self.reconnectdevice()
continue
else:
cprint(f"|-Packet[{i}]{'-' * 80}", color="red", attrs=['blink'])
cprint(f"|\t Error:", color="red") # not blinking to grab attention
cprint(f"|\t\tSent: {binascii.hexlify(s)}", color='red', attrs=['blink'])
cprint(f"|\t\t|____{e}", color='red', attrs=['blink'])
cprint(f"|{'_' * 90}[{i}]", color="red", attrs=['blink'])
if reset is not None:
try:
self.device.reset()
self.showMessage("Device reset complete")
except usb.core.USBError as e:
if e.args[0] == 19 or e.args[0] == 2 or e.args[0] == 5:
self.reconnectdevice()
except KeyboardInterrupt:
self.showMessage("Keyboard interrupt detected! Ending...")
break
i += 1
def devReset(self):
"""This method Resets the device"""
self.device.reset()
self.showMessage("The device has been reset!")
def reconnectdevice(self,fromMITM=0):
"""This method reconnects your disconnected device automatically
We need to add a check if the MITM was started with save files ## not implemented yet
:param return: None
"""
print("Waiting for device....")
sleep(5)
while True:
try:
self.device = usb.core.find(idVendor=int(self.idVen), idProduct=int(self.idProd))
if self.device.bMaxPacketSize0:
break
except:
continue
for configurations in self.device:
for inter in range(configurations.bNumInterfaces + 1):
try:
if self.device.is_kernel_driver_active(inter):
self.device.detach_kernel_driver(inter)
except:
pass
for i in range(self.devcfg.bNumInterfaces):
try:
usb.util.claim_interface(self.device, i)
except:
pass
if fromMITM == 1:
self.stopMITM()
self.startMITM(epin=self.mitmin,epout=self.mitmout, hostsave=self.hostsave, devsave=self.devsave)
cprint("[-] Device reconnected!",color='blue')
def describeFuzz(self, epin=None, epout=None, packet=None, howmany=100, match=None, timeout=0, direction=None):
"""This method allows you to describe a packet and select which bytes will be fuzzed
:param epin: endpoint in
:param epout: endpoint out
:param packet: a string of the packet that you want to use for fuzzing
:param howmany: how many packets to be sent
:param timeout: Timeout between each packet sent
:param Direction: 'h' for sending to Host , 'd' for sending to device
:return None
"""
if direction == 'h':
self.startQueuewrite()
sleep(1)
p = [packet[i:i + 2] for i in range(0, len(packet), 2)]
cprint(f"Packet: {''.join(p)}",color='green')
theBytes = input("Which byte indexes do you want to fuzz? [Separate indexes by a space] ").split()
for i in range(howmany):
for b in theBytes:
p[int(b)] = urandom(1).hex()
try:
s = binascii.unhexlify(''.join(p))
if direction == 'd':
self.device.write(epout, s)
r = self.device.read(epin, self.device.bMaxPacketSize0)
sdec, checks = self.decodePacketAscii(payload=s,rec=1)
rdec, checkr = self.decodePacketAscii(payload=r)
if match:
if match not in rdec:
self.fuzzchange = (sdec, rdec)
self.showMessage("Received data has changed!")
cprint(f"|-Packet[{i}]{'-' * 80}", color="green")
cprint(f"|\t Bytes:", color="blue")
cprint(
f"|\t\tSent: {binascii.hexlify(s)}\n|\t\tDiff: {checks}\n|\t\t |____Received: {binascii.hexlify(r)}\n",
color="green")
cprint(f"|\t Decoded:", color="blue")
cprint(f"|\t\t Sent: {sdec}\n|\t\t |____Received: {rdec}", color="green")
cprint(f"|{'_' * 90}[{i}]", color="green")
sleep(timeout)
elif direction == 'h':
cprint(f"|-Packet[{i}]{'-' * 80}", color="green")
cprint(f"|\t Bytes:", color="blue")
cprint(f"|\t\tSent: {binascii.hexlify(s)}", color="green")
cprint(f"|{'_' * 90}[{i}]", color="green")
self.hostwrite(s, isfuzz=1)
else:
return cprint("you must select a direction to send either host or device",color='red',attrs=['blink'])
except usb.core.USBError as e:
cprint(f"|-Packet[{i}]{'-' * 80}", color="red", attrs=['blink'])
cprint(f"|\t Error:", color="red") # not blinking to grab attention
cprint(f"|\t\tSent: {binascii.hexlify(s)}", color='red', attrs=['blink'])
cprint(f"|\t\t|____{e}", color='red', attrs=['blink'])
cprint(f"|{'_' * 90}[{i}]", color="red", attrs=['blink'])
self.device.reset()
self.showMessage("Device reset complete")
except KeyboardInterrupt:
self.showMessage("Keyboard interrupt detected! Ending...")
if direction == 'h':
self.stopQueuewrite()
break
if direction == 'h':
self.stopQueuewrite()
self.showMessage("All Packets sent successfully!",color="blue")
def SmartFuzz(self, engine=None, samples=10, direction=None, filename=None):
"""
This method is generates packets based on what it has learned from a sniff from either the host or the device
:param engine: choice between smart, random
random: [truly random based on charset , length , chars found]
smart: [based on input , weight & positions]
:param samples: number of samples to be generated
:param direction: 'hst' or 'dev'
:param filename: 'filename to learn from'
:return: None
"""
try:
if filename is not None:
self.edap.readwords = list(set([i.decode('utf-8').strip() for i in open(filename, 'rb')]))
else:
return cprint("nothing to do! Supply a file path which holds the payloads to learn from",color="red")
self.edap.charset = list()
self.edap.alphaupperindexes = list()
self.edap.alphalowerindexes = list()
self.edap.integerindexes = list()
self.edap.nonalphanumindexes = list()
self.edap.frequencies = dict()
self.edap.fullkeyboard = list(
"`1234567890-=qwertyuiop[]\\asdfghjkl;\'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?")
self.edap.discardedcharset = list()
self.edap.finalcharset = list()
self.edap.countUpper = 0
self.edap.countLower = 0
self.edap.countDigits = 0
self.edap.countOther = 0
self.edap.pppc = 1
self.edap.word_dct = dict()
self.edap.packets = []
self.edap.howmany = samples
self.edap.unusedindexes = list(range(len(max(self.edap.readwords, key=len).strip())))
self.edap.getcharset()
self.edap.getindexes()
self.edap.printgeneralstats()
self.edap.frequency_index_vertical()
self.edap.frequency_index_horizontal()
self.edap.charswithfriendswithwords()
self.edap.PrefinalAnalysis()
if engine == "smart":
for i in range(samples):
self.edap.smartGenerator()
elif engine == "random":
self.edap.randomgenerator()
else:
return cprint("No engine was selected. Nothing to do",color="red")
self.showMessage(f"generated:{len(self.edap.packets)} Packets", color='green')
if direction == 'hst':
self.startQueuewrite()
sleep(1)
for i in self.edap.packets:
self.hostwrite(i)
self.stopQueuewrite()
elif direction == 'dev':
cprint("Starting to send to device")
self.deviceInterfaces()