zmap / zgrab2

Fast Go Application Scanner
Other
1.71k stars 294 forks source link

Fix Parser for ModuleIdentificationRequest of s7 Protocol #423

Closed developStorm closed 4 months ago

developStorm commented 4 months ago

Rewrote response parser with information provided in the issue. Resolves #212.

Tested against some real S7 modules and appears to work, absolutely makes more sense than the original code. However, would be great if we can confirm on a device we control.

Also, ModuleId field in type S7Log struct formerly reflects "Order number of the module". This behavior is kept in current version for forward compatibility but not sure if we should rename it to sth else.

Valentinbist commented 4 months ago

I was just reminded to this. I wrote an s7 Parser some years ago and did some larger measurements.

I also used the "Indexes 6 and 7", but also found an additional one, "129", which was so common, that I added it.

    switch int(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+1)]){
    case 1:
        logStruct.ModuleId = string(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+2):((i-1)*28 + S7_DATA_BYTE_OFFSET+22)])
        major := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-3)]
        minor := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-2)]
        patch := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-1)]
        logStruct.VersionModule = fmt.Sprintf("%d.%d.%d", major, minor, patch)
        logStruct.VersionModuleByte = string(s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-4)])
    case 6:
        logStruct.Hardware = string(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+2):((i-1)*28 + S7_DATA_BYTE_OFFSET+22)])
        major := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-3)]
        minor := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-2)]
        patch := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-1)]
        logStruct.VersionHardware = fmt.Sprintf("%d.%d.%d", major, minor, patch)
        logStruct.VersionHardwareByte = string(s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-4)])
    case 7:
        logStruct.Firmware = string(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+2):((i-1)*28 + S7_DATA_BYTE_OFFSET+22)])
        major := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-3)]
        minor := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-2)]
        patch := s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-1)]
        logStruct.Version = fmt.Sprintf("%d.%d.%d", major, minor, patch)
        logStruct.VersionByte = string(s7Packet.Data[(i*28 + S7_DATA_BYTE_OFFSET-4)])
    case 129:
        logStruct.Unknown11 = string(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+2):((i)*28 + S7_DATA_BYTE_OFFSET)])
        major := s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET)]
        minor := s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+1)]
        logStruct.Unknown11num = fmt.Sprintf("%d%d", major, minor)
    default:
        logStruct.Unknown112 = string(s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+2):((i)*28 + S7_DATA_BYTE_OFFSET)])
        major := s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET)]
        minor := s7Packet.Data[((i-1)*28 + S7_DATA_BYTE_OFFSET+1)]
        logStruct.Unknown112num = fmt.Sprintf("%d%d", major, minor)
    }

(Sorry for the dodgy quality, it was my first go code written in a nighty hurry...)

Some documentation I wrote back in the day:

s7_scanner_info.pdf

The source of the Siemens data was: Siemens. System Software for S7-300/400 System and Standard Functions . Technical report, Siemens, 2010.

I can also recommend getting an account at https://support.industry.siemens.com/cs/start?lc=de-DE, to get the manuals.

This code was stable enough to conduct Interner-wide Measurements and the resulting IDs and numbers were checked against expected Siements behaviour (such as "legal and existing" patch levels and device IDs) and verififed as far as possible to detect honeypots. Maybe that helps you!

developStorm commented 4 months ago

@Valentinbist Thank you for sharing all these details! These are really helpful and we'll patch our parser accordingly soon. Btw, did the unknown11 field help in your analysis of your scan results? Do you think it would be good if we were to include it in ZGrab output?