촉촉한초코칩
Command Injection 취약점 본문
Command Injection
- 시스템 해킹 관점의 명령 주입 취약점
- ;, &&, ||, < 등 메타 문자를 사용하여 엔터를 한 번만 눌러도 여러 명령어를 실행할 수 있다.
- 사용자의 입력을 필터링하지 않을 때, 사용자가 의도하는 명령을 추가적으로 삽입하여 호스트 운영 체제(OS)에서 임의의 명령을 실행할 수 있다.
시스템에서 사용자의 입력이 system() 함수의 인자로 사용되거나 웹에서는 사용자 제공 데이터(서버에 전달하는 양식, 쿠키, HTTP 헤더 등)가 시스템 쉘에 전달될 때 입력을 검사하지 않으면 문제가 된다.
system() 함수 원형 : int system(const char* command)
→ 인자인 command를 시스템에 명령어로 바로 전달한다.
command_injection.c 파일을 실행하면 첫 번째 인자값에 해당하는 cat 명령어로 파일을 읽는다.
하지만 command를 필터링하는 과정이 없기 때문에 ;을 사용해서 command를 추가할 수 있다.
" " 를 사용하여 하나의 문자열로 전송한다 .
"Hacking.txt; cat Hacking_JJack.txt"로 한 번에 인자를 보내주면 Hacking_JJack.txt 파일도 읽게 된다.
strncpy() 함수 (참고 : https://www.ibm.com/docs/ko/i/7.3?topic=functions-strncpy-copy-strings)
#include <string.h>
char *strncpy(char *string1, const char *string2, size_t count);
- string2를 string1로 복사한다.
- count가 string2 길이 이하이면, 널 문자는 복사된 string1에 추가되지 않는다.
만약 count가 string2의 길이보다 큰 경우, string1에는 count 길이 까지 널 문자로 채워진다. - string1에 대한 포인터를 리턴한다.
strncat() 함수 (참고 : https://www.ibm.com/docs/ko/i/7.3?topic=functions-strncat-concatenate-strings)
#include <string.h>
char *strncat(char *string1, const char *string2, size_t count);
- string2에 있는 문자열 count개를 string1에 추가한다.
- count가 string2 길이보다 크면 count 대신 string2 길이를 사용한다.
- 널로 끝나느 스트링에서 작동한다.
함수에 대한 스트링 인수는 스트링 끝에 널 문자를 포함해야 한다. - 결합된 스트링에 대한 포인터를 리턴한다. (string1)
파일에 SetUID가 설정되어 있으면 일반 사용자는 해당 파일의 소유자 권한을 가지게 된다.
이때 command_injection.c 파일이 root 사용자에 SetUID가 설정되어 있다면 root 권한의 쉘도 가질 수 있게 된다.
만약, 여기서 rm -rf / 명령을 내린다면 시스템의 모든 파일을 강제로 삭제하게 된다.
Christmas CTF 2020, oil system 문제
파일의 이름을 사용자가 입력할 수 있고, Out of Bounds(OOB)* 취약점을 이용하여 리턴값을 조작하도록 만든 문제이다. 하지만 의도하지 않게 Command Injection으로도 풀리게 되었다.
아래 부분이 파일명을 필터링하는 함수이다.
//qword_4120 값을 검사하여 소문자 알파벳 이외의 값이 들어오면 오류 메시지를 출력하고 -1을 반환한다.
unsigned __int64 sub_166B() {
unsigned __int64 result; // rax
__int64 v1; // rsi
//qword_4120 값을 갱신하는 호출 (사용자 입력을 받거나 특정 데이터를 처리함)
result = sub_11D0(&qword_4120);
if ( result > 0 ) {
//소문자 알파벳 범위 검사 (소문자가 아닐 경우 오류 처리를 수행하는 코드이다.)
//char 타입을 사용하여 qword_4120 값을 문자로 변환한 후, (문자 1개만 검사)
//'a'~'z' 범위를 벗어난 문자가 입력되었는지 확인한다.
//96 : ``, 97 : a, 122 : z
if ( (char)qword_4120 <= 96 || //96 (`) 이하인 경우
//unsigned__int8로 변환하고 result에 저장, char로 변환하고 122(z)보다 큰 지 체크
(result = (unsigned __int8)qword_4120, (char)qword_4120 > 122) ) {
//입력된 문자를 unsigned_int 형식으로 저장한다.
v1 = (unsigned int)(char)qword_4120;
//입력된 문자를 출력하는 함수를 호출한다.
sub_1200("\n %c \n");
//메시지와 함께 v1 값을 출력한다.
sub_11A0("\n Only lower case Alphabets are allowed", v1);
//에러 코드를 반환한다.
result = sub_12C0(0xFFFFFFFFLL);
}
}
return result;
}
영문 소문자(a-z) 이외의 사용자 입력이 들어오면 종료하도록 하지만, 모든 입력을 검사하는 것이 아닌 첫 문자만 검사한다.
따라서 a;sh 등의 입력으로 쉘을 실행할 수 있게 된다.
OOB (Ou of Bounds)
- 프로그램이 메모리의 허용되지 않는 영역에 접근하거나 데이터를 쓰는 경우 발생한다.
- 공격 방식
- 데이터 손상 : 공격자는 메모리의 허용되지 않은 위치에 데이터를 덮어쓰거나 읽을 수 있어 프로그램의 예기치않은 공격을 유발한다.
- 코드 실행 : 악의적인 코드를 함수 포인터, 리턴 주소 등 중요한 메모리 위치에 덮어 쓰면, 공격자는 임의의 코드 실행이 가능해진다.
- 서비스 거부 (DoS) : 프로그램이 예상치 못한 데이터를 처리하거나 크래시를 일으켜 서비스가 중단될 수 있다.
Command Injection은 꼭 명령어로만 공격 가능한 것이 아닌, 스크립트가 실행될 때도 공격을 수행할 수 있다.
Cisco HyperFlex에서 3가지 취약점이 발견되었는데 그 중 CVSS 점수가 9.8로 가장 높은 취약점이 Command Injection이다.
사용자에게 계정 정보를 입력받을 때, 파이썬 스크립트를 활용해 계정 정보를 해시화하는 작업을 수행한다.
python -c
"import crypt;
print(crypt.crypt(\"OUR_PASS\", \"$6$$\"));"
#crypt 모듈을 사용하여 암호화된 해시를 생성한다.
#"%6%%"은 솔트(Salt) 값으로, SHA_512 알고리즘을 뜻한다.
#SHA-512 알고리즘을 사용하여 OUR_PASS 문자열을 해시한다.
패스워드 필드에 아래와 같은 스크립트를 삽입하면 시스템을 재부팅시킬 수도 있다.
123", "$6$$"));import os;os.system("reboot");print(crypt.crypt("
#코드 인젝션 공격을 시도하는 공격이다.
#os 모듈을 가져와 os.system("reboot")을 실행하여 시스템을 재부팅한다.
#이후에 crypt 실행을 시도한다.
Command Injection을 막는 방법
- 사용자의 입력에는 허용된 명령어 또는 허용된 문자만 사용하도록 한다.
- 문자열의 길이 제한
- && & || | ; $ < > \ ! 등의 문자를 블랙리스트에 포함하여 실행시키지 못하도록 해야 한다.
- 프로세스에게 작업에 필요한 최소한의 권한만 제공하도록 하여 사용자가 명령을 주입할 수 있는 경우에도 권한이 제한되도록 해야 한다.
'Study > CVE 및 취약점' 카테고리의 다른 글
MS 윈도우 압축 헤더(CAB)를 악용한 배치 파일(*.cmd) 악성코드 ModiLoader(DBatLoader) 유포 (0) | 2025.01.15 |
---|---|
Notion 설치파일로 위장한 MSIX 악성코드 유포 (0) | 2025.01.15 |
LummaC2 악성코드 - 게임 플랫폼 'Steam' 악용 (0) | 2025.01.04 |
[CVE-2023-46604] Apache ActiveMQ 취약점 (0) | 2024.12.17 |