NetSPI Announces 50% Year-Over-Year Revenue Growth And Rapid Expansion

Minneapolis, Minnesota  –  NetSPI LLC, the leading provider of application and network security testing solutions, announced today it achieved a 50% year-over-year revenue increase in 2018 as it continued to expand its product line up, staff, clients, and office locations.

“In 2018, NetSPI evolved into a high performance, high growth security company,” said President and Chief Operating Officer, Aaron Shilts. “We achieved significant growth driven from our top accounts, adding new clients, and taking market share from competitors in the penetration testing space.” In a mature market that is growing less than 10% per year, NetSPI is growing at more than five times that rate due to the increased efficiency and accuracy of its Resolve™ platform.

To manage this rapid growth, NetSPI strengthened its senior management team with the addition of two industry veterans, Chief Financial Officer, Jeni Bahr, and Chief Information Security Officer, Bill Carver.  The company also added more staff, bringing the total to over 100 employees in Q418. To accommodate a larger workforce the company completed significant renovations to its Minneapolis corporate headquarters and opened its first office in the Pacific Northwest, a region that delivered significant revenue in 2018.

Last year also marked the first full year of operation for the company’s Dallas office, ground zero for new product development.  Due to the efforts of the development team, NetSPI rolled out a number of new offerings in 2018. These included a complete rebuild of the company’s flagship Resolve™ software platform as well as new offerings spanning test and vulnerability management, cloud security, and mainframe testing.

“With the launch of these new capabilities we were able to move beyond tactical penetration testing and vulnerability assessments to offer more strategic services,” said Shilts. “Looking forward, I expect us to increasingly help leading companies define and then build their security programs.”

Last year NetSPI also increased its thought leadership activities and ramped up customer communication with the launch of the Our Thinking blog and hosted its first customer advisory board at The Biltmore in Asheville, NC. This new annual event brings together some of NetSPI’s largest customers to help set current and future product direction, prioritize new product capabilities, and gain insights into current challenges and markets.

The company also hosted its largest class size ever at NetSPI University, more than doubling the number of students compared to 2017. “Attracting and retaining qualified talent is the number one challenge for cybersecurity leaders today, so NetSPI doubled-down on our rigorous training program, helping develop the next generation of penetration testing experts,” said Shilts.

Looking forward, NetSPI expects another strong year of growth in 2019 with increasing revenue as a result of bringing the new Resolve™ 7 platform to market as well as continued account and geographic expansion.

About NetSPI

NetSPI LLC is the leading provider of application and network security testing solutions that supports organizations in scaling and operationalizing their threat and vulnerability management programs. The solution portfolio includes security testing services, vulnerability orchestration software platform, and advisory services. Trusted by seven of the top 10 United States banks, the largest global cloud providers, and many of the Fortune® 500, NetSPI has deep expertise in financial institutions, healthcare providers, retailers, and technology companies. To learn why the world’s top brands trust NetSPI, visit or follow us on Facebook, Twitter, and LinkedIn.


Using Azure Automation Accounts to Access Key Vaults

This is the second post in a series of blogs that focuses around Azure Automation. Check out “Exporting Azure RunAs Certificates for Persistence” for more info on how authentication works for Automation Accounts. In this installment, we’re going to focus on making use of Automation Accounts to gain access to sensitive data stored in Key Vaults.

High Level TLDR:

  1. Gain access to an AzureAD account that has rights to create/modify/run automation runbooks in existing Automation Accounts
    1. This AzureAD account doesn’t have access to any Key Vaults, but you want to read the vaults
  2. Access an Automation Account configured with rights to read keys from a Key Vault
  3. Add (or modify) a runbook for the Automation Account to access the Key Vault and dump the secrets
  4. Use your newfound secrets for further pivoting in the environment

I have been frequently running into situations where I have contributor access to a subscription, but I’m unable to access the data in the Azure Key Vaults. Since I can’t grant myself access to the vaults (requires Owner rights), I’ve had to come up with some creative ways for accessing the Key Vaults with Contributor accounts.

Initial Access

Most of the time that we have access to a Contributor account in Azure, the account does not have access to any of the Key Vaults in the subscription. Security conscious developers/engineers will limit the rights for normal users and assign application specific accounts to handle accessing Key Vaults. This limits the liability put on user accounts, and keeps the secrets with the application service accounts.

While we may not have access to the Key Vaults, we do have contributor rights on the Automation Account runbooks. This grants us the rights to create/modify/run automation runbooks for the existing Automation Accounts, which allows us to run code as automation users, that may have rights to access Key Vaults.


So why does this happen? As a best practice for automating specific tasks within Azure, engineers may vault keys/credentials that are used by automation runbooks. The Automation Accounts are then granted access to the Key Vaults to make use of the keys/credentials as part of the automation process to help abstract the credentials away from the runbook code and the users.

Common Automation Key Vault Applications:

  • Keys for encrypting data in an application
  • Local administrator passwords for VMs
  • SQL database credentials for accessing AzureSQL databases
  • Access key storage for other Azure services

As a side note: Azure developers/engineers are getting better at making use of Key Vaults for automation credentials, but we still occasionally see credentials that are hard-coded in runbooks. If you have read access on runbooks, keep an eye out for hard-coded credentials.


Creating a New Runbook

In order to access the key vaults from the Automation Accounts, we will need to create a new runbook that will list out each of the vaults, and all of the keys for each vault. We will then run this runbook with the RunAs account, along with any credentials configured for the account. So far, this shotgun approach has been the easiest way to enumerate key values in a vault, but it’s not the most opsec friendly method. In many of the environments that I’ve seen, there are specific alerting rules already set up for unauthorized access attempts to key vaults. So be careful when you’re doing this.


It has been a little difficult trying to come up with a method for determining Automation user access before running a runbook in the Automation Account. There’s no way to grab cleartext automation credentials from an Automation Account without running a runbook, and it’s a little tricky (but possible) to get the Key Vault rights for RunAs accounts before running a runbook.

Grand scheme of things… you will need to run a runbook to pull the keys, so you might as well go for all the keys at once. If you want to be more careful with the Automation Accounts that you use for this attack, keep an eye out for runbooks that have code to specifically read from Key Vaults. Chances are good that the account has access to one or more vaults. You can also choose the specific automation accounts that you want to use in the following script.

Automating the Process

At a high level, here’s what we will accomplish with the “Get-AzureKeyVaults-Automation” PowerShell function”:

  1. List the Automation Accounts
    1. Select the Automation Accounts that you want to use
  2. Iterate through the list and run a standardized runbook in each selected account (with a randomized job name)
    1. List all of the Key Vaults
    2. Attempt to read every key with the current account
    3. Complete these actions with both the RunAs and Stored Credential accounts
  3. Output all of the keys that you can access
    1. There may be duplicate results at the end due to key access overlap

This PowerShell function is available under the MicroBurst repository. You can find MicroBurst here –


Here’s a sample run of the function in my test domain:

Get-AzureKeyVaults-Automation -ExportCerts Y -Subscription "SUBSCRIPTION_NAME" -Verbose | ft -AutoSize


Example Output:



For the Attackers – You may have a situation where you need to access Key Vaults with a lesser privileged user. Hopefully the code/function presented in this blog allows you to move laterally to read the secrets in the vault.

For the Defenders – If you’re using Automation Accounts in your subscription, there’s a good chance that you will need to configure an Automation Account with Key Vault reader rights. When doing this, make sure that you’re limiting the Key Vaults that the account has access to. Additionally, be careful with who you give subscription contributor access to. If a contributor is compromised, your Automation Accounts may just give up your secrets.

Update – 12/30/2019

This issue was not initially reported to MSRC, due to the fact that it’s a user misconfiguration issue and not eligible for reporting per the MSRC guidelines (“Security misconfiguration of a service by a user“). However, they became aware of the blog and ended up issuing a CVE for it –


MachineAccountQuota is USEFUL Sometimes: Exploiting One of Active Directory's Oddest Settings

MachineAccountQuota (MAQ) is a domain level attribute that by default permits unprivileged users to attach up to 10 computers to an Active Directory (AD) domain. My first run-in with MAQ was way back in my days as a network administrator on a new job. I was assigned the task of joining a remote location’s systems to AD. After adding 10, I received this message on my next attempt:


Researching the error message lead me to ms-DS-MachineAccountQuota. The details lined up with the unprivileged AD access I had been provided. I reached out to a fellow admin and explained the situation. He wasn’t familiar with how to increase the quota for my account. Instead, he provided me with a domain admin account to continuing working (privesc!).


In late 2017, I released Powermad, which is a collection of PowerShell functions derived from an LDAP domain join packet capture. After digging through the packets, I identified the single encrypted LDAP add that created the machine account object. Here is an example of the LDAP add in an unencrypted state:


My main motivation while developing Powermad was to more easily incorporate MAQ during testing. In the past, I had seen testers leverage MAQ by attaching a full Windows OS to a domain. Rather than only using MAQ in this way, I wanted to be able to just add a machine account (aka computer account) set with a known password.


So Is MachineAccountQuota Useful?

Since I first started working on Powermad, I’ve learned a lot about MAQ beyond just the default 10 system limit. Recently, I’ve learned even more from some amazing blog posts by Elad Shamir, Harmj0y, and Dirk-jan, in which MAQ makes an appearance.

Overall, the conclusion I’ve reached is that MachineAccountQuota is useful…



