보안 공부/소스코드 보안약점 진단

소스코드 보안약점 진단 - 운영체제 명령어 삽입

H.J.World 2021. 11. 30. 10:10
728x90
반응형

소스코드 보안약점 진단 // 소프트웨어 보안약점 진단 // SW 보안약점 진단과 같이 다양한 이름으로 불리는 진단 과업 중 하나이다.

SW개발보안은 해킹 등 사이버공격의 원인인 보안약점을 SW개발단계에서 사전에 제거하고 SW 개발 생명주기의 각 단계별로 수행하는 일련의 보안활동을 통하여 안전한 SW를 개발·운영하기 위한 목적으로 적용하는 개발체계이다.

해당 내용은 KISA에서 발간하는 취약점 진단 가이드 항목을 기준으로 작성한다.

 

4. 운영체제 명령어 삽입

가. 개요

적절한 검증절차를 거치지 않은 사용자 입력값이 운영체제 명령어의 일부 또는 전부로 구성되어 실행 되는 경우, 의도하지 않은 시스템 명령어가 실행되어 부적절하게 권한이 변경되거나 시스템 동작 및 운영에 악영향을 미칠 수 있다.

일반적으로 명령어 라인의 파라미터나 스트림 입력 등 외부 입력을 사용하여 시스템 명령어를 생성 하는 프로그램이 많이 있다. 하지만 이러한 경우 외부 입력 문자열은 신뢰할 수 없기 때문에 적절한 처리를 해주지 않으면, 공격자가 원하는 명령어 실행이 가능하게 된다.

나. 보안대책

웹 인터페이스를 통해 서버 내부로 시스템 명령어를 전달시키지 않도록 응용프로그램을 구성하고, 외부에서 전달되는 값을 검증 없이 시스템 내부 명령어로 사용하지 않는다. 외부 입력에 따라 명령어 를 생성하거나 선택이 필요한 경우에는 명령어 생성에 필요한 값들을 미리 지정해 놓고 외부 입력에 따라 선택하여 사용한다

 

다. 코드예제

- 안전하지 않은 코드의 예 JAVA -

다음의 예제는 Runtime.getRuntime().exec()명령어를 통해 프로그램을 실행하며, 외부에서 전달 되는 인자값은 명령어의 생성에 사용된다. 그러나 해당 프로그램에서 실행할 프로그램을 제한하지 않고 있기 때문에 외부의 공격자는 가능한 모든 프로그램을 실행시킬 수 있다.

