渗透技巧——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信息,如下图

Alt text

MFT的结构如下图

Alt text

注:

图片截取自http://www.blogfshare.com/detail-ntfs-filesys.html

接下来删除文件test.txt,并且清空回收站的文件

使用WinHex再次查看硬盘内容,关闭当前盘符,重新选择Tools -> Open Disk,选择盘符

弹框提示,选择更新快照,如下图

Alt text

再次查看MFT结构,如下图

Alt text

经对比发现,区别如下:

  • 偏移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进行安全删除


LEAVE A REPLY

Written on June 30, 2018