IgnatiusHeo

구현단계 보안약점 제거 기준-운영체제 명령어 삽입 본문

자격/SW보안약점진단원

구현단계 보안약점 제거 기준-운영체제 명령어 삽입

Ignatius Heo 2023. 7. 3. 05:42

작성일: 230703

 

※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.

 

정리 내용: 소프트웨어 보안약점 진단 가이드(223~231p)

 

구분 - 입력데이터 검증 및 표현
설계단계 - 시스템 자원 접근 및 명령어 수행 입력값 검증
https://cryptocurrencyclub.tistory.com/92
개요
적절한 검증절차를 거치지 않은 사용자 입력값이 운영체제 명령어의 일부 또는 전부로 구성되어 실행되는 경우, 의도하지 않은 시스템 명령어가 실행되어 부적절하게 권한이 변경되거나 시스템 동작 및 운영에 악영향을 미칠 수 있다.

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

진단 세부사항
(설계단계)

 ① 외부 입력값을 이용하여 시스템자원을 식별하는 경우 허가되지 않은 자원이 사용되지 않도록 해야 한다.
  ㅇ  접근 허가된 시스템자원이 식별되고, 사용가능한 기능과 매핑되어 있는지 확인
  ㅇ  시스템자원 접근 이력이 로그로 기록되도록 설계되어 있는지 확인
  ㅇ  조작된 입력으로 비허가 자원의 접근가능 여부를 점검할 수 있는 테스트 계획의 수립 여부 확인
    → 경로조작에 사용 가능한 문자: / \\
 
 ② 서버프로그램 안에서 셸을 생성하여 명령어를 실행해야 하는 경우 외부입력값에 의해 악의적인 명령어가 실행되지 않도록 해야 한다.
  ㅇ  명령어 실행 기능 포함여부를 식별, 해당 기능의 실행 가능 명령어 파라미터의 제한 설계 여부 확인
  ㅇ  운영체제 명령어 실행 이력이 로그로 기록되도록 설계되어 있는지 확인
    → 누가/언제/어느IP에서 썼는지 확인 가능하도록 설계되어야함.
 
  ㅇ  조작된 입력으로 비허가 명령어의 실행 가능 여부 테스트 계획 수립 여부 확인
   → 검증해야 할 특수문자: | & ; 

보안대책
(구현단계)

웹 인터페이스로 서버 내부로 시스템 명령어를 전달시키지 않도록 응용프로그램을 구성하고, 외부에서 전달되는 값을 검증 없이 시스템 내부 명령어로 사용하지 않는다.

외부 입력에 따라 명령어를 생성하거나 선택이 필요한 경우에는 명령어 생성에 필요한 값들을 미리 지정해 놓고 외부 입력에 따라 선택하여 사용한다.

진단방법
(구현단계)

① 운영체제 명령어(exec(), system(), Runtime.getRuntime().exec 등)를 실행할 수 있는 함수가 호출되는지 확인하고

② 외부에서 전달되는 값이 시스템 내부명령어의 일부 또는 전부로 사용되는지 확인한다.
정해진 후보군에서 선택된 값(White List)이거나 적절하게 검증하면 안전하고 그 외에는 취약하다.

 

 

다. 코드예제


ㅇ 분석

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: ...

ㅇ 설명

1. cmd 필터가 하나도 안되어있음 어쩔려고 그래

 

ㅇ 수정

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: ......

ㅇ 설명

1. 4~5에서 실행 프로그램 화이트리스트를 만들어 메모장, 계산기만 사용 가능하게 함

2. 아닌 경우 8에러 에러


ㅇ 분석

1: 
2: String date = request.getParameter("date");
3: String command = new String("cmd.exe /c backuplog.bat");
4: Runtime.getRuntime().exec(command + date);

ㅇ 설명

1.  이것도 왜 data 필터를 안했어 + 붙이면 되는줄 아니

2. date값에 ; rm -rf 넣으면 ?

 

ㅇ 수정

1: String date = request.getParameter("date");
2: String command = new String("cmd.exe /c backuplog.bat");
3: //외부로부터 입력 받은 값을 필터링으로 우회문자를 제거하여 사용한다.
4: date = date.replaceAll("|","");
5: date = date.replaceAll(";","");
6: date = date.replaceAll("&","");
7: date = date.replaceAll(":","");
8: date = date.replaceAll(">","");
9: Runtime.getRuntime().exec(command + date);

ㅇ 설명

1. r4~r8과 같이 멀티라인, 리다이렉트 문자 제거 후 사용


ㅇ 분석

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: }

ㅇ 설명

1. cmd_data 값 검증 안하고 system에서 실행함!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 

ㅇ 수정

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: }

ㅇ 설명

1. for문 돌리면서 입력값에 멀티라인/리다이렉트 문자가 있는지 체크함

2. 있으면 빽

 

 

 

 

 

 

 

 

 

 

끝.