diff --git a/chepy/__version__.py b/chepy/__version__.py index 594b065..4afe48d 100644 --- a/chepy/__version__.py +++ b/chepy/__version__.py @@ -1,2 +1,2 @@ -__version__ = "7.2.0" # pragma: no cover +__version__ = "7.3.0" # pragma: no cover __author__ = "@securisec" # pragma: no cover diff --git a/chepy/core.py b/chepy/core.py index 784c30c..0826e3d 100644 --- a/chepy/core.py +++ b/chepy/core.py @@ -1666,3 +1666,38 @@ def encode_bytes(obj): self.state = json.dumps(encode_bytes(self.state)) return self + + @ChepyDecorators.call_stack + def walk_dir(self): + """Walk a directory and get all file paths + + Returns: + Chepy: The Chepy object. + """ + p = self._convert_to_str() + paths = Path(p).glob("**/*") + self.state = [str(p) for p in paths] + return self + + @ChepyDecorators.call_stack + def search_dir(self, pattern: Union[bytes, str]): + """Search all files in a directory. Pattern is case insensitive + + Args: + pattern (Union[bytes, str]): regex to search + + Returns: + Chepy: The Chepy object. + """ + paths = self._convert_to_str() + rgx = re.compile(self._str_to_bytes(pattern), flags=re.I) + hold = [] + for path in Path(paths).glob("**/*"): + if path.is_dir(): # pragma: no cover + continue + data = path.read_bytes() + matched = rgx.findall(data) + if matched: + hold += matched + self.state = hold + return self diff --git a/chepy/core.pyi b/chepy/core.pyi index dd5f859..3ecaa85 100644 --- a/chepy/core.pyi +++ b/chepy/core.pyi @@ -88,3 +88,5 @@ class ChepyCore: def get_register(self: ChepyCoreT, key: str) -> Union[str, bytes]: ... def set_register(self: ChepyCoreT, key: str, val: Union[str, bytes]) -> ChepyCoreT: ... def dump_json(self: ChepyCoreT) -> ChepyCoreT: ... + def walk_dir(self: ChepyCoreT) -> ChepyCoreT: ... + def search_dir(self: ChepyCoreT, pattern: Union[str, bytes]) -> ChepyCoreT: ... diff --git a/chepy/modules/dataformat.py b/chepy/modules/dataformat.py index 504cc6e..2143db0 100644 --- a/chepy/modules/dataformat.py +++ b/chepy/modules/dataformat.py @@ -551,7 +551,6 @@ def from_base64( Chepy: The Chepy object. Examples: - Base64 decode using a custom string >>> c = Chepy("QqxhNG/mMKtYPqoz64FVR42=") >>> c.from_base64(alphabet="./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") >>> c.out diff --git a/chepy/modules/encryptionencoding.py b/chepy/modules/encryptionencoding.py index b52bf46..4925a0e 100644 --- a/chepy/modules/encryptionencoding.py +++ b/chepy/modules/encryptionencoding.py @@ -1,6 +1,5 @@ import base64 import binascii -import codecs import itertools import string import random @@ -162,7 +161,9 @@ def rotate_bruteforce(self) -> EncryptionEncodingT: return self @ChepyDecorators.call_stack - def rot_13(self, amount=13, rotate_lower=True, rotate_upper=True, rotate_numbers=False) -> EncryptionEncodingT: + def rot_13( + self, amount=13, rotate_lower=True, rotate_upper=True, rotate_numbers=False + ) -> EncryptionEncodingT: """Rot 13 Args: @@ -172,20 +173,20 @@ def rot_13(self, amount=13, rotate_lower=True, rotate_upper=True, rotate_numbers rotate_numbers (bool, optional): Rotate numbers. Defaults to False. Returns: - Chepy: The Chepy object. + Chepy: The Chepy object. """ text = self._convert_to_str() result = [] for char in text: - if rotate_lower and 'a' <= char <= 'z': # Lowercase letters - result.append(chr((ord(char) - ord('a') + amount) % 26 + ord('a'))) - elif rotate_upper and 'A' <= char <= 'Z': # Uppercase letters - result.append(chr((ord(char) - ord('A') + amount) % 26 + ord('A'))) - elif rotate_numbers and '0' <= char <= '9': # Numbers - result.append(chr((ord(char) - ord('0') + amount) % 10 + ord('0'))) + if rotate_lower and "a" <= char <= "z": # Lowercase letters + result.append(chr((ord(char) - ord("a") + amount) % 26 + ord("a"))) + elif rotate_upper and "A" <= char <= "Z": # Uppercase letters + result.append(chr((ord(char) - ord("A") + amount) % 26 + ord("A"))) + elif rotate_numbers and "0" <= char <= "9": # Numbers + result.append(chr((ord(char) - ord("0") + amount) % 10 + ord("0"))) else: result.append(char) # Non-alphabetical characters remain unchanged - self.state = ''.join(result) + self.state = "".join(result) return self @ChepyDecorators.call_stack @@ -311,19 +312,19 @@ def rot_8000(self): def xor( self, key: str, - key_type: Literal["hex", "utf", "base64", "decimal"] = "hex", + key_type: Literal["hex", "utf8", "base64", "decimal"] = "hex", ) -> EncryptionEncodingT: """XOR state with a key Args: key (str): Required. The key to xor by - key_type (str, optional): The key type. Valid values are hex, utf, decimal and base64. Defaults to "hex". + key_type (str, optional): The key type. Valid values are hex, utf8, decimal and base64. Defaults to "hex". Returns: Chepy: The Chepy object. Examples: - >>> Chepy("secret").xor(key="secret", key_type="utf").to_hex() + >>> Chepy("secret").xor(key="secret", key_type="utf8").to_hex() 000000000000 """ @@ -334,7 +335,7 @@ def xor( x.append(char ^ key_val) else: - if key_type == "utf": + if key_type == "utf8": key = str(key) key = binascii.hexlify(key.encode()) elif key_type == "base64": @@ -1918,3 +1919,22 @@ def railfence_decode(self, key=2, offset=0) -> EncryptionEncodingT: self.state = "".join(plaintext).strip() return self + + @ChepyDecorators.call_stack + def gpp_decrypt(self): + """Decrypt Group Policy Preferences (GPP) password + + Returns: + Chepy: The Chepy object. + """ + password = self._convert_to_bytes() + password = base64.b64decode(password + b"=" * 3) + key = ( + b"\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8" + b"\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b" + ) + iv = b"\x00" * 16 + aes = AES.new(key, AES.MODE_CBC, iv) + self.state = Padding.unpad(aes.decrypt(password), 16) + self.remove_nullbytes() + return self diff --git a/chepy/modules/encryptionencoding.pyi b/chepy/modules/encryptionencoding.pyi index 3e08d3b..8a72e83 100644 --- a/chepy/modules/encryptionencoding.pyi +++ b/chepy/modules/encryptionencoding.pyi @@ -17,10 +17,10 @@ class EncryptionEncoding(ChepyCore): def rotate(self: EncryptionEncodingT, rotate_by: int) -> EncryptionEncodingT: ... def rotate_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ... def rot_13(self: EncryptionEncodingT, amount: int=13, rotate_lower: bool=True, rotate_upper: bool=True, rotate_numbers: bool=False) -> EncryptionEncodingT: ... - def rot_47(self: EncryptionEncodingT, amount: int=...) -> EncryptionEncodingT: ... + def rot_47(self: EncryptionEncodingT, amount: int=47) -> EncryptionEncodingT: ... def rot_47_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ... def rot_8000(self: EncryptionEncodingT) -> EncryptionEncodingT: ... - def xor(self: EncryptionEncodingT, key: Union[str, bytearray], key_type: Literal['hex', 'utf', 'base64', 'decimal']=...) -> EncryptionEncodingT: ... + def xor(self: EncryptionEncodingT, key: Union[str, bytearray], key_type: Literal['hex', 'utf8', 'base64', 'decimal']='hex') -> EncryptionEncodingT: ... def xor_bruteforce(self: EncryptionEncodingT, length: int=..., crib: Union[str, bytes, None]=...) -> EncryptionEncodingT: ... def jwt_decode(self: EncryptionEncodingT) -> EncryptionEncodingT: ... def jwt_verify(self: EncryptionEncodingT, secret: str, algorithm: list=...) -> EncryptionEncodingT: ... @@ -69,3 +69,4 @@ class EncryptionEncoding(ChepyCore): def fernet_decrypt(self: EncryptionEncodingT, key:Union[bytes, str], encode_key: bool=False) -> EncryptionEncodingT: ... def railfence_encode(self: EncryptionEncodingT, key: Union[int, str]=2, offset: Union[int, str]=0) -> EncryptionEncodingT: ... def railfence_decode(self: EncryptionEncodingT, key: Union[int, str]=2, offset: Union[int, str]=0) -> EncryptionEncodingT: ... + def gpp_decrypt(self: EncryptionEncodingT) -> EncryptionEncodingT: ... diff --git a/tests/test_core.py b/tests/test_core.py index ea3dfc1..c34bac2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -185,7 +185,7 @@ def test_run_recipe(): recipes=[ { "function": "from_base64", - "args": {"alphabet": 'standard'}, + "args": {"alphabet": "standard"}, }, {"function": "swap_case", "args": {}}, ] @@ -356,3 +356,11 @@ def test_dump_json(): Chepy(data).dump_json().json_to_dict().get_by_key("byte_key").o == b"\x00\x01" ) assert Chepy(True).dump_json().o == b"true" + + +def test_walk_dir(): + assert b"script.py" in Chepy("tests/files/").walk_dir().join("\n").o + + +def test_search_dir(): + assert len(Chepy("tests/files/qr/").search_dir("PNG").o) == 2 diff --git a/tests/test_encryptionencoding.py b/tests/test_encryptionencoding.py index 1c2d440..d9448aa 100644 --- a/tests/test_encryptionencoding.py +++ b/tests/test_encryptionencoding.py @@ -58,7 +58,7 @@ def test_rotate_bruteforce(): def test_xor_utf(): - assert Chepy("some data").xor("UD", "utf").o == b"&+8!u 404" + assert Chepy("some data").xor("UD", "utf8").o == b"&+8!u 404" def test_xor_base64(): @@ -74,17 +74,17 @@ def test_xor_binary(): Chepy("./tests/files/hello") .load_file() .to_hex() - .xor("A", "utf") + .xor("A", "utf8") .to_hex() .o.decode()[0:6] == "222727" ) assert ( - Chepy("./tests/files/hello").load_file().xor(41, "utf").to_hex().o[0:6] + Chepy("./tests/files/hello").load_file().xor(41, "utf8").to_hex().o[0:6] == b"fbcbd9" ) assert ( - Chepy("./tests/files/hello").xor(key=41, key_type="utf").to_hex().o[0:6] + Chepy("./tests/files/hello").xor(key=41, key_type="utf8").to_hex().o[0:6] == b"1a1e40" ) @@ -910,3 +910,8 @@ def test_railfence(): def test_rot13(): assert Chepy("Abc1").rot_13(rotate_numbers=True).o == b"Nop4" + + +def test_gpp_decrypt(): + p = "B+iL/dnbBHSlVf66R8HOuAiGHAtFOVLZwXu0FYf+jQ6553UUgGNwSZucgdz98klzBuFqKtTpO1bRZIsrF8b4Hu5n6KccA7SBWlbLBWnLXAkPquHFwdC70HXBcRlz38q2" + assert Chepy(p).gpp_decrypt().o == b"DUCTF{D0n7_Us3_P4s5w0rds_1n_Gr0up_P0l1cy}"