Sunday, August 28, 2005

Regedit vulnerability: Hiding Registry values

Last week I started getting emails that there is a vulnerability in the Windows Registry Editor (Regedit.exe and Regedt32.exe) that can cause it to hide certain Registry values with absurdly long names. Here are a few links on the subject: (also 3 or 4 pages following it)

So the Regedit utility won't show these Registry values, but they're still there. It probably took malware writers only 1.5 second to figure out this means they can hide autorun Registry values, since - unfortunately - the Windows system itself does not hide these values and faithfully runs them at startup when present.

As far as I can see from my tests, this is because the vulnerable utilities use the RegEnumValue() function with a name buffer size of only 260 bytes (the standard, ironically this is normally used as a constant named MAX_LENGTH). Since this buffer receives the name of the Registry value during enumeration of all values in a Registry key, it will fail when the value name is longer than 260 bytes.
Due to an unfortunate coding convention, the error itself is often not checked and assumed to be ERROR_NO_MORE_ITEMS - like a few of my apps do as well - and the enumeration stops there, confident that it listed all values. This also means that, in addition to not showing the value with the really long name, it will also fail to show and values following that, long name or not. Note that this means NEWER values, not ones following it in an alphabetic list.

While messing with a few Registry values with names varying from 100 characters to 100,000 characters (on WinXP+SP2), I noticed a few things:

  • The size limit for the value name is 16383 bytes, which means 14 bytes are reserved for this name. The page on about Registry Element Size Limits seems to confirm this. It also says that the limit for Win9x/ME is 255 characters, making those Windows versions immune to this vulnerability.
  • RegEnumValue(), when it can't retrieve a value and/or its data when either is too large for the buffer, is supposed to return the error ERROR_MORE_DATA and say what the required size for the buffer should be. However, it seems to do this only when the buffer to receive the actual data is too small. When the buffer that receives the value name is too small, it just returns ERROR_MORE_DATA (possibly explaining the cases of programs crashing on long Registry value names caused by infinite loops).

So it seems to me the easiest way to fix this problem, is to use two buffer sizes for the value name, depending on the Windows version:
  • For Windows 95/98/98SE/ME: use a buffer length of 260 bytes - since the system can't handle setting values with longer names anyway.
  • For Windows NT4/2000/XP/2003/Vista: use a buffer length of 16400 or so bytes - since the maximum allowem by the system is 16383.

So there you have it. The problem, the cause, and the solution. Case closed. :)


walied said...

wonderful explanation for this dangerous vulnerability..

ur explanation showed me why the registry values created after this overlong registry value, were also invisible.

Jim said...

Rather than allocating a 16K buffer for this, you can use the RegQueryInfoKey API function to get the length of the longest value name. Then, you can allocate a buffer that's only as long as you need.

Note - the lpcMaxValueNameLen element will be filled with the number of unicode characters that make up the maximum name length, not including a terminating null. You need to increase this by one to account for a null, and, depending on your locale settings, you may need to double it first.