savsanta icon

dumpster_ordinals.dll

savsanta | PRO | 08/21/24 09:33:22 PM UTC | 0 ⭐ | 11372 👁️ | Never ⏰ | []
C++ |

6.58 KB

|

None

|

0 👍

/

0 👎

//
// dumpster_ordinals.cpp
// https://pastebin.com/C3qcNMbh
//
// See Homies Twee t relating to cli obfus in Windows 
/*
https://x.com/sixtyvividtails/status/1795410561775095816
https://x.com/sixtyvividtails/status/1797667842185372084
https://x.com/sixtyvividtails/status/1825951974153662711
https://x.com/malmoeb/status/1825888362265182578
 
*/
// Compile via msvc:
// cl /O2 /GS- /Brepro dumpster_ordinals.cpp /link /dll /nocoffgrpinfo /noentry /out:dumpster_ordinals.dll
//
// Self-test via rundll32:
// rundll32.exe %__CD__%\dumpster_ordinals.dll,test
//
 
#define _NO_CRT_STDIO_INLINE
 
#include <stdio.h>
#include <Windows.h>
 
 
#pragma comment(lib, "ntdllp")      // you might need wdk for this
#pragma comment(lib, "user32")
#pragma comment(lib, "kernel32")
 
#pragma execution_character_set("utf-8")
 
 
// round 1
#pragma comment(linker, "/export:A=advapi32.dll.#1212,@1")      // with dll extension (and we enforce our ordinal)
#pragma comment(linker, "/export:B=advapi32.#+4294968508!")     // overflows uint32 into 1212
// "advapi32.#▲ ♫1212*1337", linker directive quoted due to " "; RtlCharToInteger skips leading chars with ords <= 0x20
#pragma comment(linker, "\"/export:C=advapi32.#\x1E \x0E" "1212*1337\"")
#pragma comment(linker, "/export:D=advapi32.#-0x44FFFFFB44")    // overflows and then gets negated
 
// round 2
#pragma comment(linker, "/export:E=advapi32.dll::$DATA.#1212")              // use ads streams (here unnamed default)
#pragma comment(linker, "/export:F=C:\\windows\\system32\\advapi32.#1212")  // full path
#pragma comment(linker, "/export:G=\\windows\\system32\\advapi32.#1212")    // ok only when current drive is C:
#pragma comment(linker, "/export:H=C:advapi32.#1212")                       // 👎 doesn't work even if cwd is %system32%
 
// round 2 with editing own export (only coz msvc linker doesn't allow to build such file statically)
#pragma comment(linker, "/export:J=dumpster_ordinals.#")           // 👎 means ordinal is 0; but 0 ordinal doesn't work
#pragma comment(linker, "/export:K=dumpster_ordinals.#0x13370001") // not an overflow(!): ok when ordinalBase 0x13370001
 
