리버싱!
juniorcrypt2025-올만에 reversing 풀이
남행이
2025. 7. 3. 17:26
평일에 CTF에 참여했는데 웹문제가 없어서 리버싱이라도 풀었다.
결과는 쉽게 나오기도 했고 llm성능이 굿(난이도가 올라감에 따라 바보가 될때가 있는데 바로 잡아주면 된다.)이라 올클
Reverse/Hybrid Encryption
import base64
def decrypt_flag():
# 암호화된 base64 문자열 (원본 코드에서 정확히 복사)
encrypted_base64 = "np2Z3p2c3s6YmZ3ezs2ZmM/Tnc7NmJmdz5yYm96cz8+Ym53Z3w=="
# 1단계: base64 디코딩
decoded_bytes = base64.b64decode(encrypted_base64)
print(f"Base64 디코딩 결과 (바이트): {list(decoded_bytes)}")
# 2단계: 각 바이트를 0xAA와 XOR 연산
xor_key = 0xAA
decrypted_bytes = bytes([byte ^ xor_key for byte in decoded_bytes])
print(f"XOR 복호화된 바이트: {list(decrypted_bytes)}")
# 3단계: 바이트를 문자열로 변환
inner_flag = decrypted_bytes.decode('utf-8')
print(f"복호화된 내용: {inner_flag}")
# 4단계: grodno{ 와 } 추가하여 완전한 플래그 구성
full_flag = f"grodno{{{inner_flag}}}"
print(f"완전한 플래그: {full_flag}")
return full_flag
def verify_flag(flag):
"""플래그가 올바른지 검증하는 함수 (원본 암호화 함수 재현)"""
if not flag.startswith("grodno{") or not flag.endswith("}"):
return False
# flag[7:-1] 부분 추출 (grodno{ 와 } 제외)
inner_part = flag[7:-1]
# 각 문자를 0xAA와 XOR
encoded = bytes([ord(c) ^ 0xAA for c in inner_part])
# base64 인코딩
encrypted = base64.b64encode(encoded).decode()
# 원본 암호문과 비교
expected = "np2Z3p2c3s6YmZ3ezs2ZmM/Tnc7NmJmdz5yYm96cz8+Ym53Z3w=="
print(f"암호화 결과: {encrypted}")
print(f"예상 결과: {expected}")
print(f"일치 여부: {encrypted == expected}")
return encrypted == expected
if __name__ == "__main__":
print("=== 플래그 복호화 ===")
flag = decrypt_flag()
print("\n=== 검증 ===")
is_correct = verify_flag(flag)
if is_correct:
print(f"\n✅ 정답: {flag}")
else:
print("\n❌ 오류가 발생했습니다.")
grodno{473t76td237tdg32ey7dg237e621t6ee217su} 딸깍
Reverse/Dynamic Key
```
import time
def dynamic_key():
return ((int(time.time()) % 256) ^ 0xFF) & 0x7F
def encrypt(s, key):
return bytes([(c + key) ^ (i * 2) for i, c in enumerate(s.encode())])
def decrypt(encrypted_bytes, key):
decrypted = []
for i, byte_val in enumerate(encrypted_bytes):
# 역산: (c + key) ^ (i * 2) = encrypted_val
# 따라서: c = (encrypted_val ^ (i * 2)) - key
original_char = (byte_val ^ (i * 2)) - key
# 바이트 값을 0-255 범위로 정규화
original_char = ((original_char % 256) + 256) % 256
decrypted.append(chr(original_char))
return ''.join(decrypted)
def is_printable_string(s):
"""출력 가능한 ASCII 문자(32-126)로만 구성되어 있는지 확인"""
return all(32 <= ord(c) <= 126 for c in s)
def solve_flag():
expected = b'\x74\xab\x9a\x62\x95\x6b\x9f\x81\x6b\x87\xbd\x99\x81\xb9\x93\x98\xb5\x80\x8d\xa9\x5b\x4a\xb1\x8e\xac\xa7\x9c\xb9\xa9\xa4\xa8\xb1\x39\xdc\xd7\x26\xd5\xea\xee\xdb\xc8\xc7\xca\xf5\x39\xc8\xc0\xcb'
print("모든 가능한 키(0-127)에 대해 시도 중...")
candidates = []
# 키는 0x7F(127)로 마스킹되므로 0-127 범위에서 모두 시도
for key in range(128):
try:
decrypted = decrypt(expected, key)
if is_printable_string(decrypted):
# 검증: 다시 암호화해서 원본과 일치하는지 확인
re_encrypted = encrypt(decrypted, key)
if re_encrypted == expected:
candidates.append({
'key': key,
'decrypted': decrypted,
'flag': f"grodno{{{decrypted}}}"
})
print(f"✓ 키 {key}: {decrypted}")
except:
continue
print(f"\n발견된 후보들: {len(candidates)}개")
if candidates:
# 가장 그럴듯한 후보 선택 (영문자, 숫자, 언더스코어가 많은 것)
best_candidate = max(candidates, key=lambda x: sum(1 for c in x['decrypted'] if c.isalnum() or c == '_'))
print(f"\n🎯 최적 후보:")
print(f"키: {best_candidate['key']}")
print(f"복호화 결과: {best_candidate['decrypted']}")
print(f"플래그: {best_candidate['flag']}")
return best_candidate['flag']
else:
print("적절한 플래그를 찾을 수 없습니다.")
return None
def check_flag(flag: str) -> bool:
"""원래 함수와 동일한 검증 함수"""
if not flag.startswith("grodno{") or not flag.endswith("}"):
return False
middle = flag[7:-1]
key = dynamic_key()
encrypted = encrypt(middle, key)
expected = b'\x74\xab\x9a\x62\x95\x6b\x9f\x81\x6b\x87\xbd\x99\x81\xb9\x93\x98\xb5\x80\x8d\xa9\x5b\x4a\xb1\x8e\xac\xa7\x9c\xb9\xa9\xa4\xa8\xb1\x39\xdc\xd7\x26\xd5\xea\xee\xdb\xc8\xc7\xca\xf5\x39\xc8\xc0\xcb'
return encrypted == expected
if __name__ == "__main__":
print("=== 플래그 복구 시도 ===")
flag = solve_flag()
if flag:
print(f"\n최종 플래그: {flag}")
# 직접 키 48로 검증 (우리가 찾은 정답)
test_flag = "grodno{Dyn4m1c_Key_is_Very_C0mplex_and_Inc0mprehens1ble}"
test_middle = test_flag[7:-1]
test_encrypted = encrypt(test_middle, 48)
expected = b'\x74\xab\x9a\x62\x95\x6b\x9f\x81\x6b\x87\xbd\x99\x81\xb9\x93\x98\xb5\x80\x8d\xa9\x5b\x4a\xb1\x8e\xac\xa7\x9c\xb9\xa9\xa4\xa8\xb1\x39\xdc\xd7\x26\xd5\xea\xee\xdb\xc8\xc7\xca\xf5\x39\xc8\xc0\xcb'
print(f"\n직접 검증 (키 48):")
print(f"테스트 플래그: {test_flag}")
print(f"암호화 일치: {test_encrypted == expected}")
if test_encrypted == expected:
print("✅ 검증 완료!")
else:
print("❌ 검증 실패")
else:
print("플래그를 찾을 수 없습니다.")
```
grodno{Dyn4m1c_Key_is_Very_C0mplex_and_Inc0mprehens1ble}
Reverse/Reverse Puzzle
grodno{774248325798612643250235431}
def puzzle(s: str, step: int) -> str:
"""주어진 puzzle 함수 (원본)"""
return s if step == 0 else puzzle(s[::2] + s[1::2], step - 1)
def reverse_puzzle(s: str) -> str:
"""puzzle 함수의 역함수"""
n = len(s)
result = [''] * n
puzzle에서 s[::2] + s[1::2]로 변환했으므로
앞쪽 절반은 원래 짝수 인덱스, 뒤쪽 절반은 원래 홀수 인덱스
even_count = (n + 1) // 2 # 짝수 인덱스 개수
원본 위치로 복원
for i in range(n):
if i % 2 == 0: # 짝수 인덱스
result[i] = s[i // 2]
else: # 홀수 인덱스
result[i] = s[even_count + i // 2]
return ''.join(result)
def solve_flag():
"""플래그를 역산으로 구하는 함수"""
target = '789603251257384214725442633'
print(f"목표 문자열: {target}")
print(f"길이: {len(target)}")
print()
5번 역변환 수행
current = target
for step in range(1, 6):
current = reverse_puzzle(current)
print(f"역변환 Step {step}: {current}")
print()
print(f"최종 결과: {current}")
검증
verification = puzzle(current, 5)
print(f"검증 - 5번 변환 후: {verification}")
print(f"목표와 일치: {verification == target}")
if verification == target:
flag = f"grodno{{{current}}}"
print(f"\n정답 플래그: {flag}")
return flag
else:
print("\n오류: 검증 실패")
return None
def check_flag(flag: str) -> bool:
"""원본 check_flag 함수"""
return False if not flag.startswith("grodno{") or not flag.endswith("}") else puzzle(flag[7:-1], 5) == '789603251257384214725442633'
실행
if name == "main":
# 플래그 구하기
solved_flag = solve_flag()
if solved_flag:
# 원본 함수로 최종 검증
print(f"\n원본 check_flag 함수로 검증: {check_flag(solved_flag)}")
print("\n" + "="50)
print("결과 요약:")
print(f"플래그: {solved_flag}")
print("="50)
Reverse/Little warm-up
(사진 옮기기 귀찮으니 디코 사진으로 대체한다)
Reverse/Compiled Python
Reverse/Elven Knot
Unsupported opcode: JUMP_IF_NOT_EXC_MATCH
import time
from os import path
from typing import List
DUO = 35
str_array = 'str(UNO) + str(DUO) + str(TRES)'
class Student:
def __init__(self = None, name = None, grade = None, average_marks = ('name', str, 'grade', int, 'average_marks', float)):
self.name = name
self.grade = grade
self.average_marks = average_marks
looks_like_secret = 'list(map(lambda x: str(int(x) ** 2), str_array))'
def __str__(self):
return f'''Student {self.name} from {self.grade} grade with average mark: {self.average_marks}'''
students: List[Student] = []
UNO = 124
def create_student():
name = input("Enter the student's name:\n")
grade = int(input("Enter the student's grade:\n"))
av_mark = float(input("Enter the student's average mark:\n"))
some_func = str('Try to use this: \n\n\n print(.join(secret variable)) and put that in grodno{}')
student = Student(name, grade, av_mark, **('average_marks',))
students.append(student)
def list_students():
for student in students:
print(student)
TRES = 56
def save_students():
Unsupported opcode: WITH_EXCEPT_START
pass
# WARNING: Decompyle incomplete
time.sleep(2)
# WARNING: Decompyle incomplete
다음 소스코드를 출력해준다.
해당결과 출력
UNO=124, DUO=35, TRES=56
↓
str_array = "124" + "35" + "56" = "1243556"
↓
각 자릿수에 map(lambda x: str(int(x) ** 2)) 적용:
1 → 1² = 1
2 → 2² = 4
4 → 4² = 16
3 → 3² = 9
5 → 5² = 25
5 → 5² = 25
6 → 6² = 36
↓
join: "1" + "4" + "16" + "9" + "25" + "25" + "36" = "14169252536"
grodno{14169252536}
Reverse/Turing Award
grodno{BarbAra-ShAfi--FranCes}
Frances Allen
Barbara Liskov
Shafi Goldwasser
역대 튜링 여성머시기 3명이름
xor 암호화이고 1대1매칭이라 그냥 브포돌리면 끝
Reverse/More than a warm-up
import hashlib
def maybeGetFlag(password):
md5_hash = hashlib.md5(password.encode()).hexdigest()
print(f"Step 1 - MD5 of password: {md5_hash}")
result_chars = []
for i, char in enumerate(md5_hash):
char_md5 = hashlib.md5(char.encode()).hexdigest()
first_char_ascii = ord(char_md5[0])
transformed_byte = (first_char_ascii & 0x7B) ^ i
result_chars.append(chr(transformed_byte))
print(f"Step 3.{i+1} - char='{char}', char_md5[0]='{char_md5[0]}', ascii={first_char_ascii}, & 0x7B = {first_char_ascii & 0x7B}, XOR {i} = {transformed_byte}, chr = '{chr(transformed_byte) if 32 <= transformed_byte <= 126 else repr(chr(transformed_byte))}'")
transformed_string = ''.join(result_chars)
print(f"Step 3 - Transformed string: {repr(transformed_string)}")
sha256_hash = hashlib.sha256(transformed_string.encode()).hexdigest()
print(f"Step 4 - SHA256 of transformed string: {sha256_hash}")
flag = f"grodno{{{sha256_hash}}}"
return flag
flag = maybeGetFlag("Filitoni2")
print(f"🚩 생성된 플래그: {flag}")
grodno{ea88897b06948c43c6c09ff49826e2b7ed2695b42f76223cb10484a4606b2114}