유형 | 입력데이터 검증 및 표현 |
보안약점 | 운영체제 명령어 삽입 |
개요 | 적절한 검증절차를 거치지 않은 사용자 입력값이 운영체제 명령어의 일부 또는 전부로 구성되어 실행되는 경우, 의도하지 않은 시스템 명령어가 실행되어 부적절하게 권한이 변경되거나 시스템 동작 및 운영에 악영향을 미칠 수 있다. |
보안대책 | 웹 인터페이스로 서버 내부로 시스템 명령어를 전달시키지 않도록 응용프로그램을 구성하고, 외부에서 전달되는 값을 검증 없이 시스템 내부 명령어로 사용하지 않는다. 외부 입력에 따라 명령어를 생성하거나 선택이 필요한 경우에는 명령어 생성에 필요한 값 들을 미리 지정해 놓고 외부 입력에 따라 선택하여 사용한다. |
진단방법 | 운영체제 명령어(exec(), system(), Runtime.getRuntime().exec 등)를 실행할 수 있는 함수가 호출되는지 확인하고 외부에서 전달되는 값이 시스템 내부 명령어의 일부 또는 전부로 사용되는지 확인한다. 정해진 후보군에서 선택된 값(White List)이거나 적절하게 검증하면 안전하고 그 외에는 취약하다. |
연관된 설계단계 기준 | 시스템 자원 접근 및 명령어 수행 입력값 검증 |
코드예제
● 안전하지 않은 코드 예 (Java)
public static void main(String args[]) throws IOException {
// 해당 프로그램에서 실행할 프로그램을 제한하고 있지 않아 파라미터로 전달되는 모든 프로그램이 실행될 수 있다.
String cmd = args[0];
Process ps = null;
try {
ps = Runtime.getRuntime().exec(cmd);
...
● 안전한 코드 예 (Java)
public static void main(String args[]) throws IOException {
// 해당 어플리케이션에서 실행할 수 있는 프로그램을 노트패드와 계산기로 제한하고 있다.
List<String> allowedCommands = new ArrayList<String>();
allowedCommands.add("notepad");
allowedCommands.add("calc");
String cmd = args[0];
if (!allowedCommands.contains(cmd)) {
System.err.println("허용되지 않은 명령어입니다.");
return;
}
Process ps = null;
try {
ps = Runtime.getRuntime().exec(cmd);
......
● 안전하지 않은 코드 예 (Java)
//외부로 부터 입력 받은 값을 검증 없이 사용할 경우 안전하지 않다.
String date = request.getParameter("date");
String command = new String("cmd.exe /c backuplog.bat");
Runtime.getRuntime().exec(command + date);
● 안전한 코드 예 (Java)
String date = request.getParameter("date");
String command = new String("cmd.exe /c backuplog.bat");
//외부로부터 입력 받은 값을 필터링으로 우회문자를 제거하여 사용한다.
date = date.replaceAll("|","");
date = date.replaceAll(";","");
date = date.replaceAll("&","");
date = date.replaceAll(":","");
date = date.replaceAll(">","");
Runtime.getRuntime().exec(command + date);
● 안전하지 않은 코드 예 (C#)
//외부 입력값이 프로세스가 실행할 파일 이름을 지정하고 있습니다.
string fileName = PgmTextBox.Text;
ProcessStartInfo proStartInfo = new ProcessStartInfo();
proStartInfo.FileName = fileName;
Process.Start(proStartInfo);
● 안전한 코드 예 (C#)
string fileName = PgmTextBox.Text;
//외부 입력값에 대해 정규식 등을 이용하여 검증을 해줘야 합니다.
if (Regex.IsMatch(fileName, "properRegexHere"))
{
ProcessStartInfo proStartInfo = new ProcessStartInfo();
proStartInfo.FileName = fileName;
Process.Start(proStartInfo);
}
● 안전하지 않은 코드 예 (C)
int main(int argc, char* argv[]) {
char cmd[CMD_LENGTH];
if (argc < 1 ) {
// error
}
// 외부 입력값으로 커맨드를 직접 수행
cmd_data = argv[1];
snprintf(cmd, CMD_LENGTH, "cat %s", cmd_data);
system(cmd);
……
}
● 안전한 코드 예 (C)
int main(int argc, char* argv[]) {
char cmd[CMD_LENGTH];
int len = 0;
if (argc < 1 ) {
// error
}
// 외부 입력값으로 커맨드를 직접 수행
cmd_data = argv[1];
len = strlen(cmd_data);
for (int i = 0; I < len; i++) {
if (cmd_data[i] == ‘|’ || cmd_data[i] == ‘&’ || cmd_data[i] == ‘;’ || cmd_data[i] == ‘:’ || cmd_data[i] == ‘>’) {
// 멀티라인을 지원하는 특수문자나 파일 리다이렉트 특수문자가 존재하여
// 안전하지 않음
return -1;
}
}
snprintf(cmd, CMD_LENGTH, "cat %s", cmd_data);
system(cmd);
……
}
진단방법
운영체제 명령어(exec(), system(), Runtime.getRuntime().exec 등)를 실행할 수 있는 함수가 호출되는지 확인하고(①) 외부에서 전달되는 값이 시스템 내부명령어의 일부 또는 전부로 사용되는지 확인한다(②).
정해진 후보군에서 선택된 값(White List)이거나 적절하게 검증하면 안전하고 그 외에는 취약하다.
● 일반적인 진단의 예
public void f() throws IOException {
Properties props = new Properties();
String fileName = "file_list";
FileInputStream in = new FileInputStream(fileName);
props.load(in);
// 외부에서 전달된 값이 시스템 내부 명령어의 일부로 사용됨.
String version = props.getProperty("dir_type") ·································②
String cmd = new String("cmd.exe /c rmanDB.bat ");
// 입력값 검증 없이 내부 명령어의 일부로 사용됨.
Runtime.getRuntime().exec(cmd + "c:\\prog_cmd\\" + version); ···················①
}
● 정탐코드
// < EgovProcessMonController.java >
@RequestMapping("/utl/sys/prm/selectProcessSttus.do")
public String selectProcessSttus(@ModelAttribute("processMonVO") ProcessMonVO processMonVO, ModelMap model) throws Exception {
…
}
// < ProcessMonChecker.java >
public staticString getProcessId(String processNm) throws Exception {
Process p = null;
String procsSttus = null;
BufferedReader buf = null;
String result = null;
String execStr = "tasklist /fo table /nh /fi \"imagename eq "+processNm+"\"";
int cnt = 0;
String str = null;
try {
if (Globals.OS_TYPE == null) {
throw new RuntimeException("Globals.OS_TYPE property value is needed");
}
if ("WINDOWS".equals(Globals.OS_TYPE)) {
cnt = -1;
p = Runtime.getRuntime().exec(execStr);
}
}
외부 입력값인 processMon의 값을 파라미터로 getProcessId를 호출하고, execStr 문자열을 만들며 그 문자열이 28라인에서 명령어로 쓰이고 있어 취약한 것으로 판정한다.
● 오탐코드
public static floatgetMoryFreeCpcty() throws Exception {
float cpcty = 0;
Process p = null;
try {
String cmdStr = EgovProperties.getPathProperty(Globals.SERVER_CONF_PATH,
"SHELL."+Globals.OS_TYPE+".getMoryInfo");
String[] command = {cmdStr.replace(‘\\’, FILE_SEPARATOR).replace('/', FILE_SEPARATOR),"FREE"}
}
p = Runtime.getRuntime().exec(command);
cmdStr 변수는 Property에서 값을 가져 온 이후 문자열을 만드는데 사용되고, 해당 문자열이 명령어로 사용되므로 안전한 것으로 판정한다.
● 오탐코드
public static String getOwner(String file) throws Exception{
String owner = "";
String src = file.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR);
BufferedReader b_err=null;
BufferedReader b_out=null;
try {
File srcFile = newFile(src);
if (srcFile.exists()) {
String parentPath = srcFile.getParent();
String fname = srcFile.getName();
Process p = null;
String cmdStr = EgovProperties.getProperty(Globals.SHELL_FILE_PATH, "SHELL."+Globals.OS_TYPE+".getDrctryOwner");
String[] command = {cmdStr.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEP- ARATOR), parentPath.replace('\\', FILE_SEPARATOR).replace('/',
FILE_SEPARATOR), fname};
p = Runtime.getRuntime().exec(command);
}
}
}
fname은 srcFile.getName()의 결과값인 파일의 이름이므로 명령어에 삽입되어 공격할 수 있는 file separator나 ".." 등의 문자열이 없으므로 취약하지 않은 것으로 판정한다.
'소프트웨어(SW) 보안약점 진단원 > 구현단계 보안약점 제거 기준' 카테고리의 다른 글
구현단계 보안약점 기준 - 신뢰되지 않는 URL 주소로 자동접속 연결 (0) | 2025.05.26 |
---|---|
구현단계 보안약점 기준 - 위험한 형식 파일 업로드 (0) | 2025.05.26 |
구현단계 보안약점 기준 - 크로스사이트 스크립트 (1) | 2025.05.15 |
구현단계 보안약점 기준 - 경로 조작 및 자원 삽입 (0) | 2024.09.12 |
구현단계 보안약점 기준 - 코드 삽입 (0) | 2024.09.09 |