渗透技巧——Windows系统的文件恢复与删除
0x00 前言
在渗透测试中,文件的恢复和删除好比矛与盾。
文件恢复是指恢复目标系统已删除的文件,而文件删除是指删除在目标系统上使用过的工具,避免被恢复。
0x01 简介
本文将要介绍以下内容:
- 文件删除的原理
- 文件恢复的原理
- 利用PowerForensics恢复文件
- 利用SDelete删除文件
- 利用SDelete删除文件后,能否使用PowerForensics恢复
- 通过文件覆盖,防止文件被恢复
- 枚举所有进程,搜索指定文件的句柄,释放该句柄,解除文件占用,实现文件删除
- 程序实现细节和开源代码
0x02 文件删除与恢复的原理
参考资料:
http://www.ntfs.com/ntfs_basics.htm
基本概念
Windows文件系统大都使用NTFS(New Technology File System)技术
NTFS中每个文件对应一个主文件表(Master File Table,MFT)
MFT作为文件索引,存储文件的属性
文件删除的直观理解:
只修改了MFT(即文件属性),没有修改删除文件的内容
文件恢复的直观理解:
恢复文件的MFT即可
简单测试
新建文件test.txt,写入内容0123456789
使用工具:WinHex
下载地址:
http://www.x-ways.net/winhex/
选择Tools
-> Open Disk
,选择盘符
找到文件test.txt,右键 -> Navigation
-> Seek FILE Record
查看test.txt的MFT信息,如下图
MFT的结构如下图
注:
图片截取自http://www.blogfshare.com/detail-ntfs-filesys.html
接下来删除文件test.txt,并且清空回收站的文件
使用WinHex再次查看硬盘内容,关闭当前盘符,重新选择Tools
-> Open Disk
,选择盘符
弹框提示,选择更新快照,如下图
再次查看MFT结构,如下图
经对比发现,区别如下:
- 偏移0x08
- 偏移0x10,数值加1
- 偏移0x16,由1变为0
在WinHex界面中选择恢复文件,成功恢复文件
注:
恢复成功的前提是该文件尚未被覆盖
综上,文件恢复的原理可以简单理解如下:
文件删除操作仅修改了文件的MFT,如果文件内容尚未被覆盖,就能恢复文件
0x03 利用PowerForensics恢复文件
文件恢复软件有多种,这里给出一个通过powershell实现文件恢复的工具:PowerForensics
工程地址:
https://github.com/Invoke-IR/PowerForensics/
下载地址:
https://github.com/Invoke-IR/PowerForensics/releases
注:
PowerForensicsv2.zip对应Powershell v2,为Win7和Sever2008默认版本
工具使用方法可直接参考xpn的博客:
https://blog.xpnsec.com/offensive-forensics/
获得所有可供恢复的文件列表,powershell命令如下:
powershell -executionpolicy bypass
import-module .\PowerForensicsv2.psd1
Get-ForensicFileRecord | Where {$_.Deleted -eq $true} | Select FullName
恢复指定的文件C:\test.txt
,保存为recovered.txt
:
$file = Get-ForensicFileRecord | Where {$_.FullName -eq "C:\test.txt"}
$file.CopyFile("recovered.txt")
0x04 防止文件被恢复
1、使用工具SDelete
下载地址:
https://docs.microsoft.com/en-us/sysinternals/downloads/sdelete
删除命令如下:
sdelete64.exe -accepteula C:\test.txt
2、通过c++实现
经过0x01的原理分析,我们知道,只要覆盖原文件即可避免文件被恢复
最简单的实现思路:
修改原文件的内容,填充随机字符串,然后再删除文件
我写了一个简单的测试代码,将待删除的文件内容先填充为0,再删除文件,即使文件被恢复,内容也全是0,可供参考的c代码如下:
#include <windows.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("\nOverwrite the file,avoid being restored\n\n");
printf("Usage:\n");
printf("%s <File Path>\n",argv[0]);
return 0;
}
printf("[*]Try to overwrite file <%s> ", argv[1]);
FILE* fp;
int err = fopen_s(&fp, argv[1], "rb+");
if (err != 0)
{
printf("\n[!]Openfile error!");
return 0;
}
fseek(fp, 0, SEEK_END);
int len = ftell(fp);
char *buf = new char[len];
memset(buf, 0, len);
fclose(fp);
err = fopen_s(&fp, argv[1], "wb+");
if (err != 0)
{
printf("\n[!]Openfile error!");
return 0;
}
fwrite(buf, len, 1, fp);
fclose(fp);
printf("done\n");
printf("[*]Try to delete file <%s> ", argv[1]);
if(DeleteFile(argv[1])!=0)
printf("done\n");
else
printf("error\n");
return 0;
}
0x05 解除文件占用
在实际删除文件的时候,常常会碰到文件被占用导致无法删除的情况
这里需要找到占用文件的进程,获得文件句柄,释放句柄后才能删除文件
实现思路如下:
- 程序提升至debug权限
- 枚举所有进程
- 获得指定文件的句柄
- 释放该句柄
具体对应到程序实现上,需要注意以下问题:
1、提升至debug权限
BOOL EnableDebugPrivilege(BOOL fEnable)
{
BOOL fOk = FALSE;
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return(fOk);
}
2、枚举所有进程,获得指定文件的句柄
使用内核API NtQuerySystemInformation查询SystemHandleInformation,获得所有进程的句柄
筛选出类型为文件的句柄: ObjectTypeNumber = 0x1e
如果无法打开句柄对应的进程,留下标志位,不再重复打开该进程
过滤出有可能导致挂起的句柄,利用API WaitForSingleObject进行判断
3、释放句柄
使用内核API NtQuerySystemInformation查询SystemHandleInformation得到的句柄是伪句柄,无法直接释放
需要使用API DuplicateHandle将伪句柄转为实句柄
函数模型如下:
BOOL WINAPI DuplicateHandle(
_In_ HANDLE hSourceProcessHandle,
_In_ HANDLE hSourceHandle,
_In_ HANDLE hTargetProcessHandle,
_Out_ LPHANDLE lpTargetHandle,
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwOptions
);
第7个参数设置为DUPLICATE_CLOSE_SOURCE,表示会释放源进程中的句柄
具体参数如下:
DuplicateHandle(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, DUPLICATE_CLOSE_SOURCE)
完整代码已开源,地址如下:
https://github.com/3gstudent/Catch-specified-file-s-handle
代码实现了枚举当前系统的所有进程,找到指定文件的句柄,对其释放
不仅可以用来解除文件占用,而且可以用来禁用日志的某些功能
例如如果释放了system.evtx的句柄,那么日志服务无法向system.evtx写入日志,导致system.evtx下的日志失效
0x06 小结
本文简单介绍了文件删除与恢复的原理,测试工具,编写程序利用文件覆盖防止文件被恢复,并且解决了文件占用的问题,开源代码。
站在渗透的角度,一是想办法恢复目标系统的文件,二是安全删除自己的工具,避免被恢复。
站在防御的角度,清除重要的文件可使用工具SDelete进行安全删除