In this blog post, I’ll go through 10 rules for working with MAQ as an attacker. I wrote these rules from the perspective of just adding a machine account to AD rather than attaching a Windows OS. Later, I’ll apply some of the rules to a MAQ + Kerberos unconstrained delegation use case. Finally, I’ll address defending against MAQ related exploits (spoiler: set to 0).

What Are The Rules?

I’ve broken up what I know about MAQ into 10 rules. Hopefully, you can use these rules to determine if MAQ can be useful in any particular situation.

  1. MAQ allows unprivileged users to add machine account objects to a domain. By default, an unprivileged user can create 10 machine accounts.


    You do not need to do anything special to invoke MAQ. You can simply attempt to add a machine account using an account that has not been directly granted the domain join privilege.
  2. The creator account’s SID is stored in the machine account’s ms-DS-CreatorSID attribute. AD populates this attribute only when the creator is not an administrator, or has not been delegated the privilege to add machine accounts.


    AD also uses ms-DS-CreatorSID to calculate the current count against MAQ. From a testing perspective, keep in mind the attribute is an arrow pointing to the creator account, even with nested MAQ usage. Therefore, using machine accounts created through MAQ does not fully shield the creator account.


    If the defense is aware of the ms-DS-CreatorSID attribute though, there is probably a good chance they have already disabled MAQ.
  3. Machine accounts created through MAQ are placed into the Domain Computers group. In situations where the Domain Computers group has been granted extra privilege, it’s important to remember that this privilege also extends to unprivileged users through MAQ. For example, you may find Domain Computers listed within a local Administrators group.


    Or even better, within a Domain Administrators group.


    As a slight extension to this rule, be on the lookout for automation that places computers in OUs and/or groups based on portions of the machine account name. You may be able to leverage the automation through MAQ.
  4. The creator account is granted write access to some machine account object attributes. Normally, this includes the following attributes:
    1. AccountDisabled
    2. description
    3. displayName
    4. DnsHostName
    5. ServicePrincipalName
    6. userParameters
    7. userAccountControl
    8. msDS-AdditionalDnsHostName
    9. msDS-AllowedToActOnBehalfOfOtherIdentity
    10. samAccountName

      You can modify these attributes as needed.
      However, the permitted values of some attributes are still subject to validation.

  5. The machine account itself has write access to some of its own attributes. The list includes the msDS-SupportedEncryptionTypes attribute which can have an impact on the negotiated Kerberos encryption method. Modern Windows OS builds will set this attribute to 28 during the process of joining a domain.Maqself
  6. The attribute validation is strict at the time the machine account is added. Basically, the attribute values need to match up. If they don’t match, the add will fail such as in this example where the samAccountName is incorrect.
    Strangely, after a machine account is added, some of the validation rules are relaxed.
  7. The samAccountName can be changed to anything that doesn’t match a samAccountName already present in a domain. Changing this attribute can be helpful for blending in activities with legitimate traffic such as by stripping the ‘$’ character or by matching an in-use account naming convention. Interestingly, the samAccountName can even end in a space which permits mimicking any existing domain account.
    Here is how the fake ‘Administrator’ account appears in action.
    Note, changing the samAccountName will not change the actual machine account object name. Therefore, you can have a machine account object that blends in with the naming convention while also having a completely different samAccountName.
  8. Adding a machine account creates 4 SPNs. The list includes the following:
    1. HOST/MachineAccountName
    2. HOST/
    3. RestrictedKrbHost/MachineAccountName
    4. RestrictedKrbhost/

      As an example, here is the default SPN list for the ‘’ machine account.
      After you add the machine account, you can append or replace the list with any SPN that passes validation.

      If you modify the samAccountName, DnsHostname, or msDS-AdditionalDnsHostName attributes, the SPN list will automatically update with the new values. The default SPNs do cover a lot of use cases though. So, you won’t always need to modify the list. If more information on SPNs is needed, Sean Metcalf has really good list at AdSecurity which includes details on Host and RestrictedKrbHost.

  9. Machine accounts do not have logon locally privilege. However, machine accounts work just fine from the command line with tools that accept credentials directly or through ‘runas /netlonly’. The tasks can include enumeration, adding DNS records, or really just about any non-GUI action that applies to a user account.Maqrunas
  10. Machine accounts added through MAQ cannot be deleted by the unprivileged creator account. To completely cleanup AD after using MAQ, you will need to elevate domain privilege or pass the task along to your client. You can however disable the account with the unprivileged creator account.Maqdisable

MachineAccountQuota In Action

Let’s take the above rules and apply them to a compromised AD account that has SeEnableDelegationPrivilege. As mentioned in rule 4, even though an account has write access to an attribute, the write attempt is still subject to validation.


However, if you happen to compromise an account with the correct privilege, such as SeEnableDelegationPrivilege, things can get interesting.


