In [ ]:
import struct, os
from Crypto.Cipher import AES
In [6]:
DNS_URL_POSTFIX = "dnsr.uk.to"
CODING_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
#CODING_TABLE = random.shuffle( CODING_TABLE )
ct = {}
for i,c in enumerate(CODING_TABLE.upper()):
ct[c] = i
SECRET_KEY = bytes(range(32)) #Not so secret anymore ^^
#SECRET_KEY = os.urandom(32) #That's better.
In [23]:
print('#ifndef SECRETS_H_')
print('#define SECRETS_H_')
print('#define SECRET_KEY_256 { \\\n\t', end='')
for i, c in enumerate(SECRET_KEY):
if i > 0 and (i % 8) == 0:
print('\\\n\t', end='')
print( '0x{0:02x}, '.format(c), end='')
print('\\\n}')
print('#define CODING_TABLE \"', end='')
print( CODING_TABLE, end='')
print('"')
print('#endif /* SECRETS_H_ */')
In [81]:
def hexdump( res ):
for i,b in enumerate(res):
if( len(res)>16 and (i%16)==0 ):
print( "\n {:04x}: ".format(i), end="" )
print( "{:02x} ".format(b), end="" )
class Crc16:
def __init__( self ):
self._resetRunnningCRC()
def _resetRunnningCRC( self ):
self.c=0xFFFF
def _runningCRC( self, inputByte ):
self.c ^= inputByte
self.c &= 0xFFFF
for b in range(8): # For each bit in the byte
if self.c & 1:
self.c = (self.c >> 1) ^ 0xA001
else:
self.c = (self.c >> 1)
self.c &= 0xFFFF
def getCrc( self, dataBytes ):
self._resetRunnningCRC()
for b in dataBytes:
self._runningCRC( b )
return self.c
class DnsDecoder:
def __init__( self, SECRET_KEY, SUBDOMAIN, CODING_TABLE ):
self.CODING_TABLE = CODING_TABLE
self.SUBDOMAIN = SUBDOMAIN
self.decodingTable = {}
for i,c in enumerate(CODING_TABLE.upper()):
self.decodingTable[c] = i
self.cipher = AES.new( SECRET_KEY, AES.MODE_ECB )
self.crc = Crc16()
def _decodeBlock( self, messageBlock ):
""" returns a bytes object """
res = 0
for i,c in enumerate(messageBlock):
res |= self.decodingTable[c]<<(i*5)
return res.to_bytes( 16, 'little' )
def dnsDecode( self, qnString ):
""" decode URL string and return payload as bytes """
if not qnString.endswith( self.SUBDOMAIN ):
raise RuntimeError("Bad hostname: " + qnString)
messageBlocks = qnString.replace( self.SUBDOMAIN, "" )
resultBytes = bytearray()
for messageBlock in messageBlocks.split("."):
messageBlock.upper()
resultBytes += self.cipher.decrypt( self._decodeBlock(messageBlock) )
plCrc = resultBytes[-2]<<8 | resultBytes[-1]
plLength = resultBytes[-3]
resultBytes = resultBytes[:plLength]
if self.crc.getCrc( resultBytes ) != plCrc:
raise RuntimeError("CRC error")
return resultBytes
In [82]:
dc = DnsDecoder( SECRET_KEY, ".dnsr.uk.to.", CODING_TABLE )
In [84]:
dc.dnsDecode("WBZZBT5EFY63RPAPOBK6KDWWAC.Q5XTF7PHP3ZI5HZZGM4NSWH2PG.NOO2UQKJ2EKPKZAUFIMKSRDCZC.2KF5E57JJTDJO3H5GQF3W2LYUG.dnsr.uk.to.")
Out[84]: