Skip to content

Commit

Permalink
🗓 Jul 17, 2023 10:07:47 PM
Browse files Browse the repository at this point in the history
🐛 fix some bugs
🧪 tests added/updateds
🚀 update aes ctr enc/dec
🚀 update xor to handle decimal keys and bytearrays
  • Loading branch information
securisec committed Jul 18, 2023
1 parent dc247f1 commit 3595273
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 33 deletions.
2 changes: 2 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ New ideas:
☐ 💡 maybe a decorator function to convert all inputs into bytes when possible? this will allow for a consistant bytes approach to all functions
☐ rsa enc/dec with key or from pem directly
☐ ✨ jwt hmac confusion
☐ 🚀 improve tar compression mode type

Bug:

Expand Down Expand Up @@ -60,6 +61,7 @@ Misc:
☐ cyberchef recipe to chepy recipe converter

Archive:
✔ 🚀 improve aes ctr enc/dec to handle iv
✔ stringify method using json.dumps
✔ jwt none algo
✔ ✨ extractor partially done
Expand Down
18 changes: 10 additions & 8 deletions chepy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ def subsection(
methods: List[Tuple[Union[str, object], dict]],
group: int = 0,
):
"""Run specified methods over a subsection of the state. This method will always treat the state
as bytes.
"""Run specified methods over a subsection of the state. This method will always treat the state
as bytes.
Args:
pattern (Union[str, bytes]): Regex pattern to match against.
methods (List[Tuple[Union[str, object], dict]]): Required. List of tuples. The first value of the
methods (List[Tuple[Union[str, object], dict]]): Required. List of tuples. The first value of the
tuple is the method name, the second value is a dictionary of arguments.
group (int, optional): Matching group. Defaults to 0.
Expand Down Expand Up @@ -599,7 +599,7 @@ def _convert_to_str(self) -> str:
# todo check more types here
raise NotImplementedError

def _str_to_bytes(self, s: str) -> bytes: # pragma: no cover
def _str_to_bytes(self, s: str) -> bytes: # pragma: no cover
"""Converts a str to bytes
Args:
Expand All @@ -608,6 +608,8 @@ def _str_to_bytes(self, s: str) -> bytes: # pragma: no cover
Returns:
bytes: Bytes
"""
if s is None:
return s
if isinstance(s, bytes):
return s
return s.encode()
Expand Down Expand Up @@ -1074,7 +1076,7 @@ def loop(self, iterations: int, callback: str, args: dict = {}):
>>> c.loop(iterations=6, callback='hmac_hash', args={'key': 'secret'})
securisec
"""
if type(callback).__name__ == 'method':
if type(callback).__name__ == "method":
# this allows for both method and string passing
callback = callback.__name__
assert isinstance(callback, str), "Callback must be a string"
Expand Down Expand Up @@ -1113,7 +1115,7 @@ def loop_list(self, callback: str, args: dict = {}):
>>> c.loop_list('to_hex').loop_list('hmac_hash', {'key': 'secret'})
['5cbe6ca2a66b380aec1449d4ebb0d40ac5e1b92e', '30d75bf34740e8781cd4ec7b122e3efd8448e270']
"""
if type(callback).__name__ == 'method':
if type(callback).__name__ == "method":
# this allows for both method and string passing
callback = callback.__name__

Expand Down Expand Up @@ -1174,11 +1176,11 @@ def loop_dict(self, keys: list, callback: str, args: dict = {}):
{"another": "aaaa"},
]
"""
if type(callback).__name__ == 'method':
if type(callback).__name__ == "method":
# this allows for both method and string passing
callback = callback.__name__
assert isinstance(callback, str), "Callback must be a string"

hold = {}
current_state = self.state
# find the last index that this method was run
Expand Down
49 changes: 29 additions & 20 deletions chepy/modules/encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
DES3 = lazy_import.lazy_module("Crypto.Cipher.DES3")
RSA = lazy_import.lazy_module("Crypto.PublicKey.RSA")
Hash = lazy_import.lazy_module("Crypto.Hash")
Counter = lazy_import.lazy_module("Crypto.Util.Counter")
PKCS1_15 = lazy_import.lazy_module("Crypto.Signature.pkcs1_15")
PKCS1_OAEP = lazy_import.lazy_module("Crypto.Cipher.PKCS1_OAEP")
Blowfish = lazy_import.lazy_module("Crypto.Cipher.Blowfish")
Expand Down Expand Up @@ -270,13 +271,13 @@ def rot_8000(self):
def xor(
self,
key: str,
key_type: Literal["hex", "utf", "base64"] = "hex",
key_type: Literal["hex", "utf", "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 and base64. Defaults to "hex".
key_type (str, optional): The key type. Valid values are hex, utf, decimal and base64. Defaults to "hex".
Returns:
Chepy: The Chepy object.
Expand All @@ -285,24 +286,30 @@ def xor(
>>> Chepy("secret").xor(key="secret", key_type="utf").to_hex()
000000000000
"""
assert key_type in [
"utf",
"hex",
"base64",
], "Valid key types are hex, utf and base64"

if isinstance(key, int):
key = str(key)
if key_type == "utf":
key = binascii.hexlify(key.encode())
elif key_type == "base64":
key = binascii.hexlify(base64.b64decode(key.encode()))
key = binascii.unhexlify(key)

x = bytearray(b"")
for char, key_val in zip(self._convert_to_bytes(), itertools.cycle(key)):
x.append(char ^ key_val)
# check if state is a list and keys are list
if isinstance(self.state, bytearray) and isinstance(key, bytearray):
for char, key_val in zip(self.state, itertools.cycle(key)):
x.append(char ^ key_val)

else:

if key_type == "utf":
key = str(key)
key = binascii.hexlify(key.encode())
elif key_type == "base64":
key = binascii.hexlify(base64.b64decode(key.encode()))
elif key_type == "decimal":
key = binascii.hexlify(
int(key).to_bytes(len(str(key)), byteorder="big")
)

key = binascii.unhexlify(key)
for char, key_val in zip(self._convert_to_bytes(), itertools.cycle(key)):
x.append(char ^ key_val)

self.state = x
self.state = bytes(x)
return self

@ChepyDecorators.call_stack
Expand Down Expand Up @@ -826,7 +833,8 @@ def aes_encrypt(
self.state = cipher.encrypt(Padding.pad(self._convert_to_bytes(), 16))
return self
elif mode == "CTR":
cipher = AES.new(key, mode=AES.MODE_CTR, nonce=b"")
counter = Counter.new(128, initial_value=int.from_bytes(iv, "big"))
cipher = AES.new(key, mode=AES.MODE_CTR, counter=counter)
self.state = cipher.encrypt(self._convert_to_bytes())
return self
elif mode == "GCM":
Expand Down Expand Up @@ -890,7 +898,8 @@ def aes_decrypt(
self.state = Padding.unpad(cipher.decrypt(self._convert_to_bytes()), 16)
return self
elif mode == "CTR":
cipher = AES.new(key, mode=AES.MODE_CTR, nonce=b"")
counter = Counter.new(128, initial_value=int.from_bytes(iv, "big"))
cipher = AES.new(key, mode=AES.MODE_CTR, counter=counter)
self.state = cipher.decrypt(self._convert_to_bytes())
return self
elif mode == "GCM":
Expand Down
2 changes: 1 addition & 1 deletion chepy/modules/encryptionencoding.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class EncryptionEncoding(ChepyCore):
def rot_47(self: EncryptionEncodingT, amount: int=...) -> EncryptionEncodingT: ...
def rot_47_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def rot_8000(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def xor(self: EncryptionEncodingT, key: str, key_type: Literal['hex', 'utf', 'base64']=..., ascii: bool=...) -> EncryptionEncodingT: ...
def xor(self: EncryptionEncodingT, key: Union[str, bytearray], key_type: Literal['hex', 'utf', 'base64', 'decimal']=..., ascii: bool=...) -> EncryptionEncodingT: ...
def xor_bruteforce(self: EncryptionEncodingT, length: int=...) -> EncryptionEncodingT: ...
def jwt_decode(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def jwt_verify(self: EncryptionEncodingT, secret: str, algorithm: list=...) -> EncryptionEncodingT: ...
Expand Down
59 changes: 55 additions & 4 deletions tests/test_encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,65 @@ def test_xor_binary():
== 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="utf").to_hex().o[0:6]
== b"1a1e40"
)


def test_xor_decimal():
assert Chepy("kfool").xor(3, "decimal").o == b"hello"


def test_xor_bytearray():
flag = bytearray(
[
212,
240,
205,
31,
239,
85,
80,
104,
31,
167, #
180,
136,
232,
240,
216,
11,
195,
144,
227,
19, #
239,
115,
81,
3,
6,
166,
183,
209,
244,
241, #
245,
80,
168,
210,
191,
7,
166,
]
) #

key = bytearray(
[156, 164, 143, 100, 219, 10, 34, 92, 113, 212, 132, 229, 159, 196, 170, 56]
)

assert Chepy(flag).xor(key).o == b"HTB{4_r4ns0mw4r3_4lw4ys_wr34k5_h4v0c}"


def test_xor_bruteforce():
assert Chepy(
b"\x85\x88\x81\x81\x82\xcd\x9a\x82\x9f\x81\x89"
Expand Down

0 comments on commit 3595273

Please sign in to comment.