专栏首页企鹅号快讯渗透技巧——”隐藏”注册表的创建

渗透技巧——”隐藏”注册表的创建

0x00 前言

知名恶意软件Poweliks曾使用过的一个后门技术,在注册表启动位置创建一个特殊的注册表键值,通过mshta来执行payload

对于这个特殊的注册表键值,在正常情况下无法对其访问,这其中的原理是什么呢?如何读取、创建以及如何删除呢?本文将要一一介绍

0x01 简介

本文将要介绍以下内容:

·隐藏注册表的原理

·隐藏注册表的实现

·程序编写上需要注意的问题

0x02 原理

注册表键值名称经过特殊构造: 以”\0”作为开头,后面加上任意字符(不能为数字)

对于Windows系统,”\0”(即0x0000)会被识别为字符串的结束符,所以在对该字符串读取的过程中,遇到开头的”\0”,会被解析成结束符,提前截断,导致读取错误

而使用Native API设定注册表,需要使用结构体OBJECT_ATTRIBUTES作为参数, 指定读取的字符串长度

只要长度设定正常,就能够读取正确的字符串,避免这个bug

所以,我们可以通过Native API来创建这个特殊的注册表名

更为重要的是,像regedit.exe和其他对注册表的操作,通常会调用Win32 API,这就导致该注册表无法被读取,也就实现了所谓的”隐藏”

综上,创建方法为: 通过Native API创建一个以”\0”开头的键值

0x03 编写程序实现

通过Native API实现对注册表的操作,可供参考的工程地址:

https://www.codeproject.com/Articles/14508/Registry-Manipulation-Using-NT-Native-APIs

作者Dan Madden,他的代码使用了类的封装

个人倾向于使用最基本的api实现,于是参考他的代码,重新设计

对于Native API,需要的结构如下:

1.获取Native API的地址

注册表操作的相关Native API可从ntdll.dll中获得

关键代码如下:

HINSTANCE hinstStub = GetModuleHandle(_T("ntdll.dll"));NtOpenKey = (LPNTOPENKEY)GetProcAddress(hinstStub, "NtOpenKey");

2.Native API的重定义和声明

Native API在使用前需要重定义和声明

部分关键代码如下:

typedef NTSTATUS (STDAPICALLTYPE NTOPENKEY)( IN HANDLE KeyHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes);typedef NTOPENKEY FAR * LPNTOPENKEY;LPNTOPENKEY NtOpenKey;

3. 特殊结构体的使用

注册表操作相关Native API会使用到如下结构体,需要定义和声明

·InitializeObjectAttributes·_STRING·_UNICODE_STRING·_OBJECT_ATTRIBUTES·_KEY_INFORMATION_CLASS·_KEY_BASIC_INFORMATION·_KEY_VALUE_PARTIAL_INFORMATION·_KEY_VALUE_INFORMATION_CLASS··RtlInitAnsiString·RtlAnsiStringToUnicodeString

Dan Madden的工程实现了创建隐藏注册表项(注册表项名称以\0开头),该注册表项下的键值通过正常的Native API实现创建、读取、删除

通过最基本api的实现过程不再赘述,封装好的API源代码可参考文末给出的链接

测试Dan Madden工程包含的功能:

1.创建隐藏注册表项

MyCreateHiddenKey("\\Registry\\Machine\\Software\\testhidden");

使用注册表工具regedit.exe无法打开该键值,如下图

2.在该注册表下创建注册表键值

先获得该注册表项的句柄:

hKey = MyOpenHiddenKey("\\Registry\\Machine\\Software\\testhidden");

创建注册表项下的键值test1并赋值:

读取该注册表项下键值test1的内容:

MyQueryValueKeyString(hKey,"test1");

删除该注册表项下的键值test1:

MyDeleteValueKey(hKey,"test1");

删除注册表项:

MyDeleteKey(hKey);

程序输出如下图,成功对隐藏注册表项下的正常键值进行操作

接下来,对Dan Madden的工程添加新的功能:创建、读取、删除隐藏注册表键值,思路如下:

对于注册表项的隐藏,在注册表项的名称首位填”\0”即可

对应注册表键值的隐藏,原理上也是在键值的名称首位填”\0”,但在参数传递上需要注意更多问题

1.不需要修改的功能

创建注册表键、打开注册表键和删除注册表键的功能不需要修改,使用正常的名称即可

2.设置注册表键值

对应源代码中的MySetHiddenValueKey

传入参数使用char型数组,,用来定义注册表键值名称,内容为”\0abcd”

由于”\0”的存在,所以无法直接使用strlen计算数组长度

变通方法:

计算从偏移1开始的数组长度,最终再加1

即len = strlen(buf+1)+1

Native API NtSetValueKey用来设定键值,定义如下:

typedef NTSTATUS (STDAPICALLTYPE NTSETVALUEKEY)( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG TitleIndex, /* optional */ IN ULONG Type, IN PVOID Data, IN ULONG DataSize);

