108 lines
2.9 KiB
Python
108 lines
2.9 KiB
Python
|
|
'''
|
||
|
|
KeyBoy C2 traffic pcap decoder
|
||
|
|
From: It's Parliamentary: KeyBoy and the targeting of the Tibetan Community
|
||
|
|
Permalink: https://citizenlab.org/2016/11/parliament-keyboy/
|
||
|
|
|
||
|
|
Adam Hulcoop / September 19, 2016
|
||
|
|
adam.hulcoop[@]citizenlab.ca
|
||
|
|
|
||
|
|
32 bit support only [64bit can use manual override]
|
||
|
|
'''
|
||
|
|
|
||
|
|
import scapy
|
||
|
|
import argparse
|
||
|
|
import logging
|
||
|
|
import sys,getopt
|
||
|
|
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
|
||
|
|
from scapy.all import TCP,rdpcap,Raw
|
||
|
|
|
||
|
|
def egcd(a, b):
|
||
|
|
if a == 0:
|
||
|
|
return (b, 0, 1)
|
||
|
|
else:
|
||
|
|
g, y, x = egcd(b % a, a)
|
||
|
|
return (g, x - (b // a) * y, y)
|
||
|
|
|
||
|
|
def modinv(a, m):
|
||
|
|
g, x, y = egcd(a, m)
|
||
|
|
if g != 1:
|
||
|
|
raise Exception('modular inverse does not exist')
|
||
|
|
else:
|
||
|
|
return x % m
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
|
||
|
|
parser = argparse.ArgumentParser(description='Decode KeyBoy C2 traffic from pcap file')
|
||
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
||
|
|
group.add_argument('-k','--key',type=int, help="Manually provide C2 decode key")
|
||
|
|
group.add_argument('-b','--binary',help="KeyBoy binary")
|
||
|
|
parser.add_argument('PCAPF', help='PCAP File containing c2 traffic')
|
||
|
|
parser.add_argument('--verbose','-v',action='store_true',help='Enable verbose output')
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
if args.verbose:
|
||
|
|
logging.basicConfig(format='%(message)s',level=logging.DEBUG)
|
||
|
|
else:
|
||
|
|
logging.basicConfig(format='%(message)s',level=logging.WARNING)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
print "============================"
|
||
|
|
print "{KeyBoy C2 Traffic decoder}"
|
||
|
|
print "============================"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
if args.binary:
|
||
|
|
with open(args.binary,'rb') as keyboy_file:
|
||
|
|
keyboy_file.seek(0,2)
|
||
|
|
num_bytes_kb = keyboy_file.tell()
|
||
|
|
|
||
|
|
count =0
|
||
|
|
for i in range(num_bytes_kb):
|
||
|
|
keyboy_file.seek(i)
|
||
|
|
sig_p1 = keyboy_file.read(4)
|
||
|
|
sig_const = keyboy_file.read(1)
|
||
|
|
sig_p2 = keyboy_file.read(7)
|
||
|
|
|
||
|
|
if ( (sig_p1 == b"\x8a\x04\x73\xb1") or (sig_p1 == b"\x8a\x04\x1e\xb1") ):
|
||
|
|
if ( (sig_p2 == b"\xf6\xe9\x88\x04\x73\x53\x46") or (sig_p2 == b"\xf6\xe9\x88\x04\x1e\x8b\xc3") ):
|
||
|
|
#sig_const should be the encoding constant
|
||
|
|
#print "Found const: 0x%x" % ord(sig_const)
|
||
|
|
logging.debug("Found constant: 0x%x" % ord(sig_const))
|
||
|
|
encode_val = ord(sig_const)
|
||
|
|
count += 1
|
||
|
|
break
|
||
|
|
|
||
|
|
|
||
|
|
if count < 1 :
|
||
|
|
logging.warn("Didn't find signature\nQuitting..")
|
||
|
|
sys.exit(1)
|
||
|
|
else:
|
||
|
|
logging.debug( "Computing decode value...")
|
||
|
|
inv = modinv(encode_val,256)
|
||
|
|
logging.debug("Found decoding multiplier (inverse): %s" % hex(inv))
|
||
|
|
else:
|
||
|
|
logging.debug("Using manually specified decoding multiplier (inverse): %s" % hex(args.key))
|
||
|
|
inv = args.key
|
||
|
|
|
||
|
|
logging.warn ("Searching PCAP for RAW TCP packets and decoding...")
|
||
|
|
logging.warn("")
|
||
|
|
|
||
|
|
|
||
|
|
data =''
|
||
|
|
p = rdpcap(args.PCAPF)
|
||
|
|
for pkt in p:
|
||
|
|
if( pkt.haslayer(TCP)):
|
||
|
|
if( pkt.haslayer(Raw)):
|
||
|
|
ba = bytearray(pkt.getlayer(Raw).load)
|
||
|
|
for byte in ba:
|
||
|
|
if byte == 0:
|
||
|
|
data += ''
|
||
|
|
else:
|
||
|
|
data += chr(int((byte*inv) & 255))
|
||
|
|
|
||
|
|
logging.warn(data)
|
||
|
|
|
||
|
|
|
||
|
|
|