Skip to content

feat(amwscan): map the Exploit vocabulary from the engine's own words - #97

Merged
thiagoluga merged 1 commit into
mainfrom
feat/the-exploit-vocabulary-from-the-engines-own-words
Aug 18, 2026
Merged

thiagoluga merged 1 commit into
mainfrom
feat/the-exploit-vocabulary-from-the-engines-own-words

Conversation

@thiagoluga

Copy link
Copy Markdown
Owner

#96 made the parenthesised token the discriminator. The Function tokens were already in the table; the Exploit ones were not — and on the account this came from that is 779 findings, 732 of them execution alone, all landing on other/medium/heuristic.

Mapped from what the engine says, not from what the names suggest

AMWScan prints a description under every finding. Each mapping quotes it in the table, so the next person can check the mapping against the source rather than against my reading of a token name.

token the engine's own description mapped to
execution "RCE (Remote Code Execution) allow remote attackers to execute PHP code on the target machine via HTTP" backdoor / critical
nano "a family of PHP webshells which are code golfed to be extremely stealthy and efficient" webshell / critical
clever_include "LFI (Local File Inclusion) … inject and execute arbitrary commands or code" injection / high
infected_comment "comments composed by 5 random chars usually used to detect if a file is infected yet" other / high

execution lands where eval lands because the engine describes them as the same thing. clever_include gets exactly what the table already gives plain include. infected_comment is a marker something left behind rather than a technique the file performs, which is why it is not backdoor or webshell.

The obfuscation family sits at medium, on purpose

base64_long, hex_char, double_var2, concat_vars_array, concat_vars_with_spaces — every one of their descriptions says the technique is usually used for malicious code, and usually is the operative word. Minified libraries, licence blobs and legitimate encoders trip the same patterns.

They match the existing encoded entry rather than obfuscated. A heuristic that fires on ordinary vendor code at high severity is one whose severity stops meaning anything.

Two left unmapped, with the reasoning next to the ones that are

  • etc_passwd — "an attacker who has accessed the /etc/passwd file may attempt a brute force attack". True of an attacker; also true of every config parser, test fixture and tutorial that names the path. The engine is describing what the string means when an attacker wrote it, and the pattern cannot tell who did.
  • php_uname — the engine files it under RCE. It is one information-gathering call, which diagnostics and installers make legitimately. Calling that critical on its own would put ordinary code next to webshells in the same list.

They stay unknown, which means other/medium and a line in the note counts, so they keep surfacing as something to decide about. An unmapped rule is not a discarded one — and a test asserts they stay that way, so mapping them later has to be a decision rather than a drift.

Scope

No verdict moves. Severity does not feed the score; confidence does, and all of these are heuristic, same as the fallback they replace. What moves is what a person reads when deciding which of 779 findings to look at first.

Confirmed by mutation in both directions:

removing `execution`:      execution: category "other", wanted "backdoor"
raising hex_char to high:  hex_char: severity "high", wanted medium — obfuscation
                           alone is not a reason to act

#96 made the parenthesised token the discriminator. The Function tokens were
already in the table; the Exploit ones were not, and on the account this came
from that is 779 findings, 732 of them `execution` alone, all landing on
other/medium/heuristic.

AMWScan prints what each pattern means, on the "- " line under every finding.
Each mapping quotes that description beside it, so the next person can check the
mapping against the source rather than against my reading of a token name:

  execution       "RCE ... execute PHP code on the target machine via HTTP"
                  -> backdoor / critical, the same thing eval means
  nano            "a family of PHP webshells ... code golfed to be stealthy"
                  -> webshell / critical
  clever_include  "LFI ... inject and execute arbitrary commands or code"
                  -> injection / high, matching the existing `include` entry
  infected_comment
                  "comments composed by 5 random chars usually used to detect
                  if a file is infected yet" -> other / high: a marker something
                  left behind, not a technique the file performs

The obfuscation family — base64_long, hex_char, double_var2, concat_vars_array,
concat_vars_with_spaces — sits at MEDIUM, matching the existing `encoded` entry
rather than `obfuscated`. Every one of their descriptions says the technique is
USUALLY used for malicious code, and usually is the operative word: minified
libraries, licence blobs and legitimate encoders trip the same patterns. A
heuristic that fires on ordinary vendor code at high severity is one whose
severity stops meaning anything.

Two are deliberately left unmapped, with the reasoning written beside the ones
that are:

  etc_passwd  true of an attacker, and also of every config parser, test fixture
              and tutorial that names the path
  php_uname   one information-gathering call that installers make legitimately;
              the engine files it under RCE, which is more than a single call
              earns

They stay unknown, which means other/medium and a line in the note counts, so
they keep showing up as something to decide about. An unmapped rule is not a
discarded one.

Severity does not feed the score — confidence does, and all of these are
heuristic — so no verdict moves. What moves is what a person reads when
deciding which of 779 findings to look at first.

Confirmed by mutation in both directions: removing `execution` fails with
'category "other", wanted "backdoor"', and raising hex_char to high fails with
'obfuscation alone is not a reason to act'.
@sonarqubecloud

Copy link
Copy Markdown

@thiagoluga
thiagoluga merged commit 1766a6e into main Aug 18, 2026
10 checks passed
@thiagoluga
thiagoluga deleted the feat/the-exploit-vocabulary-from-the-engines-own-words branch August 18, 2026 20:15
@thiagoluga

Copy link
Copy Markdown
Owner Author

A/B against the real engines — the "no verdict moves" claim holds

This finished after the merge, so posting it for the record rather than as a gate. Same image, same corpus, only the new table entries differing:

without the vocabulary:  confirmed=1  likely=3  suspicious=3  clean=0
with the vocabulary:     confirmed=1  likely=3  suspicious=3  clean=0

And the votes are byte-identical, which is the part that actually settles it — the summary could match by coincidence, the multipliers cannot:

before:  1 x amwscan  weight 0.80 x heuristic = 0.64  (rule Exploit)
         2 x amwscan  weight 0.80 x signature = 0.80  (rule Signature)
after:   1 x amwscan  weight 0.80 x heuristic = 0.64  (rule Exploit)
         2 x amwscan  weight 0.80 x signature = 0.80  (rule Signature)

0 failure(s), 2 warning(s) — the two long-standing ones, unrelated.

Worth contrasting with #96, where I made the same claim and it was half wrong: Signature findings moved from heuristic to signature and their effective weight rose from 0.64 to 0.80. Severity genuinely does not feed the score, but confidence does, and #96 changed a confidence. This one changes only categories and severities, and now that is measured rather than reasoned.

@thiagoluga

Copy link
Copy Markdown
Owner Author

Deployed, and the arc closes on the account this started from

v0.1.12-10-g1766a6e on the validation host, sha256 verified, public in 0.55s.

The categories across that account's votes, across today:

category before #96 after #96 after #97
other 180 45 17
backdoor — 97 99
webshell — 38 47
obfuscation — — 16
known_malware 36 36 36
injection — — 1

Confidences unchanged at 180 heuristic, 36 signature, which confirms on a second environment what the container A/B showed: this change moves categories, not weights.

The 17 still in other are the ones that should be there — etc_passwd and php_uname left unmapped on purpose, and infected_comment mapped to other deliberately because it describes a marker something left behind rather than a technique the file performs.

That number is now small enough to be read rather than skimmed, which was the point. It started at 180.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant