2017-04-10 11:14:12 +00:00
|
|
|
package mysql
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2017-05-29 10:52:11 +00:00
|
|
|
"git.loafle.net/overflow/commons_go/matcher/packet"
|
|
|
|
"git.loafle.net/overflow/commons_go/model/scaninfo"
|
2017-04-10 11:14:12 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type PacketSize struct {
|
|
|
|
PacketLength [3]byte
|
|
|
|
PacketNumber byte
|
|
|
|
}
|
|
|
|
|
|
|
|
type mySql struct {
|
|
|
|
Payload PacketSize
|
|
|
|
Protocol byte
|
|
|
|
Version [256]byte
|
|
|
|
|
|
|
|
TreadId uint32
|
|
|
|
Salt1 [9]byte
|
|
|
|
ServerCapa uint16
|
|
|
|
ServerLang uint8
|
|
|
|
ServerStat uint16
|
|
|
|
ExtServerCapa uint16
|
|
|
|
AuthPlugLen uint8
|
|
|
|
_ [10]uint8
|
|
|
|
Salt2 [13]uint8
|
|
|
|
AuthPlugName [64]uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
type MySqlMatcher struct {
|
|
|
|
packets []*packet.Packet
|
|
|
|
version string
|
|
|
|
isErrResp bool
|
|
|
|
errCode int
|
|
|
|
errMsg string
|
|
|
|
isSSL bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMySqlMatcher() *MySqlMatcher {
|
|
|
|
return &MySqlMatcher{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) ServiceName() string {
|
|
|
|
if t.isErrResp {
|
|
|
|
return "MySQL" + "(Err-" + strconv.Itoa(t.errCode) + " : " + t.errMsg + ")"
|
|
|
|
}
|
|
|
|
if t.isSSL {
|
|
|
|
return "MySQL" + "-" + t.version + "(SSL)"
|
|
|
|
}
|
|
|
|
return "MySQL" + "(" + t.version + ")"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) PacketCount() int {
|
|
|
|
return len(t.packets)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) Packet(index int) *packet.Packet {
|
|
|
|
return t.packets[index]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) IsError(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) IsNoResponse(index int) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) IsPrePacket() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) Match(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {
|
|
|
|
|
|
|
|
if packet == nil || len(packet.Buffer) <= 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
r := new(bytes.Buffer)
|
|
|
|
r.Write(packet.Buffer)
|
|
|
|
|
|
|
|
m := mySql{}
|
|
|
|
if err := binary.Read(r, binary.LittleEndian, &m); err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(m.Payload.PacketLength[:])
|
|
|
|
packetLen, _ := binary.ReadUvarint(buf)
|
|
|
|
|
|
|
|
if packetLen != uint64(packet.Len-4) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if m.Protocol == 0xff {
|
|
|
|
//MySQL error response
|
|
|
|
var code [2]uint8
|
|
|
|
copy(code[:], m.Version[:2])
|
|
|
|
|
|
|
|
var msg [256]uint8
|
|
|
|
copy(msg[:], m.Version[2:])
|
|
|
|
|
|
|
|
errCode := binary.LittleEndian.Uint16(code[:])
|
|
|
|
if errCode < 1000 || errCode > 1727 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
errMsg := bytes.Trim(msg[:], "\x00")
|
|
|
|
|
|
|
|
t.isErrResp = true
|
|
|
|
t.errCode = int(errCode)
|
|
|
|
t.errMsg = string(errMsg)
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if m.Protocol != 10 && m.Protocol != 9 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
t.checkSSL(packet)
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *MySqlMatcher) checkSSL(packet *packet.Packet) {
|
|
|
|
|
|
|
|
temp := make([]byte, packet.Len)
|
|
|
|
r := new(bytes.Buffer)
|
|
|
|
r.Write(packet.Buffer)
|
|
|
|
|
|
|
|
if err := binary.Read(r, binary.LittleEndian, &temp); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t.version = strings.Split(string(packet.Buffer)[5:packet.Len], "\x00")[0]
|
|
|
|
versionLen := len(t.version) + 1
|
|
|
|
data := binary.LittleEndian.Uint16(temp[18+versionLen : 20+versionLen])
|
|
|
|
s := fmt.Sprintf("%b", data)
|
|
|
|
|
|
|
|
for i, b := range s {
|
|
|
|
if i == 4 {
|
|
|
|
if b == 49 {
|
|
|
|
t.isSSL = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|