오래 못 할 짓 하지 않기

[ 컴퓨터 보안 ] 4. Capability Leaking / Environment Variable 본문

3학년 2학기/컴퓨터 보안(Computer Security)

[ 컴퓨터 보안 ] 4. Capability Leaking / Environment Variable

쫑알bot 2024. 9. 10. 20:05
728x90

(참고) https://juyeong-lee.tistory.com/26

 

우리는 프로그램을 EUID로 실행시킨다.

- EUID : 특정 프로세스를 실행할 때 해당 프로세스에게 받는 권한

- RUID : 특정 프로세스를 실행할 때 프로세스가 인식하는 내 권한

 

ex) 프로그램 A 를 실행할 땐 root 권한으로 실행하게 되어있다.

      Alice가 이 프로그램을 시켰을 때 EUID , RUID는? 

➡️ EUID = root , RUID = Alice

 

EUID : 프로그램이 실행될 때 적용되는 권한

RUID : 실행한 사용자의 권한 및 ID

 

 


Capability Leaking

 

특정 프로그램을 실행할 때 권한을 얻게 되는 게 Set-UID의 기능이다.

그럼 프로그램 실행이 끝났을 땐 어떻게 되는가?

권한이 제대로 반환되어야 한다.

 

하지만, 그 권한이 제대로 반환되지 않았을 때, 즉 아이언맨 슈트를 입고 임무를 완수한 뒤에도

계속 슈트를 입고 활동하는 것과 같아진다.

 

악의적으로 사용하면 관리자의 권한으로 모든 것을 할 수 있다.

 

아래 예시를 보자

 

fd = open("/etc/zzz", O_RDWR | O_APPEND);
if (fd == -1) {
    printf("Cannot open /etc/zzz\n");
	exit(0); 
}


// Print out the file descriptor value
printf("fd is %d\n", fd);


// Permanently disable the privilege by making the
// effective uid the same as the real uid
setuid(getuid());


// Execute /bin/sh
v[0] = "/bin/sh"; v[1] = 0;
execve(v[0], v, 0);

 

 

이 프로그램에 대해 다음과 같은 Shell command를 사용한다고 하자.

 

파일에 echo(aaaaaa) 는 되지 않지만

File Descriptor에 쓰는 것(ccccc)는 되는 상황

 

우선 이 프로그램을 실행시키는 것에 대해서는 우리가 root 권한을 가질 수 있다.

하지만 write 하는 경우에는 root 권한이 없다.

 

1) 이를 적용해보면 fd를 여는 건 root 권한이므로 문제없다.

fd에게 문자열을 보내는 것 또한 문제가 없다.

 

2) 하지만 파일에 직접 쓰는 것은 우리에게 권한이 없다.

따라서 Permission denied 메시지가 나온다.

 


 

int main(int argc, char *argv[]){

char *v[3]; 
char *command; 
if(argc < 2) {
    printf("Please type a file name.\n");
	return 1; 
}

v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = NULL; 

command = malloc(strlen(v[0]) + strlen(v[1]) + 2); 
sprintf(command, "%s %s", v[0], v[1]);

system(command);
return 0 ;

}

해당 프로그램을 실행시킬 때 넣은 인자값들로
System command를 실행시키는 프로그램이다.

 

 

 

input을 code로 만드는 방법이다.

input 안에 ; 을 넣어서 그 앞을 명령어인 것처럼 넣어서

입력을 명령어로 바꿀 수 있는 것이다.

 

 

이와 같은 걸로는 XSS , SQL Injection 이 있다.

 

 

 

안전하게 하려면 Command 말고 execve로 하는 게 낫다.

그래야 중간에 명령어인 걸로 착각해서 자르지 않고

그 문자열 채로 읽어내기 때문에

 

가장 중요한 건

Data와 Code가 절대 섞이지 않게 해라

➡️ Data가 들어갈 자리에는 Data로 / Code가 들어갈 자리에는 Code로 인식하게 해라

 

 


Environment Variable

 

: 프로세스가 동작하는 데에 영향을 미치는 동적인 변수

 

위와 같이 사용할 수 있다.

 

프로세스가 환경변수를 가지게 되는 방법은 2가지가 있다.

 

- 부모 프로세스로부터 fork()

- execve() system call

 

여기에서 인자로 envp를 받을 수 있다.

 

extern char ** environ;

void main(int argc, char* argv[], char* envp[])
{
	int i = 0; char* v[2]; char* newenv[3]; 
    if (argc < 2) return;
    
    // Construct the argument array
    v[0] = "/usr/bin/env";  v[1] = NULL;
    
   	// Construct the environment variable array
	newenv[0] = "AAA=aaa"; newenv[1] = "BBB=bbb"; newenv[2] = NULL;
    
    switch(argv[1][0]) {
        case '1': // Passing no environment variable.
   	    	execve(v[0], v, NULL);
        case '2': // Passing a new set of environment variables.
        	execve(v[0], v, newenv);
        case '3': // Passing all the environment variables.
            execve(v[0], v, environ);
        default:
            execve(v[0], v, NULL);
     }
   }

 

 

 

 

 

 

3번째 명령어에서 ' LOGNAME =bob ' 가 

5번째 명령어에서 반영이 안 된 이유 : 쉘 변수라서 환경 변수로 안 들어감

 

➡️ 반영 시키기 위해서는 ' export {변수 명} = { 변수 값 } ' 를 써야한다.

➡️ 쉘 변수를 해제하려면 ' unset { 쉘 변수명 } '

 

 

(출처)

한동대학교 고윤민교수님 - 컴퓨터보안