mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-02-03 21:52:24 +00:00
Rework samplekex.py to use the new -proxycmd.
It now expects its standard input to be connected to the same PuTTY its standard output is talking to, i.e. expects to be invoked as a proxy command. It conducts the same sample key exchange as it used to, but now reads the SSH greeting and first couple of packets back from PuTTY and minimally checks that they're something like what it was expecting. (In the process, I've also fixed a mistake in the Python message code enumeration, which caused one of those expect() calls to fail.)
This commit is contained in:
parent
e65e5d165f
commit
cc9d920c78
@ -65,10 +65,11 @@ SSH2_MSG_KEXINIT = 20
|
|||||||
SSH2_MSG_NEWKEYS = 21
|
SSH2_MSG_NEWKEYS = 21
|
||||||
SSH2_MSG_KEXDH_INIT = 30
|
SSH2_MSG_KEXDH_INIT = 30
|
||||||
SSH2_MSG_KEXDH_REPLY = 31
|
SSH2_MSG_KEXDH_REPLY = 31
|
||||||
SSH2_MSG_KEX_DH_GEX_REQUEST = 30
|
SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30
|
||||||
SSH2_MSG_KEX_DH_GEX_GROUP = 31
|
SSH2_MSG_KEX_DH_GEX_GROUP = 31
|
||||||
SSH2_MSG_KEX_DH_GEX_INIT = 32
|
SSH2_MSG_KEX_DH_GEX_INIT = 32
|
||||||
SSH2_MSG_KEX_DH_GEX_REPLY = 33
|
SSH2_MSG_KEX_DH_GEX_REPLY = 33
|
||||||
|
SSH2_MSG_KEX_DH_GEX_REQUEST = 34
|
||||||
SSH2_MSG_KEXRSA_PUBKEY = 30
|
SSH2_MSG_KEXRSA_PUBKEY = 30
|
||||||
SSH2_MSG_KEXRSA_SECRET = 31
|
SSH2_MSG_KEXRSA_SECRET = 31
|
||||||
SSH2_MSG_KEXRSA_DONE = 32
|
SSH2_MSG_KEXRSA_DONE = 32
|
||||||
@ -113,3 +114,16 @@ def clearpkt(msgtype, *stuff):
|
|||||||
s += byte(random.randint(0,255))
|
s += byte(random.randint(0,255))
|
||||||
s = byte(padlen) + s
|
s = byte(padlen) + s
|
||||||
return string(s)
|
return string(s)
|
||||||
|
|
||||||
|
def decode_uint32(s):
|
||||||
|
assert len(s) == 4
|
||||||
|
return struct.unpack(">I", s)[0]
|
||||||
|
|
||||||
|
def read_clearpkt(fh):
|
||||||
|
length_field = fh.read(4)
|
||||||
|
s = fh.read(decode_uint32(length_field))
|
||||||
|
import sys
|
||||||
|
padlen = ord(s[0])
|
||||||
|
s = s[1:-padlen]
|
||||||
|
msgtype = ord(s[0])
|
||||||
|
return msgtype, s[1:]
|
||||||
|
@ -2,18 +2,27 @@
|
|||||||
|
|
||||||
# Example Python script to synthesise the server end of an SSH key exchange.
|
# Example Python script to synthesise the server end of an SSH key exchange.
|
||||||
|
|
||||||
# This is an output-only script; you run it by means of saying
|
# This script expects to be run with its standard input and output
|
||||||
# something like
|
# channels both connected to PuTTY. Run it by means of a command such
|
||||||
|
# as
|
||||||
#
|
#
|
||||||
# samplekex.py | nc -l 2222 | hex dump utility of your choice
|
# rm -f test.log && ./plink -sshrawlog test.log -v -proxycmd './contrib/samplekex.py' dummy
|
||||||
#
|
#
|
||||||
# and then connecting PuTTY to port 2222. Being output-only, of
|
# It will conduct the whole of an SSH connection setup, up to the
|
||||||
# course, it cannot possibly get the key exchange _right_, so PuTTY
|
# point where it ought to present a valid host key signature and
|
||||||
# will terminate with an error when the signature in the final message
|
# switch over to the encrypted protocol; but because this is a simple
|
||||||
# doesn't validate. But everything until then should be processed as
|
# script (and also because at that point PuTTY would annoyingly give a
|
||||||
# if it was a normal SSH-2 connection, which means you can use this
|
# host key prompt), it doesn't actually bother to do either, and will
|
||||||
# script as a starting point for constructing interestingly malformed
|
# instead present a nonsense signature and terminate. The above sample
|
||||||
# key exchanges to test bug fixes.
|
# command will log the whole of the exchange from PuTTY's point of
|
||||||
|
# view in 'test.log'.
|
||||||
|
#
|
||||||
|
# The intention is that this forms example code that can be easily
|
||||||
|
# adapted to demonstrate bugs in our SSH connection setup. With more
|
||||||
|
# effort it could be expanded into some kind of a regression-testing
|
||||||
|
# suite, although in order to reliably test particular corner cases
|
||||||
|
# that would probably also need PuTTY-side modifications to make the
|
||||||
|
# random numbers deterministic.
|
||||||
|
|
||||||
import sys, random
|
import sys, random
|
||||||
from encodelib import *
|
from encodelib import *
|
||||||
@ -30,9 +39,17 @@ rsamod = 0xB98FE0C0BEE1E05B35FDDF5517B3E29D8A9A6A7834378B6783A19536968968F755E34
|
|||||||
# 16 bytes of random data for the start of KEXINIT.
|
# 16 bytes of random data for the start of KEXINIT.
|
||||||
cookie = "".join([chr(random.randint(0,255)) for i in range(16)])
|
cookie = "".join([chr(random.randint(0,255)) for i in range(16)])
|
||||||
|
|
||||||
|
def expect(var, expr):
|
||||||
|
expected_val = eval(expr)
|
||||||
|
if var != expected_val:
|
||||||
|
sys.stderr.write("Expected %s (%s), got %s\n" % (
|
||||||
|
expr, repr(expected_val), repr(var)))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
sys.stdout.write(greeting("SSH-2.0-Example KEX synthesis"))
|
sys.stdout.write(greeting("SSH-2.0-Example KEX synthesis"))
|
||||||
|
|
||||||
# Expect client to send KEXINIT
|
greeting = sys.stdin.readline()
|
||||||
|
expect(greeting[:8], '"SSH-2.0-"')
|
||||||
|
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
clearpkt(SSH2_MSG_KEXINIT,
|
clearpkt(SSH2_MSG_KEXINIT,
|
||||||
@ -49,18 +66,27 @@ sys.stdout.write(
|
|||||||
name_list(()), # server->client languages
|
name_list(()), # server->client languages
|
||||||
boolean(False), # first kex packet does not follow
|
boolean(False), # first kex packet does not follow
|
||||||
uint32(0)))
|
uint32(0)))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
# Expect client to send SSH2_MSG_KEX_DH_GEX_REQUEST(0x1000)
|
intype, inpkt = read_clearpkt(sys.stdin)
|
||||||
|
expect(intype, "SSH2_MSG_KEXINIT")
|
||||||
|
|
||||||
|
intype, inpkt = read_clearpkt(sys.stdin)
|
||||||
|
expect(intype, "SSH2_MSG_KEX_DH_GEX_REQUEST")
|
||||||
|
expect(inpkt, "uint32(0x400) + uint32(0x400) + uint32(0x2000)")
|
||||||
|
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP,
|
clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP,
|
||||||
mpint(group),
|
mpint(group),
|
||||||
mpint(groupgen)))
|
mpint(groupgen)))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
# Expect client to send SSH2_MSG_KEX_DH_GEX_INIT
|
intype, inpkt = read_clearpkt(sys.stdin)
|
||||||
|
expect(intype, "SSH2_MSG_KEX_DH_GEX_INIT")
|
||||||
|
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY,
|
clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY,
|
||||||
ssh_rsa_key_blob(rsaexp, rsamod),
|
ssh_rsa_key_blob(rsaexp, rsamod),
|
||||||
mpint(random.randint(2, group-2)),
|
mpint(random.randint(2, group-2)),
|
||||||
ssh_rsa_signature_blob(random.randint(2, rsamod-2))))
|
ssh_rsa_signature_blob(random.randint(2, rsamod-2))))
|
||||||
|
sys.stdout.flush()
|
||||||
|
Loading…
Reference in New Issue
Block a user