This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author r3pwnx
Recipients r3pwnx
Date 2021-04-30.08:42:00
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1619772121.59.0.631498595724.issue43984@roundup.psfhosted.org>
In-reply-to
Content
The library winreg[1] can be used to access registry on windows.  
>reg.SetValueEx(key, 'X', 0, reg.REG_DWORD, -1337)  
`SetValueEx` could store data in the value field of an open registry key, 

In the source file of "PC/winreg.c", `Py2Reg`is called first

```
static PyObject *
winreg_SetValueEx_impl(PyObject *module, HKEY key,
                       const Py_UNICODE *value_name, PyObject *reserved,
                       DWORD type, PyObject *value)
/*[clinic end generated code: output=811b769a66ae11b7 input=900a9e3990bfb196]*/
{
    BYTE *data;
    DWORD len;

    LONG rc;

    if (!Py2Reg(value, type, &data, &len))
    ...
```

`Py2Reg` is implemented in the same file:

```
Py2Reg(PyObject *value, DWORD typ, BYTE **retDataBuf, DWORD *retDataSize)
{
    Py_ssize_t i,j;
    switch (typ) {
        case REG_DWORD:
            if (value != Py_None && !PyLong_Check(value))
                return FALSE;
            *retDataBuf = (BYTE *)PyMem_NEW(DWORD, 1);
            if (*retDataBuf == NULL){
                PyErr_NoMemory();
                return FALSE;
            }
            *retDataSize = sizeof(DWORD);
            if (value == Py_None) {
                DWORD zero = 0;
                memcpy(*retDataBuf, &zero, sizeof(DWORD));
            }
            else {
                DWORD d = PyLong_AsUnsignedLong(value);
                memcpy(*retDataBuf, &d, sizeof(DWORD));
            }
            break;
```

When the type is set with reg.REG_DWORD, `PyLong_AsUnsignedLong(value)` will change  the value's type, and then memcpy the returned value directly, without any check.  

In the Objects/longobject.c, as the comment said:

/* Get a C unsigned long int from an int object.
   Returns -1 and sets an error condition if overflow occurs. */

If PyLong_AsUnsignedLong return -1, the -1 will be stored in the registry though the error occured


PoC:


import winreg as reg

key = reg.CreateKey(reg.HKEY_CURRENT_USER, 'SOFTWARE\\Classes\\r3pwn')

try:
	print("Set Subkey X: -1337")
	reg.SetValueEx(key, 'X', 0, reg.REG_DWORD, -1337)
except Exception as e:
	print("Get Subkey: ", reg.QueryValueEx(key, "X")[0])

try:
	print("Set Subkey Y: 2**33")
	reg.SetValueEx(key, 'Y', 0, reg.REG_DWORD, 2**33)
except Exception as e:
	print("Get Subkey: ", reg.QueryValueEx(key, "Y")[0])

The Test Environment
OS: Windows
Python Version: 3.9.0

python winreg_bug.py
Set Subkey X: -1337
Get Subkey:  4294967295
Set Subkey Y: 2**33
Get Subkey:  4294967295

the return value should be checked:

     DWORD d = PyLong_AsUnsignedLong(value);
 +   if (d == (unsigned long)-1 && PyErr_Occurred())
 +       return False;
     memcpy(*retDataBuf, &d, sizeof(DWORD));


[1] https://docs.python.org/3.9/library/winreg.html#winreg.SetValueEx
History
Date User Action Args
2021-04-30 08:42:01r3pwnxsetrecipients: + r3pwnx
2021-04-30 08:42:01r3pwnxsetmessageid: <1619772121.59.0.631498595724.issue43984@roundup.psfhosted.org>
2021-04-30 08:42:01r3pwnxlinkissue43984 messages
2021-04-30 08:42:01r3pwnxcreate