lemontree55 / packetgen

Ruby library to easily generate and capture network packets
MIT License
98 stars 13 forks source link

Build an SNMP #86

Closed KINGSABRI closed 6 years ago

KINGSABRI commented 6 years ago

Hello there, if you have seen this issue. I tried to use packetgen to do so but I could do it using the current documentation

Scapy version

pkt = IP(src="192.168.30.100", dst="192.168.30.199")/UDP(sport=161)/SNMP(community="private",PDU=SNMPset(varbindlist=SNMPvarbind(oid=ASN1_OID("1.3.6.1.4.1.9.2.1.55.192.168.30.10"),value="pwnd-router.config")]))

send(pkt)

tried

pkt1 = PacketGen.gen('IP', src: '192.168.102.205', dst: '127.0.0.1').
                 add('UDP', dport: 161, sport: 161).
                 add('SNMP', community: 'private', pdu: "DID KNOW HOW TO ADD PDU DETAILS THIS WAY" )

and

snmp = PacketGen::Header::SNMP.new
snmp.community = 'private'
snmp.data.chosen = 3 # SetRequest
snmp.pdu

I believe my issue that I couldn't understand how to assign the PDU details

PDU=SNMPset(varbindlist=SNMPvarbind(oid=ASN1_OID("1.3.6.1.4.1.9.2.1.55.192.168.30.10"),value="pwnd-router.config")])
picatz commented 6 years ago

Started looking at this issue for fun. This is some code using scapy to help dissect the problem.

from scapy.all import *

# I was having issues with the pkt in the example, this is the cleaned one
pkt = IP(src="192.168.30.100", dst="192.168.30.199")/UDP(sport=161)/SNMP(community="private",PDU=SNMPset(varbindlist=SNMPvarbind(oid=ASN1_OID("1.3.6.1.4.1.9.2.1.55.192.168.30.10"),value="pwnd-router.config")))

# get snmp layer
snmp = pkt.getlayer("SNMP")

# get PDU from snmp layer
pdu = snmp.PDU
# => <SNMPset  varbindlist=<SNMPvarbind  oid=<ASN1_OID['.1.3.6.1.4.1.9.2.1.55.192.168.30.10']> value='pwnd-router.config' |> |>

The Python code for PSNMPset is here.

picatz commented 6 years ago

I seem to be getting somewhere using this and this.

snmp = PacketGen::Header::SNMP.new

snmp.data.root.chosen = 3

snmp.pdu
# => pdu (SetRequest) pdu SEQUENCE:
#    id INTEGER: 0
#    error ENUMERATED: 0
#    error_index INTEGER: 0
#    bindings (VariableBindings) SEQUENCE OF:

snmp.pdu[:varbindlist] 
# => bindings (VariableBindings) SEQUENCE OF:

snmp.pdu[:varbindlist] << { name: '1.3.6.1.4.1.9.2.1.55.192.168.30.10', value: 'pwnd-router.config' }
# => [ varbind (VarBind) varbind SEQUENCE:
#    name OBJECT ID: "1.3.6.1.4.1.9.2.1.55.192.168.30.10"
#    value (ANY) "pwnd-router.config"
# ]
picatz commented 6 years ago

I have a hunch I may not be doing the oid=<ASN1_OID['.1.3.6.1.4.1.9.2.1.55.192.168.30.10']> part right when translating to Ruby, but I think @sdaubert will know how. πŸ™

This is the source for ANS1_OID in Python

picatz commented 6 years ago

To help debug the issue further I used wrpcap('example.pcap', pkt) with scapy, and see this in Wireshark:

screen shot 2018-07-02 at 1 13 34 pm
pkt_binary = "E\x00\x00^\x00\x01\x00\x00@\x11\xBC\x12\xC0\xA8\x1Ed\xC0\xA8\x1E\xC7\x00\xA1\x00\xA1\x00J\xCD\x990@\x02\x01\x01\x04\aprivate\xA32\x02\x01\x00\x02\x01\x00\x02\x01\x000'0%\x06\x0F+\x06\x01\x04\x01\t\x02\x017\x81@\x81(\x1E\n\x04\x12pwnd-router.config"
picatz commented 6 years ago

Working my way backwards with the pcap I generated with scapy, I've gotten this @KINGSABRI πŸ‘

pkt = PacketGen.gen('IP', src: '192.168.30.100', dst: '192.168.30.199')
pkt.add('UDP', dport: 162, sport: 162)
pkt.add('SNMP', community: 'private')
pkt.snmp.data.root.chosen = 3
pkt.snmp.pdu[:varbindlist] << { name: '1.3.6.1.4.1.9.2.1.55.192.168.30.10', value: 'pwnd-router.config' }
=> -- PacketGen::Packet -------------------------------------------------
---- PacketGen::Header::IP -------------------------------------------
              Int8           u8: 69         (0x45)
                        version: 4
                            ihl: 5
              Int8          tos: 0          (0x00)
             Int16       length: 20         (0x0014)
             Int16           id: 12303      (0x300f)
             Int16         frag: 0          (0x0000)
                          flags: none
                    frag_offset: 0          (0x0000)
              Int8          ttl: 64         (0x40)
              Int8     protocol: 17         (0x11)
             Int16     checksum: 0          (0x0000)
              Addr          src: 192.168.30.100
              Addr          dst: 192.168.30.199
           Options      options: 
