리버싱!

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}