0x00 前言
在上篇文章《渗透技巧——”隐藏”注册表的创建》介绍了Poweliks使用过的注册表隐藏技术,分析原理,编写c程序实现功能
本文将做进一步测试,分享一种更为”隐蔽”的方法(该方法暂未找到公开资料,待定)
0x01 简介
本文将要介绍以下内容:
- 使用Win32 API读取时的错误
- “ ”放在字符串中间的情况
- 其他Native API(如NtCreateFile)的应用
- 更加隐蔽的利用方法
- 防御检测
0x02 隐藏原理
对于Windows系统,” ”(即0x0000)会被识别为字符串的结束符
所以在对该字符串读取的过程中,遇到开头的” ”,会被解析成结束符,提前截断,导致读取错误
而使用Native API设定注册表,需要使用结构体OBJECT_ATTRIBUTES作为参数, 指定读取的字符串长度
只要长度设定正常,就能够读取正确的字符串,避免这个bug
利用的关键:
使用Native API多了一个参数,能够指定读取字符串的长度
那么,对该问题展开进一步思考,就有了如下测试
0x03 使用Win32 API读取时,具体是什么样的错误?
使用HiddenNtRegistry创建测试注册表键值,c++调用代码如下:
printf("=================Normal Key=================n");
printf("1.CreateKey:n");
MyCreateKey("\Registry\Machine\Software\test1");
printf("2.OpenKey:n");
hKey = MyOpenKey("\Registry\Machine\Software\test1");
printf("3.SetValueKey:n");
MySetValueKey(hKey,"test1","0123456789abcdef",REG_SZ);
printf("=================Hidden Key=================n");
printf("1.OpenKey:n");
hKey = MyOpenKey("\Registry\Machine\Software\test1");
printf("2.SetHiddenValueKey:n");
MySetHiddenValueKey(hKey," test1","hidden0123456789abcdef",REG_SZ);
printf("3.QueryHiddenValueKey:n");
MyQueryHiddenValueKeyString(hKey," test1");
程序实现以下功能:
- 创建注册表键值test1,内容为0123456789abcdef
- 创建注册表键值 test1,内容为hidden0123456789abcdef
运行如下图
使用Win32 API RegQueryValueEx尝试读取以上两个注册表键值
关键代码如下:
LONG lReturnCode = 0;
HKEY hkey;
LPCTSTR RegPath = _T("Software\test1");
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegPath, 0, KEY_READ, &hkey))
{
char dwValue[1024];
DWORD dwSzType = REG_SZ;
DWORD dwSize = sizeof(dwValue);
lReturnCode = ::RegQueryValueEx(hkey, _T("test1"), 0, &dwSzType, (LPBYTE)&dwValue, &dwSize);
if(lReturnCode != ERROR_SUCCESS)
{
printf("lReturnCode:%dn",lReturnCode);
if(lReturnCode = 2)
printf("ERROR_FILE_NOT_FOUNDn");
return 0;
}
printf("RegQueryValue:");
for (int i=0;i<dwSize/2-1;i++)
{
printf("%c",dwValue[i*2]);
}
}
::RegCloseKey(hkey);
读取注册表键值test1,成功获取内容
读取注册表键值 test1,修改代码如下:
lReturnCode = ::RegQueryValueEx(hkey, _T(" test1"), 0, &dwSzType, (LPBYTE)&dwValue, &dwSize);
读取失败,返回ERROR_FILE_NOT_FOUND
验证上文原理: 由于” ”的作用,字符串提前被截断,识别为空字符,导致无法获得名称
接着做进一步尝试
0x04 “ ”放在字符串中间会怎样?
HiddenNtRegistry的代码为:
printf("1.OpenKey:n");
hKey = MyOpenKey("\Registry\Machine\Software\test2");
printf("2.SetHiddenValueKey:n");
MySetHiddenValueKey2(hKey,"test2 abc","hidden0123456789abcdef",REG_SZ);
printf("3.QueryHiddenValueKey:n");
MyQueryHiddenValueKeyString2(hKey,"test2 abc");
注:
原工程HiddenNtRegistry中的MySetHiddenValueKey
函数和MyQueryHiddenValueKeyString
函数需要作适当修改,重新计算字符串长度,新的函数命名为MySetHiddenValueKey2
和MyQueryHiddenValueKeyString2
程序实现以下功能:
- 创建注册表键值test2 abc,内容为hidden0123456789abcdef
- 读取注册表键值test2 abc的内容
运行如下图
使用regedit.exe查询该键值,弹框提示无法获取,如下图
这里可以做一个大胆的尝试:
既然test2 abc中的” ”会截断字符串,那么我们再创建一个名为test2的键值会怎么样呢?
创建注册表键值test2,内容为0123456789abcdef,关键代码如下:
hKey = MyOpenKey("\Registry\Machine\Software\test2");
MySetValueKey(hKey,"test2","0123456789abcdef",REG_SZ);
再次使用regedit.exe查看注册表,有趣的事情发生了,如下图
查询注册表键值RegistryMachineSoftwaretest2不再弹框报错,而是显示两个名为test2的键值,内容均为0123456789abcdef
我们知道,注册表不允许创建两个名称相同的注册表键值,而上述测试产生的两个同名键值,实际上是因为其中的一个被错误的截断,导致显示键值名称相同,键值内容也相同,为0123456789abcdef(实际上内容为hidden0123456789abcdef)
这样我们就又多了一种”隐藏”注册表的方法,相比于之前的在首位填” ”,这个隐藏方法最大的优点是使用regedit.exe查看该键值时不会弹框报错,隐蔽效果更好,同时又具有欺骗性,同正常键值内容相同
对比如下图
显示键值内容为0123456789abcdef,实际上为hidden0123456789abcdef
0x05 其他Native API(如NtCreateFile)能否应用?
参考NtCreateKey的实现思路,测试其他Native API,例如NtCreateFile,在创建文件时是否存在相同问题?
使用NtCreateFile创建特殊文件: