Misbehaving binaries: Hunting LOLBins 

Misbehaving binaries: Hunting LOLBins 

Views: 5

🕵️‍♂️ Detection Engineering: Catching jsc.exe Abuse (T1127)

🟣 Technique: Trusted Developer Utilities Proxy Execution (MITRE ATT&CK T1127)
🔵 Tool abused: jsc.exe, the Microsoft JScript .NET compiler
🟢 Stack: Atomic Red Team ➜ Sysmon ➜ Elastic Agent ➜ Elastic Security (EQL)
🟠 Rule severity: High (risk score 73)


🌈 1. The Big Picture

Attackers love tools that Windows already trusts. Instead of dropping a custom compiler or a suspicious binary, they reach for utilities that ship with the operating system or the .NET Framework. These are known as LOLBINs (Living Off the Land Binaries). Because they are signed by Microsoft and present on almost every host, they blend in with normal activity.

jsc.exe is a perfect example. It is a legitimate compiler that turns JScript source code into a real EXE or DLL. In the wrong hands, that means:

  • 🧪 Malicious code can be compiled on the victim machine, so no pre-built payload has to cross the network.
  • 🛡️ The compiler is a trusted, signed Microsoft binary, so simple allow-lists often wave it through.
  • 📦 The output can be an executable or a library, giving the attacker flexibility in how it is later launched.

This article walks through the full detection engineering loop: simulate ➜ observe the telemetry ➜ write the query ➜ turn it into a rule ➜ verify the alert.


🧠 2. Concept Explained: Trusted Developer Utilities

MITRE groups this behavior under T1127 (Trusted Developer Utilities Proxy Execution). The idea is simple:

🎯 Idea💬 What it means
TrustedThe binary comes from Microsoft / .NET and is expected on Windows systems
Developer utilityIt exists for building software (compilers, build tools)
Proxy executionThe attacker uses it as a middleman to run or build code, hiding behind its reputation

For jsc.exe, the compiler flags decide what gets produced:

🚩 FlagLong form🎁 Output
/t:exe/target:exeConsole executable
/t:winexe/target:winexeWindows (GUI-style) executable
/t:library/target:libraryDLL

Those three switches are the heart of the detection. A normal workstation that never compiles JScript should almost never see them.

📝 Note on the ID: the Atomic tests for jsc.exe live under the parent technique T1127. The rule in the lab was named with the sub-technique tag T1127.001, so keep your tagging consistent with how your team maps rules to ATT&CK.


🔴 3. Step One: Simulate the Behavior (Red Side)

The emulation uses Atomic Red Team. A single command runs every test for the technique:

Invoke-AtomicTest T1127

Output from the console:

✅ Two tests ran, both finished with exit code 0:

🧪 TestGoal
T1127-1Compile JavaScript to an EXE with jsc.exe
T1127-2Compile JavaScript to a DLL with jsc.exe

The compiler banner (JScript Compiler 14.00.4084 for .NET Framework 4.0.30319) confirms the genuine Microsoft binary did the work.


🔎 4. Step Two: Hunt the Telemetry (Blue Side)

With the simulation done, we switch to Elastic Discover and look for the process:

process.name : "jsc.exe"

Searching the last 50 minutes returned a burst of events (25 documents in the broader search), all coming from the lab workstation. The events come from the Sysmon data stream (windows.sysmon_operational) shipped by Elastic Agent.

🧩 What kinds of events show up?

A single compile action produces a small chain of Sysmon events, each with its own event.code:

🔢 event.code🏷️ Event type💡 Why it matters
1Process creationShows jsc.exe starting, with its full command line. This is the event our detection uses
7Image loadedDLLs loaded by jsc.exe (for example .NET runtime components)
11File createdThe compiled EXE/DLL (and related artifacts) appearing on disk
23File delete (archived)Temporary files removed after the run

Filtering by event.code : 1 narrows the view to just the process-start events, which is exactly where the compiler flags are visible.

📋 Useful fields inside the log

Opening a single event reveals the fields worth building on:

Field🎯 Use in detection
process.nameName of the running binary (jsc.exe)
process.pe.original_file_nameThe name baked into the PE header. Survives renaming
process.command_lineReveals the target type flags (/t:exe, /t:library, …)
process.executablePath of the binary, useful to spot odd locations
process.parent.*Who launched the compiler (for example PowerShell)
user.name, host.nameScoping and triage
event.code, event.typePick the right Sysmon event
winlog.event_data.*Raw Sysmon details (company, product, signature status)

🧬 5. Step Three: Write the EQL Detection

EQL (Event Query Language) is built for event-based data. It lets you declare the event category up front and then filter with readable conditions.

process where event.type == "start"
  and (
    process.name : "jsc.exe"
    or process.pe.original_file_name : "jsc.exe"
  )
  and process.command_line : (
    "*/t:exe*",
    "*/target:exe*",
    "*/t:library*",
    "*/target:library*",
    "*/t:winexe*",
    "*/target:winexe*"
  )

🔬 Query breakdown

