首页
社区
课程
招聘
[原创]一个规避安装包在当前目录下被DLL劫持的想法
发表于: 2022-7-16 17:43 17829

[原创]一个规避安装包在当前目录下被DLL劫持的想法

2022-7-16 17:43
17829

本文描述了一个避免安装包目录下被DLL劫持的思路。

之前碰到了一个被DLL劫持的情况,通过打包工具做好的安装包在启动会去加载某个系统DLL,设想一种情况,我们下载文件时经常是经常是放在了默认目录下,假如安装包和恶意DLL放在同一目录下,以管理员权限运行安装包时会优先加载本地的恶意DLL,前提是这个dll不在注册表的KnownDlls项中。即:对于不在KnownDlls项下的系统dll,很有可能会被当前进程目录下的DLL劫持。

注册表下的KnownDlls项保存了多项内容,当进程加载时如果需要加载的dll中在KnownDlls中存在,那么系统会优先在系统路径下加载此DLL。

为了解决这个问题,想了两个思路,这两个思路都需要对安装包进行一个封装,有一个释放程序将安装包嵌入到自己的资源文件中,但是实际操作过程中第一个还无法实现,如下:

1.有一个释放程序以管理员权限运行,操作KnownDlls,将即将被劫持的DLL假如到KnownDlls注册表项中,然后再释放安装包到当前目录或者其他目录下都可以,接着运行安装包程序,但是在实际操作过程中发现,很难获取到修改KnownDlls项的权限,目前还没有在代码层面想到办法,所以第一个思路失败。

2.有一个释放程序以管理员权限运行,在系统的临时目录(或者其他文件夹)下创建一个文件夹,创建时设置文件夹的访问权限,只能以管理员权限或者更高的权限才可以访问,接着将安装包释放到此目录下,此目录下只有安装包程序,没有恶意攻击者存放的恶意DLL,这样就避免了安装包运行时被恶意DLL在当前进程目录下劫持,这种方式只适用于NTFS文件系统,现在大部分的Windows硬盘都使用此文件系统,NTFS可以存储许多额外的信息。

第一个思路需要具体到某个dll的名字写入到注册表,第二个方法应该实现更加简单。

第二个思路的重点在于创建一个新的文件夹,这个文件夹只有管理员权限及以上权限才可以读写,而恶意攻击者一般情况下都运行于普通用户权限或者受限管理员权限下,有两种方式:

1.使用现成的已经限制号权限的文件夹,如将文件夹存放在C:\ProgramData(通过环境变量%ALLUSERSPROFILE%可以获得),这个文件夹下的内容默认情况下只有不受限管理员及以上权限才可以读写,可以将安装包释放到这个文件夹及子文件夹下,创建目录时文件描述符传入空即可。

2.可以在临时目录C:\Users\用户名\AppData\Local\Temp(通过%TEMP%来获取)下来创建文件夹,这个文件夹是everyone用户都可以读写的,所以释放程序以管理员权限运行时需要创建控制访问权限的目录,只能让不受限管理员和更高权限来读写此文件夹,然后将程序释放到此目录下,以不受限管理员权限运行安装包,此时刚创建目录下只有安装包,也不用担心被恶意攻击者在当前进程所在目录下劫持DLL了,反过来如果恶意攻击者可以在此目录下存放恶意DLL,那就意味着恶意攻击者已经拥有了不受限的管理员,本身就拥有很高权限了,正常情况下也不需要通过DLL劫持来提权了。

有的高权限程序会去修改C:\ProgramData文件夹的权限,所以自己创建一个新的文件夹更可靠一些。
下面代码演示了如何在临时文件夹下创建带有访问权限控制的文件夹:

程序打开当前进程的Token令牌,获取令牌默认的访问控制权限DACL,然后将ACL赋值给SECURITY_ATTRIBUTES结构中,使用此结构来创建文件夹,由于当前进程是以管理员权限启动的,所以只有high权限或者system打开此进程,现在将进程令牌赋予文件夹,所以只有high权限或者system权限可以拥有对此文件夹的完全访问权限。

还有几个步骤的代码没有编写,此处大概写下代码思路:

1.获取当前释放程序的资源

2.创建有权限的文件夹,文件夹的名字可以创建一个随机的GUID字符串,如果重复目录就继续随机字符串,直到一个不存在的GUID字符串,使用此字符串创建目录

3.释放安装包到刚才创建的文件夹

4.调用CreateProcess启动安装包

5.等待安装进程结束后,删除刚才创建的文件夹,删除垃圾文件

