8000 [security][3.3] bpo-30730: Prevent environment variables injection in subprocess on Windows. (GH-2325) by serhiy-storchaka · Pull Request #2363 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

[security][3.3] bpo-30730: Prevent environment variables injection in subprocess on Windows. (GH-2325) #2363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1363,8 +1363,12 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
# and pass it to fork_exec()

if env is not None:
env_list = [os.fsencode(k) + b'=' + os.fsencode(v)
for k, v in env.items()]
env_list = []
for k, v in env.items():
k = os.fsencode(k)
if b'=' in k:
raise ValueError("illegal environment variable name")
env_list.append(k + b'=' + os.fsencode(v))
else:
env_list = None # Use execv instead of execve.
executable = os.fsencode(executable)
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,46 @@ def test_empty_env(self):
# environment
b"['__CF_USER_TEXT_ENCODING']"))

def test_invalid_cmd(self):
# null character in the command name
cmd = sys.executable + '\0'
with self.assertRaises((ValueError, TypeError)):
subprocess.Popen([cmd, "-c", "pass"])

# null character in the command argument
with self.assertRaises((ValueError, TypeError)):
subprocess.Popen([sys.executable, "-c", "pass#\0"])

def test_invalid_env(self):
# null character in the enviroment variable name
newenv = os.environ.copy()
newenv["FRUIT\0VEGETABLE"] = "cabbage"
with self.assertRaises((ValueError, TypeError)):
subprocess.Popen([sys.executable, "-c", "pass"], env=newenv)

# null character in the enviroment variable value
newenv = os.environ.copy()
newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
with self.assertRaises((ValueError, TypeError)):
subprocess.Popen([sys.executable, "-c", "pass"], env=newenv)

# equal character in the enviroment variable name
newenv = os.environ.copy()
newenv["FRUIT=ORANGE"] = "lemon"
with self.assertRaises(ValueError):
subprocess.Popen([sys.executable, "-c", "pass"], env=newenv)

# equal character in the enviroment variable value
newenv = os.environ.copy()
newenv["FRUIT"] = "orange=lemon"
with subprocess.Popen([sys.executable, "-c",
'import sys, os;'
'sys.stdout.write(os.getenv("FRUIT"))'],
stdout=subprocess.PIPE,
env=newenv) as p:
stdout, stderr = p.communicate()
self.assertEqual(stdout, b"orange=lemon")

def test_communicate_stdin(self):
p = subprocess.Popen([sys.executable, "-c",
'import sys;'
Expand Down
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Core and Builtins
Library
-------

- [Security] bpo-30730: Prevent environment variables injection in subprocess on
Windows. Prevent passing other invalid environment variables and command arguments.

- [Security] bpo-30585: Fix TLS stripping vulnerability in smptlib,
CVE-2016-0772. Reported by Team Oststrom

Expand Down
23 changes: 19 additions & 4 deletions Modules/_winapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,20 @@ getenvironment(PyObject* environment)
"environment can only contain strings");
goto error;
}
if (PyUnicode_FindChar(key, '\0', 0, PyUnicode_GET_LENGTH(key), 1) != -1 ||
PyUnicode_FindChar(value, '\0', 0, PyUnicode_GET_LENGTH(value), 1) != -1)
{
PyErr_SetString(PyExc_ValueError, "embedded null character");
goto error;
}
/* Search from index 1 because on Windows starting '=' is allowed for
defining hidden environment variables. */
if (PyUnicode_GET_LENGTH(key) == 0 ||
PyUnicode_FindChar(key, '=', 1, PyUnicode_GET_LENGTH(key), 1) != -1)
{
PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
goto error;
}
if (totalsize > PY_SSIZE_T_MAX - PyUnicode_GET_LENGTH(key) - 1) {
PyErr_SetString(PyExc_OverflowError, "environment too long");
goto error;
Expand Down Expand Up @@ -621,12 +635,13 @@ winapi_CreateProcess(PyObject* self, PyObject* args)

if (env_mapping != Py_None) {
environment = getenvironment(env_mapping);
if (! environment)
if (environment == NULL) {
return NULL;
}
/* contains embedded null characters */
wenvironment = PyUnicode_AsUnicode(environment);
if (wenvironment == NULL)
{
Py_XDECREF(environment);
if (wenvironment == NULL) {
Py_DECREF(environment);
return NULL;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -2682,8 +2682,8 @@ _PySequence_BytesToCharpArray(PyObject* self)
array[i] = NULL;
goto fail;
}
data = PyBytes_AsString(item);
if (data == NULL) {
/* check for embedded null bytes */
if (PyBytes_AsStringAndSize(item, &data, NULL) < 0) {
/* NULL terminate before freeing. */
array[i] = NULL;
goto fail;
Expand Down
0