Aggregator
2.2 Million Users Affected By Latest Data Exposure: 4 Tips to Stay Secure
The digitalization of data allows it to move effortlessly and be accessed from devices and places around the world within...
The post 2.2 Million Users Affected By Latest Data Exposure: 4 Tips to Stay Secure appeared first on McAfee Blog.
漏洞之王:漫谈高阶漏洞攻防演进和现实威胁
Akamai Foundation and Social Responsibility
A simple story of DsSvc, "Live and Die"
Author: k0shl of 360 Vulcan Team
OverviewDsSvc is a data sharing service that provides data sharing between processes. I have not conducted an in-depth analysis of the specific functions of this service. It is known that it provides some methods of file sharing between processes. As shown in the following figure, the process specifies a shared file through DsSvc, and calls CoCreateGuid to create a GUID as a file token, and stores information such as its token and file path into DbTable. Other processes can obtain file objects through this token and perform other files operating.
Data sharing services contain many file operations, which also bring a lot of security issues. Microsoft spent nearly a year to fix the logical issue in this service. The security issue caused by file operations is one of the types of logical vulnerability. Important partitions, it's necessary to be careful when dealing with files' operation, especially for file security attributes. I will analyze the logical vulnerabilities in DsSvc, as well as Microsoft's patch, and bypass. Let's start our story.
The Beginning of story...In November 2018, Microsoft patched a data sharing service vulnerability discovered by SandboxEscaper (PolarBear). SandboxEscaper shared details about this vulnerability on the blog. Since this article on the SandboxEscaper's blog is inaccessible, it is not possible to reference the SandboxEscaper blog address. A description of vulnerability is as follows:
Bug description: RpcDSSMoveFromSharedFile(handle,L"token",L"c:\\blah1\\pci.sys"); This function exposed over alpc, has a arbitrary delete vuln. Hitting the timing was pretty annoying. But my PoC will keep rerunning until c:\windows\system32\drivers\pci.sys is deleted. I believe it’s impossible to hit the timing on a single core VM. I was able to trigger it using 4 cores on my VM. (Sadly I wasn’t able to use OPLOCKS with this particular bug) Root cause is basically just a delete without impersonation because of an early revert to self. Should be straight forward to fix it… Exploitation wise.. you either try to trigger dll hijacking issues in 3rd party software.. or delete temp files used by a system service in c:\windows\temp and hijack them and hopefully do some evil stuff.This is an arbitrary file deletion vulnerability. The vulnerability occurs in the RPC interface RpcDSSMoveFromSharedFile. The issue existed in function PolicyChecker::CheckFilePermission. The code is as follows:
__int64 __fastcall PolicyChecker::CheckFilePermission(const WCHAR *FileName, unsigned int a2, unsigned int a3, int a4, __int64 a5) { [...CreateFile flag check...] [...Impersonate...] v12 = CreateFileW(v5, dwDesiredAccess, dwShareMode, 0i64, 4u, 0x80u, 0i64); [...RevertToSelf...] if ( v12 == (void *)-1i64 ) { [...] } else { v17 = v14; if ( !a5 || (v8 = DSUtils::GetFinalPathFromHandle(v12, a5), (v8 & 0x80000000) == 0) ) { CloseHandle(v12);//Close v12 = (void *)-1i64; if ( v17 ) return v8; DeleteFileW(v5);//arbitrary file deletion } if ( v13 != (void *)-1i64 ) CloseHandle(v13); } return v8; }In this function, FileName is defined by the user. First, the parameters DesiredAccess and ShareMode will be checked. Then RpcImpersonateClient will be called to impersonate client and call CreateFile to open the file. DsSvc will delete the file after RevertToSelf.
Although DsSvc calls ImpersonateClient to open the file, which means that when I try to open a limit file, it will fail and return, but there is still have TOCTOU issue. Before calling CeateFile, you can create a junction to the user-controllable path, so CreateFile will succeed. After that, you can change the junction to a limit directory. It will invoke DeleteFile to delete limit file finally.
In the patch, Microsoft no longer uses DeleteFile but uses the FileDispositionInfo class of SetFileInformationByHandle to delete file. Thus, calling SetFileInformationByHandle refers to the file handle created by CreateFile instead of the file path. The final deletion is a normal file opened after ImpersonateClient.
else { v17 = v14; if ( !a5 || (v8 = DSUtils::GetFinalPathFromHandle(v13, a5), v8 >= 0) ) { if ( !v17 ) { FileInformation = 1; if ( !SetFileInformationByHandle(v13, FileDispositionInfo, &FileInformation, 1u) ) { [...] } } } CloseHandle(v13); My research has started...After this vulnerability was exposed, I started my study on the logic vulnerability. One day, when I was chatting with my friend 0x9k, he talked about the complete full chain of 11 Android logical vulnerabilities used by MWRLab in Pwn2Own. Then I read MWRLab's slide about this full chain exploitation which they talked on CanSecWest.
There is a show in this slide about the tool jandroid that they use when finding android logic vulnerabilities.
After read about jandroid in slide, I came up with the idea that such this method can be used to finding logical vulnerabilities on Windows? The answer is yes.
I think I can assist the subsequent reverse engineering by parsing the path travesal of the sensitive operation on the RPC-related dll. I took some time to implement my idea. (Later in August 2019 Adam Chester published a blog post about his RPC parsing implementation idea)
I used James Forshaw's project NtApiDotNet when writing the parsing code. It can complete pre-working in my parsing framework, there is a class called NdrProcedureDefinition in NtApiDotNet, which plays a key role in RPC interface parsing, it can parse out of the RPC interface of the DCE syntax, I made a few modifications to the NdrProcedureDefinition part of the method, so that it can parse the RPC interface of the Ndr64 syntax, which may resolve more potential attack surfaces in the x64 system. (The figure below shows the analysis result of Chakra.dll which use Ndr64 syntax)
Here are two points to mention.
[+] The first is that almost all RPC dlls in Windows x64 systems use DCE syntax, but also contain a very small number of RPC dlls for Ndr64 syntax, such as Chakra.dll.
[+] And the second is that I am not find the way to parsing incoming parameter of RPC interface of Ndr64 syntax, so I can only parse the RPC interface function without parameters, but this does not affect sensitive operation path travesal parsing.
The following picture shows the logs of some of the path travesals after I run my parsing code. Actually, I found out some interesting path travesals in DsSvc after that time, but James Forshaw report most of them :).
Get down to business
In January 2019, Microsoft patched 5 DsSvc EoP Vulnerabilities reported by James Forshaw , there is a interesting patch in these 5 vulnerabilities which about the MoveFileInheritSecurity function, the vulnerability code is as follows:
__int64 __fastcall PolicyChecker::MoveFileInheritSecurity(const WCHAR *lpNewFileName, const WCHAR *lpExistingFileName) { [...] if ( MoveFileExW(lpNewFileName, lpExistingFileName, 3u) ) { if ( !InitializeAcl(&pAcl, 8u, 2u) ) { LABEL_4: v6 = GetLastError(); goto LABEL_10; } v6 = SetNamedSecurityInfoW(lpExistingFileName, SE_FILE_OBJECT, 0x20000004u, 0i64, 0i64, &pAcl, 0i64); if ( v6 ) MoveFileExW(lpExistingFileName, lpNewFileName, 3u); } [...] }As the code show, DsSvc will set the DACL of the new file through SetNamedSecurityInfoW after the MoveFile. James forshaw create a hardlink to a limit file, and call RpcDSSMoveFromSharedFile interface, the DsSvc get the file path directly, but not check if the file is accessible, it will finally set the limit file's DACL.
Before patch:
__int64 __fastcall DSUtils::VerifyPathRoundTrip(wchar_t *Str2, wchar_t *a2) { [...] v3 = CreateFileW(Str2, 0x80000000, 7u, 0i64, 4u, 0x80u, 0i64); if ( v3 != (HANDLE)-1i64 ) { v2 = v3; LABEL_6: v5 = DSUtils::VerifyPathFromHandle(v1, v2); goto LABEL_7; } [...] }After patch:
__int64 __fastcall DSUtils::VerifyPathRoundTrip(wchar_t *Str2, wchar_t *a2) { [...] v5 = CreateFileW(Str2, 0x80000000, 7u, 0i64, 4u, 0x80u, 0i64); if ( v5 != (HANDLE)-1i64 ) { v4 = v5; LABEL_6: v7 = DSUtils::VerifyPathFromHandle(v3, v4); if ( v7 >= 0 ) v7 = DSUtils::VerifyFileIdFromHandle(v2, v4); goto LABEL_8; } [...] }After patch, function DSUtils::VerifyFileIdFromHandle is added. The function contains a check for the hardlink. The BY_HANDLE_FILE_INFORMATION structure returned by calling the GetFileInformationByHandle function contains the member variable nNumberOfLinks. If it is greater than 1, it indicates that there is a symbolic link, and function will fail and return.
__int64 __fastcall DSUtils::GetFileIdFromHandle(HANDLE hFile, __int64 a2) { [...] if ( GetFileInformationByHandle(v3, &FileInformation) ) goto LABEL_19; [...] LABEL_19: if ( FileInformation.nNumberOfLinks <= 1 ) { v11 = (_WORD *)*v2; v2[1] = *v2; *v11 = 0; } [...] } The story is far from ending...Obviously, Microsoft's patch still have problem. I found that there is still a time window between the end of the check of the symbolic link and the call to the MoveFileInheritSecurity function, which means that there is a TOCTOU vulnerability, I can make a hardlink to limit file after the symbolink check, so that when DsSvc calls the MoveFileInheritSecurity function, it will set the limit file's DACL finally. I later reported this vulnerability to Microsoft.
Microsoft's patch is very simple, they canceled RpcDSSMoveFromSharedFile and RpcDSSMoveToSharedFile two RPC interfaces of DsSvc in Windows 10 rs6 and later.
///After parse DsSvc RPC interface you can find out that ///RpcDSSMoveFromSharedFile and RpcDSSMoveToSharedFile are canceled [uuid("bf4dc912-e52f-4904-8ebe-9317c1bdd497"), version(1.0)] interface intf_bf4dc912_e52f_4904_8ebe_9317c1bdd497 { HRESULT RpcDSSCreateSharedFileToken( handle_t p0, [In] wchar_t[1]* p1, [In] struct Struct_0* p2, [In] /* ENUM16 */ int p3, [In] /* ENUM16 */ int p4, [Out] wchar_t** p5); HRESULT RpcDSSGetSharedFileName( handle_t p0, [In] wchar_t[1]* p1, [Out] wchar_t** p2); HRESULT RpcDSSGetSharingTokenInformation( handle_t p0, [In] wchar_t[1]* p1, [Out] wchar_t** p2, [Out] wchar_t** p3, [Out] /* ENUM16 */ int* p4); HRESULT RpcDSSDelegateSharingToken( handle_t p0, [In] wchar_t[1]* p1, [In] struct Struct_1* p2); HRESULT RpcDSSRemoveSharingToken( handle_t p0, [In] wchar_t[1]* p1); HRESULT RpcDSSOpenSharedFile( handle_t p0, [In] wchar_t[1]* p1, [In] int p2, [Out] long* p3); HRESULT RpcDSSCopyFromSharedFile( handle_t p0, [In] wchar_t[1]* p1, [In] wchar_t[1]* p2); HRESULT RpcDSSRemoveExpiredTokens(); }The old version still exists these two interfaces. In the old version, Microsoft's patch is also very simple. The PolicyChecker::MoveFileInheritSecurity function is directly deleted, and DsSvc use another method for file copy. I will share this method later.
Other attack surfacesIn my RPC parsing log, I noticed another RPC interface, DSSCopyFromSharedFile, which calls the CopyFile function to copy file to a controllable path.
__int64 __fastcall DSSCopyFromSharedFile(const unsigned __int16 *a1, wchar_t *a2) { [...Check File...] if ( !CopyFileW(*(LPCWSTR *)(v10 + 184), v4, 0) ) { [...] } [...] }This vulnerability is very obvious, although DsSvc checked the permissions of the copied target file before CopyFile, I can still use race condition to link the file to the limit file after checking the target file permissions, and finally copy the shared file to limit file. In this function, the shared file can be specified by the user, so it is easy to write the payload into the file under system32.
When I reported this vulnerability, Microsoft did not award bounty for the vulnerability because Microsoft introduced a mitigation for hardlink. Whether normal user have control permission for the target file, if not, the hard link cannot be created, that is, the hardlink cannot be used by normal user in rs6 and WIP.
DsSvc security feature bypassAfter receiving the reply from Microsoft, I made a quick review on the DsSvc service code again and found a very interesting place. In DsSvc, it will protect the folder where the target file is to be operated. Create a lock file to prevent this folder from being mounted to another directory. The function that implements this security feature is DSUtils::DirectoryLock::Lock. The function code is as follows:
__int64 __fastcall DSUtils::DirectoryLock::Lock(signed __int64 this, const unsigned __int16 *a2) { [...] v20 = CreateFileW(v19, 0x80000000, 7u, 0i64, 4u, 0x4000100u, 0i64); [...] }The function calls CreateFile to create the lock file. I found that the lock file inherits the security descriptor of the parent directory, so actually, I have full control on lock file. It means I can delete the lock file after the lock file is created and after that I mount the directory to limit directory(the directory will be empty after I delete file). This way, even if I don't use hardlink, I can finally call CopyFile to copy the payload to a limit file in the limit directory.
The story is still going onMicrosoft finally patched vulnerability I report. In CopyFromSharedFile, Microsoft use a new function DSUtils::CopyFileWithProgress with the following code:
__int64 __fastcall DSUtils::CopyFileWithProgress(BOOL *this, DSUtils *a2, DSUtils *a3, const unsigned __int16 *a4) { DSUtils::OpenFile((const WCHAR *)a2, (const unsigned __int16 *)0x80000000i64, 7u, 0, &hObject); v7 = DSUtils::OpenFile((const WCHAR *)a3, (const unsigned __int16 *)0x80000000i64, 3u, 0, &pbCancel); [...] DSUtils::IsHardLinkFile(pbCancel, &vars0); [...] v8 = DSUtils::GetFinalPathFromHandle(v7, (__int64)&lpData); if ( v8 >= 0 && !CopyFileExW( (LPCWSTR)a2, (LPCWSTR)a3, (LPPROGRESS_ROUTINE)CopyFileProgressRoutine, lpData, (LPBOOL)dwCopyFlags, 0) ) [...] }In the patch, Microsoft not only checks whether the file is hardlink, but also uses CopyFileExW function. This function calls a callback function CopyFileProgressRoutine when copying the file. The callback function will check if the target file path is the same as before the IsHardLinkFile check.
signed __int64 __fastcall CopyFileProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) { [...] v9 = DSUtils::GetFinalPathFromHandle(hDestinationFile, (__int64)&Str2); if ( v9 >= 0 && _wcsicmp((const wchar_t *)lpData, Str2) ) [...] } Is it really over?After I analyze Microsoft's patch, I sent an email to Microsoft to confirm whether they fixed the problem I reported later, that is, the problem about lock file created. At that time, my suggestion was to create a lock file that can not be controlled by normal users. This way the user cannot delete the lock file and mount the current directory to another directory. Microsoft confirmed that it had fixed the previous problem, but they may did not understand my suggestion.
Although Microsoft added multiple checks, it can be found that Microsoft has finished checking the target file when it calls DSUtils::OpenFile to open the file and the callback function CopyFileProgressRoutine compares the file path before and after, so there is a very obvious issue:
If I still have full control over the lock file, I can still bypass the check in another way, James Forshaw's symboliclink-testing-tools introduce a method, this way it mounts the directory to the root directory of the namespace, and then links the named object to other files. This method will cause the file path to be resolved to other file when NT parsing the file path. Instead of setting the symbolic link through the SetFileInformation method, the advantage of this method is that the target file parsed when calling GetFileInformationByHandle is the target file, so the member variable nNumberOfLinks is still 1, you can easily bypass the IsHardlinkFile check.
Therefore, you can link file to other file in this way before OpenFile. After OpenFile, including the GetFinalPathNameByHandle in the callback function, they all will be parsed to the same path. Therefore, the patch is finally bypassed, and the payload file can still be copied to the limit file by CopyFile.
last of the last...Microsoft released a new patch in November 2019. Finally, Microsoft deleted the DirectoryLock function and the MoveFileInheritSecurity function I mentioned earlier, and used a new method DSUtils::OpenFileAlways to open the file and return the file handle which will be used in DSUtils::CopyFileWithProgress, it will opened until the function return. So, when the file is opened, the file no longer can be deleted and the mount point can not be created to other directory. Before CopyFile, the file directory to be copied is parsed by the GetFileInformationByHandle function.
///Instead of file path, DsSvc use file handle as incoming parameter DSUtils::CopyFileWithProgress(v5, (const unsigned __int16 *)hObject, hFile, (void *)v19); { [...] DSUtils::GetFinalPathFromHandle(hObject, (__int64)lpExistingFileName); [...] DSUtils::GetFinalPathFromHandle(hFile, (__int64)lpNewFileName); [...] DSUtils::IsHardLinkFile(hFile, dwCopyFlags, v8); [...] else if ( !CopyFileExW( lpExistingFileName[0], lpNewFileName[0], CopyFileProgressRoutine, v4, (LPBOOL)&dwCopyFlags[1], 0) ) [...] }And also, DsSvc uses ImpersonateClient to make sure the target file is an accessible file.
v6 = AutoImpersonate<1>::ImpersonateClient(&v39); [...] v6 = DSUtils::OpenFileAlways(lpFileName, Str, (unsigned __int64)&hFile); [...] if ( (_DWORD)v39 ) RpcRevertToSelf();Similarly, Microsoft has rewritten the CopyFileProgressRoutine callback function. In the function, DsSvc will compare the source file and target file with the FilePath and the FileID.
signed __int64 __fastcall CopyFileProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, __int64 dwStreamNumber, __int64 dwCallbackReason) { [...] v7 = DSUtils::GetFileIdFromHandle(hFile); [...] v7 = DSUtils::GetFileIdFromHandle((HANDLE)dwStreamNumber); [...] v7 = DSUtils::GetFinalPathFromHandle(hFile, (__int64)&v33); [...] v7 = DSUtils::GetFinalPathFromHandle((HANDLE)dwStreamNumber, (__int64)&v36); [...] if ( (unsigned int)utl::basic_string<unsigned short,utl::char_traits<unsigned short>,utl::allocator<unsigned short>>::compare( dwCallbackReason, &v27) ) [...] if ( (unsigned int)utl::basic_string<unsigned short,utl::char_traits<unsigned short>,utl::allocator<unsigned short>>::compare( dwCallbackReason + 64, &v30) ) [...] if ( (unsigned int)utl::basic_string<unsigned short,utl::char_traits<unsigned short>,utl::allocator<unsigned short>>::compare( dwCallbackReason + 32, &v33) ) [...] if ( (unsigned int)utl::basic_string<unsigned short,utl::char_traits<unsigned short>,utl::allocator<unsigned short>>::compare( dwCallbackReason + 96, &v36) ) [...] }In the old version, MoveFromSharedFile and MoveToSharedFile also use CopyFileWithProgress to move files. The story about DsSvc ends here.
时间攻击
Regional Threat Perspectives, Fall 2019: Middle East
S3 Bucket 如何配置才能做到只允许某一个IAM User操作?
聊一聊AssumeRole和Trust Relationship
Customer Guidance for the Dopplepaymer Ransomware
Customer Guidance for the Dopplepaymer Ransomware
再谈 ZoomEye:打造世界领先网络空间测绘能力
福利 | 2019京麒国际安全峰会即将开幕
The One Thing You Can't Outsource: Risk
Fake Cozy Bear Group Making DDoS Extortion Demands
In Conversation: Successful Women
被好友嫌弃拉黑?这一招绝地反杀你一定不知道!
来到乙方的这六个月
控制python随机数
起因是看到v2ex有人发了个送E卡的推广 https://www.v2ex.com/t/618739#reply392 里面有说抽奖的方式
1 2 3 4 5 6 import random seed = [第 300 楼的用户 ID] random.seed(seed) print(sorted(random.sample(range(1, 300), 5)))选择第300楼的用户名作为种子,然后抽奖 一般来说,种子确定了,生成的随机数的序列就确定了。 python2 3之间生成序列不同,但是 python2或python3自己的小版本内序列是相同的
那么能否控制中奖楼层呢?
attack第300楼的用户ID是唯一的输入,而这个 ID是可以控制的,可以注册一个用户名,在第300楼回复即可。顺手写了个脚本来爆破可以生成指定中奖楼层的种子的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #!/usr/bin/env python # coding: utf-8 # In[5]: import random import string from itertools import product # In[96]: def find(seed, target): random.seed(seed) t = random.sample(range(1,300), 5) c = 0 for i in target: if i in t: c += 1 return c == len(target) # In[100]: count = 1 result = [] asc = string.ascii_letters+string.digits for s in product(asc, repeat=15): count += 1 if not count%100000: print("count: {:,d}".format(count)) s = "".join(s) if find(s, [1,2, 3,4]): # print(s) print(count, s) result.append(s) break # In[95]: print(set(result)) for seed in set(result): random.seed(seed) print(random.sample(range(1,300),5))这是计算密集型,可以多进程,但是我是丢到服务器上跑的,只有一个CPU :) 所以就单进程吧
result秒速爆破出来可以生成包含1,2,3位指定数字的种子
1 # trafbpcszonjeil hpscybwevjuzlkr 生成 1,2,3三十分钟跑到了可以生成包含四位指定随机数的种子 [1,2,3,4]
1 2 3 4 5 6 7 8 9 In [1]: seed = 'aaaaaaaaaaeNWk4' In [2]: import random In [3]: random.seed(seed) In [4]: random.sample(range(1,300),5) Out[4]: [1, 59, 3, 2, 4]