From 0b88e72b0da75c4315d43f61052d443a1acf4961 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Mon, 24 Apr 2023 16:48:14 -0500 Subject: [PATCH 1/4] Added IPMI Detection Plugin --- go.mod | 2 + go.sum | 4 + pkg/plugins/services/ipmi/ipmi.go | 153 +++++++++++++++++++++++++ pkg/plugins/services/ipmi/ipmi_test.go | 53 +++++++++ pkg/plugins/types.go | 5 + 5 files changed, 217 insertions(+) create mode 100644 pkg/plugins/services/ipmi/ipmi.go create mode 100644 pkg/plugins/services/ipmi/ipmi_test.go diff --git a/go.mod b/go.mod index 085ee03..aa53aaa 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( ) require ( + github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect ) @@ -37,6 +38,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.3 // indirect + github.com/ory/dockertest v3.3.5+incompatible github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect diff --git a/go.sum b/go.sum index 3a65c26..f317610 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VM github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= @@ -77,6 +79,8 @@ github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/pkg/plugins/services/ipmi/ipmi.go b/pkg/plugins/services/ipmi/ipmi.go new file mode 100644 index 0000000..d4678e0 --- /dev/null +++ b/pkg/plugins/services/ipmi/ipmi.go @@ -0,0 +1,153 @@ +// Copyright 2022 Praetorian Security, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipmi + +import ( + "encoding/hex" + "fmt" + "io" + "net" + "time" + + "github.com/praetorian-inc/fingerprintx/pkg/plugins" +) + +// http://72.47.221.139/sites/default/files/standards/documents/DSP0114.pdf + +var ipmiInitialPacket = [23]byte{ + + // + // Remote Management Control Protocol, Class: IPMI + // Version: 0x06 + // Reserved: 0x00 + // Sequence: 0xFF + // Type: 0x07 + // + + 0x06, 0x00, 0xFF, 0x07, + + // + // IPMI v1.5 Session Wrapper, Session ID 0x00 + // Authentication Type: NONE (0x00) + // Session ID: 0x00 0x00 0x00 0x00 + // Session Sequence number: 0x00 0x00 0x00 0x00 + // Message Length: 9 + // + + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x09, + + // + // Intelligent Platform Management Bus + // Bus Command Data: 20 18 C8 81 00 38 8E 04 B5 + // + + 0x20, 0x18, 0xC8, 0x81, 0x00, 0x38, 0x8E, 0x04, 0xB5, +} + +var ipmiExpectedResponse = [13]byte{ + + /* + * Remote Management Control Protocol, Class: IPMI + * Version: 0x06 + * Reserved: 0x00 + * Sequence: 0xFF + * Type: 0x07 + */ + + 0x06, 0x00, 0xFF, 0x07, + + // + // IPMI v1.5 Session Wrapper, Session ID 0x00 + // Authentication Type: NONE (0x00) + // Session ID: 0x00 0x00 0x00 0x00 + // Session Sequence number: 0x00 0x00 0x00 0x00 + // + + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +} + +type IPMIPlugin struct{} + +const IPMI = "ipmi" + +func isIPMI(conn net.Conn, timeout time.Duration) (bool, error) { + fmt.Println("isIPMI Running!") + + // Send the initial packet to the server + _, err := conn.Write(ipmiInitialPacket[:]) + if err != nil { + return false, err + } + fmt.Println("Sent to server:") + fmt.Println(hex.Dump(ipmiInitialPacket[:])) + + // Wait for a response from the server + response := make([]byte, len(ipmiExpectedResponse)) + conn.SetReadDeadline(time.Now().Add(timeout)) + _, err = io.ReadFull(conn, response) + + // Print a hex dump of the response + fmt.Println("len(response) = ", len(response)) + fmt.Println("Response from server:") + fmt.Println(hex.Dump(response)) + + if err != nil { + return false, err + } + + // Check if the response matches the expected response + for i, b := range ipmiExpectedResponse { + if response[i] != b { + return false, nil + } + } + + return true, nil +} + +func init() { + plugins.RegisterPlugin(&IPMIPlugin{}) +} + +func (p *IPMIPlugin) PortPriority(port uint16) bool { + return port == 623 +} + +func (p *IPMIPlugin) Run(conn net.Conn, timeout time.Duration, target plugins.Target) (*plugins.Service, error) { + if isIPMI, err := isIPMI(conn, timeout); !isIPMI || err != nil { + fmt.Printf("Err = %v\n", err) + return nil, nil + } + payload := plugins.ServiceIPMI{} + + return plugins.CreateServiceFrom(target, payload, false, "", plugins.UDP), nil +} + +func (p *IPMIPlugin) Name() string { + return IPMI +} + +func (p *IPMIPlugin) Type() plugins.Protocol { + return plugins.TCP +} + +func (p *IPMIPlugin) Priority() int { + return 80 +} diff --git a/pkg/plugins/services/ipmi/ipmi_test.go b/pkg/plugins/services/ipmi/ipmi_test.go new file mode 100644 index 0000000..38c2532 --- /dev/null +++ b/pkg/plugins/services/ipmi/ipmi_test.go @@ -0,0 +1,53 @@ +// Copyright 2022 Praetorian Security, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipmi + +import ( + "testing" + + "github.com/ory/dockertest/v3" + "github.com/praetorian-inc/fingerprintx/pkg/plugins" + "github.com/praetorian-inc/fingerprintx/pkg/test" +) + +func TestIPMI(t *testing.T) { + testcases := []test.Testcase{ + { + Description: "ipmi", + Port: 623, + Protocol: plugins.UDP, + Expected: func(res *plugins.Service) bool { + return res != nil + }, + RunConfig: dockertest.RunOptions{ + Repository: "vaporio/ipmi-simulator", + ExposedPorts: []string{"623/udp"}, + }, + }, + } + + p := &IPMIPlugin{} + + for _, tc := range testcases { + tc := tc + t.Run(tc.Description, func(t *testing.T) { + t.Parallel() + err := test.RunTest(t, tc, p) + if err != nil { + t.Errorf(err.Error()) + } + }) + } +} diff --git a/pkg/plugins/types.go b/pkg/plugins/types.go index 9a6c633..e05e7ec 100644 --- a/pkg/plugins/types.go +++ b/pkg/plugins/types.go @@ -45,6 +45,7 @@ const ( ProtoHTTP2 = "http2" ProtoIMAP = "imap" ProtoIMAPS = "imaps" + ProtoIPMI = "ipmi" ProtoIPSEC = "ipsec" ProtoKafka = "kafka" ProtoLDAP = "ldap" @@ -488,6 +489,10 @@ type ServiceEcho struct{} func (e ServiceEcho) Type() string { return ProtoEcho } +type ServiceIPMI struct{} + +func (e ServiceIPMI) Type() string { return ProtoIPMI } + type ServiceRsync struct{} func (e ServiceRsync) Type() string { return ProtoRsync } From 3c52d984e53a3323e4730bcc7ef108639dd0559e Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Tue, 25 Apr 2023 10:40:05 -0500 Subject: [PATCH 2/4] Updated IPMI Detection Plugin --- pkg/plugins/services/ipmi/ipmi.go | 13 +------------ pkg/scan/plugin_list.go | 1 + 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/pkg/plugins/services/ipmi/ipmi.go b/pkg/plugins/services/ipmi/ipmi.go index d4678e0..e4ea16f 100644 --- a/pkg/plugins/services/ipmi/ipmi.go +++ b/pkg/plugins/services/ipmi/ipmi.go @@ -15,8 +15,6 @@ package ipmi import ( - "encoding/hex" - "fmt" "io" "net" "time" @@ -88,26 +86,18 @@ type IPMIPlugin struct{} const IPMI = "ipmi" func isIPMI(conn net.Conn, timeout time.Duration) (bool, error) { - fmt.Println("isIPMI Running!") // Send the initial packet to the server _, err := conn.Write(ipmiInitialPacket[:]) if err != nil { return false, err } - fmt.Println("Sent to server:") - fmt.Println(hex.Dump(ipmiInitialPacket[:])) // Wait for a response from the server response := make([]byte, len(ipmiExpectedResponse)) conn.SetReadDeadline(time.Now().Add(timeout)) _, err = io.ReadFull(conn, response) - // Print a hex dump of the response - fmt.Println("len(response) = ", len(response)) - fmt.Println("Response from server:") - fmt.Println(hex.Dump(response)) - if err != nil { return false, err } @@ -132,7 +122,6 @@ func (p *IPMIPlugin) PortPriority(port uint16) bool { func (p *IPMIPlugin) Run(conn net.Conn, timeout time.Duration, target plugins.Target) (*plugins.Service, error) { if isIPMI, err := isIPMI(conn, timeout); !isIPMI || err != nil { - fmt.Printf("Err = %v\n", err) return nil, nil } payload := plugins.ServiceIPMI{} @@ -145,7 +134,7 @@ func (p *IPMIPlugin) Name() string { } func (p *IPMIPlugin) Type() plugins.Protocol { - return plugins.TCP + return plugins.UDP } func (p *IPMIPlugin) Priority() int { diff --git a/pkg/scan/plugin_list.go b/pkg/scan/plugin_list.go index 3e3070e..75b4772 100644 --- a/pkg/scan/plugin_list.go +++ b/pkg/scan/plugin_list.go @@ -24,6 +24,7 @@ import ( _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/ftp" _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/http" _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/imap" + _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/ipmi" _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/ipsec" _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/kafka/kafkaNew" _ "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/kafka/kafkaOld" From e20b48451e0861cc4aaf046990f889eaee7b6278 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Tue, 25 Apr 2023 10:42:26 -0500 Subject: [PATCH 3/4] Fix Linter Error --- pkg/plugins/services/ipmi/ipmi.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/plugins/services/ipmi/ipmi.go b/pkg/plugins/services/ipmi/ipmi.go index e4ea16f..6c22846 100644 --- a/pkg/plugins/services/ipmi/ipmi.go +++ b/pkg/plugins/services/ipmi/ipmi.go @@ -95,9 +95,13 @@ func isIPMI(conn net.Conn, timeout time.Duration) (bool, error) { // Wait for a response from the server response := make([]byte, len(ipmiExpectedResponse)) - conn.SetReadDeadline(time.Now().Add(timeout)) - _, err = io.ReadFull(conn, response) + err = conn.SetReadDeadline(time.Now().Add(timeout)) + if err != nil { + return false, err + } + + _, err = io.ReadFull(conn, response) if err != nil { return false, err } From f3d12c97bc186b7ecf82f84edc2e9251500cf42b Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Tue, 25 Apr 2023 10:44:45 -0500 Subject: [PATCH 4/4] Fix Linter Error --- pkg/plugins/services/ipmi/ipmi.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/plugins/services/ipmi/ipmi.go b/pkg/plugins/services/ipmi/ipmi.go index 6c22846..f0babc4 100644 --- a/pkg/plugins/services/ipmi/ipmi.go +++ b/pkg/plugins/services/ipmi/ipmi.go @@ -86,14 +86,11 @@ type IPMIPlugin struct{} const IPMI = "ipmi" func isIPMI(conn net.Conn, timeout time.Duration) (bool, error) { - - // Send the initial packet to the server _, err := conn.Write(ipmiInitialPacket[:]) if err != nil { return false, err } - // Wait for a response from the server response := make([]byte, len(ipmiExpectedResponse)) err = conn.SetReadDeadline(time.Now().Add(timeout)) @@ -106,7 +103,6 @@ func isIPMI(conn net.Conn, timeout time.Duration) (bool, error) { return false, err } - // Check if the response matches the expected response for i, b := range ipmiExpectedResponse { if response[i] != b { return false, nil