Skip to content

Commit

Permalink
🗓 Sep 21, 2023 10:15:26 PM
Browse files Browse the repository at this point in the history
✨ version 6.2.0
✨ to/from_base
✨ rotate right and left
🧪 tests added/updated
  • Loading branch information
securisec committed Sep 22, 2023
1 parent be37613 commit 1968a96
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 3 deletions.
2 changes: 1 addition & 1 deletion chepy/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "6.1.0" # pragma: no cover
__version__ = "6.2.0" # pragma: no cover
__author__ = "@securisec" # pragma: no cover
84 changes: 83 additions & 1 deletion chepy/modules/dataformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import string
from random import randint
from .internal.constants import Encoding
from .internal.helpers import detect_delimiter
from .internal.helpers import detect_delimiter, Rotate

yaml = lazy_import.lazy_module("yaml")
import regex as re
Expand Down Expand Up @@ -1839,3 +1839,85 @@ def unicode_replacer(match):
)
self.state = escaped_string
return self

@ChepyDecorators.call_stack
def to_base(self, radix: int = 36) -> DataFormatT:
"""Convert int to base
Args:
radix (int, optional): Radix. Defaults to 36.
Returns:
Chepy: The Chepy object.
"""
num = self._convert_to_int()
if num == 0:
self.state = "0"
return self

chars = string.digits + string.ascii_lowercase
result = ""

while num > 0:
remainder = num % radix
result = chars[remainder] + result
num //= radix

self.state = result
return self

@ChepyDecorators.call_stack
def from_base(self, radix: int = 36) -> DataFormatT:
"""Convert string to int base
Args:
radix (int, optional): Radix. Defaults to 36.
Returns:
Chepy: The Chepy object.
"""
chars = string.digits + string.ascii_lowercase
result = 0

string_num = self._convert_to_str()
for char in string_num:
result = result * radix + chars.index(char)

self.state = result
return self

@ChepyDecorators.call_stack
def rotate_right(self, radix: int = 1, carry: bool = False) -> DataFormatT:
"""Rotate right
Args:
radix (int, optional): Radix. Defaults to 1.
carry (bool, optional): Carry. Defaults to False.
Returns:
Chepy: The Chepy object.
"""
r = Rotate(self._convert_to_bytes(), radix)
if carry:
self.state = r.rot_right_carry()
else:
self.state = r.rot(Rotate.rotate_right)
return self

