I would like to write a Technical Detail about CVE-2021–4034, a 1-day vulnerability used for LPE (Local Privilege Escalation) during a penetration testing engagement.
Since the LPE had to be performed on a live production server during the penetration test, it was necessary to use a relatively safe vulnerability. CVE-2021–4034 was determined to be a vulnerability that does not adversely affect the system, and this write-up covers the underlying mechanism that led to that safety assessment.
The most detailed blog post available was used as a reference, and the GitHub PoC code reviewed was the one with the most stars.
[CVE-2021–4034 Overview]
CVE-2021–4034 leverages an OOB (Out-of-Bounds) access to inject an environment variable into a location where it normally cannot be written.
The malicious environment variable written via OOB is one related to character encoding within the code. When it operates in conjunction with other environment variables, the system mistakenly treats an executable planted by the attacker in an intended location as an encoding-related executable, and runs it.
[CVE-2021–4034 Key Mechanism_EN]
Inducing OOB (Out-of-Bounds) via a null argument value
execve(“/usr/bin/pkexec”, args, environ);
By passing null for the args argument, OOB is induced inside pkexec’s internal for loop
Misuse of the PATH parameter in pkexec.c’s main function
Inside s = g_find_program_in_path(path);, g_getenv("PATH") is called internally
See gutils.c in the references
Injecting the GCONV_PATH parameter into envp[0] via OOB
argv[1] = path = s;
Although the memory position is envp[0], it is misidentified as argv[1], which allows GCONV_PATH — normally considered an unsafe environment variable — to be written there
validate_environment_variable triggers g_printerr with a non-existent shell environment variable
g_printerr executes iconv_open via the CHARSET setting
If CHARSET is not UTF-8, iconv_open is executed to perform character set conversion
iconv_open checks for a file specified in gconv-modules under GCONV_PATH and executes it
See the man7 page in the references for detailed behavior of iconv_open
[CVE-2021–4034 Key Mechanism_KR]
Inducing OOB (Out of Bounds) via a null argument value
execve(“/usr/bin/pkexec”, args, environ);
By passing null for the args argument, OOB is induced inside pkexec’s internal for loop
Misuse of the PATH parameter in pkexec.c’s main function
Inside s = g_find_program_in_path(path);, g_getenv("PATH") is called internally
See gutils.c in the references
Injecting the GCONV_PATH parameter into envp[0] via OOB
argv[1] = path = s;
Although the memory position is envp[0], it is misidentified as argv[1], which allows GCONV_PATH — normally considered an unsafe environment variable — to be written there
validate_environment_variable triggers g_printerr with a non-existent shell environment variable
g_printerr executes iconv_open via the CHARSET setting
If CHARSET is not UTF-8, iconv_open is executed to perform character set conversion
iconv_open checks for a file specified in gconv-modules under GCONV_PATH and executes it
See the man7 page in the references for detailed behavior of iconv_open
// 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 }