Updated reencode.py to include ZLIB compression

This commit is contained in:
Mariusz 2018-04-16 17:39:09 +02:00 committed by GitHub
parent a5a81500a0
commit c70fec6774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 6 deletions

View File

@ -2,7 +2,10 @@
# #
# ReEncoder.py - script allowing for recursive encoding detection, decoding and then re-encoding. # ReEncoder.py - script allowing for recursive encoding detection, decoding and then re-encoding.
# To be used for instance in fuzzing purposes. # To be used for instance in fuzzing purposes. Imagine you want to fuzz XML parameters within
# **PaReq** packet of 3DSecure standard. This packet has been ZLIB compressed, then Base64 encoded,
# then URLEncoded. In order to modify the inner HTML you would need to peel off that encoding layers
# and then reaplly them in reversed order. This script allows you to do that in an automated manner
# #
# NOTICE: # NOTICE:
# If the input string's length is divisble by 4, Base64 will be able to decode it - thus, the script # If the input string's length is divisble by 4, Base64 will be able to decode it - thus, the script
@ -21,6 +24,7 @@
import re import re
import sys import sys
import jwt import jwt
import zlib
import math import math
import base64 import base64
import urllib import urllib
@ -33,7 +37,23 @@ from collections import Counter
class ReEncoder: class ReEncoder:
# Switch this to show some verbose informations about decoding process. # Switch this to show some verbose informations about decoding process.
DEBUG = False DEBUG = True
class Utils:
@staticmethod
def isBinaryData(data):
nonBinary = 0
percOfBinaryToAssume = 0.10
for d in data:
c = ord(d)
if c in (10, 13):
nonBinary += 1
elif c >= 0x20 and c <= 0x7f:
nonBinary += 1
binary = len(data) - nonBinary
return binary >= int(percOfBinaryToAssume * len(data))
# ============================================================ # ============================================================
# ENCODERS SECTION # ENCODERS SECTION
@ -75,7 +95,7 @@ class ReEncoder:
if urllib.quote(urllib.unquote(data)) == data and (urllib.unquote(data) != data): if urllib.quote(urllib.unquote(data)) == data and (urllib.unquote(data) != data):
return True return True
if re.match(r'^(?:%[0-9a-f]{2})+$', data, re.I): if re.search(r'(?:%[0-9a-f]{2})+', data, re.I):
return True return True
return False return False
@ -157,6 +177,28 @@ class ReEncoder:
def decode(self, data): def decode(self, data):
return jwt.decode(data, verify = False) return jwt.decode(data, verify = False)
class ZlibEncoder(Encoder):
def name(self):
return 'ZLIB'
def check(self, data):
if not ReEncoder.Utils.isBinaryData(data):
return False
try:
if zlib.compress(zlib.decompress(data)) == data:
return True
except:
pass
return False
def encode(self, data):
return zlib.compress(data)
def decode(self, data):
return zlib.decompress(data)
# ============================================================ # ============================================================
# ENCODING DETECTION IMPLEMENTATION # ENCODING DETECTION IMPLEMENTATION
@ -172,6 +214,7 @@ class ReEncoder:
ReEncoder.Base64Encoder(), ReEncoder.Base64Encoder(),
ReEncoder.Base64URLSafeEncoder(), ReEncoder.Base64URLSafeEncoder(),
ReEncoder.JWTEncoder(), ReEncoder.JWTEncoder(),
ReEncoder.ZlibEncoder(),
# None must always be the last detector # None must always be the last detector
ReEncoder.NoneEncoder(), ReEncoder.NoneEncoder(),
@ -352,6 +395,9 @@ class ReEncoder:
return encodings return encodings
def getWinningDecodePath(self, root):
return [x for x in self.evaluateEncodingTree(root) if x != 'None']
def process(self, data): def process(self, data):
root = anytree.Node('None', decoded = data) root = anytree.Node('None', decoded = data)
prev = root prev = root
@ -368,9 +414,10 @@ class ReEncoder:
prev = currNode prev = currNode
for pre, fill, node in anytree.RenderTree(root): for pre, fill, node in anytree.RenderTree(root):
ReEncoder.log("%s%s (%s)" % (pre, node.name, node.decoded[:20].decode('ascii', 'ignore'))) if node.name != 'None':
ReEncoder.log("%s%s (%s)" % (pre, node.name, node.decoded[:20].decode('ascii', 'ignore')))
self.encodings = self.evaluateEncodingTree(root) self.encodings = self.getWinningDecodePath(root)
ReEncoder.log('[+] Selected encodings: {}'.format(str(self.encodings))) ReEncoder.log('[+] Selected encodings: {}'.format(str(self.encodings)))
def decode(self, data, encodings = []): def decode(self, data, encodings = []):
@ -399,6 +446,10 @@ class ReEncoder:
return data return data
def main(argv): def main(argv):
# Sample 1: ZLIB -> Base64 -> URLEncode
sample = 'eJzzSM3JyVcozy%2FKSVFIK8rPVQhKdc1Lzk9JLVIEAIr8Cck%3D'
# Sample 2: URLEncode -> Base64 -> HexEncode
sample = '4a5451344a5459314a545a6a4a545a6a4a545a6d4a5449774a5463334a545a6d4a5463794a545a6a4a5459304a5449784a5449774a544e684a544a6b4a544935' sample = '4a5451344a5459314a545a6a4a545a6a4a545a6d4a5449774a5463334a545a6d4a5463794a545a6a4a5459304a5449784a5449774a544e684a544a6b4a544935'
if len(argv) != 2: if len(argv) != 2:
@ -421,4 +472,4 @@ def main(argv):
print('(3) ENCODED FORM: "{}"'.format(decoded)) print('(3) ENCODED FORM: "{}"'.format(decoded))
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv) main(sys.argv)