| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- ADsP시험
- OpenClaw
- randzzz
- AWS
- Simple Crack Me
- adsp독학
- Juice Shop
- OWASP TOP 10
- Pacu
- ADsP교재
- 라운드로빈
- ADsP
- ADsP책추천
- dreamhack
- 운영체제
- rev-basic-4
- wargame
- HRRN
- ADsP합격
- rev-basic-3
- 병행성
- id pool
- user pool
- OS
- 시나공ADsP
- 세그먼테이션
- 26년 1회
- cloudgoat
- 교체 정책
- writeup
- Today
- Total
chon
[Dreamhack] Happy New Year! 본문
[ 문제 링크 | https://dreamhack.io/wargame/challenges/2650 ]
이 문제는 진짜 조심해야 해요. 근데 일단 전 다운 받아서 풀었어요.

[ 문제 설명 ]

문제에 대한 설명은 딱히 없어요! 새해 복 많이 받으세요.
[ 문제 풀이 ]
프로그램을 실행하면 "Input : "이라는 메시지와 함께 입력을 받고 입력값을 검증하여 correct나 wrong을 출력합니다.

- IDA 디컴파일
IDA로 코드를 분석해보면 main 함수에서 다음과 같은 핵심 로직을 발견할 수 있습니다.

printf("Input : ");
__isoc99_scanf("%59s", s);
if ( strlen(s) == 59 )
{
strcpy(dest, s);
for ( i = 0; i <= 58; ++i )
dest[i] ^= *((_BYTE *)v5 + i);
if ( !strcmp(dest, "Please_input_the_correct_new_year_greetings_at_this_problem") )
printf("Correct! Flag is DH{%s}\n", s);
else
puts("Wrong!");
}
else
{
puts("Wrong!");
}
return 0;
}
코드를 해석하면
1. 사용자에게 59글자의 (s) 문자열을 입력받습니다.
2. 이 입력 문자열(s)을 어딘가에 있는 59바이트짜리 비밀키(Secret Key)와 한 글자씩 XOR 연산을 합니다.
3. XOR 연산이 끝난 결과가 특정 문자열("Please_input_the_correct...")과 일치하는지 확인합니다.
4. 일치하면 우리가 입력한 59글자가 바로 플래그라고 알려주며 "Correct!"를 출력합니다.
dest[i] ^= *((_BYTE *)v5 + i);
C 언어에서 ^= 연산자는 비트와이즈 XOR 할당 연산자입니다.
이는 dest[i] = dest[i] ^ *((_BYTE *)v5 + i); 와 동일한 의미로 이 연산자 자체가 비트 XOR 연산을 수행한다는 것을 명확하게 알려줍니다.
v5 배열은 40바이트인데, for문은 59번이나 돌면서 v6까지 보는 이유는 *((_BYTE *)v5 + i) 코드에 있습니다.
C언어의 '형 변환(Type Casting)' 문법으로 v5의 메모리 주소에서 i칸 만큼 앞으로 가서, 거기에 있는 딱 한 칸의 값만 가져오라는 뜻입니다.
역연산 코드
secret_key = bytes([
0x18, 0x0D, 0x15, 0x11, 0xA1, 0xA0, 0x13, 0xC3,
0x19, 0x2F, 0x47, 0x44, 0x6D, 0x42, 0x37, 0x2C,
0x00, 0x0F, 0x00, 0x04, 0x17, 0x3A, 0x1A, 0x1B,
0x2A, 0x31, 0x04, 0x19, 0x3B, 0x26, 0x01, 0x13,
0x17, 0x3E, 0x0A, 0x1A, 0x04, 0x06, 0x1F, 0x36,
0x02, 0x02, 0x07, 0x2C, 0x3E, 0x1C, 0x3E, 0x04,
0x18, 0x10, 0x2C, 0x2B, 0x1F, 0x15, 0x0A, 0x16,
0x04, 0x00, 0x1F
])
target = b"Please_input_the_correct_new_year_greetings_at_this_problem"
flag = bytes(t ^ k for t, k in zip(target, secret_key))
print(f"DH{{{flag.decode()}}}")
어셈블리 코드에서 알아낸 정확한 값들을 사용해 59바이트짜리 완전한 비밀키를 만들고, 위에서 설명한 XOR연산을 적용하면 우리가 찾던 정답이 나옵니다.
리틀 엔디언이란, 숫자의 가장 낮은 자리(가장 오른쪽) 바이트부터 메모리에 먼저 저장하는, 즉 '거꾸로 저장'하는 방식입니다.
이 원리를 이 문제의 8바이트(QWORD) 값인 0xC313A0A11150D18에 적용했습니다.
1. 먼저 8바이트 숫자를 바이트 단위로 나눕니다.
> C3 13 A0 A1 11 15 0D 18
2. 리틀 엔디언 규칙에 따라, 오른쪽 바이트부터 순서대로 메모리에 나열합니다.
> 18 0D 15 11 A1 A0 13 C3
이것이 바로 아래 역연산 코드에 있는 secret_key의 첫 8바이트가 0x18, 0x0D, 0x15, ... 로 시작하는 이유입니다.
나머지 숫자들도 모두 이와 같은 방식으로 변환하여 59바이트 길이의 완전한 secret_key를 조합한 것입니다.
따라서 XOR 연산(플래그 = 목표 문자열 ^ 비밀키)으로 플래그를 얻을 수 있습니다.
'Reversing > Dreamhack Write up' 카테고리의 다른 글
| [Dreamhack] Recover (0) | 2026.01.16 |
|---|---|
| [Dreamhack] Simple Crack Me Write up (0) | 2025.12.14 |
| [Dreamhack] randzzz Write up (0) | 2025.11.20 |
| [Dreamhack] rev-basic-4 Write up (1) | 2025.11.18 |
| [Dreamhack] rev-basic-3 Write up (0) | 2025.11.02 |