diff --git a/README.md b/README.md
index 5b6bd1387..910f7c3d2 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,9 @@
Code-level optimization, parameter optimization, and individual modules, such as vscan filefuzz, have been rewritten for these integrated projects.
In principle, do not repeat the wheel, unless there are bugs, problems
- Cross-platform: based on golang implementation, lightweight, highly customizable, open source, supports Linux, windows, mac os, etc.
-- Support [22] password blasting, support custom dictionary, open by "priorityNmap": true
+- Support [23] password blasting, support custom dictionary, open by "priorityNmap": true
* RDP
+ * VNC
* SSH
* Socks5
* rsh-spx
diff --git a/README_CN.md b/README_CN.md
index 258d5971c..95e67f17d 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -11,7 +11,7 @@
# 特性
-Vulnerabilities Scan;15000+PoC漏洞扫描;22种应用弱口令爆破;7000+Web指纹;146种协议90000+规则Port扫描;Fuzz、HW打点、BugBounty神器...
+Vulnerabilities Scan;15000+PoC漏洞扫描;[ 23 ] 种应用弱口令爆破;7000+Web指纹;146种协议90000+规则Port扫描;Fuzz、HW打点、BugBounty神器...
@@ -20,8 +20,9 @@ Vulnerabilities Scan;15000+PoC漏洞扫描;22种应用弱口令爆破;7000
并对这些集成的项目进行代码级别优化、参数优化,个别模块,如 vscan filefuzz部分进行了重写
原则上不重复造轮子,除非存在bug、问题
- 跨平台:基于golang实现,轻量级、高度可定制、开源,支持Linux、windows、mac os等
-- 支持【22】种密码爆破,支持自定义字典, 通过 "priorityNmap": true 开启
+- 支持[ 23 ] 种密码爆破,支持自定义字典, 通过 "priorityNmap": true 开启
* RDP
+ * VNC
* SSH
* Socks5
* rsh-spx
diff --git a/pkg/hydra/cracker.go b/pkg/hydra/cracker.go
index df4a677ef..961a6c4a5 100644
--- a/pkg/hydra/cracker.go
+++ b/pkg/hydra/cracker.go
@@ -18,6 +18,7 @@ import (
"github.com/hktalent/scan4all/pkg/hydra/socks5"
"github.com/hktalent/scan4all/pkg/hydra/ssh"
"github.com/hktalent/scan4all/pkg/hydra/telnet"
+ "github.com/hktalent/scan4all/pkg/hydra/vnc"
"github.com/hktalent/scan4all/pkg/hydra/winrm"
"github.com/hktalent/scan4all/pkg/kscan/core/slog"
"github.com/hktalent/scan4all/pkg/kscan/lib/gotelnet"
@@ -66,6 +67,13 @@ func Socks5Cracker(i interface{}) interface{} {
}
return nil
}
+func VncCracker(i interface{}) interface{} {
+ info := i.(AuthInfo)
+ if ok, _ := vnc.Check(info.IPAddr, info.Auth.Username, info.Auth.Password, info.Port); ok {
+ return info
+ }
+ return nil
+}
func sshCracker(i interface{}) interface{} {
info := i.(AuthInfo)
diff --git a/pkg/hydra/doNmapResult.go b/pkg/hydra/doNmapResult.go
index d5bd91c25..b06f56302 100644
--- a/pkg/hydra/doNmapResult.go
+++ b/pkg/hydra/doNmapResult.go
@@ -84,7 +84,7 @@ func DoParseXml(s string, bf *bytes.Buffer) {
m1[ip] = append(xx09, []string{szPort, service})
}
if os.Getenv("NoPOC") != "true" {
- if "socks5" == service {
+ if "socks5" == service || "vnc" == service {
CheckWeakPassword(ip, service, port)
} else if "445" == szPort && service == "microsoft-ds" || "135" == szPort && service == "msrpc" {
util.PocCheck_pipe <- &util.PocCheck{
diff --git a/pkg/hydra/hydra.go b/pkg/hydra/hydra.go
index 24de3ac58..f11166e3a 100644
--- a/pkg/hydra/hydra.go
+++ b/pkg/hydra/hydra.go
@@ -22,7 +22,7 @@ var (
CustomAuthMap *AuthList
// rtsp://admin:admin@192.168.0.111:554/0x8b6c42
// rtsp: 554, 5554,8554
- ProtocolList = strings.Split("rdp,ssh,rsh-spx,mysql,mssql,oracle,postgresql,redis,ftp,mongodb,mongod,smb,telnet,snmp,wap-wsp,router,winrm,pop3,socks5", ",")
+ ProtocolList = strings.Split("rdp,ssh,rsh-spx,mysql,mssql,oracle,postgresql,redis,ftp,mongodb,mongod,smb,telnet,snmp,wap-wsp,router,winrm,pop3,socks5,vnc", ",")
)
func NewCracker(info *AuthInfo, isAuthUpdate bool, threads int) *Cracker {
@@ -82,6 +82,8 @@ func (c *Cracker) Run() {
c.Pool.Function = postgresqlCracker
case "socks5":
c.Pool.Function = Socks5Cracker
+ case "vnc":
+ c.Pool.Function = VncCracker
case "ldap", "rsh-spx", "ssh":
c.Pool.Function = sshCracker
case "telnet":
diff --git a/pkg/hydra/loadDicts.go b/pkg/hydra/loadDicts.go
index fdadcfaad..5902e2815 100644
--- a/pkg/hydra/loadDicts.go
+++ b/pkg/hydra/loadDicts.go
@@ -145,6 +145,7 @@ func init() {
}
md["pop3"] = md["ssh"]
md["socks5"] = md["ssh"]
+ md["vnc"] = md["ssh"]
md["rsh-spx"] = md["ssh"]
md["snmp"] = &PPDict{
Username: util.GetVal4File("snmp_user", snmp_user),
diff --git a/pkg/hydra/vnc/checkvnc.go b/pkg/hydra/vnc/checkvnc.go
new file mode 100644
index 000000000..53227133a
--- /dev/null
+++ b/pkg/hydra/vnc/checkvnc.go
@@ -0,0 +1,20 @@
+package vnc
+
+import (
+ "fmt"
+ "net"
+)
+
+func Check(Host, Username, Password string, Port int) (bool, error) {
+ nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", Host, Port))
+ if err != nil {
+ return false, err
+ }
+ cc1, err := Client(nc, &ClientConfig{Auth: []ClientAuth{&PasswordAuth{Password: Password}}})
+ if err != nil {
+ return false, err
+ } else {
+ cc1.Close()
+ return true, nil
+ }
+}
diff --git a/pkg/hydra/vnc/client.go b/pkg/hydra/vnc/client.go
new file mode 100644
index 000000000..42f59bfaf
--- /dev/null
+++ b/pkg/hydra/vnc/client.go
@@ -0,0 +1,478 @@
+package vnc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "unicode"
+)
+
+type ClientConn struct {
+ c net.Conn
+ config *ClientConfig
+
+ // If the pixel format uses a color map, then this is the color
+ // map that is used. This should not be modified directly, since
+ // the data comes from the server.
+ ColorMap [256]Color
+
+ // Encodings supported by the client. This should not be modified
+ // directly. Instead, SetEncodings should be used.
+ Encs []Encoding
+
+ // Width of the frame buffer in pixels, sent from the server.
+ FrameBufferWidth uint16
+
+ // Height of the frame buffer in pixels, sent from the server.
+ FrameBufferHeight uint16
+
+ // Name associated with the desktop, sent from the server.
+ DesktopName string
+
+ // The pixel format associated with the connection. This shouldn't
+ // be modified. If you wish to set a new pixel format, use the
+ // SetPixelFormat method.
+ PixelFormat PixelFormat
+}
+
+// A ClientConfig structure is used to configure a ClientConn. After
+// one has been passed to initialize a connection, it must not be modified.
+type ClientConfig struct {
+ // A slice of ClientAuth methods. Only the first instance that is
+ // suitable by the server will be used to authenticate.
+ Auth []ClientAuth
+
+ // Exclusive determines whether the connection is shared with other
+ // clients. If true, then all other clients connected will be
+ // disconnected when a connection is established to the VNC server.
+ Exclusive bool
+
+ // The channel that all messages received from the server will be
+ // sent on. If the channel blocks, then the goroutine reading data
+ // from the VNC server may block indefinitely. It is up to the user
+ // of the library to ensure that this channel is properly read.
+ // If this is not set, then all messages will be discarded.
+ ServerMessageCh chan<- ServerMessage
+
+ // A slice of supported messages that can be read from the server.
+ // This only needs to contain NEW server messages, and doesn't
+ // need to explicitly contain the RFC-required messages.
+ ServerMessages []ServerMessage
+}
+
+func Client(c net.Conn, cfg *ClientConfig) (*ClientConn, error) {
+ conn := &ClientConn{
+ c: c,
+ config: cfg,
+ }
+
+ if err := conn.handshake(); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ go conn.mainLoop()
+
+ return conn, nil
+}
+
+func (c *ClientConn) Close() error {
+ return c.c.Close()
+}
+
+// CutText tells the server that the client has new text in its cut buffer.
+// The text string MUST only contain Latin-1 characters. This encoding
+// is compatible with Go's native string format, but can only use up to
+// unicode.MaxLatin values.
+//
+// See RFC 6143 Section 7.5.6
+func (c *ClientConn) CutText(text string) error {
+ var buf bytes.Buffer
+
+ // This is the fixed size data we'll send
+ fixedData := []interface{}{
+ uint8(6),
+ uint8(0),
+ uint8(0),
+ uint8(0),
+ uint32(len(text)),
+ }
+
+ for _, val := range fixedData {
+ if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
+ return err
+ }
+ }
+
+ for _, char := range text {
+ if char > unicode.MaxLatin1 {
+ return fmt.Errorf("Character '%s' is not valid Latin-1", char)
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, uint8(char)); err != nil {
+ return err
+ }
+ }
+
+ dataLength := 8 + len(text)
+ if _, err := c.c.Write(buf.Bytes()[0:dataLength]); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Requests a framebuffer update from the server. There may be an indefinite
+// time between the request and the actual framebuffer update being
+// received.
+//
+// See RFC 6143 Section 7.5.3
+func (c *ClientConn) FramebufferUpdateRequest(incremental bool, x, y, width, height uint16) error {
+ var buf bytes.Buffer
+ var incrementalByte uint8 = 0
+
+ if incremental {
+ incrementalByte = 1
+ }
+
+ data := []interface{}{
+ uint8(3),
+ incrementalByte,
+ x, y, width, height,
+ }
+
+ for _, val := range data {
+ if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
+ return err
+ }
+ }
+
+ if _, err := c.c.Write(buf.Bytes()[0:10]); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// KeyEvent indiciates a key press or release and sends it to the server.
+// The key is indicated using the X Window System "keysym" value. Use
+// Google to find a reference of these values. To simulate a key press,
+// you must send a key with both a down event, and a non-down event.
+//
+// See 7.5.4.
+func (c *ClientConn) KeyEvent(keysym uint32, down bool) error {
+ var downFlag uint8 = 0
+ if down {
+ downFlag = 1
+ }
+
+ data := []interface{}{
+ uint8(4),
+ downFlag,
+ uint8(0),
+ uint8(0),
+ keysym,
+ }
+
+ for _, val := range data {
+ if err := binary.Write(c.c, binary.BigEndian, val); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// PointerEvent indicates that pointer movement or a pointer button
+// press or release.
+//
+// The mask is a bitwise mask of various ButtonMask values. When a button
+// is set, it is pressed, when it is unset, it is released.
+//
+// See RFC 6143 Section 7.5.5
+func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error {
+ var buf bytes.Buffer
+
+ data := []interface{}{
+ uint8(5),
+ uint8(mask),
+ x,
+ y,
+ }
+
+ for _, val := range data {
+ if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
+ return err
+ }
+ }
+
+ if _, err := c.c.Write(buf.Bytes()[0:6]); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// SetEncodings sets the encoding types in which the pixel data can
+// be sent from the server. After calling this method, the encs slice
+// given should not be modified.
+//
+// See RFC 6143 Section 7.5.2
+func (c *ClientConn) SetEncodings(encs []Encoding) error {
+ data := make([]interface{}, 3+len(encs))
+ data[0] = uint8(2)
+ data[1] = uint8(0)
+ data[2] = uint16(len(encs))
+
+ for i, enc := range encs {
+ data[3+i] = int32(enc.Type())
+ }
+
+ var buf bytes.Buffer
+ for _, val := range data {
+ if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
+ return err
+ }
+ }
+
+ dataLength := 4 + (4 * len(encs))
+ if _, err := c.c.Write(buf.Bytes()[0:dataLength]); err != nil {
+ return err
+ }
+
+ c.Encs = encs
+
+ return nil
+}
+
+// SetPixelFormat sets the format in which pixel values should be sent
+// in FramebufferUpdate messages from the server.
+//
+// See RFC 6143 Section 7.5.1
+func (c *ClientConn) SetPixelFormat(format *PixelFormat) error {
+ var keyEvent [20]byte
+ keyEvent[0] = 0
+
+ pfBytes, err := writePixelFormat(format)
+ if err != nil {
+ return err
+ }
+
+ // Copy the pixel format bytes into the proper slice location
+ copy(keyEvent[4:], pfBytes)
+
+ // Send the data down the connection
+ if _, err := c.c.Write(keyEvent[:]); err != nil {
+ return err
+ }
+
+ // Reset the color map as according to RFC.
+ var newColorMap [256]Color
+ c.ColorMap = newColorMap
+
+ return nil
+}
+
+const pvLen = 12 // ProtocolVersion message length.
+
+func parseProtocolVersion(pv []byte) (uint, uint, error) {
+ var major, minor uint
+
+ if len(pv) < pvLen {
+ return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), pvLen)
+ }
+
+ l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor)
+ if l != 2 {
+ return 0, 0, fmt.Errorf("error parsing ProtocolVersion.")
+ }
+ if err != nil {
+ return 0, 0, err
+ }
+
+ return major, minor, nil
+}
+
+func (c *ClientConn) handshake() error {
+ var protocolVersion [pvLen]byte
+
+ // 7.1.1, read the ProtocolVersion message sent by the server.
+ if _, err := io.ReadFull(c.c, protocolVersion[:]); err != nil {
+ return err
+ }
+
+ maxMajor, maxMinor, err := parseProtocolVersion(protocolVersion[:])
+ if err != nil {
+ return err
+ }
+ if maxMajor < 3 {
+ return fmt.Errorf("unsupported major version, less than 3: %d", maxMajor)
+ }
+ if maxMinor < 8 {
+ return fmt.Errorf("unsupported minor version, less than 8: %d", maxMinor)
+ }
+
+ // Respond with the version we will support
+ if _, err = c.c.Write([]byte("RFB 003.008\n")); err != nil {
+ return err
+ }
+
+ // 7.1.2 Security Handshake from server
+ var numSecurityTypes uint8
+ if err = binary.Read(c.c, binary.BigEndian, &numSecurityTypes); err != nil {
+ return err
+ }
+
+ if numSecurityTypes == 0 {
+ return fmt.Errorf("no security types: %s", c.readErrorReason())
+ }
+
+ securityTypes := make([]uint8, numSecurityTypes)
+ if err = binary.Read(c.c, binary.BigEndian, &securityTypes); err != nil {
+ return err
+ }
+
+ clientSecurityTypes := c.config.Auth
+ if clientSecurityTypes == nil {
+ clientSecurityTypes = []ClientAuth{new(ClientAuthNone)}
+ }
+
+ var auth ClientAuth
+FindAuth:
+ for _, curAuth := range clientSecurityTypes {
+ for _, securityType := range securityTypes {
+ if curAuth.SecurityType() == securityType {
+ // We use the first matching supported authentication
+ auth = curAuth
+ break FindAuth
+ }
+ }
+ }
+
+ if auth == nil {
+ return fmt.Errorf("no suitable auth schemes found. server supported: %#v", securityTypes)
+ }
+
+ // Respond back with the security type we'll use
+ if err = binary.Write(c.c, binary.BigEndian, auth.SecurityType()); err != nil {
+ return err
+ }
+
+ if err = auth.Handshake(c.c); err != nil {
+ return err
+ }
+
+ // 7.1.3 SecurityResult Handshake
+ var securityResult uint32
+ if err = binary.Read(c.c, binary.BigEndian, &securityResult); err != nil {
+ return err
+ }
+
+ if securityResult == 1 {
+ return fmt.Errorf("security handshake failed: %s", c.readErrorReason())
+ }
+
+ // 7.3.1 ClientInit
+ var sharedFlag uint8 = 1
+ if c.config.Exclusive {
+ sharedFlag = 0
+ }
+
+ if err = binary.Write(c.c, binary.BigEndian, sharedFlag); err != nil {
+ return err
+ }
+
+ // 7.3.2 ServerInit
+ if err = binary.Read(c.c, binary.BigEndian, &c.FrameBufferWidth); err != nil {
+ return err
+ }
+
+ if err = binary.Read(c.c, binary.BigEndian, &c.FrameBufferHeight); err != nil {
+ return err
+ }
+
+ // Read the pixel format
+ if err = readPixelFormat(c.c, &c.PixelFormat); err != nil {
+ return err
+ }
+
+ var nameLength uint32
+ if err = binary.Read(c.c, binary.BigEndian, &nameLength); err != nil {
+ return err
+ }
+
+ nameBytes := make([]uint8, nameLength)
+ if err = binary.Read(c.c, binary.BigEndian, &nameBytes); err != nil {
+ return err
+ }
+
+ c.DesktopName = string(nameBytes)
+
+ return nil
+}
+
+// mainLoop reads messages sent from the server and routes them to the
+// proper channels for users of the client to read.
+func (c *ClientConn) mainLoop() {
+ defer c.Close()
+
+ // Build the map of available server messages
+ typeMap := make(map[uint8]ServerMessage)
+
+ defaultMessages := []ServerMessage{
+ new(FramebufferUpdateMessage),
+ new(SetColorMapEntriesMessage),
+ new(BellMessage),
+ new(ServerCutTextMessage),
+ }
+
+ for _, msg := range defaultMessages {
+ typeMap[msg.Type()] = msg
+ }
+
+ if c.config.ServerMessages != nil {
+ for _, msg := range c.config.ServerMessages {
+ typeMap[msg.Type()] = msg
+ }
+ }
+
+ for {
+ var messageType uint8
+ if err := binary.Read(c.c, binary.BigEndian, &messageType); err != nil {
+ break
+ }
+
+ msg, ok := typeMap[messageType]
+ if !ok {
+ // Unsupported message type! Bad!
+ break
+ }
+
+ parsedMsg, err := msg.Read(c, c.c)
+ if err != nil {
+ break
+ }
+
+ if c.config.ServerMessageCh == nil {
+ continue
+ }
+
+ c.config.ServerMessageCh <- parsedMsg
+ }
+}
+
+func (c *ClientConn) readErrorReason() string {
+ var reasonLen uint32
+ if err := binary.Read(c.c, binary.BigEndian, &reasonLen); err != nil {
+ return ""
+ }
+
+ reason := make([]uint8, reasonLen)
+ if err := binary.Read(c.c, binary.BigEndian, &reason); err != nil {
+ return ""
+ }
+
+ return string(reason)
+}
diff --git a/pkg/hydra/vnc/client_auth.go b/pkg/hydra/vnc/client_auth.go
new file mode 100644
index 000000000..601e470ed
--- /dev/null
+++ b/pkg/hydra/vnc/client_auth.go
@@ -0,0 +1,124 @@
+package vnc
+
+import (
+ "net"
+
+ "crypto/des"
+ "encoding/binary"
+)
+
+// A ClientAuth implements a method of authenticating with a remote server.
+type ClientAuth interface {
+ // SecurityType returns the byte identifier sent by the server to
+ // identify this authentication scheme.
+ SecurityType() uint8
+
+ // Handshake is called when the authentication handshake should be
+ // performed, as part of the general RFB handshake. (see 7.2.1)
+ Handshake(net.Conn) error
+}
+
+// ClientAuthNone is the "none" authentication. See 7.2.1
+type ClientAuthNone byte
+
+func (*ClientAuthNone) SecurityType() uint8 {
+ return 1
+}
+
+func (*ClientAuthNone) Handshake(net.Conn) error {
+ return nil
+}
+
+// PasswordAuth is VNC authentication, 7.2.2
+type PasswordAuth struct {
+ Password string
+}
+
+func (p *PasswordAuth) SecurityType() uint8 {
+ return 2
+}
+
+func (p *PasswordAuth) Handshake(c net.Conn) error {
+ randomValue := make([]uint8, 16)
+ if err := binary.Read(c, binary.BigEndian, &randomValue); err != nil {
+ return err
+ }
+
+ crypted, err := p.encrypt(p.Password, randomValue)
+
+ if err != nil {
+ return err
+ }
+
+ if err := binary.Write(c, binary.BigEndian, &crypted); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (p *PasswordAuth) reverseBits(b byte) byte {
+ var reverse = [256]int{
+ 0, 128, 64, 192, 32, 160, 96, 224,
+ 16, 144, 80, 208, 48, 176, 112, 240,
+ 8, 136, 72, 200, 40, 168, 104, 232,
+ 24, 152, 88, 216, 56, 184, 120, 248,
+ 4, 132, 68, 196, 36, 164, 100, 228,
+ 20, 148, 84, 212, 52, 180, 116, 244,
+ 12, 140, 76, 204, 44, 172, 108, 236,
+ 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226,
+ 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234,
+ 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230,
+ 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238,
+ 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225,
+ 17, 145, 81, 209, 49, 177, 113, 241,
+ 9, 137, 73, 201, 41, 169, 105, 233,
+ 25, 153, 89, 217, 57, 185, 121, 249,
+ 5, 133, 69, 197, 37, 165, 101, 229,
+ 21, 149, 85, 213, 53, 181, 117, 245,
+ 13, 141, 77, 205, 45, 173, 109, 237,
+ 29, 157, 93, 221, 61, 189, 125, 253,
+ 3, 131, 67, 195, 35, 163, 99, 227,
+ 19, 147, 83, 211, 51, 179, 115, 243,
+ 11, 139, 75, 203, 43, 171, 107, 235,
+ 27, 155, 91, 219, 59, 187, 123, 251,
+ 7, 135, 71, 199, 39, 167, 103, 231,
+ 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239,
+ 31, 159, 95, 223, 63, 191, 127, 255,
+ }
+
+ return byte(reverse[int(b)])
+}
+
+func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) {
+ keyBytes := []byte{0, 0, 0, 0, 0, 0, 0, 0}
+
+ if len(key) > 8 {
+ key = key[:8]
+ }
+
+ for i := 0; i < len(key); i++ {
+ keyBytes[i] = p.reverseBits(key[i])
+ }
+
+ block, err := des.NewCipher(keyBytes)
+
+ if err != nil {
+ return nil, err
+ }
+
+ result1 := make([]byte, 8)
+ block.Encrypt(result1, bytes)
+ result2 := make([]byte, 8)
+ block.Encrypt(result2, bytes[8:])
+
+ crypted := append(result1, result2...)
+
+ return crypted, nil
+}
diff --git a/pkg/hydra/vnc/pixel_format.go b/pkg/hydra/vnc/pixel_format.go
new file mode 100644
index 000000000..ff8edbff5
--- /dev/null
+++ b/pkg/hydra/vnc/pixel_format.go
@@ -0,0 +1,151 @@
+package vnc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+)
+
+// PixelFormat describes the way a pixel is formatted for a VNC connection.
+//
+// See RFC 6143 Section 7.4 for information on each of the fields.
+type PixelFormat struct {
+ BPP uint8
+ Depth uint8
+ BigEndian bool
+ TrueColor bool
+ RedMax uint16
+ GreenMax uint16
+ BlueMax uint16
+ RedShift uint8
+ GreenShift uint8
+ BlueShift uint8
+}
+
+func readPixelFormat(r io.Reader, result *PixelFormat) error {
+ var rawPixelFormat [16]byte
+ if _, err := io.ReadFull(r, rawPixelFormat[:]); err != nil {
+ return err
+ }
+
+ var pfBoolByte uint8
+ brPF := bytes.NewReader(rawPixelFormat[:])
+ if err := binary.Read(brPF, binary.BigEndian, &result.BPP); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.Depth); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil {
+ return err
+ }
+
+ if pfBoolByte != 0 {
+ // Big endian is true
+ result.BigEndian = true
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil {
+ return err
+ }
+
+ if pfBoolByte != 0 {
+ // True Color is true. So we also have to read all the color max & shifts.
+ result.TrueColor = true
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.RedMax); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.GreenMax); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.BlueMax); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.RedShift); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.GreenShift); err != nil {
+ return err
+ }
+
+ if err := binary.Read(brPF, binary.BigEndian, &result.BlueShift); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func writePixelFormat(format *PixelFormat) ([]byte, error) {
+ var buf bytes.Buffer
+
+ // Byte 1
+ if err := binary.Write(&buf, binary.BigEndian, format.BPP); err != nil {
+ return nil, err
+ }
+
+ // Byte 2
+ if err := binary.Write(&buf, binary.BigEndian, format.Depth); err != nil {
+ return nil, err
+ }
+
+ var boolByte byte
+ if format.BigEndian {
+ boolByte = 1
+ } else {
+ boolByte = 0
+ }
+
+ // Byte 3 (BigEndian)
+ if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil {
+ return nil, err
+ }
+
+ if format.TrueColor {
+ boolByte = 1
+ } else {
+ boolByte = 0
+ }
+
+ // Byte 4 (TrueColor)
+ if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil {
+ return nil, err
+ }
+
+ // If we have true color enabled then we have to fill in the rest of the
+ // structure with the color values.
+ if format.TrueColor {
+ if err := binary.Write(&buf, binary.BigEndian, format.RedMax); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, format.GreenMax); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, format.BlueMax); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, format.RedShift); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, format.GreenShift); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(&buf, binary.BigEndian, format.BlueShift); err != nil {
+ return nil, err
+ }
+ }
+
+ return buf.Bytes()[0:16], nil
+}
diff --git a/pkg/hydra/vnc/pointer.go b/pkg/hydra/vnc/pointer.go
new file mode 100644
index 000000000..f4f66fc77
--- /dev/null
+++ b/pkg/hydra/vnc/pointer.go
@@ -0,0 +1,87 @@
+package vnc
+
+import (
+ "encoding/binary"
+ "io"
+)
+
+type ButtonMask uint8
+
+// All available button mask components.
+const (
+ ButtonLeft ButtonMask = 1 << iota
+ ButtonMiddle
+ ButtonRight
+ Button4
+ Button5
+ Button6
+ Button7
+ Button8
+)
+
+type Color struct {
+ R, G, B uint16
+}
+
+// An Encoding implements a method for encoding pixel data that is
+// sent by the server to the client.
+type Encoding interface {
+ // The number that uniquely identifies this encoding type.
+ Type() int32
+
+ // Read reads the contents of the encoded pixel data from the reader.
+ // This should return a new Encoding implementation that contains
+ // the proper data.
+ Read(*ClientConn, *Rectangle, io.Reader) (Encoding, error)
+}
+
+// RawEncoding is raw pixel data sent by the server.
+//
+// See RFC 6143 Section 7.7.1
+type RawEncoding struct {
+ Colors []Color
+}
+
+func (*RawEncoding) Type() int32 {
+ return 0
+}
+
+func (*RawEncoding) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
+ bytesPerPixel := c.PixelFormat.BPP / 8
+ pixelBytes := make([]uint8, bytesPerPixel)
+
+ var byteOrder binary.ByteOrder = binary.LittleEndian
+ if c.PixelFormat.BigEndian {
+ byteOrder = binary.BigEndian
+ }
+
+ colors := make([]Color, int(rect.Height)*int(rect.Width))
+
+ for y := uint16(0); y < rect.Height; y++ {
+ for x := uint16(0); x < rect.Width; x++ {
+ if _, err := io.ReadFull(r, pixelBytes); err != nil {
+ return nil, err
+ }
+
+ var rawPixel uint32
+ if c.PixelFormat.BPP == 8 {
+ rawPixel = uint32(pixelBytes[0])
+ } else if c.PixelFormat.BPP == 16 {
+ rawPixel = uint32(byteOrder.Uint16(pixelBytes))
+ } else if c.PixelFormat.BPP == 32 {
+ rawPixel = byteOrder.Uint32(pixelBytes)
+ }
+
+ color := &colors[int(y)*int(rect.Width)+int(x)]
+ if c.PixelFormat.TrueColor {
+ color.R = uint16((rawPixel >> c.PixelFormat.RedShift) & uint32(c.PixelFormat.RedMax))
+ color.G = uint16((rawPixel >> c.PixelFormat.GreenShift) & uint32(c.PixelFormat.GreenMax))
+ color.B = uint16((rawPixel >> c.PixelFormat.BlueShift) & uint32(c.PixelFormat.BlueMax))
+ } else {
+ *color = c.ColorMap[rawPixel]
+ }
+ }
+ }
+
+ return &RawEncoding{colors}, nil
+}
diff --git a/pkg/hydra/vnc/server_messages.go b/pkg/hydra/vnc/server_messages.go
new file mode 100644
index 000000000..c0aae5665
--- /dev/null
+++ b/pkg/hydra/vnc/server_messages.go
@@ -0,0 +1,192 @@
+package vnc
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// A ServerMessage implements a message sent from the server to the client.
+type ServerMessage interface {
+ // The type of the message that is sent down on the wire.
+ Type() uint8
+
+ // Read reads the contents of the message from the reader. At the point
+ // this is called, the message type has already been read from the reader.
+ // This should return a new ServerMessage that is the appropriate type.
+ Read(*ClientConn, io.Reader) (ServerMessage, error)
+}
+
+// FramebufferUpdateMessage consists of a sequence of rectangles of
+// pixel data that the client should put into its framebuffer.
+type FramebufferUpdateMessage struct {
+ Rectangles []Rectangle
+}
+
+// Rectangle represents a rectangle of pixel data.
+type Rectangle struct {
+ X uint16
+ Y uint16
+ Width uint16
+ Height uint16
+ Enc Encoding
+}
+
+func (*FramebufferUpdateMessage) Type() uint8 {
+ return 0
+}
+
+func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
+ // Read off the padding
+ var padding [1]byte
+ if _, err := io.ReadFull(r, padding[:]); err != nil {
+ return nil, err
+ }
+
+ var numRects uint16
+ if err := binary.Read(r, binary.BigEndian, &numRects); err != nil {
+ return nil, err
+ }
+
+ // Build the map of encodings supported
+ encMap := make(map[int32]Encoding)
+ for _, enc := range c.Encs {
+ encMap[enc.Type()] = enc
+ }
+
+ // We must always support the raw encoding
+ rawEnc := new(RawEncoding)
+ encMap[rawEnc.Type()] = rawEnc
+
+ rects := make([]Rectangle, numRects)
+ for i := uint16(0); i < numRects; i++ {
+ var encodingType int32
+
+ rect := &rects[i]
+ data := []interface{}{
+ &rect.X,
+ &rect.Y,
+ &rect.Width,
+ &rect.Height,
+ &encodingType,
+ }
+
+ for _, val := range data {
+ if err := binary.Read(r, binary.BigEndian, val); err != nil {
+ return nil, err
+ }
+ }
+
+ enc, ok := encMap[encodingType]
+ if !ok {
+ return nil, fmt.Errorf("unsupported encoding type: %d", encodingType)
+ }
+
+ var err error
+ rect.Enc, err = enc.Read(c, rect, r)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return &FramebufferUpdateMessage{rects}, nil
+}
+
+// SetColorMapEntriesMessage is sent by the server to set values into
+// the color map. This message will automatically update the color map
+// for the associated connection, but contains the color change data
+// if the consumer wants to read it.
+//
+// See RFC 6143 Section 7.6.2
+type SetColorMapEntriesMessage struct {
+ FirstColor uint16
+ Colors []Color
+}
+
+func (*SetColorMapEntriesMessage) Type() uint8 {
+ return 1
+}
+
+func (*SetColorMapEntriesMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
+ // Read off the padding
+ var padding [1]byte
+ if _, err := io.ReadFull(r, padding[:]); err != nil {
+ return nil, err
+ }
+
+ var result SetColorMapEntriesMessage
+ if err := binary.Read(r, binary.BigEndian, &result.FirstColor); err != nil {
+ return nil, err
+ }
+
+ var numColors uint16
+ if err := binary.Read(r, binary.BigEndian, &numColors); err != nil {
+ return nil, err
+ }
+
+ result.Colors = make([]Color, numColors)
+ for i := uint16(0); i < numColors; i++ {
+
+ color := &result.Colors[i]
+ data := []interface{}{
+ &color.R,
+ &color.G,
+ &color.B,
+ }
+
+ for _, val := range data {
+ if err := binary.Read(r, binary.BigEndian, val); err != nil {
+ return nil, err
+ }
+ }
+
+ // Update the connection's color map
+ c.ColorMap[result.FirstColor+i] = *color
+ }
+
+ return &result, nil
+}
+
+// Bell signals that an audible bell should be made on the client.
+//
+// See RFC 6143 Section 7.6.3
+type BellMessage byte
+
+func (*BellMessage) Type() uint8 {
+ return 2
+}
+
+func (*BellMessage) Read(*ClientConn, io.Reader) (ServerMessage, error) {
+ return new(BellMessage), nil
+}
+
+// ServerCutTextMessage indicates the server has new text in the cut buffer.
+//
+// See RFC 6143 Section 7.6.4
+type ServerCutTextMessage struct {
+ Text string
+}
+
+func (*ServerCutTextMessage) Type() uint8 {
+ return 3
+}
+
+func (*ServerCutTextMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
+ // Read off the padding
+ var padding [1]byte
+ if _, err := io.ReadFull(r, padding[:]); err != nil {
+ return nil, err
+ }
+
+ var textLength uint32
+ if err := binary.Read(r, binary.BigEndian, &textLength); err != nil {
+ return nil, err
+ }
+
+ textBytes := make([]uint8, textLength)
+ if err := binary.Read(r, binary.BigEndian, &textBytes); err != nil {
+ return nil, err
+ }
+
+ return &ServerCutTextMessage{string(textBytes)}, nil
+}
diff --git a/test/vnc/test.go b/test/vnc/test.go
new file mode 100644
index 000000000..d0f83c1e2
--- /dev/null
+++ b/test/vnc/test.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/hktalent/scan4all/pkg/hydra/vnc"
+ "log"
+ "net"
+)
+
+func main() {
+ nc, err := net.Dial("tcp", "192.168.0.100:5900")
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ cc1, err := vnc.Client(nc, &vnc.ClientConfig{Auth: []vnc.ClientAuth{&vnc.PasswordAuth{Password: "test"}}})
+ if err != nil {
+ log.Println(err)
+ } else {
+ cc1.Close()
+ }
+}