Brent- 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? >
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*. > The key blob is *encoded* , not hashed. base64(x) can never equal base64(y), and therefore cannot collide. *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? > Even if we assert you got this far, AND that everything happens this way : y = md5_hash(KP1) x = The data you 'injected' into the key blob that is KP2. If md5_hash(x) = y , then yes you have tricked the device into using KP2. HOWEVER, finding x such that md5_hash(x) = y is a preimage attack. And as has been stated, preimage against MD5 is still not computationally feasible. On Sun, Aug 31, 2025 at 6:40 PM brent saner <[email protected]> wrote: > 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/VQCQSU5VAA5VXGB7WGXX3OPCGL576UTP/
