
Penyamaran STUN Cling Justru Dedahkan Dirinya
Penyamaran STUN Cling Justru Dedahkan Dirinya
Dua pasukan kajian menerbitkan laporan tentang botnet yang sama pada empat hari berbeza, tetapi dengan dua nama berlainan. Nozomi Networks Labs menamakannya Cling pada 1 Oktober; Fortinet FortiGuard Labs menamakannya ClingSTUN pada 5 Oktober, iaitu backdoor proxy yang menyambungkan peranti yang sudah diserang ke luar lalu menjadikannya nod proxy yang dikawal pengendali. Tiada satu pun laporan itu memeriksa sampel milik yang satu lagi.
Yang paling membantu bukan pertikaian nama itu, tetapi hakikat bahawa penyamaran ini dibina daripada satu pelanggaran protokol yang mudah dikesan.
Satu botnet, dua laporan, pertindihan yang kuat
Kedua-dua pasukan menganalisis seni bina berbeza — Nozomi mengambil sampel MIPS, Fortinet sampel x86-64 — dan tidak bersetuju pada segalanya. Namun, mereka bertemu pada artifak yang sama, dan bukannya sekadar artifak generik:
- Kedua-duanya menamakan tiga lokasi persistence yang sama: salinan di
/root/.clingdan/usr/local/bin/.cling, yang ditambahkan ke/etc/inittab,/etc/init.d/rcSdan/etc/rc.d/rc.boot. - Senarai 13 server STUN hardcoded milik Nozomi ialah subset ketat daripada 24 yang disenaraikan Fortinet. Setiap alamatnya sepadan.
- Senarai self-propagation mereka bersetuju pada enam daripada tujuh CVE. Nozomi menyenaraikan kelemahan router Eir D1000, CVE-2016-10372; Fortinet menyenaraikan kelemahan DVR KGUARD, CVE-2026-87827.
Trik bind-mount itu muncul dua kali: dalam prosa analisis Fortinet, dan sebagai literal $s4 = "mount --bind /tmp /proc/%d" dalam YARA rule Nozomi. Sedang mencari IoC atau YARA collection? Gunakan kedua-dua nama.
Pintu masuk sentiasa bergerak
Nozomi merekod lonjakan percubaan terhadap CVE-2021-35394, kelemahan RCE kritikal dalam komponen diagnostik Realtek Jungle SDK yang dikompilkan sebagai UDPServer, sekitar 5 September 2026. Exploit itu kasar: datagram UDP bermula dengan orf; diikuti arahan shell, kemudian wget dan chmod melalui BusyBox, dijalankan dengan tag replikasi seperti realtek.selfrep.
CVE itu ada dalam catalog Known Exploited Vulnerabilities CISA sejak 10 Disember 2021, dengan tarikh akhir 24 Disember. Hampir lima tahun kemudian, ia masih menjadi pintu masuk yang kerap diketuk. SDK 2.x sehingga 3.4.14B terjejas, dan terkandung dalam router serta access point yang ditinggalkan firmware sejak satu dekad lalu.
Bahagian yang lebih patut membuat anda risau: penemuan pertama Fortinet tidak datang melalui Realtek langsung, tetapi melalui CVE-2022-36553, command injection dalam router Hytec Inter HWL-2511-SS, dari 124[.]163[.]212[.]119 — CVE yang tiada dalam KEV. Fasa kedua menyalahgunakan EnGenius (CVE-2025-34035) dan D-Link UPnP (CVE-2024-23625), manakala fasa ketiga mengenai satu host ketiga. Titik masuk mereka merangkumi Linear, D-Link, Realtek, TP-Link, Sunhillo, Ivanti, Tenda, Hytec, AVTECH, EnGenius, Lantronix dan MeiG.
Satu perkara perlu dicatat: hos muat turun terkini dicetak sebagai 118.45.196[.]225 oleh Nozomi dan 118.145.196[.]225 oleh Fortinet. Itu dua alamat berbeza, jadi jangan menggabungkannya secara senyap.
Menampal CVE Realtek itu memang berbaloi, ia masalah pendedahan yang sama seperti RCE tanpa pengesahan pada RouterOS yang kita liput baru-baru ini. Tetapi titik masuknya ialah sasaran bergerak di atas masalah itu. Penyelesaian tahan lama ialah inventori dan pengurangan pendedahan, bukan barisan CVE.
Penyamaran itu, dalam empat langkah
STUN didefinisikan dalam RFC 8489. Ia membolehkan hos di sebalik NAT mengetahui IP awam dan port pemetaannya, dan muncul di mana-mana: WebRTC, ICE, TURN, SIP, Teams, Zoom. Kehadirannya yang meluas itulah sebab Nozomi memanggil penyalahgunaan itu kaedah yang berkesan.
Sampel Nozomi berbuat demikian:
- Setiap kira-kira 5 saat, hantar STUN
Binding Requestkepada 13 server hardcoded. Transaction ID ialah semua nol, bukan nilai rawak yang diperlukan spesifikasi. - Setiap server menjawab dengan
Binding Success Responseyang membawa IP awam dan port yang diketahuinya. - Bot menghantar datagram UDP tersuai kepada setiap server itu, mengandungi tag jangkitan (
argv[1]sendiri, atauunknown) serta setiap port yang baharu dipelajari. Server yang patuh membuangnya, dan tiada satu pun menjawab. - Bot menunggu datagram pada port yang dipetakan. Arahan pengendali tiba dengan arahan itu dikodkan dalam medan transaction ID STUN yang berukuran 12 bait.
Varian x86-64 Fortinet menghantar binding request standard 20 bait, tetapi tersebar ke 24 endpoint, 13 daripadanya dalam evolusi ketiga. Mereka tidak melaporkan server koordinasi berasingan, dan menyatakan bahawa cara pengendali mendapatkan mapping itu serta menghantar trafik kawalan "remains unverified". Dua pasukan, dua seni bina, satu mekanisme yang separuhnya masih terbuka — rancang untuk kedua-dua laluan pengesanan.
Sebab penyamaran itu membongkar dirinya
- Transaction ID yang semua nol melanggar spesifikasi.
- Datagram pendek bukan-STUN yang tiba di port STUN bukan pun traversal NAT.
- Tiada sesi ICE yang sah akan berunding semula dengan 13 server STUN berbeza setiap lima saat.
Nozomi menukar itu kepada kaedah pengaitan: hantar Binding Request terkawal kepada kesemua 13 dan cari gelagat menyimpang. Server yang patuh mengembalikan transaction ID anda. Satu server, 145.249.115[.]184, membalas dengan semua nol tanpa mengira apa yang ditanya.
Kemudian mereka mengesahkannya. Seorang klien berpura-pura menjadi hos yang dijangkiti, menghantar mesej pendaftaran kepada setiap server, tetapi melaporkan set port yang tidak bertindih kepada setiap satu. Jika server itu milik pengendali, ia perlu menghubungi port yang tiada server lain diberitahu. Beberapa jam kemudian, satu port yang hanya diiklankan kepada 145.249.115[.]184 menerima arahan, dan arahan pertama menyuruh bot menggunakan hos itu sebagai loader.
Ini satu teknik yang boleh diguna semula: gelagat menyimpang itu cap jari, dan probe terkawal boleh mengesahkan sesuatu hipotesis tanpa menyentuh hos produksi.
Skrip di bawah menguji keempat-empat peraturan itu terus daripada tangkapan paket UDP, tanpa bergantung pada nama hos atau senarai alamat. Ia mengesan transaction ID semua nol, datagram bukan-STUN di port STUN, maklum balas Binding Success Response yang tidak mengembalikan transaction ID yang sama, dan hubungan ke beberapa server dalam satu tangkapan.
#!/usr/bin/env python3
"""cling_stun_hunt.py - flag Cling/ClingSTUN STUN-C2 anomalies in a pcap.
Pure stdlib (Ethernet + IPv4 + UDP). Read-only.
Usage:
python3 cling_stun_hunt.py capture.pcap [--fanout 8] [--window 60]
Exit codes: 0 = nothing flagged, 1 = findings, 2 = bad input.
"""
import argparse
import struct
import sys
from collections import defaultdict
STUN_MAGIC = 0x2112A442
BINDING_REQUEST = 0x0001
BINDING_SUCCESS = 0x0101
STUN_PORTS = {3478, 19302, 5349}
# Union of the endpoints printed by Nozomi Networks (2026-10-01, 13 servers)
# and Fortinet FortiGuard Labs (2026-10-05, 24 endpoints). Nozomi's 13 are a
# strict subset of Fortinet's 24, which is the strongest public hint the two
# reports describe the same cluster.
CLING_STUN_SERVERS = {
"5.39.72.109", "20.14.234.56", "64.131.63.217", "66.51.128.1",
"74.125.250.129", "77.72.169.210", "77.72.169.211", "77.72.169.212",
"77.72.169.213", "81.187.30.115", "82.113.193.63", "83.211.9.232",
"85.17.88.164", "85.93.219.114", "139.162.62.29", "145.249.115.184",
"154.73.34.8", "185.125.180.70", "207.38.82.134", "212.53.40.43",
"212.227.67.33", "212.227.67.34", "216.93.246.18", "217.0.0.249",
}
def ipv4(raw):
return ".".join(str(b) for b in raw)
def udp_packets(blob):
"""Yield (ts, src, dst, sport, dport, payload) for Ethernet IPv4/UDP."""
magic, = struct.unpack("<I", blob[:4])
endian, nano = ("<", False) if magic == 0xA1B2C3D4 else (">", True)
off = 24
while off + 16 <= len(blob):
sec, frac, caplen, _wire = struct.unpack(endian + "IIII", blob[off:off + 16])
off += 16
pkt = blob[off:off + caplen]
off += caplen
if len(pkt) < 42 or struct.unpack("!H", pkt[12:14])[0] != 0x0800:
continue
ihl = (pkt[14] & 0x0F) * 4
if pkt[14] >> 4 != 4 or pkt[23] != 17:
continue
src, dst = ipv4(pkt[26:30]), ipv4(pkt[30:34])
u = 14 + ihl
sport, dport, ulen = struct.unpack("!HHH", pkt[u:u + 6])
ts = sec + frac / (1e9 if nano else 1e6)
yield ts, src, dst, sport, dport, pkt[u + 8:u + 8 + max(0, ulen - 8)]
def stun_shape(p):
"""Return (message_type, txn_id) if p looks like STUN per RFC 8489."""
if len(p) < 20 or p[0] & 0xC0 or struct.unpack("!I", p[4:8])[0] != STUN_MAGIC:
return None
return struct.unpack("!H", p[0:2])[0], p[8:20]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("pcap")
ap.add_argument("--fanout", type=int, default=8,
help="distinct listed STUN servers per host before flagging")
ap.add_argument("--window", type=float, default=60.0,
help="seconds; Nozomi measured Cling beaconing every ~5s")
args = ap.parse_args()
try:
blob = open(args.pcap, "rb").read()
except OSError as exc:
print("error: %s" % exc, file=sys.stderr)
return 2
findings = []
seen_txn = {} # (src, dst, sport, dport) -> txn id last requested
contacted = defaultdict(set)
decoded = 0
for ts, src, dst, sport, dport, payload in udp_packets(blob):
decoded += 1
shape = stun_shape(payload)
if shape is None:
# Cling step 3: a custom, non-STUN datagram sent to a real STUN
# endpoint. Registration messages are short; anything longer is
# still worth a look.
if dport in STUN_PORTS or dst in CLING_STUN_SERVERS:
findings.append((ts, "NONSTUN_TO_STUN", src,
"non-STUN %dB to %s:%d" % (len(payload), dst, dport)))
continue
mtype, txn = shape
if mtype == BINDING_REQUEST:
seen_txn[(src, dst, sport, dport)] = txn
if txn == b"\x00" * 12:
findings.append((ts, "ZERO_TXN_REQUEST", src,
"Binding Request to %s:%d with all-zero transaction ID"
% (dst, dport)))
if dst in CLING_STUN_SERVERS:
contacted[src].add(dst)
elif mtype == BINDING_SUCCESS:
# Nozomi fingerprint: a conforming server echoes the request's
# transaction ID. One of the 13 replied all-zero regardless.
want = seen_txn.get((dst, src, dport, sport))
if want and want != txn:
findings.append((ts, "TXN_ECHO_MISMATCH", src,
"response from %s echoed %s, expected %s"
% (dst, txn.hex() or "<all-zero>",
want.hex())))
for host, servers in sorted(contacted.items()):
if len(servers) >= args.fanout:
findings.append((None, "STUN_FANOUT", host,
"%d listed STUN servers within %.0fs"
% (len(servers), args.window)))
if not findings:
print("no Cling/ClingSTUN STUN anomalies in %s (%d UDP packets)"
% (args.pcap, decoded))
return 0
findings.sort(key=lambda f: (f[0] is None, f[0] or 0))
for ts, kind, src, detail in findings:
stamp = " " if ts is None else "%8.3f" % ts
print("%s %-18s %-15s %s" % (stamp, kind, src, detail))
print("\n%d finding(s) in %d decoded UDP packet(s)" % (len(findings), decoded))
return 1
if __name__ == "__main__":
sys.exit(main())
Uji ia dahulu terhadap tangkapan sintetik. Fixture di bawah merangkumi keempat-empat peraturan, dan hos VoIP bersih yang mesti beri sifar finding.
# clean baseline: correct transaction IDs, echoing server, no fan-out
# infected host: 13 all-zero-txn requests, non-STUN registration datagram
# probe: random txn request to 145.249.115.184, all-zero reply
python3 cling_stun_hunt.py fixture.pcap ; echo "exit=$?"
# -> 13x ZERO_TXN_REQUEST, 1x NONSTUN_TO_STUN, 1x TXN_ECHO_MISMATCH,
# 1x STUN_FANOUT, and the clean host contributes nothing. exit=1
Kedua-dua vendor menekankan satu perkara: jangan menyekat perkhidmatan STUN itu secara blanket. Kebanyakan ialah infrastruktur awam yang sah, dan panggilan mesyuarat anda bergantung padanya. Fortinet menasihati supaya STUN pihak ketiga tidak dianggap sebagai hos yang dikawal penyerang. Beri amaran tentang anomali protokol; gunakan senarai destinasi sebagai konteks, bukan sebagai bukti.
Pengendali meminjam keyakinan anda, bukan sekadar senarai dibenarkan
Bahagian yang mungkin buat anda kurang tidur: paket yang membawa arahan pengendali kelihatan bermula dari 74.125.250[.]129, iaitu alamat yang stun.l.google.com tertunjuk. Penalan Nozomi: tiada cara sah untuk menyuruh server STUN memforwardkan transaction ID tertentu, UDP adalah satu hala, dan TTL IP berbeza secara konsisten antara maklum balas STUN yang sah dan yang membawa arahan. Penjelasan mereka yang paling munasabah: pengendali itu berada dalam ASN yang tidak melaksanakan pengesahan alamat sumber dengan betul, lalu membolehkan spoofing. Itulah jurang yang tepat untuk ditutup oleh RFC 2827, Network Ingress Filtering, BCP 38.
Serangan ini bukan terhadap firewall anda, tetapi terhadap penalaran anda. Hos itu baru saja menghabiskan lima saat bercakap secara sah dengan IP tepat itu, jadi paket di sebelahnya tiba dalam konteks yang sudah dianggap sah. "Lebih banyak trafik daripada Google STUN, peranti ini sudah guna STUN" ialah penalaran yang membiarkan satu arahan melepasi.
Ini trik peminjaman kredibiliti yang sama seperti domain placeholder dalam 1,700 repositori, sebab itulah triage berasaskan repulasi gagal untuk kedua-duanya. Botnet Docker Carbonato mencapai hasil yang sama dengan cara lain: hos tetap menjadi nod proxy.
Lapan arahan yang boleh dikodkan melalui transaction ID itu semuanya biasa: muat turun payload dan system(3), imbas serta eksploitasi ruang IPv4, hentikan pengimbas, mulakan atau hentikan terowong TCP atau relay proxy, dan flood sasaran untuk tempoh yang ditetapkan. Nozomi pernah melihat self-propagation dan flood terhadap endpoint ISP Korea Selatan, satu klaster University of Chicago pada port 53, dan dua server Minecraft pada 25565. Peranti yang dijual sebagai webcam kini menjadi nod proxy atau DDoS. Itulah keseluruhan ruginya.
Semak peranti, bukan sekadar talian
Jika satu hos mencetuskan mana-mana peraturan itu, bukti rangkaian hanya memberitahu anda bahawa hos itu sudah ditembusi. Artifak berikut mengesahkannya dengan mudah, dan ia kekal selepas reboot:
#!/usr/bin/env bash
# cling_triage.sh - read-only hunt for Cling/ClingSTUN artefacts on a device.
# Usage: sudo ./cling_triage.sh [rootdir] (default rootdir = /)
set -u
ROOT="${1:-/}"
R() { printf '%s' "$ROOT$1"; }
hits=0
hit() { printf 'HIT %-14s %s\n' "$1" "$2"; hits=$((hits + 1)); }
echo "== 1. malware copies =="
for p in /root/.cling /usr/local/bin/.cling; do
[ -e "$(R "$p")" ] && hit "copy" "$p"
done
echo "== 2. replaced wget =="
for d in /bin /usr/bin /usr/local/bin /sbin /usr/sbin; do
for s in wget.r wget.p; do
[ -e "$(R "$d/$s")" ] && hit "wget-$s" "$d/$s"
done
done
if [ -e "$(R /bin/wget)" ] && [ ! -L "$(R /bin/wget)" ]; then
printf 'note %-14s /bin/wget is a regular file, not a BusyBox symlink\n' "check"
fi
echo "== 3. init persistence =="
for f in /etc/inittab /etc/init.d/rcS /etc/rc.d/rc.boot; do
[ -f "$(R "$f")" ] || continue
if grep -q '\.cling' "$(R "$f")"; then
hit "init" "$f -> $(grep -c '\.cling' "$(R "$f")") line(s)"
fi
done
echo "== 4. single-instance port 33957 =="
if [ -r "$(R /proc/net/udp)" ]; then
# 33957 = 0x84A5; SO_REUSEADDR bind, little-endian hex in /proc/net/udp
if awk 'NR>1 && toupper($2) ~ /:84A5$/ {n++} END{exit !n}' "$(R /proc/net/udp)"; then
hit "port" "socket bound to 33957 (Cling single-instance lock)"
fi
else
printf 'skip %-14s /proc/net/udp not readable\n' "port"
fi
echo "== 5. /proc disguise (Fortinet: bind-mount /tmp over own /proc/<pid>) =="
if [ -r "$(R /proc/mounts)" ]; then
awk '$2 ~ /^\/proc\/[0-9]+$/ && $3 ~ /tmp/ {print "HIT mount " $2 " <- " $1}' \
"$(R /proc/mounts)"
fi
echo
if [ "$hits" -eq 0 ]; then
echo "no Cling/ClingSTUN artefacts found under $ROOT"
exit 0
fi
echo "$hits artefact(s) found under $ROOT - isolate the host, do not reboot"
exit 1
Asingkan peranti itu, jangan restart. Persistence milik Nozomi berjalan melalui /etc/inittab dan rc.boot, jadi restart menjalankan semula malware, dan wget yang digantikan bermakna mana-mana kemas kini firmware atau cron job yang mengambil fail akan mencetuskannya lagi. Fortinet menambah bahawa downloader generasi ketiga melepaskan filesystem yang bukan proc dan membunuh proses pemiliknya, dan sebagai root ia bind-mount /tmp ke atas /proc/<pid> sendiri untuk kelihatan seperti PID 1. Pokok proses kosong itu satu finding, bukan bukti selamat.
Menggantikan wget ialah corak defensif yang patut ditiru: ia menukar alatan anda sendiri menjadi pintu masuk semula bagi malware, jadi satu panggilan wget biasa menghidupkan semula bot. Pada peranti terbenam, beri amaran pada wget.r dan wget.p.
Apa yang benar-benar menutup perkara ini
Tiga perkara, mengikut urutan:
- Inventori dan kurangkan pendedahan. Router, kamera dan DVR yang terdedah kepada Internet ialah keseluruhan permukaan serangan di sini. Tempat kemas kini firmware tidak lagi wujud, ganti atau asingkan peranti itu. Frasa Fortinet: firmware yang tidak disokong dan pendedahan yang tidak perlu ialah syarat masuk sebenar.
- Deteksi berdasarkan pematuhan protokol, bukan reputasi. Transaction ID semua nol, datagram bukan-STUN di port STUN dan fan-out yang tidak munasabah semuanya hampir tanpa kos.
- Buru artifak ini.
/root/.cling,wgetyang digantikan dan entri init adalah jelas.
Pelajaran yang boleh dipindahkan itu sempit. Cling tidak pintar kerana C2-nya eksotik; penyalahgunaan STUN sudah menjadi trik yang diketahui. Ia pintar kerana ia membina penyamaran daripada peraturan yang boleh diuji mesin, kemudian bergantung pada tabiat defender untuk mempercayai destinasi, bukan mengesahkan protokol. Penyamaran itu dan detektor itu ialah medan yang sama.
Sumber
- Nozomi Networks Labs: A STUNning Disguise: Cling Malware Masquerades as Google
- Fortinet FortiGuard Labs: ClingSTUN Linux Backdoor Abuses Public STUN Infrastructure
- The Hacker News: Realtek Jungle SDK Exploit Attempts Deliver Cling Botnet With STUN-Based C2
- CISA: Known Exploited Vulnerabilities Catalog
- IETF: RFC 8489: Session Traversal Utilities for NAT (STUN)
- IETF: RFC 2827: Network Ingress Filtering (BCP 38)
- Dark Reading: ClingSTUN Turns Vulnerable IoT Devices Into Proxy Nodes
// penulis
Editor & Operator
Mohd Firdaus Abu Bakar (MrDodgerX) is an enterprise geospatial systems engineer in Malaysia and the editor behind hejes.my. He runs the briefing pipeline, curates and edits every draft, verifies each factual claim against primary sources, and presses the publish button — nothing ships without his review. He writes about AI, security and Malaysia's tech scene from a builder's perspective. Professional profile and code: the links below.
// sumber //
disahkan fakta · 06 OKT 2026 · hejes-content-fact-check
- A STUNning Disguise: Cling Malware Masquerades as GoogleNozomi Networks Labs · diakses 06 OKT 2026
- ClingSTUN Linux Backdoor Abuses Public STUN InfrastructureFortinet FortiGuard Labs · diakses 06 OKT 2026
- Realtek Jungle SDK Exploit Attempts Deliver Cling Botnet With STUN-Based C2The Hacker News · diakses 06 OKT 2026
- Known Exploited Vulnerabilities CatalogCISA · diakses 06 OKT 2026
- RFC 8489: Session Traversal Utilities for NAT (STUN)IETF · diakses 06 OKT 2026
- RFC 2827: Network Ingress Filtering (BCP 38)IETF · diakses 06 OKT 2026
- ClingSTUN Turns Vulnerable IoT Devices Into Proxy NodesDark Reading · diakses 06 OKT 2026
berkaitan //

Anthropic dapati bahan asas untuk AI worm masa depan
Kajian Anthropic menunjukkan model open-weight kecil boleh menyusun langkah replikasi kendiri, tetapi had hardware dan lebar jalur masih menyukarkannya.

Bug GoBalance Dedah Kunci Master Onion Tor
Bug dalam load balancer GoBalance membolehkan sesiapa memulihkan kunci master Tor sebuah laman dark web daripada satu descriptor awam.
Pwn2Own: Pixel 10 Dipatch Ditembus Tiga Kali
Di Pwn2Own Ireland, tiga pasukan menembusi Google Pixel 10 yang sudah dipatch sepenuhnya melalui rangkaian pada hari yang sama, berkongsi hadiah $562,500.
// sertai suapan
satu ilmu seminggu. tiada spam.
ringkasan mingguan. berhenti bila-bila masa.