---- PacketGen::Header::UDP ------------------------------------------
             Int16        sport: 162        (0x00a2)
             Int16        dport: 162        (0x00a2)
             Int16       length: 8          (0x0008)
             Int16     checksum: 0          (0x0000)
---- PacketGen::Header::SNMP -----------------------------------------
        ENUMERATED      version: v2c        (0x01)
      OCTET STRING    community: "private"
              PDUs         data: SetRequest
---- ASN.1 content ---------------------------------------------------
  pdu (SetRequest) pdu SEQUENCE:
        id INTEGER: 0
        error ENUMERATED: 0
        error_index INTEGER: 0
        bindings (VariableBindings) SEQUENCE OF:
            varbind (VarBind) varbind SEQUENCE:
                name OBJECT ID: "1.3.6.1.4.1.9.2.1.55.192.168.30.10"
                value (ANY) "pwnd-router.config"

---- ASN.1 DER -------------------------------------------------------
 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
----------------------------------------------------------------------
 30 3e 02 01 01 04 07 70 72 69 76 61 74 65 a3 30  0>.....private.0
 02 01 00 02 01 00 02 01 00 30 25 30 23 06 0f 2b  .........0%0#..+
 06 01 04 01 09 02 01 37 81 40 81 28 1e 0a 70 77  .......7.@.(..pw
 6e 64 2d 72 6f 75 74 65 72 2e 63 6f 6e 66 69 67  nd-router.config

----------------------------------------------------------------------
pkt_binary = "E\x00\x00\x140\x0F\x00\x00@\x11\x00\x00\xC0\xA8\x1Ed\xC0\xA8\x1E\xC7\x00\xA2\x00\xA2\x00\b\x00\x000>\x02\x01\x01\x04\aprivate\xA30\x02\x01\x00\x02\x01\x00\x02\x01\x000%0#\x06\x0F+\x06\x01\x04\x01\t\x02\x017\x81@\x81(\x1E\npwnd-router.config"

There's only one weird thing I see where the value from scapy is "\x04\x12pwnd-router.config", where the \x04\x12 doesn't show up in Ruby. πŸ€·β€β™‚οΈ

picatz commented 6 years ago

Looking at the SNMP layer in python with scapy to figure out the \x04\x12 part...

snmp.PDU
#=> <SNMPset  id=0x0 <ASN1_INTEGER[0]> error='no_error' 0x0 <ASN1_INTEGER[0]> error_index=0x0 <ASN1_INTEGER[0]> varbindlist=[<SNMPvarbind  oid=<ASN1_OID['.1.3.6.1.4.1.9.2.1.55.192.168.30.10']> value=<ASN1_STRING[b'pwnd-router.config']> |>] |>

I wonder if it's some sort of property of being an ASN1_STRING? πŸ€·β€β™‚οΈ

sdaubert commented 6 years ago

@picatz wouh! You are too fast for me.

@KINGSABRI my only add: a VarBind object (kind of items in VariableBindings object) is a sequence of an ObjectId and another type, which depends on object id. The VariableBindings#<< operator knows how to transform a dotted string to an OID. That's why @picatz suceeded to add this OID. But, the value has no specified type, so #<< has no transform. It seems this value is an OCTET STRING (type 0x04, with a length od 0x12). So replace last line from picatz's code by:

pkt.snmp.pdu[:varbindlist] << { name: '1.3.6.1.4.1.9.2.1.55.192.168.30.10', value: RASN1::Types::OctetString.new('pwnd-router.config') }

And you will have:

-- PacketGen::Packet -------------------------------------------------
---- PacketGen::Header::IP -------------------------------------------
              Int8           u8: 69         (0x45)
                        version: 4
                            ihl: 5
              Int8          tos: 0          (0x00)
             Int16       length: 20         (0x0014)
             Int16           id: 37951      (0x943f)
             Int16         frag: 0          (0x0000)
                          flags: none
                    frag_offset: 0          (0x0000)
              Int8          ttl: 64         (0x40)
              Int8     protocol: 17         (0x11)
             Int16     checksum: 0          (0x0000)
              Addr          src: 192.168.30.100
              Addr          dst: 192.168.30.199
           Options      options: 
---- PacketGen::Header::UDP ------------------------------------------
             Int16        sport: 162        (0x00a2)
             Int16        dport: 162        (0x00a2)
             Int16       length: 8          (0x0008)
             Int16     checksum: 0          (0x0000)
---- PacketGen::Header::SNMP -----------------------------------------
        ENUMERATED      version: v2c        (0x01)
      OCTET STRING    community: "private"
              PDUs         data: SetRequest
---- ASN.1 content ---------------------------------------------------
  pdu (SetRequest) pdu SEQUENCE:
        id INTEGER: 0
        error ENUMERATED: 0
        error_index INTEGER: 0
        bindings (VariableBindings) SEQUENCE OF:
            varbind (VarBind) varbind SEQUENCE:
                name OBJECT ID: "1.3.6.1.4.1.9.2.1.55.192.168.30.10"
                value (ANY) OCTET STRING: "pwnd-router.config"

---- ASN.1 DER -------------------------------------------------------
 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
----------------------------------------------------------------------
 30 40 02 01 01 04 07 70 72 69 76 61 74 65 a3 32  0@.....private.2
 02 01 00 02 01 00 02 01 00 30 27 30 25 06 0f 2b  .........0'0%..+
 06 01 04 01 09 02 01 37 81 40 81 28 1e 0a 04 12  .......7.@.(....
 70 77 6e 64 2d 72 6f 75 74 65 72 2e 63 6f 6e 66  pwnd-router.conf
 69 67                                            ig
----------------------------------------------------------------------
picatz commented 6 years ago

Thank you for the explanation @sdaubert πŸ™ ❀️

Example Code

Complete example to show how to generate this packet.

pkt = PacketGen.gen('IP', src: '192.168.30.100', dst: '192.168.30.199')
pkt.add('UDP', dport: 162, sport: 162)
pkt.add('SNMP', community: 'private')
pkt.snmp.data.root.chosen = 3
pkt.snmp.pdu[:varbindlist] << { name: '1.3.6.1.4.1.9.2.1.55.192.168.30.10', value: RASN1::Types::OctetString.new('pwnd-router.config') }
KINGSABRI commented 6 years ago

@picatz you're crazy fast, touch wood! @sdaubert I don't know why I got confused from documentation or maybe because accessors were explained in the GetRequest part Do you think more @example in code docs would help?

@sdaubert isn't the value attribute has to be/translated to RASN1::Types::OctetString by detault? finally, how is that supposed to be in one line?

sdaubert commented 6 years ago

@sdaubert isn't the value attribute has to be/translated to RASN1::Types::OctetString by detault?

It can't as its type depends on OID. It may be an integer, a float, a bit string, or even a more complex type (ie one defined by a sequence or a set).

finally, how is that supposed to be in one line?

It is not. For now, chosen value from type cannot be set when adding a SNMP header (but it could). The difficult part is setting the varbindlist.

sdaubert commented 6 years ago

@KINGSABRI it should be possible to set varbindlist from new, but I have to update RASN1 first.

sdaubert commented 6 years ago

Fixed in 2.6.0

PacketGen.gen('IP', src: '192.168.30.100', dst: '192.168.30.199').add('UDP', dport: 162, sport: 162).add('SNMP', community: 'private', chosen_pdu: 3, pdu: { id: 1, varbindlist: [{name: "1.3.6.1.4.1.9.2.1.55.192.168.30.10", value: RASN1::Types::OctetString.new("pwnd-router.config")}]})
-- PacketGen::Packet -------------------------------------------------
---- PacketGen::Header::IP -------------------------------------------
              Int8           u8: 69         (0x45)
                        version: 4
                            ihl: 5
              Int8          tos: 0          (0x00)
             Int16       length: 20         (0x0014)
             Int16           id: 53977      (0xd2d9)
             Int16         frag: 0          (0x0000)
                          flags: none
                    frag_offset: 0          (0x0000)
              Int8          ttl: 64         (0x40)
              Int8     protocol: 17         (0x11)
             Int16     checksum: 0          (0x0000)
              Addr          src: 192.168.30.100
              Addr          dst: 192.168.30.199
           Options      options: 
---- PacketGen::Header::UDP ------------------------------------------
             Int16        sport: 162        (0x00a2)
             Int16        dport: 162        (0x00a2)
             Int16       length: 8          (0x0008)
             Int16     checksum: 0          (0x0000)
---- PacketGen::Header::SNMP -----------------------------------------
        ENUMERATED      version: v2c        (0x01)
      OCTET STRING    community: "private"
              PDUs         data: SetRequest
---- ASN.1 content ---------------------------------------------------
  (SetRequest) pdu SEQUENCE:
    id INTEGER: 1
    error ENUMERATED: 0
    error_index INTEGER: 0
    (VariableBindings) bindings SEQUENCE OF:
      (VarBind) varbind SEQUENCE:
        name OBJECT ID: "1.3.6.1.4.1.9.2.1.55.192.168.30.10"
        value (ANY) OCTET STRING: "pwnd-router.config"

---- ASN.1 DER -------------------------------------------------------
 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
----------------------------------------------------------------------
 30 40 02 01 01 04 07 70 72 69 76 61 74 65 a3 32  0@.....private.2
 02 01 01 02 01 00 02 01 00 30 27 30 25 06 0f 2b  .........0'0%..+
 06 01 04 01 09 02 01 37 81 40 81 28 1e 0a 04 12  .......7.@.(....
 70 77 6e 64 2d 72 6f 75 74 65 72 2e 63 6f 6e 66  pwnd-router.conf
 69 67                                            ig
----------------------------------------------------------------------
KINGSABRI commented 6 years ago

@sdaubert tested and it works, you're awesome!