What
Two families in the bundled DB — Deimos C2 and Koadic — top out below the default detection threshold (DEFAULT_THRESHOLD = 35) even when an observation carries every documented indicator for them. They can never be reported at the default floor.
Why
_score_signature sums distinct indicator-class weights (weight_seen[klass] = w), so multiple URI hits count once. With their strongest available indicators:
| Family |
Indicators present |
Score |
| Deimos C2 |
port(6) + uri(16) + http_banner(12) |
34 |
| Koadic |
port(6) + uri(16) + user_agent(10) |
32 |
Both are < 35, so a fully-matching observation produces no finding at the default threshold.
Reproduce
from c2detect.core import signatures, scan_observation, Observation
s = next(x for x in signatures() if x.family == "Deimos C2")
o = Observation(port=s.ports[0], uris=list(s.uris[:2]), http_banner=s.http_banners[0])
print(scan_observation(o, threshold=35).count) # -> 0
print(scan_observation(o, threshold=30).count) # -> 1
Impact
These two families are effectively undetectable at the shipped default; only an operator who lowers --threshold will ever see them. The self-check marks them as feature/heuristic-adjacent so it stays HEALTHY, which masks the gap.
Options (non-breaking preferred)
- Add a decisive indicator (a captured JA3/JA3S/JARM) to each family so a single strong hit clears 35.
- Or nudge a corroboration bonus for
port + uri + (banner|ua) co-occurrence.
- Or document these as "requires
--threshold 30" in the signature notes.
Changing DEFAULT_THRESHOLD or the class weights would alter the detection contract for every family, so a targeted signature/enrichment fix is the safer path.
What
Two families in the bundled DB — Deimos C2 and Koadic — top out below the default detection threshold (
DEFAULT_THRESHOLD = 35) even when an observation carries every documented indicator for them. They can never be reported at the default floor.Why
_score_signaturesums distinct indicator-class weights (weight_seen[klass] = w), so multiple URI hits count once. With their strongest available indicators:Both are
< 35, so a fully-matching observation produces no finding at the default threshold.Reproduce
Impact
These two families are effectively undetectable at the shipped default; only an operator who lowers
--thresholdwill ever see them. The self-check marks them as feature/heuristic-adjacent so it stays HEALTHY, which masks the gap.Options (non-breaking preferred)
port + uri + (banner|ua)co-occurrence.--threshold 30" in the signature notes.Changing
DEFAULT_THRESHOLDor the class weights would alter the detection contract for every family, so a targeted signature/enrichment fix is the safer path.