Introduction

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 curl or 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.

Command:

openssl s_client -connect google.com:443

Result:

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!

Certificate extensions

After some research into this area, I found out that you could embed x.509 extensions into a certificate in the form of OIDs. Some websites such as MSDN maintain a list of OIDs that could be used.

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.

ssl.cnf:

[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]
1.1.1.1=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 1.1.1.1 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.

Future work

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.

Conclusion

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 😘!