On Sun, Aug 31, 2025, 17:25 Tom Beecher <[email protected]> wrote: > Which means if IOS does no pubkey packet length validation, you no longer >> need to generate a keypair that has a pubkey that collides on MD5. You >> just >> need a blob that collides with that hash, and will *truncate* to a key you >> control. Which is much easier to collide. > > > To actually exploit this : > > Generating a collision isn't hard. But you'd have to generate a collision > that was also valid to use in the key algorithm specified > in SSH_MSG_USERAUTH_REQUEST. >
Which is an undefined size, which isn't normally a problem, but keep that in mind. So now you actually need a preimage attack, and even for MD5 that's not > feasible still. > > Even if you managed to pull that off, you STILL don't have valid private > half of the keypair. And if you could do that you just broke all of modern > cryptography so we have other problems. :) > Bob generates an RSA 4096 keypair, *KP1*. This keypair has public key *K1*. On the wire, that pubkey component is sent like this in *SSH_MSG_USERAUTH_REQUEST*: # ( ... ) *0x00000007* (length: 7 bytes follow (or whatever length for the key type, depending on if you're trying to auth with ssh-rsa, ssh-rsa2-sha256, ssh-rsa2-sha512, etc.) *0x7373682d727361* (string, "ssh-rsa". see above) *0x00000003* (length: 3 bytes follow) *0x010001* (RSA's *e*) *0x00000201* (length: 513 bytes follow) *0x......* (RSA's *n*) Worth noting that this is the *same exact* format, encoded to base64, that is the second column in your *~/.ssh/id_<type>.pub* file (e.g. in this case, *~bob/.ssh/id_rsa.pub*). IOS device *foo* has authorized a(ny) key with MD5 *C,* an MD5 checksum of public key *K1*. As stated in the other thread, *foo* only bases authn on if the *checksum* presented key matches *C*. *foo* does not store *K1* locally. It does not use *C* to look up a local *K1*. The only course of action forward, given that ...*interesting* design choice, then is to use the key that the client presents - provided its checksum matches *C*. We agree on that, yes? Alice creates keypair *KP2*, with public key *K2*. Alice then pads junk to *K2*'s *n* until she reaches collision in the wire-packed form with *C,* creating *Blob1*. Let's say Alice had to add 512 bytes to reach collision with *C*. Alice now initiates an SSH connection with *foo*, and starts *SSH_MSG_SERVICE_REQUEST*. Alice sends *Blob1* to *foo* as part of *SSH_MSG_USERAUTH_REQUEST*: # (...) *0x00000007* *0x7373682d727361* *0x00000003* *0x010001* *0x00000401* (length: *1025* bytes follow instead of 513) *0x......* (Alice's RSA's *n)* *0x.... (junk data)* *Foo* reads in that key and parses it. While parsing, it already knows from KEX that it's RSA 4096. So let's say the IOS version of sshd does something extremely stupid[0] and uses a fixed length lookup table. "Oh, it's RSA 4096. I need exactly 3 bytes bytes from the buffer for the pubkey's *e* and 513 bytes for *n*. I can skip over the other unnecessary bits and pieces in the buffer. Efficiency!!1[1]" So it grabs.... 3 bytes for *e*. And it grabs... 513 bytes. For *n*. Hey Tom, what would happen in that case? [0] More stupid than authing based on key checksum, I mean, instead of locally storing keys. And more stupid than using MD5 for that checksum. I'm sure those are just flukes. [1] "Efficiency", like you know. Not storing the entirety of a pubkey locally, or using MD5 instead of a more expensive hashing algo. > _______________________________________________ NANOG mailing list https://lists.nanog.org/archives/list/[email protected]/message/GTPOZK25WZSH2HSF6VCBZJQAUP7G7BK2/