In this case, we can use the ‘INVEIGHkevin’ account, along with MAQ, to create and configure a machine account object capable of performing Kerberos unconstrained delegation. Conveniently, this can remove the requirement for finding an existing suitable AD object for leveraging SeEnableDelegationPrivilege. Basically, we can just make our own through MAQ.

Note, this is one of those scenarios where, if you can, just joining a domain with your own Windows system can make things easier. If you do go that route, just enable unconstrained delegation on the machine account object and leverage it just like you would on a compromised system. The good news is that the process is still very manageable when we use only a machine account.

Kerberos Unconstrained Delegation Setup

For this scenario, let’s assume that we have unprivileged access on a random Windows domain system and have also compromised an SeEnableDelegationPrivilege account.

Here are the steps to setup the attack:

  1. Use the SeEnableDelegationPrivilege account to add a machine account through MAQ.Maqldapadd
  2. Enable unconstrained delegation by setting the userAccountControl attribute to 528384.Maqdelegation
  3. Optionally, set the msDS-SupportedEncryptionTypes attribute to set the desired Kerberos encryption type using the machine account’s credentials.Maqencryption
  4. Optionally, add a corresponding DNS record matching the SPNs and pointing to the compromised Windows system. This can usually be done with through either dynamic updates or LDAP. This is optional since with the default SPNs, Kerberos will can also trigger from other name resolution methods such as LLMNR/NBNS.Maqadidns

Kerberos Unconstrained Delegation Attack

With the above in place, we next need to sort out how to get the desired account traffic to the compromised host. For this first round, we’ll use tifkin’s printer bug to get a domain controller machine account to connect to our system over SMB. Additionally, we will be using the dev Branch version of Inveigh. Through packet sniffing, this version can grab SMB Kerberos TGT traffic and attempt to output a kirbi file for use with tools such as Mimikatz and Rubeus.

For Inveigh, we will need to have either the unconstrained delegation account’s AES256 hash or a PSCredential object with a Kerberos salt as the username. Shown below is the process of generating the correct AES256 hash using Powermad’s Get-KerberosAESKey function.


Note, Inveigh currently only supports AES256 Kerberos decryption.

Since we want to use our unconstrained delegation machine account’s SPN, we will need to have the target connect to the correct host name. For this example, I’ll use Dirk-jan‘s printerbug script from his recently released Krbrelayx toolkit.


At this point, let’s take a step back and go over the various SPNs in play. First, we have a compromised system running an SMB server as SYSTEM. This means that the SMB server will decrypt Kerberos tickets using the system’s machine account credentials. If we cause an SPN mismatch and attempt to perform Kerberos authentication with data encrypted under a different SPN, the SMB authentication will fail. However, the SMB server will not reject the authentication attempt until after the client has transmitted the AP-REQ.


More importantly for this scenario, the SMB server will reject the connection after receiving the TGT. Therefore, if we can grab the Kerberos traffic through packet sniffing, we can decrypt the needed data with the machine account credentials we have in our possession.


Note, using the SPN mismatch trick has a tendency to trigger multiple Kerberos authentication attempts from a client. I have Inveigh set to only output 2 kirbi files per user by default. Inveigh stores the rest in memory for access through Get-Inveigh.

Now that we have a kirbi TGT for the domain controller, we can pass it to Mimikatz and attempt a dcsync.


For another quick example, I used Inveigh to capture a Domain Administrator TGT over SMB.


Next, I used the kirbi file with Rubeus.


As a last example, below is Inveigh catching a TGT over HTTP.


HTTP may, in ideal conditions, remove the requirement for local administrator access on the compromised system.

Finally, the Kerberos unconstrained delegation technique above would also work well with the new krbrelayx toolkit.

One last word on SeEnableDelegationPrivilege + MAQ, fully setting up standard constrained delegation is usually out of reach due to the lack of write access to msDS-AllowedToDelegateTo.

Defending Against MachineAccountQuota

I believe that MAQ is just one of those default settings without enough awareness. I’m guessing companies rarely actually need the default MAQ setting or even need it enabled at all. To disable MAQ simply set the count to 0. If you do need to allow unprivileged users to add systems, the better route is to just delegate the privilege to specific groups. Just be aware that much of what is listed in this blog post would also apply to a compromised account with delegated domain join privilege.

Defenders can also keep an eye out for two things:

  • Populated ms-DS-CreatorSID attributes
  • Machine accounts that are not changing their passwords


As with most things, MachineAccountQuota usage is situational. For testers, it is however something that is worthy of consideration for your bag of tricks. This has become even more apparent with recently released techniques from researchers such as Elad Shamir. For defenders, I recommend just disabling MachineAccountQuota.

Special thanks to Karl Fosaaen for the Always Sunny photoshop.

Discover why security operations teams choose NetSPI.