Skip to content

Instantly share code, notes, and snippets.

@voutilad
Last active November 28, 2020 23:37
Show Gist options
  • Save voutilad/bcd20f85942edc5c9030003b92e94883 to your computer and use it in GitHub Desktop.
Save voutilad/bcd20f85942edc5c9030003b92e94883 to your computer and use it in GitHub Desktop.
POC code for checking Neo4j server bolt protocol behavior after an invalid auth attempt
/*
Build via `go build`...should just work. Use the "-mangle" flag to simulation a
bad password (it just tacks on an extra char). This lets you play with testing
the behavior of good vs bad auth attempts.
NOTE: don't give a bolt uri...just ip/host and port!
Usage of ./authtest:
-host string
hostname:ip for neo4j (default "localhost:7687")
-mangle
Mangle the password?
-password string
Neo4j password (default "password")
-user string
Neo4j user (default "neo4j")
*/
package main
import (
"encoding/binary"
"flag"
"fmt"
"io"
"net"
"strings"
"time"
)
func main() {
var host, user, password string
var mangle bool
flag.StringVar(&host, "host", "localhost:7687", "hostname:ip for neo4j")
flag.StringVar(&user, "user", "neo4j", "Neo4j user")
flag.StringVar(&password, "password", "password", "Neo4j password")
flag.BoolVar(&mangle, "mangle", false, "Mangle the password?")
flag.Parse()
if mangle {
fmt.Println("mangling your password")
password = password + "XXX"
}
buf := make([]byte, 512)
fmt.Printf("Dialing host %s\n", host)
conn, err := net.Dial("tcp", host)
if err != nil {
panic(err)
}
// shake hands
_, err = conn.Write([]byte{
0x60, 0x60, 0xb0, 0x17,
0x00, 0x00, 0x02, 0x04,
0x00, 0x00, 0x01, 0x04,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x03})
if err != nil {
panic(err)
}
n, err := conn.Read(buf)
if err != nil || n != 4 {
panic("bad handshake")
}
fmt.Println("handshake complete")
// don't care about the version
// build hello message
pos := 2
pos = pos + copy(buf[pos:], []byte{
0xb1, 0x1, // hello
0xa5, // 5 element tiny map
0x8a, // tiny string...then 'user-agent'
0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74,
})
pos = pos + copy(buf[pos:], []byte{
0x84, // tiny string
0x64, 0x61, 0x76, 0x65, // 'dave'
0x87, // tiny string
0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, // 'routing'
0xa1, // 1 element struct
0x87, // tiny string
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, // 'address'
})
if len(host) > 256 {
panic("host is way too long for me")
} else if len(host) > 16 {
size := uint16(0xd000 + len(host)) // with the string prefix
binary.BigEndian.PutUint16(buf[pos:], size)
pos = pos + 2
} else { // tiny string
buf[pos] = byte(0x80 + len(host))
pos++
}
pos = pos + copy(buf[pos:], host)
pos = pos + copy(buf[pos:], []byte{
0x86, // tiny string
0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, // 'scheme'
0x85, // tiny string
0x62, 0x61, 0x73, 0x69, 0x63, // 'basic'
0x89, // tiny string
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, // 'principal'
})
if len(user) > 16 {
panic("sorry username is too long")
}
buf[pos] = byte(0x80 + len(user))
pos++
pos = pos + copy(buf[pos:], user)
pos = pos + copy(buf[pos:], []byte{
0x8b, // tiny string...then 'credentials'
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
})
if len(password) > 255 {
panic("password too long!")
} else if len(password) > 16 {
size := uint16(0xd000 + len(password))
binary.BigEndian.PutUint16(buf[pos:], size)
pos = pos + 2
} else {
buf[pos] = byte(0x80 + len(password))
pos++
}
pos = pos + copy(buf[pos:], password)
// set message length
binary.BigEndian.PutUint16(buf[0:2], uint16(pos-2))
// double zeros for the end
buf[pos] = 0x00
pos++
buf[pos] = 0x00
pos++
// send our begin message
_, err = conn.Write(buf[:pos])
if err != nil {
panic(err)
}
fmt.Printf("sent Hello message:\n%#v\n", buf[:pos])
fmt.Printf("as string:\n%s\n", buf[:pos])
// check for response and behavior
n, err = conn.Read(buf)
if err != nil {
panic(err)
}
fmt.Printf("got Response: %#v\n", buf[:n])
fmt.Printf("as string: %s\n", string(buf[:n]))
// check if connection is still open
conn.SetReadDeadline(time.Now())
n, err = conn.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Println("server closed connection!")
}
if strings.Contains(err.Error(), "timeout") {
fmt.Println("server still connected.")
}
}
if n > 0 {
fmt.Println("did not expect to read data...server sent more?")
} else {
fmt.Println("checking if server will timeout and close after 30s...")
conn.SetReadDeadline(time.Now().Add(31 * time.Second))
_, err = conn.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Println("server closed the connection.")
}
panic(err)
}
fmt.Println("still more data on the wire?!")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment