Favicon Hash & Shodan: Odkrivanje phishing strani

Phishing strani so pogosto narejeni tako, da 1:1 prekopirajo originalno stran, vključno s faviconom. Shodan za vsako HTTP storitev, ki jo skenira, izračuna hash favicona. Če poznaš hash favicona originalne strani, lahko najdeš vse strežnike, ki prikazujejo isto ikono, tudi tiste, ki niso uradni.

Korak 1: Poišči pravi favicon

Favicon ni vedno na /favicon.ico. Odpri izvorno kodo strani (Ctrl+U) in poišči oznako <link rel="icon" ...> ali <link rel="shortcut icon" ...>. Pot v atributu href je pravi favicon.

Korak 2: Izračunaj hash

Shodan ne uporablja klasičnega MD5 ali SHA-256 hasha. Vsebino ikone najprej pretvori v zapis Base64 (z prelomi vrstic na vsakih 76 znakov po standardu RFC 2045) in nad tem nizom izračuna 32-bitni predznačeni MurmurHash3.

Skripta favhash.py:

import sys, codecs, mmh3, requests, urllib3
from urllib.parse import urlparse, urljoin
from html.parser import HTMLParser

# Phishing strani in slabo nastavljeni strezniki imajo pogosto neveljaven
# ali nepopoln certifikat. Za prenos ikone preverjanje ni potrebno.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
HEADERS = {"User-Agent": "Mozilla/5.0"}


class IconParser(HTMLParser):
    """Poisce vse <link rel="icon">, "shortcut icon", "apple-touch-icon" ..."""
    def __init__(self):
        super().__init__()
        self.icons = []

    def handle_starttag(self, tag, attrs):
        if tag != "link":
            return
        a = dict(attrs)
        if "icon" in (a.get("rel") or "").lower() and a.get("href"):
            self.icons.append(a["href"])


def prenesi(url):
    r = requests.get(url, timeout=10, verify=False, headers=HEADERS)
    r.raise_for_status()
    return r


if len(sys.argv) != 2:
    sys.exit(f"Uporaba: python3 {sys.argv[0]} <URL strani ali ikone>")

url = sys.argv[1]
p = urlparse(url)
base = f"{p.scheme}://{p.netloc}/"

# Vedno preverimo /favicon.ico v korenu domene
kandidati = [urljoin(base, "favicon.ico")]

# Ce je URL stran, poiscemo ikone v HTML-ju, sicer je URL ze ikona
try:
    r = prenesi(url)
    if "text/html" in r.headers.get("Content-Type", ""):
        parser = IconParser()
        parser.feed(r.text)
        kandidati += [urljoin(url, h) for h in parser.icons]
    else:
        kandidati.append(url)
except requests.RequestException as e:
    print(f"[!] {url}: {e}")

for ikona in dict.fromkeys(kandidati):  # odstrani duplikate, ohrani vrstni red
    try:
        r = prenesi(ikona)
    except requests.RequestException as e:
        print(f"[!] {ikona}: {e}")
        continue

    tip = r.headers.get("Content-Type", "?")
    if "text/html" in tip:
        print(f"[!] {ikona}: vrne HTML namesto slike (verjetno stran 404), preskakujem")
        continue

    h = mmh3.hash(codecs.encode(r.content, "base64"))
    print(f"\n{ikona}  ({tip}, {len(r.content)} B)")
    print(f"  Shodan: http.favicon.hash:{h}")
    print(f"  FOFA:   icon_hash=\"{h}\"")

Skripta potrebuje knjižnico:

pip install mmh3 requests

Zagon:

python3 favhash.py https://www.primer-banka.si/favicon.ico

image

Hash lahko preveriš tudi brez skripte: v Shodanu odpri katerikoli rezultat uradne strani in v podrobnostih HTTP storitve najdeš vrednost http.favicon.hash.

Korak 3: Poizvedba v Shodanu

Osnovna poizvedba:

http.favicon.hash:-1160765396

Rezultat vsebuje tudi uradne strežnike organizacije. Te izločiš z negativnimi filtri:

http.favicon.hash:-1234567890 -org:"Ime uradnega ponudnika" -ssl.cert.subject.cn:"primer-banka.si"

Uporabne kombinacije:

http.favicon.hash:X -ssl:“primer-banka.si” - Strežnike z ikono, a s tujim certifikatom

http.favicon.hash:X http.title:“Prijava” - Strani z ikono in naslovom prijavne strani

http.favicon.hash:X http.html:“password” - Strani z ikono in poljem za geslo

http.favicon.hash:X -country:SI - Kopije, gostovane izven Slovenije

http.favicon.hash:X port:8080,8443 - Kopije na nestandardnih vratih

Phishing ChatGPT: