Exploring Sliver C2 - Part 1 : C2 over mTLS
Published: 2023-04-08 05:15:10

## What is `mTLS` ?

`mTLS`stands for mutual TLS.

In normal TLS, a target server/website is required to provide to visitors browsers its certificate. This certificate is generally issued by a trusted third-party (a Certificate Authority).

Client browsers are shipped by default with a list of public keys from known trusted Certificate Authorities. The client browser can use them to verify if a certificate presented to it by a server/website was issued/signed by one of the Certificate Authorities it knows (trusts).

mTLS enforces that the client too presents a similar certificate to complete a hand-shake, there-by enabling the server to be sure that the client is also whom it pretends to be.


![mTLS flow cloudflare](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7trx0zx33wn04utfstj.png)[^1]


## mTLS Implant side

- The Implant is the component which is deployed on target/victim endpoint for control.
- Implant specific code within sliver is located in `./sliver/implant` directory.


![location of implant code for sliver](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xvt1g91dcjgkwq5t0tr.png)


- Implant code related to transport mechanisms, including mTLS is located in `./sliver/implant/sliver/transports`


![location of transport mechanism code for implant](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq0b2mpgfmz0ycf08uvd.png)


## Poking around `mtls.go`

- The file has a quiet curious template-like feature which allows for conditionally importing the `log` package.
- Based on wether we are or not in Debug mode as expressed by some magic `.Config` variable, the `log` package gets imported.
- I guess this could be for Opsec reasons, as no one would like production implants to have debug messages enabled on blue team monitored endpoints.

```go
	// {{if .Config.Debug}}
	"log"
	// {{end}}
```

- Other interesting observations where the fact that some variables within the scope of this code get provisioned by this magically injected `{{.Config}}` object.
- These variables are apparently:
- the **Certificate Authority's public key** (expected to be the key of the trusted CA between the implant and server)
- the private key of the implant itself (**keyPEM**)
- the certificate of the implant **certPEM** (Implant's public key signed by the CA, with some metadata)

```go
var (
	// PingInterval - Amount of time between in-band "pings"
	PingInterval = 2 * time.Minute

	// caCertPEM - PEM encoded CA certificate
	caCertPEM = `{{.Config.MtlsCACert}}`

	keyPEM  = `{{.Config.MtlsKey}}`
	certPEM = `{{.Config.MtlsCert}}`
)
```

There are a few functions too, whose intent i will try to derive based on the comments and code logic.

- **`WriteEnvelope`**:
    - This appears to be some kind of write primitive the implant can use to pass down messages of its protocol through an mTLS socket.
    - It depends on a protobuf defined structure call **pb.Envelope**
    - Interestingly enough, it sends bytes over the socket in `LittleEndian` (host order).
    - `LittleEndian` is generally the byte order used by CPUs to read process memory and instructions.
    - In the network, Big Endian (also called network order) is often more prevalent for byte transmission. [^2]

```go
// WriteEnvelope - Writes a message to the TLS socket using length prefix framing
// which is a fancy way of saying we write the length of the message then the message
// e.g. [uint32 length|message] so the receiver can delimit messages properly
func WriteEnvelope(connection *tls.Conn, envelope *pb.Envelope) error {
	data, err := proto.Marshal(envelope)
	if err != nil {
		// {{if .Config.Debug}}
		log.Print("Envelope marshaling error: ", err)
		// {{end}}
		return err
	}
	dataLengthBuf := new(bytes.Buffer)
	binary.Write(dataLengthBuf, binary.LittleEndian, uint32(len(data)))
	connection.Write(dataLengthBuf.Bytes())
	connection.Write(data)
	return nil
}
```

- **`WritePing`**:
    - kind of keep alive method to constantly feed something over the socket.
    - just to notify the server that the implant is still there.
    - it uses the **WriteEnvelope** primitive to pass down these pings over the socket.
    - The ping consists of sending the message `31337` (elite in leet speak), i guess every `PingInterval` units of time.


```go
// WritePing - Send a "ping" message to the server
func WritePing(connection *tls.Conn) error {
	// {{if .Config.Debug}}
	log.Print("Socket ping")
	// {{end}}

	// We don't need a real nonce here, we just need to write to the socket
	pingBuf, _ := proto.Marshal(&pb.Ping{Nonce: 31337})
	envelope := pb.Envelope{
		Type: pb.MsgPing,
		Data: pingBuf,
	}
	return WriteEnvelope(connection, &envelope)
}
```

- **`ReadEnvelope`**:
    - The function name and comments are enough to convince me not to read the whole  of it, and infer a little its goal :D.
    - Reads data from the TLS connection.

```go
	// Unmarshal the protobuf envelope
	envelope := &pb.Envelope{}
	err = proto.Unmarshal(dataBuf, envelope)
	if err != nil {
		// {{if .Config.Debug}}
		log.Printf("Unmarshal envelope error: %v", err)
		// {{end}}
		return nil, err
	}

	return envelope, nil
```

- **`MtlsConnect`**:
    - establish a TLS connection with sliver server given the server ip and port.
    - depends on **`getTLSConfig`** to load CA certificate.

At this stage the dependency graph i made for myself is the following.


![mind map of function dependencies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8qi98h62mz7345pq41t.png)

It becomes quiet evident that the big players here are the :
- **`Envelope`** structure
- The magical **`Config`** variable.


## The `Envelope` proto definition

```go
// Envelope - Used to encode implant<->server messages since we 
//            cannot use gRPC due to the various transports used.
message Envelope {
  int64 ID = 1;   // Envelope ID used to track request/response
  uint32 Type = 2; // Message type
  bytes Data = 3;  // Actual message data

  bool UnknownMessageType = 4; // Set if the implant did not understand the message
}
```
- from the description of this structure, it is used to encode messages between implant and server
- Evidence of this could be traced back to:
    - the **`WriteEnvelope`** function; `pb.Envelope` is marshaled into raw bytes before transmission the TLS connection.
    - the **`ReadEnvelope`** function; raw bytes are read from a TLS connection, and unmarshaled into a `pb.Envelope` structure which Go can understand.
- Moving protocol specific types/structures into proto files gives in my opinion some flexibility in modifying the protocol, because the proto files will act as the single source of truth for the protocol's definition.

## Conclusion

- I'm tempted at this stage to think that a solid detection technique  for sliver could rely on identifying these message structures in-memory.
- We could hypothetically imagine some sort of memory scanner, which will scan process memory space on the monitored system, and attempt to recognise sections of memory whose layout/structure matches the alignment of the protobuf messages defined in sliver's implant protocol.
- Doubts will get settled anyway by better understanding the protocol, and also how Go structures look like in memory.


## Reference
[^1]: https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/
[^2]: https://beej.us/guide/bgnet/html/split/ip-addresses-structs-and-data-munging.html#byte-order