[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.
- 서론
- Command Injection
- Q&A
- 마치며
프로그램을 만들다보면 스스로 코드를 작성하기보다, 이미 설치된 소프트웨어를 사용하는 것이 필요한 경우가 있습니다.
C/C++으로 프로그래밍할 때는 system
함수를 사용하기도 합니다.
이 함수는 함수에 전달된 인자를 셸 프로그램에 전달해 명령어를 실행시킵니다.
system("cat /etc/passwd")
를 호출하면, 셸 프로그램으로 cat /etc/passwd
를 실행한 것과 같습니다.
이러한 이점도 있지만, 그와 동시에 함수의 인자를 셸 명령어로 전달한다는 점에서 치명적인 취약점으로 이어지기도 합니다.
- 인젝션(Injection)
: 악의적인 데이터를 프로그램에 입력하여, 이를 시스템 명령어, 코드, 데이터베이스 쿼리 등으로 실행되게 하는 기법
: 이 중, 사용자의 입력을 시스템 명령어로 실행하게 하는 것을 'Command Injection'이라고 함
Command Injection은 명령어를 실행하는 함수에 사용자가 임의의 인자를 전달할 수 있을 때 발생합니다.
사용자가 입력한 임의 IP에 ping을 전송하고 싶다면 system("ping [user-input]")
,
임의 파일을 읽고 싶다면 system("cat [user-input]")
등의 형태로 system
함수를 사용할 수 있습니다.
하지만, 이러한 호출 과정에서 사용자의 입력을 제대로 검사하지 않으면 임의 명령어가 실행될 수도 있습니다.
이는 리눅스 셸 프로그램이 지원하는 여러 메타 문자 때문입니다.
system
함수는 셸 프로그램에 명령어를 전달하여 실행합니다.
셸 프로그램은 다양한 메타 문자를 지원하기도 하죠.
Meta 문자 | 설명 | Example |
---|---|---|
$ | 셸 환경변수 | $ echo $PWD /homeTheori |
&& | 이전 명령어 실행 후 다음 명령어 실행 | $ echo hello && echo theori hello theori |
; | 명령어 구분자 | $ echo hello ; echo theori hello theori |
| | 명령어 파이핑 | $ echo id | /bin/sh uid=1001(theori) ... |
* | 와일드 카드 | $ echo .* ... |
` | 명령어 치환 | $ echo `echo hello theori` hellotheori |
아래의 예제에서는, 사용자가 입력한 IP를 ping의 인자로 전달합니다.
(ping은 특정 IP의 서버가 작동하는지 확인하려고 자주 사용합니다.)
// Name: cmdi.c
// Compile: gcc -o cmdi cmdi.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int kMaxIpLen = 36;
const int kMaxCmdLen = 256;
int main() {
char ip[kMaxIpLen];
char cmd[kMaxCmdLen];
// Initialize local vars
memset(ip, '\0', kMaxIpLen);
memset(cmd, '\0', kMaxCmdLen);
strcpy(cmd, "ping -c 2 ");
// Input IP
printf("Health Check\n");
printf("IP: ");
fgets(ip, kMaxIpLen, stdin);
// Construct command
strncat(cmd, ip, kMaxCmdLen);
printf("Execute: %s\n",cmd);
// Do health-check
system(cmd);
return 0;
}
IP로 127.0.0.1
을 입력하면 다음과 같이 작동합니다.
$ ./cmdi
Health Check
IP: 127.0.0.1
Execute: ping -c 2 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.019 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.032 ms
--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1065ms
rtt min/avg/max/mdev = 0.019/0.025/0.032/0.008 ms
만약, 악의적인 사용자라고 하면, 코드 상에서 아무런 검사가 없다는 점을 파악해 Command Injection을 시도할 수 있습니다.
다음은 ;
를 메타문자로 사용하여 셸을 실행시키는 예제입니다.
ping이 정상적으로 실행된 후, /bin/sh
가 이어서 실행됩니다.
$ ./cmdi
Health Check
IP: 127.0.0.1; /bin/sh
Execute: ping -c 2 127.0.0.1; /bin/sh
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.046 ms
--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1059ms
rtt min/avg/max/mdev = 0.020/0.033/0.046/0.013 ms
$ id
uid=1000(dreamhack) gid=1000(dreamhack) groups=1000(dreamhack)
따라서 개발자는 이러한 취약점을 막기 위해,
입력값에 대해 메타 문자의 유무를 철저히 검사해야합니다.
또한, 꼭 필요한 상황이 아니면 system
과 같은 함수의 사용을 자제해야 합니다.
이러한 함수는 memory corruption과 관련된 익스플로잇에도 사용될 수 있기 때문입니다.
-
-
[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.