Created
November 22, 2024 22:18
-
-
Save 0awawa0/7e4cb6a697914bebee03dc9bef4f6708 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pwn | |
import json | |
import curses | |
CHECKS_COUNT = 15 | |
address = ("socket.cryptohack.org", 13422) | |
connection = pwn.connect(address[0], address[1]) | |
connection.recvline() | |
def createEncryptCommand() -> str: | |
return "{\"option\":\"encrypt\"}" | |
def createUnpadCommand(ct: str) -> str: | |
return "{\"option\":\"unpad\",\"ct\":\"" + ct + "\"}" | |
def createCheckCommand(message: str) -> str: | |
return "{\"option\":\"check\",\"message\":\"" + message + "\"}" | |
def splitBlocks(value: str, size: int): | |
blocks = [] | |
for i in range(0, len(value), size): | |
blocks.append(value[i: min(i + size, len(value))]) | |
return blocks | |
def joinToHex(array): | |
result = "" | |
for value in array: | |
result += "{0:02x}".format(value) | |
return result | |
def main(stdscr): | |
curses.resize_term(100, 300) | |
stdscr.refresh() | |
curses.start_color() | |
curses.curs_set(0) | |
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) | |
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) | |
curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_BLACK) | |
stdscr.clear() | |
stdscr.addstr(0, 0, "***************************************************{ AES CBC PKCS7 PADDING ORACLE ATTACK }***************************************************", curses.color_pair(2)) | |
stdscr.addstr(3, 0, "[*] Getting ciphertext", curses.color_pair(1)) | |
stdscr.refresh() | |
# Получаем зашифрованное сообщение | |
command = createEncryptCommand() | |
connection.sendline(command.encode()) | |
ciphertext = json.loads(connection.recvline().decode())["ct"] | |
stdscr.addstr(4, 4, "Received ciphertext: " + ciphertext, curses.color_pair(2)) | |
stdscr.refresh() | |
stdscr.addstr(6, 0, "[*] Preparing attack", curses.color_pair(1)) | |
stdscr.refresh() | |
# Разбиваем полученный шифротекст на инициализирующий вектор, блок 1 и блок 2 | |
blocks = splitBlocks(ciphertext, 32) | |
ivHex = blocks[0] | |
ct1Hex = blocks[1] | |
ct2Hex = blocks[2] | |
ivBytes = bytes.fromhex(ivHex) | |
ct1Bytes = bytes.fromhex(ct1Hex) | |
ct2Bytes = bytes.fromhex(ct2Hex) | |
ivArr = [] | |
ct1Arr = [] | |
ct2Arr = [] | |
for idx in range(0, 16): | |
ivArr.append(ivBytes[idx]) | |
ct1Arr.append(ct1Bytes[idx]) | |
ct2Arr.append(ct2Bytes[idx]) | |
stdscr.addstr(7, 4, f"IV hex: {ivHex}, IV bytes: {ivArr}", curses.color_pair(2)) | |
stdscr.addstr(8, 4, f"Block 1 hex: {ct1Hex}, Block 1 bytes: {ct1Arr}", curses.color_pair(2)) | |
stdscr.addstr(9, 4, f"Block 2 hex: {ct2Hex}, Block 2 bytes: {ct2Arr}", curses.color_pair(2)) | |
stdscr.addstr(11, 0, "[*] Attacking", curses.color_pair(1)) | |
stdscr.refresh() | |
knownPlaintext = "" | |
decryptedArr = [] | |
requestsCount = 0 | |
while len(decryptedArr) < 32: | |
knownLength = len(decryptedArr) | |
attackingIndex = knownLength | |
isFirstBlock = knownLength < 16 | |
paddingSize = knownLength % 16 + 1 | |
targetValue = paddingSize | |
knownPlaintextString = f"Knwon plaintext: {knownPlaintext}, " | |
stdscr.addstr(12, 0, " " * 200) | |
stdscr.addstr(12, 4, knownPlaintextString, curses.color_pair(2)) | |
stdscr.addstr(12, 4 + len(knownPlaintextString), f"Target padding: {targetValue}", curses.color_pair(2)) | |
stdscr.refresh() | |
replacingValues = [] | |
for idx in range(knownLength): | |
replacingValues.append(decryptedArr[idx] ^ targetValue) | |
replacedIvArr = ivArr.copy() | |
replacedCt1Arr = ct1Arr.copy() | |
for idx in range(knownLength): | |
if idx < 16: | |
if isFirstBlock: | |
replacedCt1Arr[len(replacedCt1Arr) - idx - 1] = replacingValues[idx] | |
else: | |
replacedIvArr[len(replacedIvArr) - (idx - 16) - 1] = replacingValues[idx] | |
stdscr.addstr(13, 4, "Sending values: ", curses.color_pair(2)) | |
stdscr.refresh() | |
attackingValue = 0 | |
checks = 0 | |
candidates = [] | |
while attackingValue < 256: | |
stdscr.addstr(14, 0, " " * 200) | |
if isFirstBlock: | |
replaceIndex = len(replacedCt1Arr) - attackingIndex - 1 | |
replacedCt1Arr[replaceIndex] = attackingValue | |
lineOffest = 8 | |
stringBeforeReplacedIdx = f"IV: {joinToHex(replacedIvArr)} Block 1: {joinToHex(replacedCt1Arr[:replaceIndex])}" | |
stdscr.addstr(14, lineOffest, stringBeforeReplacedIdx, curses.color_pair(2)) | |
lineOffest += len(stringBeforeReplacedIdx) | |
replacedCt1ValueString = "{0:02x}".format(replacedCt1Arr[replaceIndex]) | |
stdscr.addstr(14, lineOffest, replacedCt1ValueString, curses.color_pair(1)) | |
lineOffest += len(replacedCt1ValueString) | |
stringAfterReplacedIdx = f"{joinToHex(replacedCt1Arr[replaceIndex + 1:])} Block 2: {joinToHex(ct2Arr)}" | |
stdscr.addstr(14, lineOffest, stringAfterReplacedIdx, curses.color_pair(2)) | |
else: | |
replaceIndex = len(replacedIvArr) - (attackingIndex - 16) - 1 | |
replacedIvArr[replaceIndex] = attackingValue | |
lineOffest = 8 | |
stringBeforeReplacedIdx = f"IV: {joinToHex(replacedIvArr[:replaceIndex])}" | |
stdscr.addstr(14, lineOffest, stringBeforeReplacedIdx, curses.color_pair(2)) | |
lineOffest += len(stringBeforeReplacedIdx) | |
replacedIvArrValueString = "{0:02x}".format(replacedIvArr[replaceIndex]) | |
stdscr.addstr(14, lineOffest, replacedIvArrValueString, curses.color_pair(1)) | |
lineOffest += len(replacedIvArrValueString) | |
stringAfterReplacedIdx = f"{joinToHex(replacedIvArr[replaceIndex + 1:])} Block 1: {joinToHex(ct1Arr)}" | |
stdscr.addstr(14, lineOffest, stringAfterReplacedIdx, curses.color_pair(2)) | |
combinedValue = replacedIvArr + replacedCt1Arr | |
if isFirstBlock: | |
combinedValue += ct2Arr | |
combinedHex = joinToHex(combinedValue) | |
command = createUnpadCommand(combinedHex) | |
stdscr.addstr(15, 8, " " * 300) | |
stdscr.addstr(15, 8, f"Command: {command}", curses.color_pair(2)) | |
connection.sendline(command.encode()) | |
requestsCount += 1 | |
stdscr.addstr(17, 0, " " * 300) | |
stdscr.addstr(17, 8, f"Requests: {requestsCount}, Attacking value: {attackingValue}", curses.color_pair(2)) | |
stdscr.refresh() | |
answer = connection.recvline().decode() | |
result = json.loads(answer)["result"] | |
if result == True: | |
foundX = attackingValue ^ targetValue | |
if isFirstBlock: | |
plaintextChar = chr(foundX ^ ct1Arr[len(ct1Arr) - knownLength - 1]) | |
else: | |
plaintextChar = chr(foundX ^ ivArr[len(ivArr) - (knownLength - 16) - 1]) | |
isCorrect = plaintextChar in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] | |
if not isCorrect: | |
checks = 0 | |
attackingValue += 1 | |
if attackingValue > 255: | |
exit(1) | |
stdscr.refresh() | |
else: | |
if checks < CHECKS_COUNT: | |
checks += 1 | |
else: | |
knownPlaintext = plaintextChar + knownPlaintext | |
decryptedArr.append(foundX) | |
attackingValue = 1000 | |
else: | |
checks = 0 | |
attackingValue += 1 | |
stdscr.refresh() | |
stdscr.refresh() | |
command = createCheckCommand(knownPlaintext) | |
connection.sendline(command.encode()) | |
answer = connection.recvline().decode() | |
stdscr.addstr(19, 0, f"Answer: {answer}", curses.color_pair(3)) | |
stdscr.refresh() | |
while True: | |
pass | |
curses.wrapper(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment