모의해킹 수행 중 LPE(로컬 권한 상승)에 사용한 1-day 취약점인 CVE-2021–4034에 대해 Technical Detail을 작성하고자 한다.
모의해킹 특성상 운영 서버에서 LPE를 수행해야 했기 때문에 비교적 안전한 취약점을 사용해야 했다. CVE-2021–4034는 시스템에 악영향을 주지 않는 취약점으로 판단하였고, 해당 판단 근거가 된 동작 원리를 다룬다.
레퍼런스로는 가장 상세하게 작성된 블로그 포스트를 참고했으며, 검토한 GitHub PoC 코드는 가장 스타가 많은 것을 기준으로 했다.
[CVE-2021–4034 개요]
CVE-2021–4034는 OOB(Out-of-Bounds) 접근을 활용해 일반적으로 쓸 수 없는 위치에 환경변수를 주입하는 취약점이다.
OOB를 통해 주입하는 악성 환경변수는 코드 내 문자 인코딩과 관련된 것으로, 다른 환경변수들과 함께 동작하면서 공격자가 의도한 위치에 심어둔 실행 파일을 시스템이 인코딩 관련 실행 파일로 오인하여 실행하게 만든다.
[CVE-2021–4034 핵심 메커니즘]
인자값 null을 통한 OOB(Out of Bound) 유도
execve(“/usr/bin/pkexec”, args, environ);
args 인자를 null로 넘겨 pkexec 내부 for 문에서 OOB를 유도함
pkexec.c의 메인함수 내 PATH 파라미터 오용
s = g_find_program_in_path(path); 내부에서 g_getenv("PATH") 호출
레퍼런스의 gutils.c 참고
OOB를 통한 envp[0]에 GCONV_PATH 파라미터 주입
argv[1] = path = s;
envp[0] 자리지만 argv[1]로 오인하게 하여, 원래 안전하지 않은 환경변수로 취급되는 GCONV_PATH가 해당 위치에 기록됨
validate_environment_variable에서 존재하지 않는 shell 환경변수로 g_printerr 유발
g_printerr에서 CHARSET 설정을 통한 iconv_open 함수 실행
CHARSET이 UTF-8이 아니면 문자셋 변환을 위해 iconv_open 실행
iconv_open에서 GCONV_PATH 내 gconv-modules에서 지정한 파일 확인 후 실행
// pkexec's main function385 validate_environment_variable (const gchar *key,386 const gchar *value)...402 /* special case $SHELL */403 if (g_strcmp0 (key, "SHELL") == 0)404 {405 /* check if it's in /etc/shells */406 if (!is_valid_shell (value))407 {408 log_message (LOG_CRIT, TRUE,409 "The value for the SHELL variable was not found the /etc/shells file");410 g_printerr ("\n"411 "This incident has been reported.\n");412 goto out;413 }414 }...435 main (int argc, char *argv[])436 {...534 for (n = 1; n < (guint) argc; n++)535 {...568 }...610 path = g_strdup (argv[n]);...629 if (path[0] != '/')630 {...631 s = g_find_program_in_path (path);632 if (s == NULL)633 {634 g_printerr ("Cannot run program %s: %s\n", path, strerror (ENOENT));635 goto out;636 }637 g_free (path);638 argv[n] = path = s;639 }...669 if (!validate_environment_variable (key, value))...1007 if (execv (path, exec_argv) != 0)1008 {1009 g_printerr ("Error executing %s: %s\n", path, g_strerror (errno));1010 goto out;1011 }