补充:这个释放程序只依赖少数的几个系统DLL,或者说导入表中依赖的DLL都存在于KnownDlls注册表中,防止释放程序被DLL劫持。

下面代码可以对文件赋予当前用户完全控制的权限,参考自https://www.cnblogs.com/findumars/p/5928694.html

当前用户拥有读写执行等权限,system进程无法读写文件中的内容,如下:
图片描述
此处记录,以备后用。

 
 
 
 
 
 
 
 
 
int queryTokenDaclToWriteFile()
{
    TCHAR buffFileName[1024] = { 0 };
    ExpandEnvironmentStrings(L"%temp%", buffFileName, _countof(buffFileName));
    if (PathIsDirectory(buffFileName))
    {
        _stprintf_s(buffFileName, countof(buffFileName), L"%s\\%s", buffFileName, L"123");;
    }
    SECURITY_ATTRIBUTES sa;
    SECURITY_DESCRIPTOR sd;
 
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    HANDLE hToken = NULL;
    OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
    DWORD dwReturnLength = 0;
    char *buffToken = NULL;
    GetTokenInformation(hToken, TokenDefaultDacl, NULL, 0, &dwReturnLength);//需要考虑内存空间不足的情况
    buffToken = (char *)LocalAlloc(LPTR, dwReturnLength);
    GetTokenInformation(hToken, TokenDefaultDacl, buffToken, dwReturnLength, &dwReturnLength);
    PTOKEN_DEFAULT_DACL pToken_Default_DACL = (PTOKEN_DEFAULT_DACL)buffToken;
    SetSecurityDescriptorDacl(&sd, TRUE, pToken_Default_DACL->DefaultDacl, FALSE);
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = FALSE;
    sa.lpSecurityDescriptor = &sd;
    if (CreateDirectoryW(buffFileName, &sa))
    {  
        MessageBox(NULL, L"Success", NULL, 0);
    }
    else
    {
        MessageBox(NULL, L"Fail", NULL, 0);
    }
    LocalFree(buffToken);
    buffToken = NULL;
    //之后删除文件夹 清理临时文件
    return 0;
}
int queryTokenDaclToWriteFile()
{
    TCHAR buffFileName[1024] = { 0 };
    ExpandEnvironmentStrings(L"%temp%", buffFileName, _countof(buffFileName));
    if (PathIsDirectory(buffFileName))
    {
        _stprintf_s(buffFileName, countof(buffFileName), L"%s\\%s", buffFileName, L"123");;
    }
    SECURITY_ATTRIBUTES sa;
    SECURITY_DESCRIPTOR sd;
 
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    HANDLE hToken = NULL;
    OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
    DWORD dwReturnLength = 0;
    char *buffToken = NULL;
    GetTokenInformation(hToken, TokenDefaultDacl, NULL, 0, &dwReturnLength);//需要考虑内存空间不足的情况
    buffToken = (char *)LocalAlloc(LPTR, dwReturnLength);
    GetTokenInformation(hToken, TokenDefaultDacl, buffToken, dwReturnLength, &dwReturnLength);
    PTOKEN_DEFAULT_DACL pToken_Default_DACL = (PTOKEN_DEFAULT_DACL)buffToken;
    SetSecurityDescriptorDacl(&sd, TRUE, pToken_Default_DACL->DefaultDacl, FALSE);
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = FALSE;
    sa.lpSecurityDescriptor = &sd;
    if (CreateDirectoryW(buffFileName, &sa))
    {  
        MessageBox(NULL, L"Success", NULL, 0);
    }
    else
    {
        MessageBox(NULL, L"Fail", NULL, 0);
    }

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-7-17 12:25 被0346954编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (3)
雪    币: 6
活跃值: (3290)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
system 进程是不是无视文件夹的任何权限设置??
2022-7-16 18:26
0
雪    币: 3754
活跃值: (5916)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
3
也不是,我碰巧创建一个任何人都无法访问的文件(右键属性 安全那一列下是空的,没有指定用户),system权限也无法删除,还是通过编辑权限,指定了一个用户,才可以删除的,system权限也需要受到文件ACL安全权限的管理。如果创建文件时指定了只有某个普通用户可以访问,那system应该也不可以访问,个人猜测,明天试试。
2022-7-16 19:42
0
雪    币: 3754
活跃值: (5916)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
4
试了试,在帖子后面贴了一段代码:创建一个只有当前用户具有完全控制权限的文件,system无法读写,但是无法删除,另外测试了下如果没有指定用户,那么system也无法读写,但是可以删除。
2022-7-17 11:21
0
游客
登录 | 注册 方可回帖
返回
//