GL.iNet Comet IP-KVM Behind Its Built-In Obfuscation Profiles
WindowsmacOSNetwork
TTP Hunt medium DPRK IT Worker FraudLaptop Farm
Fingerprints for the GL.iNet Comet, an IP-KVM whose firmware ships with ready-made presets that impersonate a Logitech, Corsair, Dell or Microsoft peripheral, and for the configuration descriptor, display identity and hardware address those presets leave untouched.
What this hunt looks for
Earlier hunts treat a spoofed identity as something an operator does to a PiKVM or TinyPilot after purchase; GL.iNet's Comet inverts that. Grumpy Goose Labs records built-in obfuscation profiles that in one step rewrite the USB vendor and product strings and identifiers and swap the HDMI EDID, presenting a Logitech receiver, a Corsair, Dell or Microsoft keyboard.
The presets leave the USB configuration descriptor name, "Glinet device" on firmware 1.5.0 and "Config 1: GLKVM device" on 1.3.0, which anchors this hunt. The serial CAFEBABE, 125 milliamps and seven interfaces under every profile corroborate it, since a wireless keyboard presenting seven interfaces is not what its name says.
On the network, GL.iNet's own documentation gives glkvm.local, the KVMCloud service at glkvm.com, and Tailscale, ZeroTier or NetBird access under the machine name glkvm. Grumpy Goose Labs also records the hardware address prefix 94:83:C4 and reads it from the ARP caches an EDR already collects.
- Technique
- Hardware addition (MITRE ATT&CK T1200) with vendor-supplied masquerading of the device identity (T1036), in support of fraudulent-employment valid-account misuse (T1078).
- Data sources
- EDR USB descriptor and device-class telemetry, Windows Security event log (6416), WMI monitor identity, Host network state, DHCP logs, Proxy/DNS logs, osquery
- Last updated
- 2026-10-04
Indicators
The atomic values this hunt matches on, listed separately from the queries so they can be lifted into tooling that speaks none of the query languages below. Every value here is reported by one of the sources cited at the foot of this page.
| Type | Value | Note |
|---|---|---|
| USB configuration name | Glinet device |
ConfigurationDescriptorName on Comet firmware 1.5.0. Grumpy Goose Labs records it unchanged under all four of the firmware's built-in obfuscation profiles, which is what makes it the anchor for this hunt rather than the vendor and product strings those profiles rewrite. |
| USB configuration name | Config 1: GLKVM device |
ConfigurationDescriptorName on Comet firmware 1.3.0 (Grumpy Goose Labs). Match both forms: the naming changed between the two firmware releases the research covers, and a fleet will hold devices on either. |
| USB Manufacturer | GLKVM (firmware 1.3.0); Glinet (firmware 1.5.0) |
DeviceManufacturer in the stock configuration only (Grumpy Goose Labs). Every obfuscation profile replaces this string, so a miss here says nothing. |
| USB descriptor | Composite KVM Device |
DeviceProduct in the stock configuration on both firmware versions (Grumpy Goose Labs). Also the DeviceProduct a stock PiKVM reports, per the first hunt in this series. |
| USB serial | CAFEBABE |
DeviceSerialNumber recorded for the Comet in the stock configuration and under all four obfuscation profiles (Grumpy Goose Labs). The same value is the default PiKVM gadget serial, so it identifies the class of device rather than the vendor. |
| USB VID/PID | VID_1D6B&PID_0104 |
Stock Comet device instance identifier on both firmware versions (Grumpy Goose Labs). This is the generic Linux USB gadget identifier, shared with PiKVM and TinyPilot and with any other Linux board emulating USB peripherals, so it is a lead rather than an attribution. |
| USB VID/PID | VID_046D&PID_C526 |
Logitech Unifying Receiver, presented by the Comet under built-in obfuscation profile 1 alongside DeviceManufacturer "Logitech Inc" (Grumpy Goose Labs). A real Unifying Receiver is commonplace; what is not is one whose configuration descriptor reads "Glinet device". |
| USB VID/PID | VID_6940&PID_6973 |
Presented under obfuscation profile 2 alongside DeviceManufacturer "Corsair" and DeviceProduct "Corsair Gaming RGB" (Grumpy Goose Labs). |
| USB VID/PID | VID_413C&PID_2011 |
Dell Computer Corp. Multimedia Pro Keyboard, presented under obfuscation profile 3 (Grumpy Goose Labs). |
| USB VID/PID | VID_045E&PID_005F |
Microsoft Corporation Wireless Multimedia Keyboard, presented under obfuscation profile 4 (Grumpy Goose Labs). This profile spoofs the USB identity alone; the research records no substitute display identity with it. |
| Descriptor field | ConfigurationDescriptorNumInterfaces = 7 with ConfigurationDescriptorMaxPowerDraw = 125 |
Interface count recorded for the Comet on firmware 1.5.0; 4 on 1.3.0 (Grumpy Goose Labs). A keyboard or a wireless receiver has no reason to present seven interfaces, which is what makes this the numeric corroboration for a spoofed identity. |
| Monitor identifier | DISPLAY\GLIC21C |
WmiMonitorID instance name for the EDID a stock Comet presents over HDMI: ManufacturerName GLI, UserFriendlyName GLIKVM, SerialNumberID 891247, week 8 of 2021, running 2560 x 1440 at 59 Hz (Grumpy Goose Labs). |
| Audio device name | GLKVM (#- HD Audio Driver for Display Audio) |
Display-audio endpoint a stock Comet mounts (Grumpy Goose Labs). Under an obfuscation profile the endpoint is named after the substituted display instead, so read the list rather than matching this string alone. |
| MAC OUI | 94:83:C4 |
Hardware address prefix observed for the Comet and included in the ARP-neighbour query Grumpy Goose Labs publishes. The research is explicit that prefixes in that list may also cover travel routers from the same manufacturers, and GL.iNet's main business is travel routers. |
| MAC OUI | 80:34:28; 48:DA:35 |
Prefixes carried by the ARP-neighbour query Grumpy Goose Labs publishes for the other KVM devices its research surveys (JetKVM and NanoKVM); they are not Comet prefixes. |
| Hostname | glkvm.local |
Default mDNS name, from GL.iNet's own documentation rather than from the research: "Enter glkvm.local in the address bar." Link-local, so a host that resolves it is on the same segment as the device. This is the GL.iNet counterpart to tinypilot.local in the network-presence hunt. |
| Domain | glkvm.com |
Vendor cloud service GL.iNet calls KVMCloud, documented by GL.iNet as the site a bound device is reached through after signing in with a GL.iNet account. It is the manufacturer's legitimate service, not attacker infrastructure; its value to a defender is that a corporate endpoint or a corporate network has no ordinary reason to reach it. |
| Hostname | glkvm.<tailnet>.ts.net |
GL.iNet documents Tailscale, ZeroTier and NetBird as alternative remote-access paths enabled from an Apps Center on the device, and gives glkvm.tail1fd0.ts.net as the example Tailscale name. The tailnet portion is per-account; the stable part is the machine name glkvm. An operator taking this path never touches glkvm.com at all. |
Logic
Every query below implements these steps, so the same hunt returns the same result in each product.
- Collect: USB descriptors and enrolment (Windows, macOS), DNS and connection records, DHCP leases and ARP neighbours
- Match: configuration name 'Glinet device' or 'Config 1: GLKVM device', or stock strings GLKVM, Glinet, GLIKVM, Composite KVM Device
- Match: or serial CAFEBABE with the gadget id or a spoofed Logitech, Corsair, Dell or Microsoft VID/PID
- Match: or a DNS name or connection containing glkvm (glkvm.local, glkvm.com, glkvm.<tailnet>.ts.net)
- Match: or (optional) 7 interfaces at 125 mA, or MAC prefix 94:83:C4, 80:34:28 or 48:DA:35 in DHCP or ARP
- Report: hosts with a match, newest first
Queries
Defender / Sentinel KQL
Written for this site, from indicators reported by Grumpy Goose Labs, GL.iNet documentationDefender does not surface the USB configuration descriptor, so this finds the stock strings and the serial-plus-VID/PID pair; the descriptor name needs Falcon.
// Hunt: GL.iNet Comet, stock or running an obfuscation profile (the serial and VID/PID pair survive the profiles).
let SpoofIds = dynamic(["VID_1D6B&PID_0104", "VID_046D&PID_C526", "VID_6940&PID_6973", "VID_413C&PID_2011", "VID_045E&PID_005F"]);
union
( DeviceEvents
| where Timestamp > ago(90d) and ActionType == "PnpDeviceConnected"
| extend Raw = tostring(AdditionalFields)
| where Raw has_any ("GLKVM", "Glinet", "GLIKVM", "GLIC21C") or Raw contains "Composite KVM Device"
or (Raw has "CAFEBABE" and Raw has_any (SpoofIds))
| project Timestamp, DeviceName, Signal = "usb-or-display-enrolment", Evidence = Raw ),
( DeviceNetworkEvents
| where Timestamp > ago(90d) and RemoteUrl has "glkvm"
| project Timestamp, DeviceName, Signal = "glkvm-lookup", Evidence = RemoteUrl )
| summarize FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Signals = make_set(Signal), Evidence = make_set(Evidence, 10) by DeviceName
| sort by LastSeen desc
Sigma
Written for this site, from indicators reported by Grumpy Goose LabsMatches the Windows Security 6416 enrolment event for the stock strings or for the serial with a gadget or spoofed identity.
title: GL.iNet Comet IP-KVM Recognized by a Corporate Endpoint
id: 35f97668-42dc-5aa5-a0ff-3f0d60a06792
status: experimental
description: >
Detects Windows recognizing a GL.iNet Comet IP-KVM, by the stock vendor and
product strings or by the default serial CAFEBABE paired with the generic
gadget id or one of the four peripheral identities the firmware's built-in
obfuscation profiles present. Hardware of this class hands an operator
console control of the host below the operating system, so the enrolment
event is often the only endpoint-side signal.
references:
- https://blog.grumpygoose.io/be-kvm-do-fraud-8ab523d26c9d
- https://docs.gl-inet.com/kvm/en/faq/local_access_via_browser/
logsource:
product: windows
service: security
detection:
selection_event:
EventID: 6416
selection_description:
DeviceDescription|contains:
- 'GLKVM'
- 'Glinet'
- 'GLIKVM'
- 'Composite KVM Device'
selection_serial:
DeviceId|contains: 'CAFEBABE'
selection_ids:
DeviceId|contains:
- 'VID_1D6B&PID_0104'
- 'VID_046D&PID_C526'
- 'VID_6940&PID_6973'
- 'VID_413C&PID_2011'
- 'VID_045E&PID_005F'
condition: selection_event and (selection_description or (selection_serial and selection_ids))
falsepositives:
- Out-of-band management hardware legitimately deployed in a lab, test bench or datacenter
- Composite KVM Device is also the product string a stock PiKVM reports, so it identifies the class of device
level: high
Splunk SPL
Written for this site, from indicators reported by Grumpy Goose Labs, GL.iNet documentationFree-text matching over raw events, so it works without field extractions on the Windows event and EDR data.
```Placeholders: index=wineventlog is WinEventLog:Security (event 6416); index=edr is your EDR USB, process, DNS and connection events; index=dhcp is DHCP leases.```
((index=wineventlog EventCode=6416) OR index=edr)
("GLKVM" OR "Glinet" OR "GLIKVM" OR "GLIC21C" OR "Composite KVM Device"
OR ("CAFEBABE" AND ("VID_1D6B&PID_0104" OR "VID_046D&PID_C526" OR "VID_6940&PID_6973" OR "VID_413C&PID_2011" OR "VID_045E&PID_005F")))
| stats min(_time) AS first_seen max(_time) AS last_seen values(sourcetype) AS sources BY host
```optional: GL.iNet, JetKVM and NanoKVM MAC prefixes in DHCP; GL.iNet's main business is travel routers, so 94:83:C4 matches those too. Delete from here to the closing bracket to drop it.```
| append
[ search index=dhcp
| eval mac_clean=lower(replace(mac, "[:.-]", ""))
| eval oui=substr(mac_clean, 1, 6)
| search oui IN ("9483c4", "803428", "48da35") OR hostname="*glkvm*"
| stats min(_time) AS first_seen max(_time) AS last_seen values(ip) AS addresses
values(hostname) AS hostnames BY mac_clean ]
| sort - last_seen
| convert ctime(first_seen) ctime(last_seen)
Lower-fidelity parts (1)
- the DHCP append block — the GL.iNet prefix covers its travel routers, and JetKVM and NanoKVM prefixes are not Comet prefixes. Remove: delete the lines from the ```optional comment through the closing bracket of the append
CrowdStrike LogScale
Written for this site, from indicators reported by Grumpy Goose LabsThe configuration name survives every built-in obfuscation profile, so a config-name hit is the anchor result.
// Hunt: GL.iNet Comet. Field names follow the Grumpy Goose Labs Falcon queries.
(#event_simpleName=DcUsbConfigurationDescriptor or #event_simpleName=DcUsbDeviceConnected or #event_simpleName=DnsRequest or #event_simpleName=NeighborListIP4)
| case {
#event_simpleName=DcUsbConfigurationDescriptor ConfigurationDescriptorName=/(GLKVM|Glinet) device/i | signal := "config-name";
#event_simpleName=DcUsbDeviceConnected DeviceManufacturer=/GLKVM|GLIKVM|Glinet/i | signal := "stock-manufacturer";
#event_simpleName=DcUsbDeviceConnected DeviceProduct=/Composite KVM Device/i | signal := "stock-product";
#event_simpleName=DcUsbDeviceConnected DeviceSerialNumber=/CAFEBABE/i | signal := "default-serial";
#event_simpleName=DnsRequest DomainName=/glkvm/i | signal := "glkvm-lookup";
// optional: seven interfaces at 125 mA is the profile shape; composite docks can also reach seven
#event_simpleName=DcUsbConfigurationDescriptor ConfigurationDescriptorNumInterfaces = 7 ConfigurationDescriptorMaxPowerDraw = 125 | signal := "seven-interfaces-125mA";
// optional: GL.iNet, JetKVM and NanoKVM MAC prefixes in the ARP neighbour list; travel routers match 94-83-c4
#event_simpleName=NeighborListIP4 /(94-83-c4|80-34-28|48-da-35)/i | signal := "kvm-mac-prefix";
* | signal := "";
}
| signal != ""
| groupBy([aid, ComputerName], function=[collect([signal, ConfigurationDescriptorName, DeviceManufacturer, DeviceProduct, DeviceSerialNumber]), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen)])
| sort(lastSeen, order=desc)
Lower-fidelity parts (2)
- the seven-interfaces branch — composite docks and headsets can present seven interfaces. Remove: delete the // optional: seven interfaces comment and the line under it
- the NeighborListIP4 MAC-prefix branch — 94-83-c4 covers GL.iNet travel routers and the other two prefixes are other vendors' KVMs. Remove: delete the // optional: GL.iNet, JetKVM comment and the line under it
Elastic
Written for this site, from indicators reported by Grumpy Goose Labs, GL.iNet documentationReads the Windows 6416 event and Elastic Defend DNS events; signals names the artifact that fired.
FROM logs-system.security-*, winlogbeat-*, logs-endpoint.events.network-*
| WHERE @timestamp > NOW() - 90 days
| EVAL usb = TO_LOWER(CONCAT(COALESCE(winlog.event_data.DeviceDescription, ""), " ", COALESCE(winlog.event_data.DeviceId, "")))
| EVAL signal = CASE(
event.code == "6416" AND usb RLIKE ".*(glkvm|glinet|glikvm|composite kvm device).*", "stock-strings",
event.code == "6416" AND usb RLIKE ".*cafebabe.*" AND usb RLIKE ".*(vid_1d6b&pid_0104|vid_046d&pid_c526|vid_6940&pid_6973|vid_413c&pid_2011|vid_045e&pid_005f).*", "serial-and-spoofed-id",
TO_LOWER(COALESCE(dns.question.name, "")) RLIKE ".*glkvm.*", "glkvm-lookup",
"")
| WHERE signal != ""
| STATS first_seen = MIN(@timestamp), last_seen = MAX(@timestamp), signals = VALUES(signal) BY host.name
| SORT last_seen DESC
Cortex XQL
Written for this site, from indicators reported by GL.iNet documentationFinds hosts that looked up a glkvm name; USB descriptor checks need the Windows event or osquery queries.
// Hunt: GL.iNet Comet. xdr_data carries the glkvm lookup; USB descriptors are not in it.
dataset = xdr_data
| filter event_type = ENUM.NETWORK and (dns_query_name ~= "(?i)glkvm" or action_external_hostname ~= "(?i)glkvm")
| comp min(_time) as first_seen, max(_time) as last_seen, values(dns_query_name) as lookups by agent_hostname
| sort desc last_seen
osquery
Written for this site, from indicators reported by Grumpy Goose LabsLocal check on macOS and Linux for the stock strings, the serial with a spoofed identity, and ARP neighbours with KVM-vendor prefixes.
-- Hunt: GL.iNet Comet on macOS and Linux (usb_devices does not exist on Windows).
SELECT 'usb-device' AS signal, vendor || ' ' || model || ' ' || serial || ' ' || vendor_id || ':' || model_id AS evidence
FROM usb_devices
WHERE vendor LIKE '%GLKVM%' OR vendor LIKE '%GLIKVM%' OR vendor LIKE '%Glinet%' OR model LIKE '%Composite KVM Device%'
OR (serial = 'CAFEBABE' AND ((vendor_id = '1d6b' AND model_id = '0104') OR (vendor_id = '046d' AND model_id = 'c526')
OR (vendor_id = '6940' AND model_id = '6973') OR (vendor_id = '413c' AND model_id = '2011')
OR (vendor_id = '045e' AND model_id = '005f')))
UNION ALL
-- optional: GL.iNet, JetKVM and NanoKVM MAC prefixes in the ARP cache; travel routers match 94:83:c4
SELECT 'arp-neighbour', address || ' ' || mac
FROM arp_cache
WHERE mac LIKE '94:83:c4:%' OR mac LIKE '80:34:28:%' OR mac LIKE '48:da:35:%'
Lower-fidelity parts (1)
- the arp_cache branch — 94:83:c4 covers GL.iNet travel routers and the other prefixes are other vendors' KVMs. Remove: delete from the -- optional comment through the end of the statement and the UNION ALL above it
False positives and tuning
Grumpy Goose Labs can neither confirm nor deny DPRK use of these devices, and the Comet is a mainstream product. Scope to standard-issue laptops and treat a hit as a question.
Several indicators are weak alone: the VID/PID, CAFEBABE and Composite KVM Device are shared with PiKVM, and the spoofed identities are real peripherals. 94:83:C4 covers GL.iNet's whole line, mostly travel routers.
Firmware values date from releases 1.3.0 and 1.5.0 of November 2025, so a later release may change the descriptor name and the hunt needs re-baselining. GL.iNet documents no default password, unlike TinyPilot.
Sources
- Be KVM, Do Fraud — Grumpy Goose Labs, 2025-11-14
- Unemployfuscation: Identifying Obfuscated TinyPilot and PiKVM Devices — Grumpy Goose Labs, 2024-11-28
- Local access via browser — GL.iNet KVM Docs
- Remote access via cloud — GL.iNet KVM Docs
- Remote access via Tailscale — GL.iNet KVM Docs
- Comet (GL-RM1) V1/V2 Quick Setup — GL.iNet KVM Docs
- Threat Spotlight: Red Flags for Red Star Hackers: Hunting for North Korean Insiders — ReliaQuest, 2025-05-08