펜테스트짐


훈련을 통해 당신의 실력을 향상시켜보세요.



OS 명령 인젝션

모두 19명의 회원님이 완료했어요.


 실습 환경

  실습을 하시려면 로그인이 필요합니다.

이 훈련에서는 OS 명령 인젝션에 관한 기초적인 지식과 공격 원리를 배우고 명령 인젝션을 테스트하는 방법을 학습합니다. 또한 명령 인젝션에 취약한 실습 환경이 제공되므로 배운 기술을 직접 시험해볼 수 있으며 일부 실습은 이 훈련에서 다루지 않은 우회 기법을 사용해야 하므로 스스로 우회 기법을 찾아봐야 합니다.






OS 명령 인젝션이란?


OS 명령 인젝션(OS Command Injection 또는 Shell Injection)은 웹 애플리케이션이 구동 중인 서버의 운영체제상에서 임의의 명령을 실행하도록 하는 웹 취약점입니다. 웹 애플리케이션은 HTML 폼, HTTP 헤더, 쿠키, GET 파라미터 등을 통해 사용자로부터 데이터를 입력받아 시스템 (Shell)에 제공할 수 있습니다. 이 때 사용자가 제공하는 입력값에 대한 유효성 검증이 구현되어있지 않거나 안전하지 않은 방식으로 검증을 한다면 OS 명령 인젝션 공격에 취약할 수 있습니다. OS 명령 인젝션을 통해 실행되는 명령은 루트나 관리자 권한의 계정이 아닌 웹 애플리케이션을 구동하는 계정의 권한으로 명령이 실행되므로 일반적으로 시스템 전체를 손상시키지 못하지만 공격자가 권한 상승이나 다른 취약점과 연계할 수 있다면 더 많은 권한을 획득할 수 있으며 취약한 웹 애플리케이션을 장악하거나 모든 데이터를 손상시킬 수 있습니다. 


OS 명령 인젝션은 어떻게 동작할까?


웹 애플리케이션상에 구현된 일부 기능은 운영체제 및 파일시스템과 통신하기 위해 운영체제 명령을 실행하기도 합니다. 이와 같은 기능 구현을 위해 PHP, JAVA, PYTHON 등 대부분의 개발 언어들은 셸과 연동되는 내장함수를 제공하고 있습니다. 이러한 내장함수는 일반적으로 system(), exec()와 같은 함수이며, 셸 함수라 부르기도 합니다. 웹 애플리케이션은 바로 이러한 셸 함수를 활용해 시스템 셸로 운영체제 명령을 전달하고 실행 결과를 얻을 수 있게 됩니다. 하지만 단지 셸 함수를 사용한다는 이유만으로 OS 명령 인젝션에 취약한 것은 아닙니다. OS 명령 인젝션은 셸 함수를 통해 시스템 셸로 전달되는 명령에 사용자로부터 전달받은 입력값이 포함될 때 발생합니다. OS 명령 인젝션이 정상적으로 수행되기 위해서는 다음과 같은 조건이 충족되어야 합니다.


  1. 시스템 셸로 전달되는 명령에 사용자로부터 입력받은 데이터를 포함해야 합니다.
  2. 사용자 데이터는 이스케이프 또는 삭제 처리없이 정상적인 셸 명령어로 인식되어야 합니다.
  3. 웹 애플리케이션을 구동 중인 계정에 시스템 명령을 실행할 수 있는 권한이 있어야 합니다.


리눅스 환경에서 호스팅되고 있는 다음의 PHP 소스코드(ex.php)를 살펴봅시다.

<?php
echo "<p>filename 매개변수(GET)를 이용하여 조회할 파일의 이름을 입력하세요</p>";
$file = $_GET['filename'];
system("cat $file");
?>

위의 소스코드는 'filename'이라는 GET 파라미터의 값을 cat 명령의 인자로 사용하고 system() 함수를 통해 명령을 실행시키고 있습니다. 'filename' 파라미터에 정상적인 데이터가 입력된다면 이 소스코드는 개발자가 의도한대로 지정된 파일의 내용을 보여주는 동작을 수행할 것입니다. 

위 소스코드를 통해 조회할 수 있는 아래와 같은 hello.html 이라는 파일이 서버상에 존재한다고 가정합시다. 

<h2>Hello, Bug bounty club!</h2>

이 파일을 웹 브라우저에서 조회하면 다음과 같이 응답이 표시됩니다.


'filename' 파라미터로 전달된 hello.html 파일의 내용이 웹 브라우저에 표시됨


사용자가 입력한 hello.html이라는 데이터는 웹 애플리케이션 내부에서 아래의 과정을 통해 시스템 명령에 포함되게 됩니다. 


사용자 데이터(hello.html)를 사용해 셸 명령을 구성하고 cat hello.html 명령을 실행함


웹 애플리케이션에서 OS 명령이 어떤 방식으로 실행되는지 간단한 PHP 소스코드를 통해 살펴 보았습니다. 

이제 이 소스코드를 통해 실험을 해 볼 수 있습니다. 'filename' GET 파라미터는 사용자에 의해 직접 조작이 가능합니다. 즉, 정상적인 파일 이름이 아닌 어떤 다른 데이터로 얼마든지 변경할 수 있다는 의미입니다. 또한 소스코드를 보시면 아시겠지만 어떠한 보안 조치도 구현되어있지 않기 때문에 OS 명령 인젝션에 취약한 코드입니다. 그럼 ex.php 웹 페이지에서 OS 명령 인젝션을 해봅시다. OS 명령 인젝션을 수행하기 위해 사용되는 특수문자들이 있습니다. 먼저 어떤 특수문자들이 사용되는지 알아보죠. 아래의 표에 있는 특수문자들은 셸 인터프리터에서 어떤 특정한 기능을 수행하는 메타문자들이며 웹 애플리케이션에서 이러한 문자들에 대해 안전한 필터링이 구현되어있지 않다면 공격자는 메타문자를 이용해 기존의 명령 구문에 임의의 명령을 주입하고 실행시킬 수 있게 됩니다.

메타문자

