This idea came out when I was musing about giving the blue team something different to look at. In a stock or minimal Linux box, there usually aren’t many different ways to get your payload into it other than our good ole
wget. After some googling, I came across an intriguing command linked to OpenSSL (which is present in most, if not all Linux servers), that could be used to retrieve an SSL certificate from a server.
openssl s_client -connect <your server>
Let’s test by running it on Google.
openssl s_client -connect google.com:443
CONNECTED(00000006) depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1 verify return:1 depth=1 C = US, O = Google Trust Services LLC, CN = GTS CA 1C3 verify return:1 depth=0 CN = www.google.com verify return:1 --- Certificate chain 0 s:CN = www.google.com i:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3 a:PKEY: id-ecPublicKey, 256 (bit); sigalg: RSA-SHA256 v:NotBefore: Oct 16 08:10:46 2023 GMT; NotAfter: Jan 8 08:10:45 2024 GMT 1 s:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3 i:C = US, O = Google Trust Services LLC, CN = GTS Root R1 a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Aug 13 00:00:42 2020 GMT; NotAfter: Sep 30 00:00:42 2027 GMT 2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1 i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256 v:NotBefore: Jun 19 00:00:42 2020 GMT; NotAfter: Jan 28 00:00:42 2028 GMT --- Server certificate -----BEGIN CERTIFICATE----- MIIEiDCCA3CgAwIBAgIRANWbO75xUE3iCciPw5PuELgwDQYJKoZIhvcNAQELBQAw RjELMAkGA1UEBhMCVVMxIjAgBgNVBAoTGUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBM TEMxEzARBgNVBAMTCkdUUyBDQSAxQzMwHhcNMjMxMDE2MDgxMDQ2WhcNMjQwMTA4 MDgxMDQ1WjAZMRcwFQYDVQQDEw53d3cuZ29vZ2xlLmNvbTBZMBMGByqGSM49AgEG CCqGSM49AwEHA0IABNXoseaoLuoVmAKAkxlfsklzcG0o0WTSfrE6hukzPe33RV5a qYH3vYFWCOYvPFE5PjtNrv1veDW87Q5G8ZbKAXSjggJnMIICYzAOBgNVHQ8BAf8E BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4E FgQU7+YfhQzBKtvwNK5qbNU9zTfECHEwHwYDVR0jBBgwFoAUinR/r4XN7pXNPZzQ 4kYU83E1HScwagYIKwYBBQUHAQEEXjBcMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz cC5wa2kuZ29vZy9ndHMxYzMwMQYIKwYBBQUHMAKGJWh0dHA6Ly9wa2kuZ29vZy9y ZXBvL2NlcnRzL2d0czFjMy5kZXIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20w IQYDVR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAHWeQIFAzA8BgNVHR8ENTAzMDGg L6AthitodHRwOi8vY3Jscy5wa2kuZ29vZy9ndHMxYzMvUXFGeGJpOU00OGMuY3Js MIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHUASLDja9qmRzQP5WoC+p0w6xxSActW 3SyB2bu/qznYhHMAAAGLN8CKdAAABAMARjBEAiAdezwSUg6BBd9E039NAbzJxjbG 5CNBzL+LF0HHet+GFQIgS/w4yFl1xd6SjYltwVc/WjWs041aIlsAeWvyafpP/F8A dwB2/4g/Crb7lVHCYcz1h7o0tKTNuyncaEIKn+ZnTFo6dAAAAYs3wIqDAAAEAwBI MEYCIQDe6skoYfPLfZboC6bVd2sxsJ2rdkD5qvJdVYd1TngFYgIhALuA+f2e8X2H boOZj0KRGLbNwKVqMHPEXigBuDh2VHYrMA0GCSqGSIb3DQEBCwUAA4IBAQAU/XRP 7utXb9y7KOeVtb0fpWPqvX+38YLAmWr2j4pITN97iFhjWYJ2GTE7sx4b46gKZ8KP pBnuOMhIhltKDYQ0BFe3Vy8T92iLf9liI8aEfXmavLMRrIRB4IMushOgwX4IQOwE eSNJ1q+ntNziOXdXHFXyDYV19A6uL3sBkRLAHqmb8gclosTTwMoeOuU59KlwcreU fCvlswixnaO5Km9CdO8Tt7G3KkIwZR5X6iMwWHDfCsdmOUixWVEMEP6NVoUSMHYX RnBN6qieEHV3ihhQw3yGHaGKJnMqjIuXVQyeLVdX6qhGUl5QmIv0ldhtY3io6EDA IlD0GxLU38BvT1HF -----END CERTIFICATE----- subject=CN = www.google.com issuer=C = US, O = Google Trust Services LLC, CN = GTS CA 1C3 --- No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: ECDSA Server Temp Key: X25519, 253 bits --- SSL handshake has read 4296 bytes and written 396 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 256 bit This TLS version forbids renegotiation. Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) --- C01E18E601000000:error:0A000126:SSL routines:ssl3_read_n:unexpected eof while reading:ssl/record/rec_layer_s3.c:304:
The Base64 content from this output is pretty much the entirety of the certificate. Once I realised this, the cogs in my mind started turning. Could I somehow embed a payload in the certificate and get a victim machine to execute it? Well, turns out that you can!
Creating your custom OID inside a cert
Doing this required some googling on the various OpenSSL commands. After investigating several websites, I came across this one which was really thorough in creating a certificate with your own OID.
Here’s how you could create such a certificate.
First, create an OpenSSL.cnf compatible file for ease of use.
[req] default_bit = 4096 distinguished_name = potato x509_extensions = v3_req prompt = no [potato] countryName = US stateOrProvinceName = Virginia localityName = Virginia organizationName = Amazon RSA 2048 M02 [v3_req] 22.214.171.124=ASN1:UTF8String:Potato
The data in the
potato section are just to fill up the common values that will be inserted into the certificate. What’s different is the
v3_req section, which defines an area for custom OIDs and their values (and types).
# generate cert using the previous ssl.cnf file openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout key.pem -out server.pem -config ssl.cnf # view the cert openssl x509 -in server.pem -text -noout
There’s a new section in the certificate, X509v3 extensions. Under that we can see OID 126.96.36.199 and the value Potato.
I got excited after succeeding in this simple proof of concept. By being able to embed my custom data into a certificate, this meant that I could actually be able to embed a payload into a certificate for execution!
The attack plan
I decided to go down this path because I felt that most people did not concern themselves too much with SSL certificates. In fact, the default application server log wouldn’t even log a request by OpenSSL to retrieve the certificate. An investigator using WireShark would most likely skip over the TLS handshake packets to investigate the payload instead. And even if we had to use a regular
curl, an investigator might be more concerned with looking at the site’s contents or downloaded file than the certificate itself, which allows for more misdirection to make the defenders look in a different direction.
Surely these standard SSL negotiation packets are nothing interesting to look at! Sidenote: I don’t need to use TLS 1.2, it’s just there for testing purposes.
Preparing our certificate
Since I could not embed payloads into my certificate. The first thing I tried was to put my entire implant (10mb) inside. Unfortunately, while the certificate was proper and could be parsed locally, the OpenSSL code powering HTTPS servers did not seem to be able to handle it. I tested with multiple HTTPS servers with the same result. After some trial and error, I determined a maximum size that I could embed inside a certificate and serve it over properly.
The maximum size wasn’t very large, but I could write a simple stage 1 loader using raw sockets that could fit into the space. I did not have much time to do it, so it was just a simple binary, but with more effort, I believe more magic could be built into the loader 🤔.
Testing our certificate
Now that I had generated a certificate that contained my payload, I extended my earlier OpenSSL command more to do it in a 1 liner.
openssl s_client -connect localhost:6443 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout
The series of commands above would retrieve a certificate from a server, extract the Base64 content and parse it out. Here’s our test certificate with a dummy payload.
With that, I could build out my bash 1 liner to extract the certificate and execute the payload. However, I’ll leave that to the exercise of the reader.
After executing this attack plan, I thought of what more I could do with this vector.
Limited extension size
For starters, while a big (say 20mb) certificate was proper, it could not be used as a normal HTTPS certificate. I tried to do some investigation into it using scapy but I ran out of time (1 man red team army is tough). Furthermore, even if I could even manage to get the HTTPS server to serve such a big certificate, I wasn’t sure if any normal client could handle it properly.
C2 data transmission vector?
I also mused about how this could be used for being a C2 communication channel. While it can be used for sending data to the victim, I think it could be rather limited in using it as a medium for the victim to respond by.
Firstly the limited size as mentioned previously. Secondly, it is difficult for a victim to be able to host a HTTPS server, unless its primary function was already so. Thirdly, most HTTPS servers read the SSL certificate on startup and do not change it unless the server is restarted, and restarting the server every time would hardly be the most stealthy operation 😅.
Development of a fancier payload
Right now the payload looks kinda suspicious and directs attention to the SSL certificate. With more work it’s probably possible to make it less obvious in the hopes that defenders will miss it.
I found it really interesting in coming up with this idea. I’m not sure if anyone has ever done this before, but I felt it was really cool! For my attack chain, I built a disappearing binary as the stage 1 loader to flummox the defenders even more. Hope blue still likes me after this 😘!