Skip to content

Instantly share code, notes, and snippets.

@vgmoose
Last active December 29, 2023 03:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vgmoose/215b6006bcc491891998026768e3872b to your computer and use it in GitHub Desktop.
Save vgmoose/215b6006bcc491891998026768e3872b to your computer and use it in GitHub Desktop.
How to set up a Twitter.com to Nitter.net redirecting via MITM with nginx, local dns server (pihole) and self-signed and self-trusted cert and root CA

What is this?

If you run a local DNS server on your network already (such as a Pi-hole), you can use these instructions to set up an HTTPS redirect for certain sites/domains to other ones. This is particularly useful, for instance, if you don't want to visit Twitter anymore but still occasionally want to automatically view content on it through Nitter.

This can be more easily accomplished via browser extensions, but this method is an option to get it on all devices on the network, as long as you trust the cert.

Is this a bad idea security wise? Probably! But if you want to try it, here you go:

Requirements

  • DNS server running, and point twitter.com to your local network IP (eg. 192.168.0.123)
  • Nginx server installed and running on the same server

Instructions

These are a shortened/summarized version of my mitm-nginx gist, which was intended for use with a Wii U. That gist contains other information about how to run a DNS server or local access point.

Generate Root CA Certificate

The custom Root CA is what will be installed on the target device to allow it to trust our MITM server. The target device ships with a bunch of root CA's in order to know which notable signed certificates to trust on the Internet.

We will generate it using openssl. The below command:

openssl req --nodes -new -x509 -days 3650 -extensions v3_ca -keyout cacert.private.pem -out cacert.pem -config /etc/ssl/openssl.cnf

Will prompt you to fill out the fields of the certificate. None of them matter, and they can all be filled out to anything you want or left blank, except you should fill out "Common Name" with something unique.

After the command runs, you will be left with two files in your current directory, cacert.pem and cacert.private.pem. The .pem file is the Root CA, which you will have to install on the target device. The .private.pem is going to be used in the next step to sign a certificate with this CA.

Generate Signed Certificate

Now that we generated the Root CA, we need to generate a certificate that is signed by it. The target device will be able to use the Root CA to cryptographically verifiy that a presented certificate is trustworthy.

The below command generates a self-signed certificate, which is not signed by any certificate authority. Once it's created, it will be signed by later steps.

openssl req -x509 -newkey rsa:4096 -keyout mycert.private.pem -out mycert.pem -days 365 --nodes

It will also prompt you for fields similar to when we created the Root CA. Again, they can all be anything, except the "Common Name (e.g. server FQDN or YOUR name)" must be set to twitter.com, or the target server that you're intercepting. Use a full domain here, for subdomains or additional domains, we will use a subject alternative name file in the next few steps.

Next, we're going to move around the files a bit into a structure that plays nicer with openssl's ca command. There's probably a better way to do this, but after seeing this excerpt from the ca man page: "The ca command is quirky and at times downright unfriendly.", I'm giving up on that, and going with their structure.

First, create a file named alt.txt in the current directory, containing the following:

[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = twitter.com
DNS.2 = *.twitter.com

Where the DNS.1, DNS.2, and so on values are replaced with whatever domain/wildcard domains you want to spoof with your HTTPS certificate. Then run the following commands to actually generate the certificate:

mkdir -p demoCA/newcerts
touch demoCA/index.txt
echo '01' > demoCA/serial
openssl ca -policy policy_anything -keyfile cacert.private.pem -cert cacert.pem -ss_cert mycert.pem -out mycert.signed.pem -extensions v3_req -extfile alt.txt

When it asks you to sign the certificate and commit the change, say yes and yes. The end result will be mycert.signed.pem in the current directory. This is the next certificate in the chain of trust, and it should be presented to the client by our nginx server alongside a copy of the Root CA.

For this guide, we'll copy these files (private and signed chain) into a new folder, /etc/nginx/mitm:

mkdir /etc/nginx/mitm
cp mycert.private.pem mycert.signed.pem /etc/nginx/mitm

Server Setup

The server certificate is what our MITM server will present to the client instead of the remote server's. It must be signed by the above generated root ca, so that when the client sees it, it wil trust it from having the root CA installed.

Open the /etc/nginx/nginx.conf file, and put the following contents inside of the http { ... } block:

server {
    listen 80;
    server_name twitter.com;
    return 302 https://nitter.net$request_uri;
}

server {
    listen 443 ssl http2;
    server_name twitter.com;
    ssl_certificate /etc/nginx/mitm/mycert.signed.pem;
    ssl_certificate_key /etc/nginx/mitm/mycert.private.pem;
    return 302 https://nitter.net$request_uri;
}

In this case, the server_name we are pretending to be is twitter.com, and we are going to handle all requests by redirecting the incoming request_uri (URL path) to nitter.net. In this case, we are passing it along to another website, but the location block could also be used to proxy_pass it to a local server such as http://localhost:8080.

Testing it Out

Up until this point, we've created a custom root ca certificate, created the nginx configuration file that uses our custom certificate to pass along the traffic, and should have already a local DNS server that forwards requests for the target domain to us. To verifiy everything is working, let's bring up the nginx server:

service nginx start

Hopefully it starts without any errors. If it does encounter errors, use service nginx status to see what they are. It would likely be a syntax issue.

We can test the DNS server as well, using the host command:

host twitter.com 192.168.0.123

Should report: "twitter.com has address 192.168.0.123", which demonstrates it's resolving to our server's internal IP rather than Twitter's.

Now, if you set your computer's DNS server to 127.0.0.1 in your network settings, and try to visit https://twitter.com in the browser, you should be presented with a security warning: security warning

The target device will reject this as it doesn't recognize the certificate that is being presented by our nginx server for twitter.com (as it shouldn't). If you import the generated cacert.pem into your browser, however, then the browser will be able to see the presented certificate as trusted.

To dismiss this warning, and test our MITM setup, import the Certificate Authority into your Browser or Operating System. This is the cacert.pem file. For example, in Chromium on Linux, this is managed in settings.

If you were successful in importing your custom CA, signing your custom Twitter.com certificate, setting up the DNS server to reroute twitter.com to your machine, and setting up nginx, you should now be redirected to nitter!

You can also verify this from the command line:

curl https://twitter.com -I

Which should return:

HTTP/2 302 
server: nginx/1.18.0 (Ubuntu)
date: Fri, 29 Dec 2023 02:42:59 GMT
content-type: text/html
content-length: 154
location: https://nitter.net/

If the certificate wasn't trusted, you will receive the following response instead:

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Additional information

On some operating systems, you may not be able to import the .pem directly, if this is the case, use the following command:

openssl x509 -in cacert.pem -out cacert.der -outform DER 

And then import cacert.der in Keychain or the equivalent. Depending on your browser and security systems, you may also just be able to trust it directly in the browser.

On macOS, the GUI in Keychain Access looks as follows: Keychain untrusted CA cert

And upon closing the dialogue after choosing "Always Trust", it should prompt you for your password.

Using it on iOS

To trust the certificate on iOS, it will need to be converted to a Profile using a Mac and Apple Configurator.

This can be done under File -> New Profile, Certificates, and then import the DER file, save and send to your iOS device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment