Last active
November 28, 2020 23:37
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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