Skip to content

Commit

Permalink
more fun with RDP
Browse files Browse the repository at this point in the history
  • Loading branch information
glaslos committed Sep 25, 2023
1 parent e170511 commit f08f0f4
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 26 deletions.
5 changes: 5 additions & 0 deletions protocols/protocols.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package protocols

import (
"bytes"
"context"
"net"
"strings"
Expand Down Expand Up @@ -77,6 +78,10 @@ func MapProtocolHandlers(log Logger, h Honeypot) map[string]HandlerFunc {
if _, ok := httpMap[strings.ToUpper(string(snip))]; ok {
return HandleHTTP(ctx, bufConn, log, h)
}
// poor mans check for RDP header
if bytes.Equal(snip, []byte{0x03, 0x00, 0x00, 0x2b}) {
return HandleRDP(ctx, bufConn, log, h)
}
// fallback TCP handler
return HandleTCP(ctx, bufConn, log, h)
}
Expand Down
64 changes: 49 additions & 15 deletions protocols/rdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,80 @@ import (
"go.uber.org/zap"
)

type parsedRDP struct {
Direction string `json:"direction,omitempty"`
Header rdp.TKIPHeader `json:"header,omitempty"`
Payload []byte `json:"payload,omitempty"`
}

type rdpServer struct {
events []parsedRDP
conn net.Conn
}

func (rs *rdpServer) write(header rdp.TKIPHeader, data []byte) error {
rs.events = append(rs.events, parsedRDP{
Header: header,
Direction: "write",
Payload: data,
})
_, err := rs.conn.Write(data)
return err
}

// HandleRDP takes a net.Conn and does basic RDP communication
func HandleRDP(ctx context.Context, conn net.Conn, logger Logger, h Honeypot) error {
server := &rdpServer{
events: []parsedRDP{},
conn: conn,
}
defer func() {
md, err := h.MetadataByConnection(conn)
if err != nil {
logger.Error("failed to get metadata", zap.Error(err))
}
var payload []byte
if len(server.events) > 0 {
payload = server.events[0].Payload
}

if err := h.Produce("rdp", conn, md, payload, server.events); err != nil {
logger.Error("failed to produce message", zap.String("protocol", "rdp"), zap.Error(err))
}
if err := conn.Close(); err != nil {
logger.Error(fmt.Sprintf("[rdp ] error: %v", err))
}
}()

md, err := h.MetadataByConnection(conn)
if err != nil {
return fmt.Errorf("failed to get metadata: %w", err)
}

buffer := make([]byte, 1024)
for {
h.UpdateConnectionTimeout(ctx, conn)
n, err := conn.Read(buffer)
if err != nil && n <= 0 {
logger.Error(fmt.Sprintf("[rdp ] error: %v", err))
logger.Error(fmt.Sprintf("rdp error: %v", err))
return err
}
if n > 0 && n < 1024 {
logger.Info(fmt.Sprintf("[rdp ] \n%s", hex.Dump(buffer[0:n])))
logger.Info(fmt.Sprintf("rdp \n%s", hex.Dump(buffer[0:n])))
pdu, err := rdp.ParseCRPDU(buffer[0:n])
if err != nil {
return err
}
if err := h.Produce("rdp", conn, md, buffer[0:n], pdu); err != nil {
logger.Error("failed to produce message", zap.String("protocol", "rdp"), zap.Error(err))
}
logger.Info(fmt.Sprintf("[rdp ] req pdu: %+v", pdu))
server.events = append(server.events, parsedRDP{
Direction: "read",
Header: pdu.Header,
Payload: buffer[0:n],
})
logger.Info(fmt.Sprintf("rdp req pdu: %+v", pdu))
if len(pdu.Data) > 0 {
logger.Info(fmt.Sprintf("[rdp ] data: %s", string(pdu.Data)))
logger.Info(fmt.Sprintf("rdp data: %s", string(pdu.Data)))
}
resp, err := rdp.ConnectionConfirm(pdu.TPDU)
header, resp, err := rdp.ConnectionConfirm(pdu.TPDU)
if err != nil {
return err
}
logger.Info(fmt.Sprintf("[rdp ] resp pdu: %+v", resp))
if _, err = conn.Write(resp); err != nil {
logger.Info(fmt.Sprintf("rdp resp pdu: %+v", resp))
if err := server.write(header, resp); err != nil {
return err
}
}
Expand Down
32 changes: 22 additions & 10 deletions protocols/rdp/rdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (
type TKIPHeader struct {
Version byte
Reserved byte
MSLength byte
LSLength byte
Length [2]byte
}

// CRTPDU see http://go.microsoft.com/fwlink/?LinkId=90588 section 13.3
Expand Down Expand Up @@ -45,27 +44,40 @@ type CCTPDU struct {
ClassOption byte
}

type NegotiationResponse struct {
Type byte
Flags byte
Length [2]byte
SelectedProtocol [4]byte
}

type ConnectionConfirmPDU struct {
Header TKIPHeader
TPDU CCTPDU
Header TKIPHeader
TPDU CCTPDU
Response NegotiationResponse
}

func ConnectionConfirm(cr CRTPDU) ([]byte, error) {
func ConnectionConfirm(cr CRTPDU) (TKIPHeader, []byte, error) {
cc := ConnectionConfirmPDU{
Header: TKIPHeader{
Version: 3,
LSLength: 11,
Version: 3,
},
TPDU: CCTPDU{
Length: 6,
CCCDT: 208, // 1101-xxxx
CCCDT: 0xd, // 1101-xxxx
DstRef: cr.DstRef,
SrcRef: cr.SrcRef,
},
Response: NegotiationResponse{
Type: 0x02,
SelectedProtocol: [4]byte{0x3},
},
}
binary.BigEndian.PutUint16(cc.Header.Length[:], 19)
binary.LittleEndian.PutUint16(cc.Response.Length[:], 8)
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, cc)
return buf.Bytes(), err
return cc.Header, buf.Bytes(), err
}

// ParsePDU takes raw data and parses into struct
Expand All @@ -77,7 +89,7 @@ func ParseCRPDU(data []byte) (ConnectionRequestPDU, error) {
}

// I wonder if we should be more lenient here
if len(data) != int(pdu.Header.LSLength) {
if len(data) != int(binary.BigEndian.Uint16(pdu.Header.Length[:])) {
return pdu, nil
}
if err := binary.Read(buffer, binary.LittleEndian, &pdu.TPDU); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion protocols/rdp/rdp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestRDPParseHeader2(t *testing.T) {

func TestConnectionConfirm(t *testing.T) {
cr := CRTPDU{}
cc, err := ConnectionConfirm(cr)
_, cc, err := ConnectionConfirm(cr)
require.NoError(t, err)
fmt.Printf("Parsed data: %+v\n", cc)
}
Expand Down

0 comments on commit f08f0f4

Please sign in to comment.