Contagious Interview Fake-Recruiter and Front-Company Infrastructure

WindowsmacOSNetwork

TTP Hunt medium Contagious InterviewDeceptiveDevelopmentDPRK IT Worker Fraud

Catches the interview lure itself: a candidate's workstation cloning and running an unvetted take-home repository, and traffic toward the front companies DPRK operators register to look like employers.

What this hunt looks for

Operators tracked under the Contagious Interview and DeceptiveDevelopment clusters pose as recruiters on LinkedIn and job boards, sometimes standing up United States registered front companies before pushing a candidate to a coding-assessment repository that delivers BeaverTail and InvisibleFerret. Silent Push documented BlockNovas LLC, Angeloper Agency and SoftGlide LLC; the FBI seized the BlockNovas and SoftGlide domains in April 2025.

This hunt targets the moment a candidate's workstation clones and executes an unvetted take-home assignment repository, and traffic toward reported front-company infrastructure, so the lure can be caught before or during initial access rather than only after malware runs.

Technique
Initial access through social-engineering job-interview lures (MITRE ATT&CK T1566, T1204.002) fronted by DPRK-controlled shell companies and recruiter personas (Group G1052, Contagious Interview).
Data sources
EDR process telemetry, DNS or proxy logs
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.

TypeValueNote
Domain blocknovas.com BlockNovas front-company domain, with subdomains such as gitlab and apply, documented by Silent Push; seized by the FBI in April 2025.
Domain apply-blocknovas.site Second BlockNovas application domain documented by Silent Push.
Domain angeloper.com Angeloper Agency front-company domain documented by Silent Push.
Domain angeloperonline.online Angeloper Agency domain documented by Silent Push.
Domain softglide.co SoftGlide front-company domain documented by Silent Push; seized by the FBI in April 2025.

Logic

Every query below implements these steps, so the same hunt returns the same result in each product.

  1. Collect: DNS lookups and web connections on Windows and macOS endpoints, plus process starts for the sequence
  2. Match: name is, or is a subdomain of, a reported front-company domain (BlockNovas, Angeloper, SoftGlide)
  3. Match: or git clone then node, npm, python or pip within 10 minutes (lower fidelity; KQL, SPL and Sigma only)
  4. Report: devices with the signals hit, newest first

Queries

Defender / Sentinel KQL

Written for this site, from indicators reported by Silent Push, Unit 42

A device with a front-company domain hit is the strongest result; a clone-then-run hit alone needs recruiting context before escalating.

union
    // optional: lower fidelity, git clone then a developer tool that names the cloned folder; delete this line and the branch below it
    (DeviceProcessEvents
        | where Timestamp > ago(1d) and FileName in~ ("node.exe", "node", "npm.cmd", "npm", "python.exe", "python", "python3", "pip.exe", "pip", "pip3")
        | join kind=inner (
            DeviceProcessEvents
            | where Timestamp > ago(1d) and FileName in~ ("git.exe", "git") and ProcessCommandLine has "clone"
            | extend RepoFolder = extract(@"([^/\\\s]+?)(?:\.git)?[\s""']*$", 1, ProcessCommandLine)
            | where isnotempty(RepoFolder)
            | project DeviceId, CloneTime = Timestamp, CloneCommand = ProcessCommandLine, RepoFolder
          ) on DeviceId
        | where Timestamp between (CloneTime .. (CloneTime + 10m))
        | where ProcessCommandLine has RepoFolder or InitiatingProcessCommandLine has RepoFolder or FolderPath has RepoFolder
        | project Timestamp, DeviceId, DeviceName, Signal = "clone then run", Detail = strcat(CloneCommand, " -> ", ProcessCommandLine)),
    (DeviceNetworkEvents
        | where Timestamp > ago(30d)
        | where RemoteUrl has_any ("blocknovas.com", "apply-blocknovas.site", "angeloper.com", "angeloperonline.online", "softglide.co")
        | project Timestamp, DeviceId, DeviceName, Signal = "front-company domain", Detail = RemoteUrl)
| summarize Hits = count(), Signals = make_set(Signal), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Details = make_set(Detail, 5) by DeviceId, DeviceName
| order by array_length(Signals) desc, LastSeen desc
Lower-fidelity parts (2)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those entries from the has_any list
  • the git clone then developer-tool sequence — legitimate technical interviews and ordinary developer work clone and run unfamiliar repositories. Remove: delete the // optional: comment line and the whole (DeviceProcessEvents ... ) branch beneath it, up to the line ending in Detail = strcat(...)),