기 능사용 예
cmd1 | cmd2왼쪽 명령의 결과를 오른쪽 명령의 입력으로 전달합니다.
$ whoami | grep 'b'
bbc
cmd1 && cmd2왼쪽 명령을 성공적으로 실행하면 오른쪽 명령을 실행합니다. 
$ whoami && pwd
bbc
/home/bbc
cmd1 || cmd2왼쪽 명령의 실행에 실패하면 오른쪽 명령을 실행합니다.
$ whoareyou || pwd
whoareyou: 명령을 찾을 수 없습니다.
/home/bbc
cmd1 ; cmd2한 줄에 여러 명령을 사용할 때 각각의 명령을 구분하기 위해 사용됩니다. (리눅스/유닉스 전용)
$ whoami; pwd
bbc
/home/bbc
`cmd`
백틱(`) 사이의 명령을 실행한 결과를 반환합니다. (리눅스/유닉스 전용)
$ echo `whoami`
bbc
$(cmd)$() 안의 명령을 실행한 결과를 반환합니다. (리눅스/유닉스 전용)
$ echo $(whoami)
bbc
cmd > /your_file왼쪽 명령의 결과를 오른쪽 파일에 기록합니다. (리눅스/유닉스 전용)
$ whoami > /path/to/file/whoami.txt
/your_file < cmd오른쪽 명령의 결과를 왼쪽 파일에 기록합니다. (리눅스/유닉스 전용)
$ /path/to/file/whoami.txt < whoami

OS 명령 인젝션에 사용되는 메타문자들


그럼 먼저 살펴보았던 '/ex.php?filename=hello.html' 뒤에 세미콜론(;)과 id 명령어를 추가하여 요청했다고 생각해봅시다. 요청 URL은 다음과 같을 것입니다.

http://www.example.com/ex.php?filename=hello.html;id

방금 전에 보았던 메타문자표에서 세미콜론(;)은 한 줄에 포함된 여러 개의 명령어를 구분한다고 배웠습니다. 위 요청이 있을 때 ex.php 소스코드의 처리 과정을 다시 살펴보면 보시다시피 마지막 단계에서 세미콜론(;)에 의해 명령어가 두 개로 구분되고 각 명령어들이 순차대로 실행되게 됩니다.


'filename' 파라미터에 추가된 ;id에 의해 명령이 분리되고 id 명령이 추가됨


요청에 대한 응답은 당연히 아래의 화면과 같이 id 명령 실행결과가 추가되어 나타나겠죠. 


OS 명령 인젝션으로 인해 id 명령도 함께 실행됨


세미콜론(;) 외에 앞서 소개된 다른 메타문자들도 각 기능에 맞게 구성한다면 임의의 명령을 주입할 수 있습니다. 만일 악의적인 공격자가 id 명령처럼 시스템에 직접적인 손상을 가하지 않는 무해한 명령어 대신 rm -rf /와 같은 명령을 주입한다면 웹 애플리케이션은 상당한 손상을 받게 됩니다. 또한 wget을 통해 웹셸과 같은 다른 악성 파일을 서버에 다운로드하여 후속공격을 하거나 조직 내 다른 시스템으로 공격을 확장할 수도 있습니다.  


OS 명령 인젝션에는 어떤 유형들이 있을까?


OS 명령 인젝션은 직접 명령 인젝션, 간접 명령 인젝션, 블라인드 명령 인젝션으로 구분될 수 있습니다.


직접 명령 인젝션 (Direct Command Injection)

앞서 OS 명령 인젝션의 동작 방식을 설명드릴 때 등장했던 예제(ex.php)처럼 사용자가 입력한 데이터가 OS 명령의 인자로 직접 포함되는 경우입니다. 


간접 명령 인젝션 (Indirect Command Injection)

파일이나 환경변수를 통해 웹 애플리케이션의 시스템 셸로 OS 명령이 전달되는 경우입니다. 보통 윈도우 시스템에서 나타나며 Forfiles, pcalua.exe 등과 같이 CMD를 사용하지 않고 명령을 호출할 수 있도록 지원하는 유틸리티를 통해 가능합니다. 


블라인드 명령 인젝션 (Blind Command Injection)

직접 명령 인젝션과 같이 사용자의 입력 데이터가 OS 명령에 포함되어 시스템 셸로 전달되지만 HTTP 응답 메시지에 명령의 실행 결과가 표시되지 않는 경우입니다. 일반적인 명령 인젝션처럼 주입된 명령이 즉각 HTTP 응답 메시지에 출력되면 취약점을 식별하기 쉽겠지만 실제로는 그렇지 않은 경우가 상당히 많습니다. 만일 이런 경우에 직접 명령 인젝션 기술만을 고집하며 응답에 명령 실행 결과가 출력되는지만 확인한다면 명령 인젝션 취약점을 눈 앞에 두고도 놓칠 수가 있습니다. 따라서 '시간 지연', '출력 리다이렉팅' 또는 '대역외(Out-Of-Band) 기술'과 같은 블라인드 명령 인젝션 기술이 주로 사용됩니다.


블라인드 명령 인젝션에 대해서는 아래의 훈련을 더 읽어보시길 바랍니다.

  더 읽어보세요.


OS 명령 인젝션을 예방 및 완화하려면


OS 명령 인젝션을 예방하기 위해 가장 좋은 방법은 웹 애플리케이션에서 system(), exec()와 같은 셸 함수를 사용하여 명령을 실행하지 않는 것입니다. 하지만 웹 애플리케이션을 통해 OS 명령을 실행해야 할 부득이한 이유가 있다면 가급적 사용자가 임의로 조작할 수 있는 입력값이 명령에 직접 포함되지 않도록 해야 합니다. 만일 사용자 입력을 OS 명령에 포함시켜야 하는 경우에는 사용자 입력에 대해 엄격한 검증을 수행해야 합니다. 이를 위해 가장 좋은 방식은 화이트리스트 방식으로 허용되는 입력값 목록을 만들고 허용되지 않는 입력값에 대한 필터링을 수행해야 합니다. 화이트리스트 방식의 필터링이 더 안전하지만 블랙리스트 방식을 사용해야 한다면 서버의 운영체제 종류에 따라 아래의 특수문자들에 대해 이스케이프 처리하거나 요청을 차단해야 합니다.

  • 윈도우의 경우:  ( ) < > & * ' | = ? ; [ ] ^ ~ ! . ” % @ / \ : + , ` 문자들 앞에 '^'를 추가하여 이스케이프하거나 요청을 차단합니다.
  • 리눅스 또는 유닉스의 경우: { } ( ) < > & * ' | = ? ; [ ] $ – # ~ ! . ” % / \ : + , ` 문자들 앞에 '\'를 추가하여 이스케이프하거나 요청을 차단합니다.


OS 명령 인젝션은 어떤 영향을 줄까?


OS 명령 인젝션 공격의 영향은 취약한 웹 애플리케이션이 구동되는 계정의 권한에 따라 사소한 것부터 심각한 수준에 이르기까지 매우 다양할 수 있습니다. 데이터베이스에 접근해 고객의 개인 정보 등 민감한 데이터를 훔칠 수 있으며 데이터를 임의로 조작하거나 삭제하는 등 데이터베이스를 손상시킬 수 있습니다. 또한 서버 또는 시스템을 제어하여 웹 애플리케이션의 내장된 모든 기능을 악용할 수 있습니다. 


OS 명령 인젝션 테스트 방법



Step 1. 셸 명령을 호출하는 기능 식별

웹 애플리케이션의 모든 기능을 사용하면서 셸 명령을 호출하는 기능을 찾습니다. 대표적인 예로 GET 파라미터값으로 파일 이름이 사용되는 진입점(엔드포인트) 등이 있습니다. 아래는 명령 인젝션 취약점이 존재할 수 있는 일반적인 파라미터 이름입니다.

cmd
exec
command
execute
ping
query
jump
code
reg
do
func
arg
option
load
process
step
read
function
req
feature
exe
module
payload
run
print

GET 파라미터가 아닌 폼 데이터, 쿠키나 HTTP 헤더를 통해서도 셸 명령으로 데이터가 전달될 수 있으므로 사용자 데이터가 전달될 수 있는 모든 입력 가능한 소스를 고려해야 합니다. 


Step 2. 운영체제(OS) 식별

OS 명령 인젝션에 사용되는 페이로드는 웹 애플리케이션이 구동되고 있는 운영체제의 커맨드라인 명령에서 유효해야만 합니다. 리눅스 계열과 윈도우 계열의 커맨드라인 명령은 유사하면서도 일부 차이가 있으므로 서버의 운영체제를 식별하는 것이 성공적인 OS 명령 인젝션 페이로드를 구성하는데 도움이 됩니다. 서버의 운영체제를 식별하는 방법은 매우 다양하게 존재하며, 관련된 자세한 내용은 OWASP WSTG의 Fingerprint Web Server 문서에 잘 정리되어 있으니 꼭 정독해보시기 바랍니다. 


Step 3. 테스트 문자열을 통한 응답 관찰

셸 명령을 호출하는 것으로 판단되는 진입점을 찾았다면 OS 명령 인젝션에 사용되는 셸 메타문자들과 기본 명령어들이 어떻게 처리되는지 확인해야 합니다. 아래와 같은 간단한 테스트 문자열을 사용할 수 있습니다.

abc;echo%20commandi;

만약 위 요청에 대한 응답으로 commandi 라는 문자열이 HTTP 응답에 출력된다면 OS 명령 인젝션에 취약하다고 판단할 수 있습니다. 하지만 대부분의 웹 애플리케이션은 기대와는 달리 매우 다양한 응답을 보여줄 것입니다. 웹 애플리케이션이 사용자 입력 유효성을 검사하는 방법과 오류를 처리하는 방법에 따라 테스터에게 유용한 응답을 반환하거나 어떠한 응답도 반환하지 않을 수 있습니다. 개발자에 의해 의도된 명백한 입력 유효성 검사 오류 메시지가 아닌 이상 잠재적으로 취약하다고 봐야 합니다. 계속해서 테스트 문자열을 수정하고 웹 애플리케이션의 응답을 관찰해가며 테스트해야 합니다.

fuzzdb에서 테스트 문자열로 사용할 수 있는 다양한 템플릿 모음을 확인할 수 있습니다.


Step 4. 개념증명(PoC)을 위한 실제 명령 사용

실제 공격에 사용될 수 있는 명령을 사용해 OS 명령 인젝션이 동작되는지 확인합니다. 유의하실 점은 rm, del, rmdir 등 시스템에 손상을 줄 수 있는 명령은 절대로 사용하시면 안됩니다. 사용하실 수 있는 명령은 whoami 또는 id와 같은 시스템에 무해한 명령입니다. 

abc;whoami;

whoami 명령의 결과가 응답에 반환된다면 OS 명령 인젝션에 취약함을 나타냅니다.


실습 문제 풀이


아래 내용을 읽기 전에 본 훈련의 가상 실습 환경을 생성하여 먼저 실습해보실 것을 권장드립니다. 또한 제시된 풀이는 문제를 해결하는 방법 중 하나일 뿐입니다. 제시된 풀이 방법 외에도 다른 방법으로 문제를 해결할 수 있으니 여러분의 창의력을 맘껏 발휘해보세요. 


Exercise 1

이 문제는 사용자가 항목을 선택하면 선택된 항목에 해당되는 파일명을 이용해 셸 명령을 실행합니다. 안일한 개발자는 OS 명령 인젝션 공격에 대해 어떠한 보호조치도 구현해놓지 않았습니다. whoami 명령을 실행시켜보세요.


Exercise 2

이 문제는 Exercise 1과 동일합니다만 다른 점이 있다면 개발자가 OS 명령 인젝션 공격에 주로 사용되는 명령어를 제거하여 공격을 방어했다는 점입니다. 방어를 우회하고 whoami 명령을 성공적으로 실행시켜보세요.


Exercise 3

이 문제를 해결하려면 웹 루트 디렉토리 안에 숨겨진 파일을 찾아 해당 파일의 내용을 확인해보세요. 당신은 숨겨진 파일을 찾기 위해 공백(Space) 문자를 사용해야 할 것입니다. 하지만 사용자 입력에 포함된 공백 문자와 공격에 주로 사용되는 명령어들(Exercise 2와 동일한 명령어들임)은 보안 필터에 의해 제거됩니다. 숨겨진 파일을 찾아 파일 내용을 웹 브라우저에 출력하세요.



참고 문헌