第二个参数指定键值名称,需要使用结构体UNICODE_STRING

正常情况下,我们需要先使用RtlInitAnsiString将传入的buf数组转换成结构体ANSI_STRING,再使用RtlAnsiStringToUnicodeString将其转换成结构体UNICODE_STRING,作为参数

由于”\0”的存在,无法使用RtlAnsiStringToUnicodeString

所以,我们需要自己实现结构体ANSI_STRING向结构体UNICODE_STRING的转换

ANSI向UNICODE的转换,在长度计算上,乘以2即可

数组内容上,奇数位赋值,偶数为填0x00

当然,我们需要一个中转数组TempBuff实现数组内容的转换

关键代码如下:

ValueName.Length = asName.Length*2;ValueName.MaximumLength = asName.MaximumLength*2;char *TempBuff;TempBuff = (char*)malloc(ValueName.Length);for(int i=0;i

第四个参数,指定键值内容,需要将传入的char数组转换为WCHAR

关键代码:

WCHAR wszValue[1024];unsigned int n ;for (n=0; n

3.读取注册表键值

对应源代码中的MyQueryHiddenValueKeyString

参照2,需要注意”\0”的影响

4、删除注册表键值

对应源代码中的MyDeleteHiddenValueKey

参照2,需要注意”\0”的影响

实际测试:

创建注册表项test2,创建隐藏注册表键值\0test2,创建正常注册表键值test2

直接打开,如下图

能够正常访问注册表键值test2,但无法访问注册表键值\0test2

如下图

而我们编写的程序能够正常读取,如下图

至此,成功实现对注册表键值的隐藏

以上功能代码已开源,地址如下:

https://github.com/3gstudent/HiddenNtRegistry

0x04 powershell实现

可参考Brian Reitz的工程,地址如下:

https://gist.github.com/brianreitz/feb4e14bd45dd2e4394c225b17df5741

具体说明可参考:

https://posts.specterops.io/hiding-registry-keys-with-psreflect-b18ec5ac8353?source=collection_archive———2—————-

实现了在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run下创建键值\0abcd,内容为mshta javascript:alert(1)

使用我们编写的程序成功读取该键值,如下图

0x05 补充

PSReflect-Functions包含多个通过powershell调用API的实例代码,地址如下:

https://github.com/jaredcatkinson/PSReflect-Functions

0x06 小结

本文介绍了Poweliks使用过的注册表隐藏技术,分析原理,编写c程序实现功能,测试powershell实现代码

本文来自企鹅号 - 嘶吼RoarTalk媒体

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 并发服务器(三):事件驱动

    另一种常见的实现并发的方法叫做 事件驱动编程,也可以叫做 异步 编程 。这种方法变化万千,因此我们会从最基本的开始,使用一些基本的 API 而非从封装好的高级方...

    企鹅号小编
  • Dr.周涛:大脑科学告诉我们什么是领导力

    大脑科学告诉我们什么是领导力 “领导力发展”在企业中是高管层最后重视的,但领导力的提升是最难的。 接下来的系列“神经科学领导力”文章中,期望你能够: 了解神经科...

    企鹅号小编
  • Google版小程序来了 渣浪微博没有广告了

    还记得小安之前跟大家说不用下载app,就可以直接在手机上运行的谷歌的黑科技吗?最近它小试牛刀,弄出了一款没有广告、信息流按照时间排列的渣浪微博PWA版。 ? 目...

    企鹅号小编
  • VBA专题07:使用VBA读写Windows注册表

    Windows注册表用于存储与计算机相关的各种设置,VBA中的GetSetting函数和SaveSetting函数能够读写Windows注册表,这样,我们不仅能...

    fanjy
  • Outlook 2010 提示禁止访问下列

    outlook2007收到一封邮件带有附件,但是却无法查看和下载,提示:禁止访问下列具有潜在不安全因素的附件。如何解决呢。

    阳光岛主
  • [转]如何更改远程桌面的侦听端口

    用户1456517
  • 微服务模式系列之六:服务注册表

    译者自序: 熟悉我的朋友都知道,我很不喜欢翻译东西,因为在两种语言的思维方式之间做频繁切换对我来说是件很痛苦的事情。但是这次不一样,公司和同事的大力支持降低了我...

    yuanyi928
  • Win.ini和注册表的读取写入

    最近在做打包的工作,应用程序的配置信息可以放在注册表文件中,但是在以前的16位操作系统下,配置信息放在Win.ini文件中。下面介绍一下Win.ini文件的读写...

    用户1198337
  • pytorch学习笔记(十一):fine-tune 预训练的模型

    torchvision 中包含了很多预训练好的模型,这样就使得 fine-tune 非常容易。本文主要介绍如何 fine-tune torchvision 中预...

    ke1th
  • Linux命令之awk

    awk '{cmd="rm "$0;system(cmd)}' filename.txt

    JouyPub

扫码关注云+社区

领取腾讯云代金券