Splunk SPL

Written for this site, from indicators reported by Silent Push, Unit 42

The same two signals, with the domain branch over DNS data and the sequence branch over process starts joined to the cloned folder.

```Placeholders: Network_Resolution carries DNS lookups (for proxy data use the host your proxy extracts); Endpoint.Processes carries Windows and macOS process starts.```
| tstats count max(_time) AS _time FROM datamodel=Network_Resolution.DNS WHERE earliest=-30d DNS.query IN ("blocknovas.com","*.blocknovas.com","apply-blocknovas.site","*.apply-blocknovas.site","angeloper.com","*.angeloper.com","angeloperonline.online","*.angeloperonline.online","softglide.co","*.softglide.co") BY DNS.src, DNS.query
| rename DNS.src AS host, DNS.query AS detail
| eval signal="front-company domain"
```optional: lower fidelity, git clone then a developer tool that names the cloned folder; delete this line and the append block below it```
| append
    [| tstats count FROM datamodel=Endpoint.Processes WHERE earliest=-1d ((Processes.process_name IN ("git","git.exe") AND Processes.process="*clone*") OR Processes.process_name IN ("node","node.exe","npm","npm.cmd","python","python3","python.exe","pip","pip3","pip.exe")) BY _time span=1s, Processes.dest, Processes.process_name, Processes.process
     | rename Processes.* AS *
     | sort 0 dest _time
     | eval is_clone=if(match(process_name, "^git"), 1, 0)
     | eval clone_time=if(is_clone=1, _time, null()), clone_cmd=if(is_clone=1, process, null())
     | streamstats current=f last(clone_time) AS prev_clone_time last(clone_cmd) AS prev_clone_cmd BY dest
     | where is_clone=0 AND _time-prev_clone_time<=600
     | rex field=prev_clone_cmd "(?<repo>[^/\s]+?)(?:\.git)?\s*$"
     | where like(process, "%".repo."%")
     | eval host=dest, detail=prev_clone_cmd." -> ".process, signal="clone then run"
     | fields _time, host, detail, signal]
| stats count AS hits min(_time) AS first_seen max(_time) AS last_seen values(signal) AS signals values(detail) AS details BY host
| convert ctime(first_seen) ctime(last_seen)
| sort - last_seen
Lower-fidelity parts (2)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those names and their *.name twins from the IN list
  • the git clone then developer-tool sequence — legitimate technical interviews and ordinary developer work clone and run unfamiliar repositories. Remove: delete the optional comment line and the whole append block beneath it

Sigma

Written for this site, from indicators reported by Silent Push, Unit 42

The domain rule applies to Windows and macOS DNS telemetry, and the correlation below the marker converts to a single-host temporal search.

title: DNS Lookup of a Contagious Interview Front-Company Domain
id: 5f0c2f0e-3c55-5a77-9a40-5d7c9d0e2b11
status: experimental
description: >
  Detects lookups of the BlockNovas, Angeloper and SoftGlide front-company
  domains documented by Silent Push, on Windows and macOS DNS telemetry.
references:
  - https://www.silentpush.com/blog/contagious-interview-front-companies/
logsource:
  category: dns_query
detection:
  selection_exact:
    QueryName:
      - 'blocknovas.com'
      - 'apply-blocknovas.site'
      - 'angeloper.com'
      - 'angeloperonline.online'
      - 'softglide.co'
  selection_sub:
    QueryName|endswith:
      - '.blocknovas.com'
      - '.apply-blocknovas.site'
      - '.angeloper.com'
      - '.angeloperonline.online'
      - '.softglide.co'
  condition: 1 of selection_*
falsepositives:
  - Researchers and defenders opening the reported domains
level: high
---
# optional: lower fidelity, git clone then a developer tool; delete this line and every document below it
title: Git Clone Executed on an Endpoint
name: dprk_git_clone
id: 0ab700d2-d8a1-5287-816c-7da7ac1ab319
description: >
  A repository being cloned. Meaningless alone; it is the first half of the
  correlation rule below.
references:
  - https://www.silentpush.com/blog/contagious-interview-front-companies/
  - https://unit42.paloaltonetworks.com/two-campaigns-by-north-korea-bad-actors-target-job-hunters/
logsource:
  category: process_creation
