Skip to content

Commit 874e430

Browse files
author
shadowy-pycoder
committed
Added basic parsing for SSH messages
1 parent 9501015 commit 874e430

File tree

4 files changed

+152
-22
lines changed

4 files changed

+152
-22
lines changed

layers/dns.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,32 +208,32 @@ func (d *DNSMessage) Summary() string {
208208
var sb strings.Builder
209209
sb.WriteString(fmt.Sprintf("DNS Message: %s %s %#04x ", d.Flags.OPCodeDesc, d.Flags.QRDesc, d.TransactionID))
210210
for _, rec := range d.Questions {
211-
if sb.Len() > 100 {
211+
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
212+
if sb.Len() > maxLenSummary {
212213
goto result
213214
}
214-
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
215215
}
216216
for _, rec := range d.AnswerRRs {
217-
if sb.Len() > 100 {
217+
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
218+
if sb.Len() > maxLenSummary {
218219
goto result
219220
}
220-
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
221221
}
222222
for _, rec := range d.AuthorityRRs {
223-
if sb.Len() > 100 {
223+
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
224+
if sb.Len() > maxLenSummary {
224225
goto result
225226
}
226-
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
227227
}
228228
for _, rec := range d.AdditionalRRs {
229-
if sb.Len() > 100 {
229+
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
230+
if sb.Len() > maxLenSummary {
230231
goto result
231232
}
232-
sb.WriteString(fmt.Sprintf("%s %s ", rec.Type.Name, rec.Name))
233233
}
234234
return sb.String()
235235
result:
236-
return sb.String()[:100] + string(ellipsis)
236+
return sb.String()[:maxLenSummary] + string(ellipsis)
237237
}
238238

239239
// Parse parses the given byte data into a DNSMessage struct.

layers/layers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"unsafe"
66
)
77

8+
const maxLenSummary = 100
9+
810
var LayerMap = map[string]Layer{
911
"ETH": &EthernetFrame{},
1012
"IPv4": &IPv4Packet{},

layers/ssh.go

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,142 @@
11
package layers
22

3-
import "fmt"
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"fmt"
7+
"strings"
8+
)
49

5-
// https://www.omnisecu.com/tcpip/ssh-packet-format.php
6-
// port 22
7-
type SSHMessage struct{}
10+
const messageSizeSSH = 6
11+
12+
type Message struct {
13+
PacketLength uint32
14+
PaddingLength uint8
15+
MesssageType uint8
16+
MesssageTypeDesc string
17+
Payload []byte
18+
}
19+
20+
func (m *Message) String() string {
21+
if m.PacketLength == 0 {
22+
return fmt.Sprintf(`- Payload: %d bytes`, len(m.Payload))
23+
}
24+
return fmt.Sprintf(` - Packet Length: %d
25+
- Padding Length: %d
26+
- Message Type: %s (%d)
27+
- Payload: %d bytes`,
28+
m.PacketLength,
29+
m.PaddingLength,
30+
m.MesssageTypeDesc,
31+
m.MesssageType,
32+
len(m.Payload))
33+
}
34+
35+
type SSHMessage struct {
36+
Protocol string
37+
Messages []*Message
38+
}
839

940
func (s *SSHMessage) String() string {
10-
return fmt.Sprintf(`%s`, s.Summary())
41+
return fmt.Sprintf(`%s
42+
%s
43+
`, s.Summary(), s.printMessages())
1144
}
1245

1346
func (s *SSHMessage) Summary() string {
14-
return fmt.Sprint("SSH Message:")
47+
var sb strings.Builder
48+
sb.WriteString("SSH Message: ")
49+
if s.Protocol != "" {
50+
sb.WriteString(s.Protocol)
51+
return sb.String()
52+
}
53+
if len(s.Messages) == 1 && s.Messages[0].PacketLength == 0 {
54+
sb.WriteString(fmt.Sprintf("Encrypted or partial data Len: %d", len(s.Messages[0].Payload)))
55+
return sb.String()
56+
}
57+
for _, message := range s.Messages {
58+
if message.PacketLength != 0 {
59+
sb.WriteString(fmt.Sprintf("%s (%d) Len: %d ",
60+
message.MesssageTypeDesc,
61+
message.MesssageType,
62+
message.PacketLength))
63+
}
64+
if sb.Len() > maxLenSummary {
65+
return sb.String()[:maxLenSummary] + string(ellipsis)
66+
}
67+
}
68+
return sb.String()
69+
}
70+
71+
func (s *SSHMessage) printMessages() string {
72+
var sb strings.Builder
73+
74+
for _, message := range s.Messages {
75+
if message.MesssageTypeDesc == "" {
76+
sb.WriteString(fmt.Sprintf("%s\n", message))
77+
} else {
78+
sb.WriteString(fmt.Sprintf("- %s:\n%s\n", message.MesssageTypeDesc, message))
79+
}
80+
}
81+
return sb.String()
1582
}
1683

1784
func (s *SSHMessage) Parse(data []byte) error {
85+
if len(data) < messageSizeSSH {
86+
return fmt.Errorf("minimum message size for SSH is %d bytes, got %d bytes", messageSizeSSH, len(data))
87+
}
88+
s.Protocol = ""
89+
s.Messages = nil
90+
if bytes.HasSuffix(data, crlf) {
91+
s.Protocol = bytesToStr(bytes.TrimSuffix(data, crlf))
92+
return nil
93+
}
94+
s.Messages = make([]*Message, 0, 3)
95+
for len(data) > 0 {
96+
m := &Message{}
97+
s.Messages = append(s.Messages, m)
98+
plen := binary.BigEndian.Uint32(data[0:4])
99+
if plen > 0xffff {
100+
m.Payload = data
101+
break
102+
}
103+
m.MesssageType = data[5]
104+
if m.MesssageTypeDesc = mtypedesc(m.MesssageType); m.MesssageTypeDesc == "Unknown" {
105+
m.Payload = data
106+
break
107+
}
108+
m.PacketLength = plen
109+
m.PaddingLength = data[4]
110+
offset := int(messageSizeSSH + m.PacketLength - 2)
111+
if offset <= len(data) {
112+
m.Payload = data[messageSizeSSH:offset]
113+
data = data[offset:]
114+
} else {
115+
m.Payload = data[messageSizeSSH:]
116+
break
117+
}
118+
}
18119
return nil
19120
}
20121

21122
func (s *SSHMessage) NextLayer() (string, []byte) {
22123
return "", nil
23124
}
125+
126+
// https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml
127+
func mtypedesc(mtype uint8) string {
128+
var mtypedesc string
129+
switch mtype {
130+
case 20:
131+
mtypedesc = "Key Exchange Init"
132+
case 21:
133+
mtypedesc = "New Keys"
134+
case 30:
135+
mtypedesc = "Elliptic Curve Diffie-Hellman Key Exchange Init"
136+
case 31:
137+
mtypedesc = "Elliptic Curve Diffie-Hellman Key Exchange Reply"
138+
default:
139+
mtypedesc = "Unknown"
140+
}
141+
return mtypedesc
142+
}

layers/tls.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"strings"
77
)
88

9+
const headerSizeTLS = 5
10+
911
type Record struct {
1012
ContentType uint8
1113
ContentTypeDesc string
@@ -48,9 +50,6 @@ func (t *TLSMessage) Summary() string {
4850
sb.WriteString(fmt.Sprintf("Ignored unknown record Len: %d", len(t.Data)))
4951
} else {
5052
for i, rec := range t.Records {
51-
if sb.Len() > 100 {
52-
return sb.String()[:100] + string(ellipsis)
53-
}
5453
if i > 0 {
5554
sb.WriteString(fmt.Sprintf("%s (%d) Len: %d ", rec.ContentTypeDesc, rec.ContentType, rec.Length))
5655
continue
@@ -64,6 +63,9 @@ func (t *TLSMessage) Summary() string {
6463
rec.ContentTypeDesc,
6564
rec.ContentType,
6665
rec.Length))
66+
if sb.Len() > maxLenSummary {
67+
return sb.String()[:maxLenSummary] + string(ellipsis)
68+
}
6769
}
6870
}
6971
return sb.String()
@@ -79,6 +81,9 @@ func (t *TLSMessage) printRecords() string {
7981
}
8082

8183
func (t *TLSMessage) Parse(data []byte) error {
84+
if len(data) < headerSizeTLS {
85+
return fmt.Errorf("minimum header size for TLS is %d bytes, got %d bytes", headerSizeTLS, len(data))
86+
}
8287
t.Records = make([]*Record, 0, 5)
8388
for len(data) > 0 {
8489
ctype := data[0]
@@ -91,8 +96,8 @@ func (t *TLSMessage) Parse(data []byte) error {
9196
if verdesc == "Unknown" {
9297
break
9398
}
94-
rlen := binary.BigEndian.Uint16(data[3:5])
95-
if 5+rlen > uint16(len(data)) {
99+
rlen := binary.BigEndian.Uint16(data[3:headerSizeTLS])
100+
if headerSizeTLS+rlen > uint16(len(data)) {
96101
break
97102
}
98103
r := &Record{
@@ -101,10 +106,10 @@ func (t *TLSMessage) Parse(data []byte) error {
101106
Version: ver,
102107
VersionDesc: verdesc,
103108
Length: rlen,
104-
data: data[5 : 5+rlen],
109+
data: data[headerSizeTLS : headerSizeTLS+rlen],
105110
}
106111
t.Records = append(t.Records, r)
107-
data = data[5+rlen:]
112+
data = data[headerSizeTLS+rlen:]
108113
}
109114
t.Data = data
110115
return nil
@@ -141,6 +146,8 @@ func ctdesc(ct uint8) string {
141146
func verdesc(ver uint16) string {
142147
var verdesc string
143148
switch ver {
149+
case 0x0200:
150+
verdesc = "SSL 2.0"
144151
case 0x0300:
145152
verdesc = "SSL 3.0"
146153
case 0x0301:
@@ -149,6 +156,8 @@ func verdesc(ver uint16) string {
149156
verdesc = "TLS 1.1"
150157
case 0x0303:
151158
verdesc = "TLS 1.2"
159+
case 0x0304:
160+
verdesc = "TLS 1.3"
152161
default:
153162
verdesc = "Unknown"
154163
}

0 commit comments

Comments
 (0)