@ChepyDecorators.call_stack
def rotate_left(self, radix: int = 1, carry: bool = False) -> DataFormatT:
"""Rotate left
Args:
radix (int, optional): Radix. Defaults to 1.
carry (bool, optional): Carry. Defaults to False.
Returns:
Chepy: The Chepy object.
"""
r = Rotate(self._convert_to_bytes(), radix)
if carry:
self.state = r.rotate_left_carry()
else:
self.state = r.rot(Rotate.rotate_left)
return self
4 changes: 4 additions & 0 deletions chepy/modules/dataformat.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ class DataFormat(ChepyCore):
def to_messagepack(self: DataFormatT) -> DataFormatT: ...
def from_messagepack(self: DataFormatT) -> DataFormatT: ...
def unicode_escape(self: DataFormatT, padding: int = 0, uppercase_hex:bool=False) -> DataFormatT: ...
def to_base(self: DataFormatT, radix: int=16) -> DataFormatT: ...
def from_base(self: DataFormatT, radix: int=16) -> DataFormatT: ...
def rotate_right(self: DataFormatT, radix: int=1, carry: bool=False) -> DataFormatT: ...
def rotate_left(self: DataFormatT, radix: int=1, carry: bool=False) -> DataFormatT: ...
56 changes: 55 additions & 1 deletion chepy/modules/internal/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,61 @@ def detect_delimiter(
if len(parts) > 1 and all(part.strip() for part in parts):
return delimiter

if default_delimiter: # pragma: no cover
if default_delimiter: # pragma: no cover
return default_delimiter.encode() if is_bytes else default_delimiter
else:
return None


class Rotate:
def __init__(self, data: bytes, radix: int):
self.data = data
self.radix = radix

def rot(self, algo):
result = []
for byte in self.data:
b = byte
for _ in range(self.radix):
b = algo(b)
result.append(b)
return b"".join([chr(x).encode() for x in result])

def rot_right_carry(self):
result = []
carryBits = 0

amount = self.radix % 8
for i in range(len(self.data)):
oldByte = self.data[i] & 0xFF # Ensure it's treated as an unsigned byte
newByte = (oldByte >> amount) | carryBits
carryBits = (oldByte & ((1 << amount) - 1)) << (8 - amount)
result.append(newByte)

result[0] |= carryBits
return b"".join([chr(x).encode() for x in result])

@staticmethod
def rotate_right(b):
bit = (b & 1) << 7
return (b >> 1) | bit

@staticmethod
def rotate_left(b):
bit = (b >> 7) & 1
return ((b << 1) | bit) & 0xFF

def rotate_left_carry(self):
result = bytearray(len(self.data))
carryBits = 0

amount = self.radix % 8
for i in range(len(self.data) - 1, -1, -1):
oldByte = self.data[i]
newByte = ((oldByte << amount) | carryBits) & 0xFF
carryBits = (oldByte >> (8 - amount)) & ((1 << amount) - 1)
result[i] = newByte

result[-1] |= carryBits

return b"".join([chr(x).encode() for x in result])
43 changes: 43 additions & 0 deletions tests/test_ctf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from chepy import Chepy

"""
general tests for CTF solvers
"""


def test_csaw_23_breakthevault():
assert (
Chepy(
"5346815611816381158830026000575759913046890410767282609674124748425112753245783703275530777684185849448083"
)
.to_base(16)
.from_hex()
.rotate_right(4)
.from_base64()
.o
== b"csawctf{w@11_ST_1s_n0t_n3ce$$@ry}"
)


def test_hero_v5_heap():
key = "c45c60232c9847e2"
payload = "kSDIsBFTYa3+aLqEpVLXtspdLse8WclEhbqGLiqvM6k="

c = (
Chepy(payload)
.from_base64()
.aes_decrypt(key=key, key_format="utf-8", mode="ECB")
)
assert c.o == b"Hero{D1G_1NT0_J4V4_H34P}"


def test_africe_23_own_reality():
k = ".__..._..__...._.___._...___._...__.__...__.._._._....__._._._..._...__..____.__._._._._.__.___..__._.__.__.___..__.____.___.___.__.___.._._____.__..._..__._.._.___._...___..__._._____..__..__..___.....__._...__.._._.__.._._.__...._..__._....___.._.__..._...__._....__..._..__.___.__.._._.__.._._..__.._..__..__..__..__...__._._.__...._..__..._..__..__.__..__..__..._..__.._...__...__.__...__.__...._..__.__..__..__...__..__..__.._...__.___._____._"
c = Chepy(k).find_replace("\\.", "0").find_replace("_", "1").from_binary()
assert c.o == b"battleCTF{Unknown_bits_384eea49b417ee2ff5a13fbdcca6f327}"


def test_springforward_23_hours_behind():
c = Chepy("vqkk{0vtg_b1um_e1tt_b3tt}")
c.rotate_bruteforce().dict_get_items().filter_list("nicc")
assert c.o == b"nicc{0nly_t1me_w1ll_t3ll}"
25 changes: 25 additions & 0 deletions tests/test_dataformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,28 @@ def test_unicode_escape():
Chepy(data).unicode_escape().o
== b"MMMMM\u200c\u200daaaaaaa\u200c\u200dssss\u200c\u200coooooonnnnnCCC\u200c\u200cCCCC"
)


def test_to_base():
assert Chepy(0).to_base().o == b"0"
assert Chepy(15295865526991442899).to_base(16).o == b"d445d4a7e477d3d3"


def test_from_base():
assert Chepy("d445d4a7e477d3d3").from_base(16).o == 15295865526991442899


def test_rotate_right():
assert Chepy("d445d4a7e477d3d3").from_hex().rotate_right(radix=4).o == b"MTMzNw=="
assert (
Chepy("d445d4a7e477d3d3").from_hex().rotate_right(radix=4, carry=True).o
== b"=D]J~G}="
)


def test_rotate_left():
assert Chepy("d445d4a7e477d3d3").from_hex().rotate_left(radix=4).o == b"MTMzNw=="
assert (
Chepy("d445d4a7e477d3d3").from_hex().rotate_left(radix=4, carry=True).o
== b"D]J~G}=="
)

0 comments on commit 1968a96

Please sign in to comment.