detection:
  clone:
    Image|endswith:
      - '\git.exe'
      - '/git'
    CommandLine|contains: 'clone'
  condition: clone
---
title: Package Manager or Interpreter Executed on an Endpoint
name: dprk_dev_tooling_exec
id: 0d4e6d4d-8a59-5124-985b-e62b2082a482
description: >
  A package manager or interpreter running. Meaningless alone; it is the second
  half of the correlation rule below.
logsource:
  category: process_creation
detection:
  install:
    Image|endswith:
      - '\node.exe'
      - '\npm.cmd'
      - '\python.exe'
      - '\pip.exe'
      - '/node'
      - '/npm'
      - '/python3'
      - '/pip3'
  condition: install
---
title: Developer Tooling Executes a Freshly Cloned Repository During an Interview-Style Session
id: 61470ffb-d857-5d4e-a58b-aa9198d04911
status: experimental
description: >
  Detects a git clone followed within ten minutes by a package manager or
  interpreter on the same host, consistent with a candidate running a malicious
  take-home assignment repository during a fake interview. A Sigma temporal
  correlation does not enforce order or compare the cloned folder.
correlation:
  type: temporal
  rules:
    - dprk_git_clone
    - dprk_dev_tooling_exec
  group-by:
    - ComputerName
  timespan: 10m
falsepositives:
  - Legitimate technical interviews and normal developer workflows also clone
    and run unfamiliar repositories
level: low
Lower-fidelity parts (2)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those names from both selection lists
  • the git clone then developer-tool sequence — legitimate technical interviews and ordinary developer work clone and run unfamiliar repositories. Remove: delete the # optional: comment line and every document below it

CrowdStrike LogScale

Written for this site, from indicators reported by Silent Push, Unit 42

The domain signal over Falcon DnsRequest events on Windows and macOS.

#event_simpleName=DnsRequest
| lower(DomainName, as=DomainName)
| regex("(?<parent>[^.]+\\.[^.]+)\\.?$", field=DomainName)
| in(parent, values=["blocknovas.com", "apply-blocknovas.site", "angeloper.com", "angeloperonline.online", "softglide.co"])
| groupBy([aid, ComputerName], function=[count(as=Lookups), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen), collect([DomainName])])
| sort(LastSeen, order=desc)
Lower-fidelity parts (1)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those names from the values list

Elastic

Written for this site, from indicators reported by Silent Push, Unit 42

The domain signal over ECS DNS and network events.

FROM logs-*
| EVAL name = TO_LOWER(COALESCE(dns.question.name, destination.domain))
| WHERE name RLIKE "(.*\\.)?(blocknovas\\.com|apply-blocknovas\\.site|angeloper\\.com|angeloperonline\\.online|softglide\\.co)\\.?"
| STATS lookups = COUNT(*), names = VALUES(name), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp) BY host.name
| SORT last_seen DESC
Lower-fidelity parts (1)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those names and their pipes from the RLIKE pattern

Cortex XQL

Written for this site, from indicators reported by Silent Push, Unit 42

The domain signal over Cortex network events carrying a DNS name.

dataset = xdr_data
| filter event_type = ENUM.NETWORK and (dns_query_name != null or action_external_hostname != null)
| alter host = lowercase(coalesce(dns_query_name, action_external_hostname))
| alter parent = arrayindex(regextract(host, "([^.]+[.][^.]+)[.]?$"), 0)
| filter parent in ("blocknovas.com", "apply-blocknovas.site", "angeloper.com", "angeloperonline.online", "softglide.co")
| comp count() as lookups, values(host) as names, max(_time) as last_seen by agent_hostname
| sort desc last_seen
Lower-fidelity parts (1)
  • the BlockNovas and SoftGlide domains — the FBI seized them in April 2025 and the infrastructure rotates, so they are of historical value and any entry may be stale. Remove: delete those names from the in list

False positives and tuning

Legitimate technical interviews routinely involve cloning a take-home repository and installing its dependencies, so on a developer's machine the sequence is normal. Correlate with recruiting context, contractor or bring-your-own-device status and domain reputation before escalating.

The domain match needs a current list of campaign domains, since this infrastructure rotates and the seized domains are of historical value only.

Sources

Tuning required before use This hunt is a starting point. Adapt the queries to your schema, validate them against your baseline and tune them for the false positives noted above. Indicators go stale as operators rotate hosts.