CRITICAL AWS ALB / ELB Automated Attack Tooling LTR-0019 ✓ validated

Detect Vulnerability Scanners and Scripted Clients Hitting Admin/Auth Endpoints (AWS ALB / ELB Logs)

Published

MITRE ATT&CK: T1595.002T1190T1020

Sigma Rule

title: AWS ALB — Vulnerability Scanner and Scripted-Client Access to Sensitive Endpoints
id: e5cc82ed-71dc-4762-8692-bc5a536b14eb
status: experimental
description: >
  Detects two distinct signals in AWS ALB/ELB access logs. First, a request bearing
  a known vulnerability-scanner user-agent (sqlmap and similar tools) — this is
  unambiguous regardless of path or status, since no legitimate browser or
  first-party integration identifies itself this way. Second, a scripted HTTP
  client (python-requests, axios) reaching an admin, login, or export path — these
  libraries are common in legitimate automation, so the selection requires the
  sensitive path to avoid flagging every internal API caller.
references:
  - https://attack.mitre.org/techniques/T1595/002/
  - https://attack.mitre.org/techniques/T1190/
author: LogTriage
date: 2026/10/06
logsource:
  product: aws
  service: alb
detection:
  scanner_signature:
    user_agent|contains:
      - 'sqlmap'
      - 'nikto'
      - 'nmap'
      - 'acunetix'
  scripted_sensitive_access:
    user_agent|contains:
      - 'python-requests'
      - 'axios'
    request|contains:
      - '/admin/'
      - '/login'
      - '/export'
  condition: scanner_signature or scripted_sensitive_access
falsepositives:
  - Authorized pentest/scanner run (allowlist source IPs and time window)
  - Internal service-to-service calls using python-requests/axios (scope to internal CIDRs)
  - Monitoring bot whose path incidentally contains 'login'
level: critical
tags:
  - attack.reconnaissance
  - attack.t1595.002
  - attack.initial_access
  - attack.t1190
  - attack.exfiltration
  - attack.t1020

What this rule detects

AWS ALB/ELB access logs record every request’s user-agent and full request line, which makes them one of the few places a scanner signature and a scripted-client-on-a-sensitive-path show up side by side. This rule fires on either of two independent conditions:

  1. A known vulnerability-scanner user-agent (sqlmap, nikto, nmap, acunetix) — present in the request regardless of which path it targets or what status code comes back.
  2. A scripted HTTP client (python-requests, axios) reaching an admin, login, or export path — these libraries are everywhere in legitimate automation, so this selection only fires when the path itself is sensitive.

Detection logic

The two selections are deliberately asymmetric, because the two signals carry different weight:

  • scanner_signature needs nothing else. A user-agent string that announces itself as sqlmap is not something a normal client ever sends by accident — it is either an attacker or an authorized engagement, and either way it belongs in your alert queue.
  • scripted_sensitive_access is a conjunction on purpose. python-requests and axios are two of the most common HTTP client libraries in existence; alerting on them alone would bury the signal in internal tooling noise. Requiring the request to also target /admin/, a login path, or an export endpoint narrows it to the cases that matter — this is the same context-dependent approach LogTriage’s UA classifier uses everywhere else: the client matters less than what it’s reaching for.

Validated against a real sample

Validated against alb_attack.log (shipped with LogTriage), four requests from two source IPs:

  • 185.220.101.42 with UA sqlmap/1.7 requests GET /admin/users — 200 (the admin resource was served)
  • The same IP, UA python-requests/2.28.0, requests GET /admin/config — 403
  • 45.142.212.100, UA python-requests/2.28.0, requests POST /api/v1/login — 401
  • The same IP, UA axios/1.3.4, requests POST /api/v1/users/export — 200 (bulk export succeeded)

The rule fires on all four: the first via scanner_signature, the remaining three via scripted_sensitive_access (each pairs a scripted client with an admin, login, or export path). It stays silent on the matching benign log, where every request carries a real browser user-agent (Mozilla/5.0 ... Chrome/120.0.0.0) and targets ordinary dashboard/data/profile paths — no scanner signature and no scripted client anywhere in the file.

False positives

The scanner selection is close to zero-noise outside of authorized security testing — allowlist the engagement’s source IPs and time window if you run scheduled scans. The scripted-client selection is quieter by design but can still catch legitimate internal automation that happens to call an admin or export endpoint; scope it to exclude known internal service CIDRs rather than excluding the user-agents entirely, since that would blind the rule to an attacker using the same libraries from outside your network.

Frequently Asked Questions

Why flag sqlmap by user-agent alone, with no path or status condition?
Because the user-agent itself is the whole signal. sqlmap identifies itself in every request unless an attacker deliberately spoofs it, and no browser or legitimate first-party integration ever sends that string. Requiring a specific path or a failing status would only create gaps — the same scanner probes dozens of paths in a single run, and a 200 response (meaning it found something) is the one result you most need to see, not one you can afford to filter out.
Why also flag python-requests and axios, which are legitimate libraries used everywhere?
Because the rule never flags them on their own — the second selection requires both the scripted client AND a sensitive path (admin, login, or export). That mirrors how LogTriage's own user-agent classifier works: a scripted client on a public endpoint is low-risk background noise, but the same client against an admin panel or a bulk-export endpoint is a materially different event. The conjunction is what keeps this selection quiet on ordinary API traffic.
Why does a 200 on /admin/users from sqlmap matter more than a 403?
A 403 means the probe was rejected — annoying, but contained. A 200 means the scanner's request for an admin resource was actually served, which is either a real authorization gap or a session/token the attacker already holds. In the validated sample, the sqlmap request against /admin/users returns elb_status_code 200, which is why this rule is scoped to critical rather than a quieter recon severity.
How does this pair with WAF or GuardDuty on the same traffic?
A WAF rule may already block the sqlmap payload itself; this rule still matters because it fires on the user-agent regardless of WAF outcome, giving you the attacker IP and full request history even when the WAF silently drops the request. GuardDuty's AWS-managed findings cover a different layer (VPC/DNS/API threat intel) and generally will not see a payload-level scanner signature inside an HTTP request body or header the way an access-log rule does.

Related Resources

See this detection run on a real report

Try the live demo with a pre-loaded malicious log set — no signup required — or upload your own log file and get a full AI-reviewed threat report in minutes.