Go-lang compare *ssh.Request.Type against a string
Jul 8, 2014 - 2 minutesI was working on the agent for SSH Pot and ran into something interesting last night. A lot of the brute force attempts attempt to run a command like this:
1ssh user@host 'uname'
This is different than:
1ssh user@host
2$ uname
The first command is executing a command then exiting, the second is actually logging in and giving the user a shell. The first requests a exec subsystem and the second requests a shell subsystem - so there are two ways to handle it.
1func HandleShellRequest(channel ssh.Channel, in <-chan *ssh.Request) {
2 for req := range in {
3 ok := true
4 logfile.Println("[request " + req.Type + "]: " + string(req.Payload))
5 switch req.Type {
6 case "shell":
7 req.Reply(ok, nil)
8 case "exec":
9 if string(req.Payload) == string("uname") {
10 channel.Write([]byte("\n\rLinux\n\r"))
11 }
12
13 channel.Close()
14 }
15 }
16}
When logging in my logfile it would show something like:
1[request exec]: uname
And even when comparing the two side by side with something like this:
1logfile.Println("["+string(req.Payload)+"]:["+"uname"+"]")
I would get this output:
1[uname]:[uname]
Yet the comparison on line 9 would not get hit. After sitting and thinking about it for a while I decided to print the bytes out:
1INFO: 2014/07/07 23:15:18 sshd.go:157: [0 0 0 5 117 110 97 109 101]
2INFO: 2014/07/07 23:15:18 sshd.go:158: [117 110 97 109 101]
Aha! So for some reason req.Payload is padded with 3 null bytes and a ENQ byte (hex 5).
Here is the corrected version removing the correct bytes - now the string comparison works:
1func HandleShellRequest(channel ssh.Channel, in <-chan *ssh.Request) {
2 for req := range in {
3 ok := true
4 logfile.Println("[request " + req.Type + "]: " + string(req.Payload))
5 switch req.Type {
6 case "shell":
7 req.Reply(ok, nil)
8 case "exec":
9 if string(req.Payload[4:]) == string("uname") {
10 channel.Write([]byte("\n\rLinux\n\r"))
11 }
12
13 channel.Close()
14 }
15 }
16}