[Playbook] OS Command Injection 진단 Cheat Sheet
목차
이 문서는 OS Command Injection 진단 중 사용자 입력이 명령에 전달되는 위치를 찾고, 실행 흔적이 남는 통로에 따라 다음 확인을 고르기 위한 빠른 참고서다. 유형별 원리와 대응 방안은 OS Command Injection Lab에 정리했다.
30초 진단 흐름
- 외부 프로그램을 호출할 가능성이 있는 기능의 정상 요청을 확보한다.
- 입력값을 하나씩 바꾸며 본문·오류·처리 시간의 차이를 기록한다.
- 명령 출력이 응답에 포함되면 변경 전후의 본문을 비교한다.
- 출력이 없다면 시간 지연, 파일 조회, OOB 통신 중 사용 가능한 통로를 확인한다.
- 관찰한 결과를 해당 요청과 연결하고 실행 프로세스의 권한 범위로 영향을 정리한다.
| 관찰 결과 | 판단 | 다음 확인 |
|---|---|---|
| 입력에 따라 명령 출력이 응답에 추가됨 | 출력이 직접 반환되는 실행 경로 | 정상 응답과 추가 출력의 대응 |
| 본문은 같고 처리 시간이 달라짐 | 시간 기반 확인 가능성 | 정상 요청 대비 반복성과 지연 크기 |
| HTTP 응답 이후 별도 처리가 진행됨 | 비동기 실행 가능성 | 파일·OOB 통로 |
| 웹에서 읽을 수 있는 위치에 쓰기 가능 | 출력 파일을 통한 확인 가능성 | 파일시스템 경로와 조회 URL 매핑 |
| Collaborator에 DNS 조회가 도착함 | 입력한 조회 명령의 실행 흔적 | 발급한 주소·시각·요청 대응 |
| DNS 이름에 명령 결과가 포함됨 | 외부 통신으로 출력값 전달 | 조회 이름과 예상 출력 형식 비교 |
1. 입력과 기준 응답 확보
| 확인 대상 | 기록할 내용 |
|---|---|
| 정상 기능 | 재고 조회, 피드백, 파일 처리 등 요청의 목적 |
| 입력 위치 | 쿼리·폼·JSON·헤더 중 실제 사용되는 값 |
| 정상 값 | 값의 타입, 길이, 허용 범위 |
| 응답 | 상태 코드·본문·오류·처리 시간 |
| 처리 방식 | 결과를 즉시 반환하는지, 접수 후 별도 처리하는지 |
- 한 번에 하나의 입력만 변경한다.
- 오류가 생기면 입력 검증 오류와 외부 프로그램의 오류를 구분한다.
- 입력에 구분자를 넣었을 때 요청 형식이 먼저 달라지지 않았는지 확인한다. 예를 들어 폼 본문의
&는 파라미터 구분자이므로 전달할 값의 인코딩을 확인해야 한다.
2. 출력이 응답에 보이는 경우
재고 조회 랩에서는 다음과 같이 응답을 비교했다.
요청의 storeId | 관찰한 응답 | 판단 |
|---|---|---|
1 | 재고 수량 62 | 정상 처리 기준 |
1;whoami | 실행 사용자 이름 peter-IBpHNv | 추가 명령 실행 확인 |
- 출력이 요청 문자열을 그대로 반사한 것인지, 명령이 생성한 값인지 확인한다.
- 원래 명령의 출력과 추가 명령의 출력을 나누어 기록한다.
- 실행 사용자 이름은 이후 파일 접근 권한을 이해하는 단서가 된다.
요청과 응답은 OS command injection, simple case에서 확인할 수 있다.
3. 응답 시간으로 확인
명령의 완료를 기다리는 요청이라면 처리 시간이 관찰 통로가 된다.
- 정상 요청의 처리 시간을 확인한다.
- 같은 입력 위치에서 지연 명령을 포함한 요청과 비교한다.
- 동일한 조건에서 지연이 반복되는지 확인한다.
- 입력한 지연과 실제 차이의 관계를 기록한다.
| 관찰 | 다음 판단 |
|---|---|
| 변경한 요청에서 일정한 지연이 반복됨 | 입력과 지연의 관계 확인 |
| 정상 요청도 비슷하게 느림 | 서버 부하·네트워크 영향 재확인 |
| 한 번만 느리고 재현되지 않음 | 추가 근거 확보 |
| 비동기 접수 응답만 즉시 반환됨 | 파일·OOB 통로 검토 |
email에 sleep 10을 넣어 지연을 확인한 기록은 time delays에 정리했다.
4. 파일로 출력 확인
출력을 파일에 저장하는 방식에는 쓰기 경로와 조회 경로가 함께 필요하다.
| 확인 항목 | 랩의 조건 |
|---|---|
| 쓰기 가능한 디렉터리 | /var/www/images/ |
| 출력 파일 | o.jpg |
| 조회 요청 | /image?filename=o.jpg |
| 확인할 본문 | 파일에 저장된 whoami 출력 |
- 서버의 파일 경로와 요청 URL이 어떻게 연결되는지 확인한다.
- 확장자나 응답의 MIME 타입과 함께 실제 본문을 읽는다.
- 파일 쓰기 실패, 조회 경로 오류, 캐시 때문에 같은 응답이 나온 경우를 나누어 본다.
- 여러 번 확인한다면 이번 요청이 만든 파일인지 식별할 수 있도록 기록한다.
상세 과정은 output redirection에서 다룬다.
5. OOB 통신으로 확인
비동기 처리라면 원래 HTTP 응답 밖에서 발생하는 동작을 살펴본다.
| 관찰 | 확인된 내용 | 다음 확인 |
|---|---|---|
| 발급한 주소에 DNS 조회 발생 | 해당 이름을 조회한 동작 | 요청 시각과 조회 시각 대조 |
| 조회 이름에 실행 사용자 값 포함 | 명령 출력의 외부 전달 | 랩 목표 값과 비교 |
| 기록된 상호작용 없음 | 이번 시도에서 외부 통신 미관찰 | 주소, 명령 전달, DNS·네트워크 조건 |
- 요청별로 구분되는 Collaborator 주소를 사용하면 결과를 대응시키기 쉽다.
- DNS 조회는 리졸버를 거칠 수 있으므로 수신 측 IP만으로 애플리케이션 서버의 주소를 단정하지 않는다.
- 조회 발생 여부를 확인하는 단계와 출력값을 읽는 단계를 구분해 기록한다.
DNS 조회 확인은 out-of-band interaction, 조회 이름에 결과를 포함한 과정은 out-of-band data exfiltration에 정리했다.
6. 예상과 다른 결과가 나올 때
| 점검할 조건 | 살펴볼 영향 |
|---|---|
| 요청 인코딩 | 셸에 도달하기 전에 파라미터가 분리되거나 문자가 바뀌는지 |
| 따옴표 위치 | 입력이 문자열 내부에 들어가 구분자가 문자로 처리되는지 |
| 구분자의 의미 | 순차 실행·조건부 실행·파이프·백그라운드 중 어떤 동작인지 |
| 원래 명령의 종료 상태 | &&·|| 조건이 달라지는지 |
| 표준 출력과 표준 오류 | 애플리케이션이 어느 스트림을 반환하는지 |
| 실행 시점 | HTTP 응답이 작업 완료를 기다리는지 |
| 프로세스 권한 | 파일 쓰기나 외부 통신이 가능한지 |
환경별 차이는 Lab의 구분자와 명령 치환 설명에서 정리했다.
7. 결과와 대응 정리
보고에는 입력 위치 → 변경한 값 → 관찰 통로 → 확인한 결과 → 필요한 수정을 연결한다.
| 확인된 원인 | 대응 방향 |
|---|---|
| 사용자 입력을 셸 명령에 문자열로 결합 | 내부 API로 대체하거나 셸 없이 인자 분리 |
| 실행 파일·옵션에 사용자 입력이 광범위하게 반영 | 실행 파일 고정, 허용 값·타입·범위 검증 |
| 불필요한 파일 접근·쓰기 권한 | 실행 계정과 디렉터리 권한 최소화 |
| 불필요한 외부 통신 | 서비스에 필요한 목적지만 허용 |
전체 풀이는 OS Command Injection Write-up 아카이브에서 확인할 수 있다.
Quick Checklist
- 정상 요청의 값·응답·처리 시간을 확보했는가?
- 한 번에 하나의 입력을 변경했는가?
- 폼·URL 인코딩 후 서버에 전달되는 값을 확인했는가?
- 반사된 입력과 명령이 생성한 출력을 구분했는가?
- 응답 지연을 정상 요청과 비교하고 반복성을 확인했는가?
- 비동기 처리 여부를 고려했는가?
- 파일 쓰기 경로와 조회 URL의 관계를 확인했는가?
- Collaborator 주소와 요청 시각을 대응시켰는가?
- 명령 실행 여부와 출력값 확인 범위를 구분해 기록했는가?
- 실행 프로세스의 권한으로 영향을 설명했는가?
- 대응에서 셸 호출 제거·인자 분리·입력 검증을 검토했는가?
참고자료
https://portswigger.net/web-security/os-command-injection/
https://docs.python.org/3.14/library/subprocess.html#security-considerations