zmap/zgrab2

SSH: Unable to marshal data

TrueSkrillor opened this issue · 4 comments

Hey everyone,

I stumbled across a fatal error while performing an internet-wide ssh scan as part of my masters thesis resulting in premature end of the scan. After some time, zgrab2 logs the following error and quits:

FATA[10636] unable to marshal data: json: error calling MarshalJSON for type ssh.kexAlgorithm: json: error calling MarshalJSON for type ssh.PublicKey: json: error calling MarshalJSON for type time.Time: Time.MarshalJSON: year outside of range [0,9999]

As the error message indicates the timestamp of the public key seems to be invalid (not within the valid range). By extending the call to the logging function I was able to isolate the list of IPs that caused this issue. As it turned out only six IPs in the entire IPv4 address space caused this issue but I will try to avoid sharing the IPs publicy due to obvious reasons.
For debugging purposes I performed a SSH handshake using OpenSSH with verbose output enabled (which was possible). See below for the captured output (I stripped the log file a little but it should still contain all necessary information):

OpenSSH_8.6p1, OpenSSL 1.1.1k 25 Mar 2021
debug1: Connecting to x.x.x.x [x.x.x.x] port 22.
debug1: Connection established.
debug1: Local version string SSH-2.0-OpenSSH_8.6
debug1: Remote protocol version 2.0, remote software version Go
debug1: compat_banner: no match: Go
debug1: Authenticating to x.x.x.x as 'none'
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: host key algorithm: ecdsa-sha2-nistp384-cert-v01@openssh.com
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host certificate: ecdsa-sha2-nistp384-cert-v01@openssh.com SHA256:[...], serial [...] ID "[...]" CA ssh-ed25519 SHA256:[...] valid from 2021-01-21T00:01:13 to 2038-01-19T04:14:07
debug1: No matching CA found. Retry with plain key

The expiration date of the host certificate suggests that this error may be directly related to the year 2038 problem, causing an arithmetic overflow in some 32 bit signed integer.

The workaround I used for this is to demote the Fatalf call to Errorf to avoid the application being exited prematurely.

log.Fatalf("unable to marshal data: %s", err)

It looks like the specific issue with the SSH scanner here has to do with the failure to marshal JSON time.Time object with a year outside of range [0,9999]. Such an object indeed exists in this serializable struct:

zgrab2/lib/ssh/certs.go

Lines 68 to 72 in 48f15ef

type JsonValidity struct {
ValidAfter time.Time `json:"valid_after"`
ValidBefore time.Time `json:"valid_before"`
Length uint64 `json:"length"`
}

The issue arises because it looks like Certificate objects can hold Unix timestamps (in c.ValidBefore and c.ValidAfter) that are before year 0 or after year 9999. However, when it is serialized to JSON, such times poses a Y10k problem since the string encoding of times does not permit years with more than 4 digits or negative years:

zgrab2/lib/ssh/certs.go

Lines 154 to 158 in 48f15ef

temp.Validity = &JsonValidity{
ValidAfter: time.Unix(int64(c.ValidAfter), 0).UTC(),
ValidBefore: time.Unix(int64(c.ValidBefore), 0).UTC(),
Length: validityLength,
}

The embedded unserializable time.Time objects ultimately causes this line to return an error:

return json.Marshal(temp)

I see there was a commit pushed for this. Did it resolve your issue @TrueSkrillor ?

mentioning #355 to link it here

Yup, it works as expected.