메뉴 건너뛰기

app

서버의 통제권을 빼앗는 CWE-78 운영체제 명령어 삽입

suritam92026.01.17 18:05조회 수 1댓글 0

    • 글자 크기

1. 운영체제 명령어 삽입(OS Command Injection)의 위험성

CWE-78은 애플리케이션이 외부 입력값을 사용하여 운영체제 명령어를 생성하는 과정에서 발생합니다. 공격자가 명령어 구분자(;, &, |)나 리다이렉션 문자를 입력값에 섞어 넣으면, 서버는 개발자가 의도한 명령 외에 공격자가 주입한 추가 명령(예: 사용자 계정 탈취, 파일 삭제, 백도어 설치 등)을 수행하게 됩니다. 이는 서버 전체의 제어권이 공격자에게 넘어가는 가장 치명적인 보안 결함입니다.

2. 흔히 발생하는 위험한 패턴: 셸(Shell) 호출 API

가장 위험한 사례는 Runtime.getRuntime().exec()나 ProcessBuilder를 사용하면서 입력값을 문자열 결합(+) 방식으로 전달하는 경우입니다. 예를 들어, 네트워크 상태를 체크하기 위해 사용자가 입력한 IP 주소를 ping 명령어 뒤에 붙여 실행하면, 공격자는 8.8.8.8 ; cat /etc/passwd와 같은 입력으로 시스템의 민감한 정보를 탈취할 수 있습니다.

3. 실무적 대응: API 활용 및 매개변수 분리

최선의 방어책은 운영체제 명령어를 직접 호출하지 않는 것입니다.

언어 라이브러리 활용: 파일 삭제는 rm 명령 대신 File.delete()를, 네트워크 체크는 ping 대신 Java의 InetAddress 클래스를 사용하는 등 언어 자체에서 제공하는 안전한 API를 우선 사용하십시오.

매개변수 분리 (ProcessBuilder): 어쩔 수 없이 명령어를 실행해야 한다면, 전체 명령어를 하나의 문자열로 만들지 말고 ProcessBuilder의 인자 리스트(List)를 사용하여 명령어와 파라미터를 엄격히 분리하십시오. 이렇게 하면 파라미터 내의 특수문자가 명령어로 해석되지 않습니다.

4. CWE-78 대응 및 안전한 명령어 실행 자바 코드 예시

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
 
public class SystemCommandProcessor {
    private static final Logger logger = LoggerFactory.getLogger(SystemCommandProcessor.class);
 
    // [CWE-78 조치] ProcessBuilder를 사용하여 인자값을 분리한 안전한 실행
    public void checkNetworkStatus(String ipAddress) {
        // [CWE-112] 입력값에 대한 기본적인 형식 검증 (정규식 등)
        if (!ipAddress.matches("^[0-9.]+$")) {
            logger.debug("Invalid IP format detected: {}", ipAddress);
            throw new IllegalArgumentException("Invalid input");
        }
 
        List<String> command = new ArrayList<>();
        command.add("ping");
        command.add("-c");
        command.add("3");
        command.add(ipAddress); // 인자로만 전달되어 명령어 삽입이 불가능함
 
        ProcessBuilder pb = new ProcessBuilder(command);
        
        try {
            logger.debug("Executing safe network check for: {}", ipAddress);
            Process process = pb.start();
            
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    logger.debug("Command Output: {}", line);
                }
            }
            process.waitFor();
            
        } catch (Exception e) {
            // [CWE-754, CWE-390] 실행 오류 대응
            logger.debug("OS command execution failed: {}", e.getMessage());
        }
    }
}

코멘트: OS 명령어 삽입 방어의 핵심은 "사용자의 입력을 절대 셸의 명령어로 해석하게 두지 않는 것"입니다. 가능하다면 운영체제 의존적인 명령 호출을 지양하고, 반드시 필요할 때는 ProcessBuilder와 같이 인자 분리가 가능한 API를 선택하십시오. logger.debug()를 통해 실행되는 명령의 문맥을 기록하되, 공격자가 시스템 명령어의 흐름을 가로채지 못하도록 입구를 철저히 봉쇄하는 것이 시큐어 코딩의 완성입니다.

 
    • 글자 크기
피싱의 게이트웨이: CWE-601 오픈 리다이렉트 방지 전략 (by suritam9) 서버의 경계를 넘나드는 위험: CWE-22 경로 조작 및 CWE-99 자원 삽입 (by suritam9)

댓글 달기

첨부 (0)
위로