Add Android forensics, IOC threat-intel DB, Compose companion; scrub secrets from configs
This commit is contained in:
196
data/ioc/spyware/citizen_lab/201611_KeyBoy/kb_configDecode.py
Normal file
196
data/ioc/spyware/citizen_lab/201611_KeyBoy/kb_configDecode.py
Normal file
@@ -0,0 +1,196 @@
|
||||
'''
|
||||
KeyBoy Config file 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
|
||||
'''
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
LookupTable = {}
|
||||
|
||||
# Function to compute 9-bit sliding window values and return list of converted indices
|
||||
def byteDecoder(rawBytes):
|
||||
#rawBytes should be bytearray of values
|
||||
k=0
|
||||
ba = []
|
||||
|
||||
bytex=0
|
||||
while( bytex < len(rawBytes) -1):
|
||||
|
||||
index = 0
|
||||
ix = bytex
|
||||
t=0
|
||||
while ( index < 9):
|
||||
|
||||
while (k<8):
|
||||
|
||||
if( ( ord(rawBytes[ix]) & (1 << (7-k))) > 0 ):
|
||||
t = 2*t +1
|
||||
else:
|
||||
t = 2*t
|
||||
|
||||
index+=1
|
||||
k+=1
|
||||
|
||||
if( index > 8):
|
||||
break
|
||||
|
||||
if ( k==8 ):
|
||||
ix+=1
|
||||
k=0
|
||||
|
||||
|
||||
if (index<=8):
|
||||
k=0
|
||||
elif ( (index == 9) and (k==8)):
|
||||
k=0
|
||||
bytex+=1
|
||||
#t should hold the resultant value
|
||||
ba.append(t)
|
||||
bytex+=1
|
||||
|
||||
|
||||
return ba
|
||||
|
||||
|
||||
|
||||
|
||||
def CreateLookupTable():
|
||||
for x in xrange(0,256):
|
||||
LookupTable[x] = "{:x}".format(x)
|
||||
|
||||
# 0x101 is used as the exit value
|
||||
LookupTable[257] = "reserved"
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
def BuildExtraLookupTable(curIndex, prevIndex):
|
||||
#curIndex and prevIndex are indices from the decoded input file
|
||||
startFrom = len(LookupTable)
|
||||
|
||||
if( curIndex > startFrom) :
|
||||
# if we are looking up an index beyond the existing table size
|
||||
vPrev = LookupTable.get(prevIndex)
|
||||
vCur = LookupTable.get(prevIndex)
|
||||
else:
|
||||
vPrev = LookupTable.get(prevIndex)
|
||||
vCur = LookupTable.get(curIndex)
|
||||
|
||||
newVal = vPrev
|
||||
|
||||
|
||||
if( len(vCur.split(',')) > 1 ):
|
||||
augItem = vCur.split(',')[0]
|
||||
else:
|
||||
augItem = vCur
|
||||
|
||||
newVal = ','.join([vPrev,augItem])
|
||||
|
||||
LookupTable[startFrom+1] = newVal
|
||||
logging.debug("0x%x => %s" % ((startFrom+1),newVal ))
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description='Decode KeyBoy backdoor configuration files')
|
||||
parser.add_argument('FILE', help='KeyBoy encoded config file')
|
||||
parser.add_argument('--verbose','-v',action='store_true',help='Enable verbose output')
|
||||
parser.add_argument('--skip','-s',type=int,help='Skip over <SKIP> bytes at beginning of file')
|
||||
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 Config file Decoder}"
|
||||
print "============================"
|
||||
print ""
|
||||
|
||||
with open(args.FILE, "rb") as f:
|
||||
|
||||
if args.skip is None:
|
||||
f.seek(0,0)
|
||||
else:
|
||||
logging.warn("Skipping %i bytes.." % args.skip)
|
||||
f.seek(args.skip,0)
|
||||
|
||||
szfile = f.read(8)
|
||||
szf_dat = int(szfile,16)
|
||||
logging.debug("Size of decoded config file:\t%i bytes" % szf_dat)
|
||||
|
||||
szconf = f.read(8)
|
||||
szc_dat = int(szconf,16)
|
||||
logging.debug("Size of encoded config data:\t%i bytes" % szc_dat)
|
||||
logging.debug("")
|
||||
|
||||
bytesRaw = f.read(szc_dat)
|
||||
ba = byteDecoder(bytesRaw)
|
||||
|
||||
#create the initial Lookup table
|
||||
CreateLookupTable()
|
||||
|
||||
ResultsList = []
|
||||
|
||||
#Build the extra lookup table values
|
||||
logging.debug("Dynamic lookup table entries:")
|
||||
for val in range(len(ba)):
|
||||
|
||||
#for each entry in the bytes array,
|
||||
# we need to look up the value in the table.
|
||||
# As we go, we need to build new entries on the table
|
||||
# corresponding to the value from this lookup, concatenated with the first character from the
|
||||
# the value printed in the previous lookup. BuildExtraLookupTable function does that step.
|
||||
|
||||
if( val == 0):
|
||||
continue
|
||||
elif (val == 1):
|
||||
ResultsList.append(LookupTable.get(ba[val]))
|
||||
prevValue = ba[val]
|
||||
else:
|
||||
curValue = ba[val]
|
||||
if (curValue != 257):
|
||||
BuildExtraLookupTable(curValue, prevValue)
|
||||
ResultsList.append(LookupTable.get(ba[val]))
|
||||
prevValue= curValue
|
||||
|
||||
logging.debug("")
|
||||
logging.debug("Decoded index list:")
|
||||
logging.debug(['0x%x' % j for j in ba])
|
||||
logging.debug("")
|
||||
|
||||
|
||||
resultData = ",".join(ResultsList)
|
||||
hChars = resultData.split(',')
|
||||
Output = ""
|
||||
for h in hChars:
|
||||
Output += chr(int(h,16))
|
||||
|
||||
logging.debug("Retrieved config character array:")
|
||||
logging.debug(['0x%x' % int(m,16) for m in hChars])
|
||||
logging.debug("")
|
||||
|
||||
TokenizedOutput = Output.split('\r\n')
|
||||
ConfigSections = ['Identity Code:','C2 Host/IP #1:','C2 Host/IP #2:','C2 Host/IP #3:','C2 Port #1:','C2 Port #2:','C2 Port #3:','Password:','Campaign ID:','']
|
||||
|
||||
logging.warn("Configuration Data:")
|
||||
headr = "=" * 30
|
||||
logging.warn(headr)
|
||||
|
||||
for x in range(len(TokenizedOutput)-1):
|
||||
logging.warn("%s\t%s" % (ConfigSections[x],TokenizedOutput[x]))
|
||||
|
||||
logging.warn("")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user