소프트웨어 개발보안 가이드 분석(2021) : 운영체제 명령어 삽입

운영체제 명령어 삽입

https://owasp.org/www-community/attacks/Command_Injection

운영체제 명령어 삽입의 경우 적절한 검증 절차를 거치지 않아 공격자가 악의적인 명령어를 애플리케이션의 입력 폼에 삽입하여 실행하도록 유도하는 공격 기법입니다. 이러한 공격은 주로 웹 애플리케이션에서 발생하며, 사용자가 입력한 데이터를 통해 명령어가 동적으로 생성되어 실행되는 경우에 발생할 수 있습니다. 이를 통해 공격자는 시스템에 대한 완전한 제어권을 획득하거나 기밀 정보를 탈취할 수 있는 치명적인 보안약점입니다.

※ 웹 애플리케이션에서 system(), exec()와 같은 시스템 명령어를 실행할 수 있는 함수를 제공하며 사용자 입력값에 대한 필터링이 제대로 이루어지지 않을 경우 공격자가 시스템 명령어를 호출할 수 있습니다.

운영체제 명령어 삽입 공격 과정

 

  1. 명령어 삽입: 공격자는 웹 애플리케이션의 입력 폼이나 매개 변수를 통해 악성 명령어를 삽입합니다.
  2. 명령어 실행: 삽입된 악성 명령어는 애플리케이션에서 동적으로 실행됩니다.
  3. 시스템 침투: 공격자가 악성 명령어를 통해 시스템에 대한 접근 권한을 획득하거나 기밀 정보를 탈취할 수 있습니다.

취약한 웹 어플리케이션의 예

1	BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
2	String InputData = reader.readLine();
3	Process process = Runtime.getRuntime().exec(InputData);
  • 1행 : reader 변수에 표준 입력(System.in)에서 데이터를 읽어올 수 있게 합니다.
  • 2행 : 입력 폼에서 입력한 데이터를 InputData로 가져옵니다. 공격자는 이곳에 악의적인 명령어를 입력합니다.
  • 3행 : 외부에서 입력한 명령어 문자열을 실행하기 위해 'Runtime.getRuntime().exec()'를 사용하여 실행합니다.
  • 입력 폼에서 입력한 데이터(InputData)에 대한 검증을 하지 않기 때문에 공격자들이 삽입한 명령어가 실행 가능합니다.

시큐어 코딩 적용 방법

  • 입력값 검증
1	public static void main(String[] args) {
2	   Scanner scanner = new Scanner(System.in);
3	   String InputData = scanner.nextLine();
4	   if (CommandSafe(InputData)) { // 입력값 허용 } 
5	   else {  // 입력값 불허용  }
6	   }
7	
8	private static boolean CommandSafe(String InputData) 
9	       { return !InputData.contains("|") && !InputData.contains(";") 
                     && !InputData.contains("&") && !InputData.contains(":") 
                     && !InputData.contains(">") && !InputData.contains(">>"); }
  • 2, 3행 : InputData 변수를 통해 입력값을 받습니다.
  • 4행 : 입력값에 운영체제 명령을 실행할 수 있는 문자열이 없을 경우 동작합니다.
  • 5행 : 입력값에 운영체제 명령을 실행할 수 있는 문자열이 있을 경우 적절히 통제해야 합니다.
  • 8행 : 입력값에 대해 운영체제 명령을 실행할 수 있는 문자열(;, &, |, >, >>, :)을 필터링합니다.
  • 입력값에 대해 유효성 검사를 수행하여 운영체제 명령을 실행할 수 있는 문자열을 필터링하여 다른 일반 문자로 교체하거나 위의 코드처럼 허용 혹은 불허용의 경우로 나누어 운영체제 명령어 삽입에 대응할 수 있습니다.

  • 화이트리스트 기반 명령어 필터링
1	public static void main(String[] args) {
2	   String[] allowedCommands = {"ls", "cd", "mkdir", "rm", "pwd"};
3	   Scanner scanner = new Scanner(System.in);
4	   String userInput = scanner.nextLine().trim(); 
5	   if (CommandAllowed(userInput, allowedCommands)) {
6			//허용 된 명령어
7	   } else {
8			//허용되지 않은 명령어
9	        }
10	    }
11	private static boolean CommandAllowed(String userInput, String[] allowedCommands) {
12	        for (String allowedCommand : allowedCommands) {
13	            if (userInput.equals(allowedCommand)) {   return true;  }
14	        }
15	        return false;
16	    }
17	}

 

  • 2행 : 허용 가능한 운영체제 명령어를 리스트로 정의해 놓습니다.
  • 5~10행 : 입력값에 허용되는 명령어는 실행하고, 허용되지 않은 명령어는 통제합니다.
  • 11~16행 : 입력값에 허용되는 명령어가 존재하는지 비교하여 판별합니다.
  • 부득이하게 명령어가 입력값을 통해 실행되는 경우에 사용할 수 있는 대응방법입니다.
  • 허용 가능한 명령어를 미리 리스트로 지정하여 해당 명령어만 실행이 가능하도록 하여 운영체제 명령어 삽입에 대응할 수 있습니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 크로스사이트 스크립트(XSS)
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 위험한 형식 파일 업로드