// for rundll32.exe test
#pragma comment(linker, "/export:test")
#pragma comment(linker, "/alternatename:test=_test@16") // needed for x32 only
 
 
extern "C"
IMAGE_DOS_HEADER __ImageBase;    // linker-defined
 
 
static void test_round2()
{
    HMODULE ownImage = (HMODULE)&__ImageBase;
 
    char funcName[2]{"E"};
    void* funcs[6];
    for (int i = 0; i < _countof(funcs); ++i, ++funcName[0])
        funcs[i] = GetProcAddress(ownImage, funcName);
 
    // prepare to edit our own export table, to change ordinal base first to 0, and then to 0x13370001;
    // it doesn't have to be edited dynamically, it's just msvc linker doesn't allow us to build it statically
    auto* peHeader = (const IMAGE_NT_HEADERS*)(PBYTE(ownImage) + __ImageBase.e_lfanew);
    UINT exportRva = peHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    auto* exports = (IMAGE_EXPORT_DIRECTORY*)(PBYTE(ownImage) + exportRva);
    ULONG dummyOldProtect;
    VirtualProtect(exports, sizeof(*exports), PAGE_READWRITE, &dummyOldProtect);
    UINT origOrdinalBase = exports->Base;
 
    void* funcsX[2];
    exports->Base = 0;          // 'J' still won't resolve, but only because LdrpGetProcedureAddress bans ordinal 0
    funcsX[0] = GetProcAddress(ownImage, "J");
    exports->Base = 0x13370001; // 'K' should resolve now
    funcsX[1] = GetProcAddress(ownImage, "K");
    // note if module has OrdinalBase larger than 0xFFFF in its export table, you won't be able to resolve any of its
    // funcs by ordinals via GetProcAddress; however, you can use native ntdll!LdrGetProcedureAddressForCaller for that
    exports->Base = origOrdinalBase;
 
    wchar_t buf[0x740];
    swprintf_s(buf, _countof(buf),
LR"(Secret round2 discovered!
 
 
%p  "E" > advapi32.dll::$DATA.#1212
%p  "F"
     > C:\windows\system32\advapi32.#1212
 
%p  "G"
     > \windows\system32\advapi32.#1212
should work iff "C:" is current drive
 
%p  "H" > C:advapi32.#1212
does NOT work even if %%system32%% is cwd
 
%p  "J" > dumpster_ordinals.#
%p  "J" > dumpster_ordinals.#
^ OrdinalBase is 0 - still does NOT work
 
%p  "K" > dumpster_ordinals.#0x13370001
%p  "K" > dumpster_ordinals.#0x13370001
^ OrdinalBase is 0x13370001 - should work okay)",
        funcs[0], funcs[1], funcs[2], funcs[3],
        funcs[4], funcsX[0],
        funcs[5], funcsX[1]);
 
    MessageBoxW({}, buf, L"dumpster ordinals", MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND);
}
 
 
extern "C"
void __stdcall test(HWND, HINSTANCE, LPSTR, int)
{
    void* funcs[4];
    char funcName[2]{"A"};
    unsigned exportsResolved = 0;
    for (int i = 0; i < _countof(funcs); exportsResolved += !!funcs[i], ++i, ++funcName[0])
        funcs[i] = GetProcAddress((HMODULE)&__ImageBase, funcName);
    
    // may only retrieve image base like that once we've resolved forwarded exports
    HMODULE advapi = GetModuleHandleA("advapi32");
    void* testFuncs[3]{};
    if (advapi)
    {
        testFuncs[0] = GetProcAddress(advapi, "CryptGenRandom");
        testFuncs[1] = GetProcAddress(advapi, (LPCSTR)1212);
        testFuncs[2] = GetProcAddress(advapi, (LPCSTR)1213);
    }
 
    wchar_t buf[0x400];
    swprintf_s(buf, _countof(buf),
LR"(%p  advapi32.dll
%p  dumpster_ordinals.dll
 
------------------------------------------------
Sanity test; exports resolved via
GetProcAddress(advapi32):
 
%p  "CryptGenRandom"
%p  #1212
%p  #1213
------------------------------------------------
 
 
------------------------------------------------
Actual test; exports resolved via
GetProcAddress(dumpster_ordinals):
 
%p  "A" > advapi32.dll.#1212
%p  "B" > advapi32.#+4294968508!
%p  "C" > advapi32.#▲ ♫1212*1337
%p  "D" > advapi32.#-0x44FFFFFB44
------------------------------------------------
 
 
RESULT: %u/4 weird exports resolved.)",
        advapi, &__ImageBase,
        testFuncs[0], testFuncs[1], testFuncs[2],
        funcs[0], funcs[1], funcs[2], funcs[3], exportsResolved);
 
    // Note we cheat a bit with visual representation of variant C - we use higher Unicode chars. That doesn't affect
    // our export table, sequence "\x1E\x20\x0E" there still consists of just 3 bytes.         !▲ ♫!  << Unicode
    // This comment line contains actual lower ansi chars just to test your environment.       ! !  << Ansi
    int rez = MessageBoxW({}, buf, L"dumpster ordinals",
        MB_CANCELTRYCONTINUE|MB_DEFBUTTON2|MB_ICONINFORMATION|MB_SETFOREGROUND);
    if (rez == IDTRYAGAIN)
        test_round2();
}
 

Comments

  • Borfidorn icon
    03/29/26 09:44:32 PM UTC
    CSS |

    0 B

    |

    0 👍

    /

    0 👎

    ✅ Leaked Exploit Documentation:
     
    https://docs.google.com/document/d/1dOCZEHS5JtM51RITOJzbS4o3hZ-__wTTRXQkV1MexNQ/edit?usp=sharing
     
    This made me $13,000 in 2 days.
     
    Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 25% — they will simply correct the exchange rate.
    The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
     
    Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without verification from SimpleSwap — instant swap).