🧱 Part💬 Meaning
process where event.type == "start"Only look at process start events
process.name : "jsc.exe"The process is called jsc.exe
or process.pe.original_file_name : "jsc.exe"…or the PE header says it is jsc.exe, which catches a renamed copy of the compiler
process.command_line : ( ... )The command line contains one of the target-type switches
"*/t:exe*", "*/target:exe*"Compile to an executable
"*/t:library*", "*/target:library*"Compile to a DLL
"*/t:winexe*", "*/target:winexe*"Compile to a windowed executable

💡 The : operator in EQL is a case-insensitive match that supports wildcards, which makes the pattern list short and forgiving of capitalization changes.

🛡️ Why the two-name check is clever

Attackers often rename binaries to dodge simple name-based rules. By checking both process.name and process.pe.original_file_name, the rule still fires if someone copies jsc.exe to notepad_helper.exe, because the original name in the PE metadata does not change.


🏗️ 6. Step Four: Turn the Query into a Detection Rule

In Elastic Security ➜ Rules ➜ Create new rule, the workflow has four stages.

🟦 Stage 1: Define rule

  • Rule type: Event Correlation (EQL). The other types shown are Custom query, Threshold, Indicator Match, New Terms and ES|QL. The Machine Learning type is unavailable without a Platinum subscription.
  • Source: index patterns. The lab used the defaults: apm-*-transaction*, auditbeat-*, endgame-*, filebeat-*, logs-*, packetbeat-*, traces-apm*, winlogbeat-*, -*elastic-cloud-logs-*
  • EQL query: paste the query from section 5.
  • Optional extras: alert suppression (per rule execution or per time period), required fields, related integrations and a timeline template. In the lab, suppression and the others were left at their defaults.

🟧 Stage 2: About rule

SettingValue used
NameT1127.001 — Trusted Developer Utility: jsc.exe LOLBIN execution (ART LAB)
DescriptionDetects JavaScript compilation to EXE or DLL via jsc.exe
Default severity🟠 High
Default risk score73
TagsOptional, none set

🟩 Stage 3: Schedule rule

SettingValue
Runs every5 minutes
Additional look-back time1 minute

The extra look-back overlaps consecutive runs slightly, so events that arrive a little late are not missed.

🟪 Stage 4: Rule actions

Elastic offers many connectors here (Index, Server log, Cases, Email, Jira, Microsoft Teams, Opsgenie, PagerDuty, ServiceNow, Slack, TheHive, Webhook, XSOAR and more) plus response actions such as Osquery and Elastic Defend. The lab did not attach any action. It simply used Create & enable rule.


✅ 7. Step Five: Verify the Rule Works

⏱️ Execution results

On the rule’s Execution results tab, the Execution log shows scheduled runs every five minutes, all marked Succeeded with the message Rule execution completed successfully. The Gaps table is empty (No items found), which means no scheduled run was skipped.

🚨 Alerts

Rerunning the Atomic tests produced alerts on the Alerts tab:

  • 📊 The Trend chart shows a spike, with 4 alerts, grouped by event.category (process).
  • 🔴 Each alert is rated High with a risk score of 73.
  • 🧾 The reason text shows a process event with jsc.exe (and its parent process) on the lab host.
  • 👤 Columns for host name, user name and process name (jsc.exe) make triage quick.

Result: the simulated attack behavior was detected end to end. 🎉


🔄 8. The Detection Engineering Loop

  🔴 Simulate         🔎 Observe          🧬 Query           🏗️ Rule            ✅ Validate
  Atomic Red Team ➜  Elastic Discover ➜  EQL pattern   ➜   Elastic Security ➜  Alert fires
  Invoke-AtomicTest   Sysmon events       jsc.exe + flags    5 min schedule      High / 73

🛠️ 9. Tuning and Hardening Ideas

These are suggestions for taking the rule beyond the lab:

💡 Idea🎯 Benefit
Add dash-style switches (-t:exe, -target:library, and so on).NET compilers accept - as well as /, so the current patterns could be sidestepped
Add exceptions for known build servers or developer machinesCuts noise where compiling is normal
Alert on jsc.exe run from unexpected parent processes (such as Office apps or script hosts)Raises confidence of malicious use
Correlate with Sysmon event 11 (file create) of an EXE/DLL shortly afterConfirms that a binary was actually produced
Hunt for the compiled output being executed afterwardsFollows the attack chain past the compile step

🏁 10. Key Takeaways

  1. 🧰 LOLBINs hide in plain sight. jsc.exe is signed, trusted and present on many systems.
  2. 🧠 Command-line flags are the signal. /t:exe, /t:library and /t:winexe separate compiling from harmless use.
  3. 🪪 Check the original file name. process.pe.original_file_name defeats simple renaming.
  4. 🔁 Test what you build. Atomic Red Team plus Elastic lets you prove the detection fires.
  5. 🎚️ Tune before production. Add exceptions and extra context so the rule stays useful.

🟣 Detection Engineering • T1127 • jsc.exe LOLBIN • Atomic Red Team + Elastic Security 🟢