1: public static void main(String args[]) throws IOException {
2: // 해당 프로그램에서 실행할 프로그램을 제한하고 있지 않아 파라미터로 전달되는 모든 프로그램이
실행될 수 있다.
3: String cmd = args[0];
4: Process ps = null;
5: try {
6: ps = Runtime.getRuntime().exec(cmd);
7: ..

 

 

 

- 안전한 코드의 예 JAVA -

다음의 예제와 같이 미리 정의된 파라미터의 배열을 만들어 놓고, 외부의 입력에 따라 적절한 파라 미터를 선택하도록 하여, 외부의 부적절한 입력이 명령어로 사용될 가능성을 배제하여야 한다.

1: public static void main(String args[]) throws IOException {
2: // 해당 어플리케이션에서 실행할 수 있는 프로그램을 노트패드와 계산기로 제한하고 있다.
3: List<String> allowedCommands = new ArrayList<String>();
4: allowedCommands.add("notepad");
5: allowedCommands.add("calc");
6: String cmd = args[0];
7: if (!allowedCommands.contains(cmd)) {
8: System.err.println("허용되지 않은 명령어입니다.");
9: return;
10: }
11: Process ps = null;
12: try {
13: ps = Runtime.getRuntime().exec(cmd);
14: .....

 

- 안전하지 않은 코드의 예  C -

공격자의 입력에 따라 의도하지 않은 명령어가 실행될 수 있다.

1: int main(int argc, char* argv[]) {
2: char cmd[CMD_LENGTH];
3: if (argc < 1 ) {
4: // error
5: }
6: // 외부 입력값으로 커맨드를 직접 수행
7: cmd_data = argv[1];
8: snprintf(cmd, CMD_LENGTH, “cat %s”, cmd_data);
9: system(cmd);
10: ……
11: }

 

- 안전한 코드의 예 C -

운영체제 명령어 실행 시에는 아래와 같이 외부에서 들어오는 값에 의하여 멀티라인을 지원하는 특수 문자(| ; & :)나 파일 리다이렉트 특수문자(> >>)등을 제거하여 원하지 않은 운영체제 명령어가 실행 될 수 없도록 필터링을 수행한다.

1: int main(int argc, char* argv[]) {
2: char cmd[CMD_LENGTH];
3: int len = 0;
4: if (argc < 1 ) {
5: // error
6: }
7: // 외부 입력값으로 커맨드를 직접 수행
8: cmd_data = argv[1];
9: len = strlen(cmd_data);
10: for (int i = 0; I < len; i++) {
11: if (cmd_data[i] == ‘|’ || cmd_data[i] == ‘&’ ||
12: cmd_data[i] == ‘;’ || cmd_data[i] == ‘:’ || cmd_data[i] == ‘>’) {
13: // 멀티라인을 지원하는 특수문자나 파일 리다이렉트 특수문자가 존재하여
14: // 안전하지 않음
15: return -1;
16: }
17: }
18: snprintf(cmd, CMD_LENGTH, “cat %s”, cmd_data);
19: system(cmd);
20: ……
21: }

 

라. 진단방법

운영체제 명령어(exec(), system(), Runtime.getRuntime().exec 등)를 실행할 수 있는 함수가 호출되는지 확인하고(①) 외부에서 전달되는 값이 시스템 내부명령어의 일부 또는 전부로 사용되는지 확인한다(②). 정해진 후보군에서 선택된 값(White List)이거나 적절하게 검증하면 안전하고 그 외에 는 취약하다.

- 일반적인 진단의 예 -

1: public void f() throws
IOException { 2:
Properties props = new
Properties(); 3:
String fileName = “file_list”
4: FileInputStream in = new FileInputStream(fileName);
5: props.load(in);
6:
7: // 외부에서 전달된 값이 시스템 내부 명령어의 일부로 사용됨.
8: String version = props.getProperty(“dir_type”)--------------②
9: String cmd = new String(“cmd.exe /c rmanDB.bat “);
10:
11: // 입력값 검증 없이 내부 명령어의 일부로 사용됨.
12: Runtime.getRuntime().exec(cmd + “c:\\prog_cmd\\” + version);----- ①
13: }

 

- 오탐코드의 예 -

프레임워크 특성상 Property 사용은 금지할 수 없다. 하지만 시스템 프로퍼티 등 공용으로 사용하는 프로퍼티가 아닌 개별 사용 프로퍼티일 경우는 취약한 것으로, 시스템 프로퍼티를 사용하는 경우는 안전한 것으로 판정한다.

다음의 예제에서 5라인 cmdStr 변수는 Property에서 값을 가져 온 이후 7라인에서 문자열을 만드 는데 사용되고, 해당 문자열이 9라인에서 명령어로 사용되므로 안전한 것으로 판정한다.

1: public static floatgetMoryFreeCpcty() throws Exception {

2: float cpcty = 0;

3: Process p = null;

4: try {

5: String cmdStr = EgovProperties.getPathProperty(Globals.SERVER_CONF_PATH,

6: “SHELL.”+Globals.OS_TYPE+”.getMoryInfo”);

7: String[] command = {cmdStr.replace(‘\\’, FILE_SEPARATOR).replace(‘/’, FILE_SEPA- RATOR),”FREE“}

8: }

9: p = Runtime.getRuntime().exec(command);

======================================================================

다음의 예제에서 fname은 srcFile.getName()의 결과값인 파일의 이름이므로 명령어에 삽입되어 공격할 수 있는 file separator나 “..” 등의 문자열이 없으므로 취약하지 않은 것으로 판정한다.

1: public static String getOwner(String file) throws Exception{
2: String owner = “”;
3: String src = file.replace(‘\\’, FILE_SEPARATOR).replace(‘/’, FILE_SEPARATOR);
4: BufferedReader b_err=null;
5: BufferedReader b_out=null;
6: try {
7: File srcFile = newFile(src);
8: if (srcFile.exists()) {
9: String parentPath = srcFile.getParent();
10: String fname = srcFile.getName();
11: Process p = null;
12: String cmdStr = EgovProperties.getProperty(Globals.SHELL_FILE_PATH,
“SHELL.”+Globals.OS_TYPE+”.getDrctryOwner”);
13: String[] command = {cmdStr.replace(‘\\’, FILE_SEPARATOR).replace(‘/’,
FILE_SEP- ARATOR), parentPath.replace(‘\\’, FILE_SEPARATOR).replace(‘/’, 
FILE_SEPARATOR), fname};
14: p = Runtime.getRuntime().exec(command);
15: }
16: }
17: }
728x90
반응형