Linux system() 문제점

김수빈·2021년 11월 19일
0

Linux

목록 보기
2/3
post-thumbnail

system 함수

#include <stdlib.h>
int system(const char *command);

system()은 command를 입력으로 받아 fork()를 통해 자식을 생성하고, 다음과 같은 execl 함수로 shell command를 실행시키는 함수다.

execl("/bin/sh", "sh", "-c", command, (char *) NULL);

command가 실행되는 동안 SIGCHLD가 blocked 되고, SIGINTSIGQUIT가 무시된다.

command가 NULL인 경우, system()은 shell이 사용가능한지 상태를 반환한다.

system()fork, execl, waitpid 호출의 세부사항을 처리해주며, 필요한 신호들을 조정해줌으로써, 단순성과 편리성을 제공한다.


system 함수의 문제점

  1. 쉘을 실행하는 프로세스를 생성하고 쉘을 실행하려면 추가적인 시스템 콜이 필요하기 때문에 비효율적이다.
  1. command 실행 중에 SIGINTSIGQUIT가 무시됨으로써, loop 에서 system을 호출하는 경우, 프로그램이 중단되지 않을 수 있다는 문제점이 있다.
    while(something){
        int ret = system("foo");
        //If Child Process Terminated by a Signal
        if(WIFSIGNALED(ret) && \
        //The Signal is SIGINT or SIGQUIT
        TERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)
            break;
    }

예시 코드 : loop 내에서 system을 호출하고 signal 발생으로 loop를 빠져나가는 경우

  1. pthread_atforkfork()를 호출할 때, 실행할 fork handler를 선언하는 함수로, 이때 지정된 fork handler는 system() 실행 중에 호출되는지의 여부는 지정되지 않았으며, glibc 구현에서는 handler는 호출되지 않는다.
	#include <pthread.h>

	int pthread_atfork(void (*prepare)(void) ,

	void (*parent)(void) , void (*child)(void));

  1. 쉘 명령이 127로 종료되는 경우(“command not found”), 자식 프로세스에서 쉘을 실행할 수 없는 경우와 구별이 불가하다.
  1. system 함수 사용 시, 일부 환경 변수에 대한 비정상적인 값이 시스템 무결성을 파괴하는 데 사용될 수 있기 때문에 setuid, setgid 기능이 있는 프로그램과 같이 권한이 있는 프로그램에서는 사용을 금한다.

환경 변수를 조작하는 예시는 아래와 같다.

  • sh 및 bash 쉘은 IFS (Internal Field Separator) 변수를 사용하여 명령 행 인수를 구분하는 문자를 판별하는데, IFS를 비정상적인 값으로 설정하여 system()을 호출하는 경우 안전한 호출이 파괴될 수 있다.
  • 임의의 프로그램이 권한이 있는 프로그램으로 실행되도록 PATH를 조작 할 수 있다. 이에 대해, /bin/sh bash version 2는 system() 시작 시 권한을 삭제하기 때문에, system()은 bash 2인 시스템에서 권한이 있는 프로그램을 실행할 경우 제대로 동작하지 않는다.


popen(), system() 은 명령 쉘을 호출함으로써 구현되며,
execlp()execvp()도 filename에 ‘/’가 붙지 않은 경우에 쉘의 동작을 복제한다.

이때 쉘 메타 문자에 영향을 받게 되는데, 이로 인해 예기치 않은 저수준 루틴을 호출할 수 있어, 많은 가이드라인에서는 popen(), system(), execlp(), execvp() 사용을 전부 피하고, 프로세스를 생성하려고 할 때는 C에서 직접적으로 execve()을 사용하도록 제안하고 있다 [2].

또한, 리눅스와 유닉스를 위한 Secure Programming 가이드 라인 [3]에서는
system()은 쉘을 사용하여 명령을 계속 주입할 수 있어, Command Injection 등 비정상적인 사용 시도가 발생할 가능성이 있기 때문에,
최소한 execve()를 사용할 수 있는 경우에 system()을 사용하지 말라고 언급하고 있다.

권한이 있는 프로그램에서 system() 을 사용할 때 예기치 않은 쉘 명령, 명령 옵션이 실행되지 않도록, command 실행의 일부로 사용되는 사용자 입력은 신중히 처리해야 한다.

system()의 대안 : execve()

execve()는 쉘을 실행하는 프로세스를 생성하고, command를 실행하는 system()과는 달리, 현재 프로세스에서 실행 중인 프로그램을 새 프로그램으로 대체하는 함수다.

기존 프로세스가 새로운 프로그램을 실행할 수 있도록 정렬한다.

  #include <unistd.h>

  int execve(const char *pathname, char *const argv[],
                          char *const envp[]);

execve()system()과 달리 전체 쉘 인터프리터를 사용하지 않으므로 Command Injection 공격에 취약하지 않다. 또한 입력이 args 배열에 통합되어 execve ()에 인수로 전달되므로 의도하지 않은 시스템 실행을 방지할 수 있다.

  #include  <stdlib.h>

  int  main(void)
  {
      char *command = "/bin/ls -a";
      system(command);
      return  0;
  }

system() 을 이용한 ls -a 커맨드 실행


  #include  <unistd.h>

  int  main(void)
  {

      char *command[] = {"ls", "-a", 0};
      char *envp[] = {0};

      execve("/bin/ls", command, envp);

      return  0;
  }

execve()을 이용한 ls -a 커맨드 실행


system()의 대안 : exec 함수 계열

exec 함수 계열은 현재 프로세스 이미지를 새로운 프로세스 이미지로 대체한다.

#include <unistd.h>

extern char **environ;

int execl(const char *pathname, const char *arg, ...
			/* (char *) NULL */);

int execlp(const char *file, const char *arg, ...
			/* (char *) NULL */);

int execle(const char *pathname, const char *arg, ...
			/*, (char *) NULL, char *const envp[] */);

int execv(const char *pathname, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[],
				char *const envp[]);

하지만 exec 함수 계열 중 execlp(), execvp(), execvpe() 함수는 지정된 파일 이름에 ‘/’ 가 포함되어 있지 않으면 실행 파일을 검색할 때 쉘의 동작을 복제하기 때문에 PATH 환경 변수 값이 신뢰할 수 있을 경우에만 파일 이름에 ‘/’ 문자 없이 사용해야 한다.

The Unix Secure Programming FAQ [2] 에 의하면 쉘을 호출할 위험이 있는 execlp(), execvp()의 사용을 피하고 가급적 execve()를 직접적으로 사용하라고 제안하고 있다.

참고 문헌

  1. system(3)-Linux manual page
  2. Peter Galvin (August 1998), The Unix Secure Programming FAQ, SunWorld
  3. David A. Wheeler(2003), Secure Programming for Linux and Unix HOWTO
  4. execve()-Linux manual page
  5. Injection Prevention-Embedded Application Security Best Practices
  6. exec()-Linux manual page,

로고 출처

Icons made by Freepik from www.flaticon.com
profile
열심히 배우는 내가 되자

0개의 댓글