Compare commits

...
1 Commits
Author SHA1 Message Date
moloch-- 73d14a32e2 WIP on new dns c2 schema 2021-07-24 11:46:17 -05:00
18 changed files with 1218 additions and 566 deletions

No files matched your search

+1
View File
@@ -121,6 +121,7 @@ windows: clean
.PHONY: pb
pb:
protoc -I protobuf/ protobuf/commonpb/common.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/dnspb/dns.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/sliverpb/sliver.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/clientpb/client.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=paths=source_relative:protobuf/ --go-grpc_out=protobuf/ --go-grpc_opt=paths=source_relative
+42
View File
@@ -0,0 +1,42 @@
package encoders
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/base32"
)
// Base32EncoderID - EncoderID
const Base32EncoderID = 65
// Base32 Encoder
type Base32 struct{}
var base32Alphabet = "ab1c2d3e4f5g6h7j8k9m0npqrtuvwxyz"
var sliverBase32 = base32.NewEncoding(base32Alphabet)
// Encode - Base32 Encode
func (e Base32) Encode(data []byte) []byte {
return []byte(sliverBase32.EncodeToString(data))
}
// Decode - Base32 Decode
func (e Base32) Decode(data []byte) ([]byte, error) {
return sliverBase32.DecodeString(string(data))
}
+210
View File
@@ -0,0 +1,210 @@
package encoders
/*
MIT License
Copyright (c) 2019 Shawn Wang <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------------------------------------------------------------
Modifications by moloch to work with Sliver
*/
import (
"math/bits"
"strconv"
)
const (
base = 62
compactMask = 0x1E // 00011110
mask5bits = 0x1F // 00011111
)
// Base62EncoderID - EncoderID
const Base62EncoderID = 43
// Base62 Encoder
type Base62 struct{}
var base62Alphabet = "aBCDEFGHIJKLMNOPQRSTUVWXYZAbcdefghijklmnopqrstuvwxyz0123456789"
// Encode - Base62 Encode
func (e Base62) Encode(data []byte) []byte {
return []byte(NewEncoding(base62Alphabet).EncodeToString(data))
}
// Decode - Base62 Decode
func (e Base62) Decode(data []byte) ([]byte, error) {
return NewEncoding(base62Alphabet).DecodeString(string(data))
}
type Base62Encoder struct {
encode [base]byte
decodeMap [256]byte
}
func NewEncoding(encoder string) *Base62Encoder {
if len(encoder) != base {
panic("encoding alphabet is not 62-bytes long")
}
for i := 0; i < len(encoder); i++ {
if encoder[i] == '\n' || encoder[i] == '\r' {
panic("encoding alphabet contains newline character")
}
}
e := new(Base62Encoder)
copy(e.encode[:], encoder)
for i := 0; i < len(e.decodeMap); i++ {
e.decodeMap[i] = 0xFF
}
for i := 0; i < len(encoder); i++ {
e.decodeMap[encoder[i]] = byte(i)
}
return e
}
func (enc *Base62Encoder) Encode(src []byte) []byte {
if len(src) == 0 {
return []byte{}
}
encoder := newEncoder(src)
return encoder.encode(enc.encode[:])
}
func (enc *Base62Encoder) EncodeToString(src []byte) string {
ret := enc.Encode(src)
return string(ret)
}
type encoder struct {
b []byte
pos int
}
func newEncoder(src []byte) *encoder {
return &encoder{
b: src,
pos: len(src) * 8,
}
}
func (enc *encoder) next() (byte, bool) {
var i, pos int
var j, blen byte
pos = enc.pos - 6
if pos <= 0 {
pos = 0
blen = byte(enc.pos)
} else {
i = pos / 8
j = byte(pos % 8)
blen = byte((i+1)*8 - pos)
if blen > 6 {
blen = 6
}
}
shift := 8 - j - blen
b := enc.b[i] >> shift & (1<<blen - 1)
if blen < 6 && pos > 0 {
blen1 := 6 - blen
b = b<<blen1 | enc.b[i+1]>>(8-blen1)
}
if b&compactMask == compactMask {
if pos > 0 || b > mask5bits {
pos++
}
b &= mask5bits
}
enc.pos = pos
return b, pos > 0
}
func (enc *encoder) encode(encTable []byte) []byte {
ret := make([]byte, 0, len(enc.b)*8/5+1)
x, hasMore := enc.next()
for {
ret = append(ret, encTable[x])
if !hasMore {
break
}
x, hasMore = enc.next()
}
return ret
}
type CorruptInputError int64
func (e CorruptInputError) Error() string {
return "illegal base62 data at input byte " + strconv.FormatInt(int64(e), 10)
}
func (enc *Base62Encoder) Decode(src []byte) ([]byte, error) {
if len(src) == 0 {
return []byte{}, nil
}
dec := decoder(src)
return dec.decode(enc.decodeMap[:])
}
func (enc *Base62Encoder) DecodeString(src string) ([]byte, error) {
return enc.Decode([]byte(src))
}
type decoder []byte
func (dec decoder) decode(decTable []byte) ([]byte, error) {
ret := make([]byte, len(dec)*6/8+1)
idx := len(ret)
pos := byte(0)
b := 0
for i, c := range dec {
x := decTable[c]
if x == 0xFF {
return nil, CorruptInputError(i)
}
if i == len(dec)-1 {
b |= int(x) << pos
pos += byte(bits.Len8(x))
} else if x&compactMask == compactMask {
b |= int(x) << pos
pos += 5
} else {
b |= int(x) << pos
pos += 6
}
if pos >= 8 {
idx--
ret[idx] = byte(b)
pos %= 8
b >>= 8
}
}
if pos > 0 {
idx--
ret[idx] = byte(b)
}
return ret[idx:], nil
}
+2 -2
View File
@@ -1,7 +1,5 @@
package encoders
import "encoding/base64"
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -20,6 +18,8 @@ import "encoding/base64"
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "encoding/base64"
// Base64EncoderID - EncoderID
const Base64EncoderID = 13
+255
View File
@@ -0,0 +1,255 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.3
// source: dnspb/dns.proto
package dnspb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type DNSMessageType int32
const (
DNSMessageType_NOP DNSMessageType = 0
DNSMessageType_DOMAIN_KEY DNSMessageType = 1
DNSMessageType_SESSION_INIT DNSMessageType = 2
DNSMessageType_SESSION_POLL DNSMessageType = 3
DNSMessageType_BLOCK_PREAMBLE DNSMessageType = 7
DNSMessageType_BLOCK_DATA DNSMessageType = 8
DNSMessageType_BLOCK_CLEAR DNSMessageType = 9
)
// Enum value maps for DNSMessageType.
var (
DNSMessageType_name = map[int32]string{
0: "NOP",
1: "DOMAIN_KEY",
2: "SESSION_INIT",
3: "SESSION_POLL",
7: "BLOCK_PREAMBLE",
8: "BLOCK_DATA",
9: "BLOCK_CLEAR",
}
DNSMessageType_value = map[string]int32{
"NOP": 0,
"DOMAIN_KEY": 1,
"SESSION_INIT": 2,
"SESSION_POLL": 3,
"BLOCK_PREAMBLE": 7,
"BLOCK_DATA": 8,
"BLOCK_CLEAR": 9,
}
)
func (x DNSMessageType) Enum() *DNSMessageType {
p := new(DNSMessageType)
*p = x
return p
}
func (x DNSMessageType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DNSMessageType) Descriptor() protoreflect.EnumDescriptor {
return file_dnspb_dns_proto_enumTypes[0].Descriptor()
}
func (DNSMessageType) Type() protoreflect.EnumType {
return &file_dnspb_dns_proto_enumTypes[0]
}
func (x DNSMessageType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DNSMessageType.Descriptor instead.
func (DNSMessageType) EnumDescriptor() ([]byte, []int) {
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
}
type DNSMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DNSMessageType `protobuf:"varint,1,opt,name=Type,proto3,enum=dnspb.DNSMessageType" json:"Type,omitempty"`
ID uint32 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
N uint32 `protobuf:"varint,3,opt,name=N,proto3" json:"N,omitempty"`
Size uint32 `protobuf:"varint,4,opt,name=Size,proto3" json:"Size,omitempty"`
Data []byte `protobuf:"bytes,5,opt,name=Data,proto3" json:"Data,omitempty"`
}
func (x *DNSMessage) Reset() {
*x = DNSMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_dnspb_dns_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DNSMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DNSMessage) ProtoMessage() {}
func (x *DNSMessage) ProtoReflect() protoreflect.Message {
mi := &file_dnspb_dns_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DNSMessage.ProtoReflect.Descriptor instead.
func (*DNSMessage) Descriptor() ([]byte, []int) {
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
}
func (x *DNSMessage) GetType() DNSMessageType {
if x != nil {
return x.Type
}
return DNSMessageType_NOP
}
func (x *DNSMessage) GetID() uint32 {
if x != nil {
return x.ID
}
return 0
}
func (x *DNSMessage) GetN() uint32 {
if x != nil {
return x.N
}
return 0
}
func (x *DNSMessage) GetSize() uint32 {
if x != nil {
return x.Size
}
return 0
}
func (x *DNSMessage) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
var File_dnspb_dns_proto protoreflect.FileDescriptor
var file_dnspb_dns_proto_rawDesc = []byte{
0x0a, 0x0f, 0x64, 0x6e, 0x73, 0x70, 0x62, 0x2f, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x05, 0x64, 0x6e, 0x73, 0x70, 0x62, 0x22, 0x7d, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x64, 0x6e, 0x73, 0x70, 0x62, 0x2e, 0x44, 0x4e, 0x53,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70,
0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49,
0x44, 0x12, 0x0c, 0x0a, 0x01, 0x4e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x01, 0x4e, 0x12,
0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x53,
0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x44, 0x4e, 0x53, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x4f,
0x50, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x4b, 0x45,
0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x49,
0x4e, 0x49, 0x54, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e,
0x5f, 0x50, 0x4f, 0x4c, 0x4c, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x4c, 0x4f, 0x43, 0x4b,
0x5f, 0x50, 0x52, 0x45, 0x41, 0x4d, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x12, 0x0e, 0x0a, 0x0a, 0x42,
0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x42,
0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52, 0x10, 0x09, 0x42, 0x2c, 0x5a, 0x2a,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f,
0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x6e, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_dnspb_dns_proto_rawDescOnce sync.Once
file_dnspb_dns_proto_rawDescData = file_dnspb_dns_proto_rawDesc
)
func file_dnspb_dns_proto_rawDescGZIP() []byte {
file_dnspb_dns_proto_rawDescOnce.Do(func() {
file_dnspb_dns_proto_rawDescData = protoimpl.X.CompressGZIP(file_dnspb_dns_proto_rawDescData)
})
return file_dnspb_dns_proto_rawDescData
}
var file_dnspb_dns_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_dnspb_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_dnspb_dns_proto_goTypes = []interface{}{
(DNSMessageType)(0), // 0: dnspb.DNSMessageType
(*DNSMessage)(nil), // 1: dnspb.DNSMessage
}
var file_dnspb_dns_proto_depIdxs = []int32{
0, // 0: dnspb.DNSMessage.Type:type_name -> dnspb.DNSMessageType
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_dnspb_dns_proto_init() }
func file_dnspb_dns_proto_init() {
if File_dnspb_dns_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_dnspb_dns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DNSMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_dnspb_dns_proto_rawDesc,
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_dnspb_dns_proto_goTypes,
DependencyIndexes: file_dnspb_dns_proto_depIdxs,
EnumInfos: file_dnspb_dns_proto_enumTypes,
MessageInfos: file_dnspb_dns_proto_msgTypes,
}.Build()
File_dnspb_dns_proto = out.File
file_dnspb_dns_proto_rawDesc = nil
file_dnspb_dns_proto_goTypes = nil
file_dnspb_dns_proto_depIdxs = nil
}
+23
View File
@@ -0,0 +1,23 @@
syntax = "proto3";
package dnspb;
option go_package = "github.com/bishopfox/sliver/protobuf/dnspb";
enum DNSMessageType {
NOP = 0; // aka FINGERPRINT
DOMAIN_KEY = 1;
SESSION_INIT = 2;
SESSION_POLL = 3;
BLOCK_PREAMBLE = 7;
BLOCK_DATA = 8;
BLOCK_CLEAR = 9;
}
message DNSMessage {
DNSMessageType Type = 1;
uint32 ID = 2;
uint32 N = 3;
uint32 Size = 4;
bytes Data = 5;
}
+230 -527
View File
@@ -16,58 +16,50 @@ package c2
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
------------------------------------------------------------------------
DNS command and control implementation
---
DNS Tunnel Implementation
*/
import (
secureRand "crypto/rand"
"crypto/sha256"
"crypto/x509"
"math"
"errors"
"hash/crc32"
insecureRand "math/rand"
"net"
"sort"
"github.com/bishopfox/sliver/implant/sliver/encoders"
"github.com/bishopfox/sliver/protobuf/dnspb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/generate"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"encoding/base32"
"encoding/base64"
"encoding/binary"
"encoding/pem"
secureRand "crypto/rand"
"errors"
"fmt"
insecureRand "math/rand"
"strconv"
"strings"
"sync"
"time"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/cryptography"
serverHandlers "github.com/bishopfox/sliver/server/handlers"
"github.com/bishopfox/sliver/server/log"
"github.com/miekg/dns"
"google.golang.org/protobuf/proto"
)
const (
sessionIDSize = 12
domainKeyMsg = "_domainkey"
blockReqMsg = "b"
clearBlockMsg = "cb"
sessionInitMsg = "si"
sessionPollingMsg = "sp"
sessionEnvelopeMsg = "se"
// Max TXT record is 255, records are b64 so (n*8 + 5) / 6 = ~250
byteBlockSize = 185 // Can be as high as n = 187, but we'll leave some slop
blockIDSize = 6
@@ -78,23 +70,59 @@ var (
dnsCharSet = []rune("abcdefghijklmnopqrstuvwxyz0123456789_")
sendBlocksMutex = &sync.RWMutex{}
sendBlocks = &map[string]*SendBlock{}
dnsSessionsMutex = &sync.RWMutex{}
dnsSessions = &map[string]*DNSSession{}
dnsSessions = map[string]*DNSSession{}
blockReassemblerMutex = &sync.RWMutex{}
blockReassembler = &map[string][][]byte{}
sendBlocksMutex = &sync.RWMutex{}
sendBlocks = map[uint32]*Block{}
dnsSegmentReassemblerMutex = &sync.RWMutex{}
dnsSegmentReassembler = &map[string](*map[int][]string){}
recvBlocksMutex = &sync.RWMutex{}
recvBlocks = map[uint32]*Block{}
)
// SendBlock - Data is encoded and split into `Blocks`
type SendBlock struct {
ID string
Data []string
// Block - A blob of data that we're sending or receiving, blocks of data
// are split up into arrays of bytes (chunks) that are encoded per-query
// the amount of data that can be encoded into a single request or response
// varies depending on the type of query and the length of the parent domain.
type Block struct {
ID uint32
data [][]byte
Size int
Started time.Time
Mutex sync.RWMutex
}
func (b *Block) AddData(index int, data []byte) (bool, error) {
b.Mutex.Lock()
defer b.Mutex.Unlock()
if len(b.data) < index+1 {
return false, errors.New("Data index out of bounds")
}
b.data[index] = data
sum := 0
for _, data := range b.data {
sum += len(data)
}
return sum == b.Size, nil
}
func (b *Block) GetData(index int) ([]byte, error) {
b.Mutex.Lock()
defer b.Mutex.Unlock()
if len(b.data) < index+1 {
return nil, errors.New("Data index out of bounds")
}
return b.data[index], nil
}
func (b *Block) Reassemble() []byte {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
data := []byte{}
for _, block := range b.data {
data = append(data, block...)
}
return data
}
// DNSSession - Holds DNS session information
@@ -135,7 +163,10 @@ func StartDNSListener(bindIface string, lport uint16, domains []string, canaries
return server
}
// DNSRequest -> C2 or canary?
// ---------------------------
// DNS Handler
// ---------------------------
// Handles all DNS queries, first we determine if the query is C2 or a canary
func handleDNSRequest(domains []string, canaries bool, writer dns.ResponseWriter, req *dns.Msg) {
if req == nil {
dnsLog.Info("req can not be nil")
@@ -158,7 +189,6 @@ func handleDNSRequest(domains []string, canaries bool, writer dns.ResponseWriter
}
if resp != nil {
// dnsLog.Debug(resp.String())
writer.WriteMsg(resp)
} else {
dnsLog.Infof("Invalid query, no DNS response")
@@ -177,7 +207,9 @@ func isC2SubDomain(domains []string, reqDomain string) (bool, string) {
return false, ""
}
// C2 -> Record type?
// The query is C2, pass to the appropriate record handler this is done
// so the record handler can encode the response based on the type of
// record that was requested
func handleC2(domain string, req *dns.Msg) *dns.Msg {
subdomain := req.Question[0].Name[:len(req.Question[0].Name)-len(domain)]
if strings.HasSuffix(subdomain, ".") {
@@ -187,24 +219,26 @@ func handleC2(domain string, req *dns.Msg) *dns.Msg {
switch req.Question[0].Qtype {
case dns.TypeTXT:
return handleTXT(domain, subdomain, req)
case dns.TypeA:
return handleA(domain, subdomain, req)
default:
}
return nil
}
// ---------------------------
// Canary Record Handler
// ---------------------------
// Canary -> valid? -> trigger alert event
func handleCanary(req *dns.Msg) *dns.Msg {
reqDomain := strings.ToLower(req.Question[0].Name)
if !strings.HasSuffix(reqDomain, ".") {
reqDomain += "." // Ensure we have the FQDN
}
canary, err := db.CanaryByDomain(reqDomain)
if err != nil {
return nil
}
resp := new(dns.Msg)
resp.SetReply(req)
if canary != nil {
@@ -239,179 +273,173 @@ func handleCanary(req *dns.Msg) *dns.Msg {
return resp
}
// handles the c2 TXT record interactions, kind hacky this probably needs to get refactored at some point
func randomIP() net.IP {
randBuf := make([]byte, 4)
secureRand.Read(randBuf)
// Ensure non-zeros with various bitmasks
return net.IPv4(randBuf[0]|0x10, randBuf[1]|0x10, randBuf[2]|0x1, randBuf[3]|0x10)
}
// ---------------------------
// C2 Record Handlers
// ---------------------------
func handleTXT(domain string, subdomain string, req *dns.Msg) *dns.Msg {
q := req.Question[0]
fields := strings.Split(subdomain, ".")
resp := new(dns.Msg)
resp.SetReply(req)
msgType := strings.ToLower(fields[len(fields)-1])
switch msgType {
case domainKeyMsg: // Send PubKey - _(nonce).(slivername).domainkey.example.com
result, err := getDomainKeyFor(domain)
if err != nil {
dnsLog.Infof("Error during session init: %v", err)
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: result,
}
resp.Answer = append(resp.Answer, txt)
case blockReqMsg: // Get block: _(nonce).(start).(stop).(block id).b.example.com
if len(fields) == 5 {
startIndex := fields[1]
stopIndex := fields[2]
blockID := fields[3]
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: dnsSendBlocks(blockID, startIndex, stopIndex),
}
resp.Answer = append(resp.Answer, txt)
} else {
dnsLog.Infof("Block request has invalid number of fields %d expected %d", len(fields), 5)
}
case clearBlockMsg: // Clear block: _(nonce).(block id)._cb.example.com
if len(fields) == 3 {
result := 0
if clearSendBlock(fields[1]) {
result = 1
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: []string{fmt.Sprintf("%d", result)},
}
resp.Answer = append(resp.Answer, txt)
}
case "_" + sessionInitMsg:
fallthrough
case sessionInitMsg: // Session init: (data)...(seq).(nonce).(_)si.example.com
result, err := startDNSSession(domain, fields)
if err != nil {
dnsLog.Infof("Error during session init: %v", err)
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: result,
}
resp.Answer = append(resp.Answer, txt)
case "_" + sessionEnvelopeMsg:
fallthrough
case sessionEnvelopeMsg:
result, err := dnsSessionEnvelope(domain, fields)
if err != nil {
dnsLog.Infof("Error during session init: %v", err)
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: result,
}
resp.Answer = append(resp.Answer, txt)
case sessionPollingMsg:
result, err := dnsSessionPoll(domain, fields)
if err != nil {
dnsLog.Infof("Error during session init: %v", err)
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: result,
}
resp.Answer = append(resp.Answer, txt)
default:
dnsLog.Infof("Unknown msg type '%s' in TXT req", fields[len(fields)-1])
checksum, dnsMsg := parseC2Query(subdomain)
if dnsMsg == nil {
dnsLog.Errorf("Failed to parse TXT query")
return nil
}
// Execute action based on message type
txtRecords := []string{}
switch dnsMsg.Type {
case dnspb.DNSMessageType_DOMAIN_KEY:
blockID := handleDomainKeyQuery(domain, dnsMsg)
data, _ := proto.Marshal(&dnspb.DNSMessage{
N: checksum,
Data: blockID,
})
record := string(new(encoders.Base64).Encode(data))
txtRecords = append(txtRecords, record)
}
txt := &dns.TXT{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
Txt: txtRecords,
}
resp.Answer = append(resp.Answer, txt)
return resp
}
// --------------------------- FIELDS ---------------------------
func handleA(domain string, subdomain string, req *dns.Msg) *dns.Msg {
q := req.Question[0]
resp := new(dns.Msg)
resp.SetReply(req)
func getFieldMsgType(fields []string) (string, error) {
if len(fields) < 1 {
return "", errors.New("Invalid number of fields in session init message (nonce)")
checksum, dnsMsg := parseC2Query(subdomain)
if dnsMsg == nil {
dnsLog.Errorf("Failed to parse A query")
return nil
}
return fields[len(fields)-1], nil
switch dnsMsg.Type {
case dnspb.DNSMessageType_NOP:
break
}
checksumBuf := make([]byte, 4)
binary.LittleEndian.PutUint32(checksumBuf, checksum)
resp.Answer = append(resp.Answer, &dns.A{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0},
A: checksumBuf,
})
return resp
}
func getFieldSessionID(fields []string) (string, error) {
if len(fields) < 2 {
return "", errors.New("Invalid number of fields in session init message (session id)")
// ---------------------------
// C2 Handler
// ---------------------------
// This parses the C2 message and performs actions based on the request
// the response is a CRC32 of the encoded data in the message
func parseC2Query(subdomain string) (uint32, *dnspb.DNSMessage) {
subdomainData := strings.Join(strings.Split(subdomain, "."), "")
dnsLog.Debugf("Subdomain = %v, Data = %v", subdomain, subdomainData)
dnsMsg := decodeMessage(subdomainData)
if dnsMsg == nil {
return 0, nil
}
sessionID := fields[len(fields)-2]
if sessionID == "_" {
return "", errors.New("Session ID is null")
}
return sessionID, nil
checksum := crc32.Checksum(dnsMsg.Data, crc32.IEEETable)
return checksum, dnsMsg
}
func getFieldNonce(fields []string) (string, error) {
if len(fields) < 3 {
return "", errors.New("Invalid number of fields in session init message (nonce)")
func decodeMessage(subdomainData string) *dnspb.DNSMessage {
encoders := detectEncoding(subdomainData)
query := &dnspb.DNSMessage{}
for _, encoder := range encoders {
dnsLog.Debugf("Attempting to decode subdomain data with %v", encoder)
data, err := encoder.Decode([]byte(subdomainData))
if err != nil {
dnsLog.Debugf("Decode attempt failed %s, continue ...", err)
continue
}
err = proto.Unmarshal(data, query)
if err != nil {
dnsLog.Debugf("Failed to parse pb %s, continue ...", err)
continue
}
return query
}
return fields[len(fields)-3], nil
return nil
}
func getFieldSeq(fields []string) (int, error) {
if len(fields) < 4 {
return -1, errors.New("Invalid number of fields in session init message (seq)")
// This function attempts to detect the most likely encoder used by the implant
// we return a list of encoders in order of most likely to least likely
func detectEncoding(subdomainData string) []encoders.Encoder {
// Our version of base32 is missing chars: i, l, o, s
// if any of these appear the message must be base62
base62Chars := strings.ContainsAny(subdomainData, "silo") // "silo" simply appeases my spell checker
if base62Chars {
return []encoders.Encoder{new(encoders.Base62)}
}
rawSeq := fields[len(fields)-4]
data, err := dnsDecodeString(rawSeq)
// Okay now we detect if the message only contains lower case letters
// our base32 encoder only uses lower case but a resolver could have
// messed with the domain letter cases on the way over to us. So we
// say it's likely base62 if it contains a mix but fallback to base32
if strings.ContainsAny(subdomainData, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
return []encoders.Encoder{new(encoders.Base62), new(encoders.Base32)}
} else {
return []encoders.Encoder{new(encoders.Base32), new(encoders.Base64)}
}
}
// --------------------------- DNS HANDLERS ---------------------------
func encodeSendBlock(domain string, data []byte) *Block {
encodedData := new(encoders.Base64).Encode(data)
blockData := [][]byte{}
for index := 0; index < len(encodedData); {
end := index + 254
if len(encodedData) < end {
end = len(encodedData)
}
blockData = append(blockData, encodedData[index:end])
index += 254
}
return &Block{
ID: blockID(),
Size: len(data),
data: blockData,
Started: time.Now(),
Mutex: sync.RWMutex{},
}
}
func handleDomainKeyQuery(domain string, query *dnspb.DNSMessage) []byte {
domainKey, err := getDomainKeyFor(domain)
if err != nil {
dnsLog.Infof("Failed to decode seq field: %#v", rawSeq)
return 0, err
dnsLog.Errorf("Failed to find domain key for '%s' %s", domain, err)
return nil
}
index := int(binary.LittleEndian.Uint32(data))
return index, nil
block := encodeSendBlock(domain, domainKey)
sendBlocksMutex.Lock()
defer sendBlocksMutex.Unlock()
sendBlocks[block.ID] = block
return []byte{}
}
func getFieldSubdata(fields []string) ([]string, error) {
if len(fields) < 5 {
return []string{}, errors.New("Invalid number of fields in session init message (subdata)")
}
subdataFields := len(fields) - 4
dnsLog.Infof("Domain contains %d subdata fields", subdataFields)
return fields[:subdataFields], nil
}
// --------------------------- DNS SESSION START ---------------------------
// Returns an confirmation value (e.g. exit code 0 non-0) and error
func startDNSSession(domain string, fields []string) ([]string, error) {
dnsLog.Infof("[start session] fields = %#v", fields)
func startDNSSession(msgID uint32, domain string) ([]string, error) {
msgType, err := getFieldMsgType(fields)
if err != nil {
return []string{"1"}, err
}
nonce, err := getFieldNonce(fields)
if err != nil {
return []string{"1"}, err
}
if !strings.HasPrefix(msgType, "_") {
return dnsSegment(fields)
}
dnsLog.Infof("Complete session init message received, reassembling ...")
// TODO: We don't have replay protection against the RSA-encrypt
// sessionInit messages, but I don't think it's an issue ...
encryptedSessionInit, err := dnsSegmentReassemble(nonce)
if err != nil {
return []string{"1"}, err
}
publicKeyPEM, privateKeyPEM, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, domain)
if err != nil {
dnsLog.Infof("Failed to fetch RSA private key")
@@ -452,7 +480,7 @@ func startDNSSession(domain string, fields []string) ([]string, error) {
sessionID := dnsSessionID()
dnsLog.Infof("Starting new DNS session with id = %s", sessionID)
dnsSessionsMutex.Lock()
(*dnsSessions)[sessionID] = &DNSSession{
dnsSessions[sessionID] = &DNSSession{
ID: sessionID,
Session: session,
Key: aesKey,
@@ -461,359 +489,16 @@ func startDNSSession(domain string, fields []string) ([]string, error) {
dnsSessionsMutex.Unlock()
encryptedSessionID, _ := cryptography.GCMEncrypt(aesKey, []byte(sessionID))
result, err := dnsSendOnce(encryptedSessionID)
if err != nil {
dnsLog.Infof("Failed to encode message into single result %v", err)
return []string{"1"}, err
}
return result, nil
}
// --------------------------- DNS SESSION RECV ---------------------------
func dnsSessionEnvelope(domain string, fields []string) ([]string, error) {
dnsLog.Infof("[session envelope] fields = %#v", fields)
msgType, err := getFieldMsgType(fields)
if err != nil {
return []string{"1"}, err
}
nonce, err := getFieldNonce(fields)
if err != nil {
return []string{"1"}, err
}
if !strings.HasPrefix(msgType, "_") {
return dnsSegment(fields)
}
dnsLog.Infof("Complete envelope received, reassembling ...")
encryptedDNSEnvelope, err := dnsSegmentReassemble(nonce)
if err != nil {
return []string{"1"}, errors.New("Failed to reassemble segments")
}
sessionID, err := getFieldSessionID(fields)
if err != nil {
return []string{"1"}, err
}
dnsSessionsMutex.Lock()
defer dnsSessionsMutex.Unlock()
if dnsSession, ok := (*dnsSessions)[sessionID]; ok {
dnsLog.Infof("Envelope has valid DNS session (%s)", dnsSession.ID)
if dnsSession.isReplayAttack(encryptedDNSEnvelope) {
dnsLog.Infof("WARNING: Replay attack detected, ignore request")
return []string{"1"}, errors.New("Replay attack")
}
envelopeData, err := cryptography.GCMDecrypt(dnsSession.Key, encryptedDNSEnvelope)
if err != nil {
return []string{"1"}, errors.New("Failed to decrypt DNS envelope")
}
envelope := &sliverpb.Envelope{}
proto.Unmarshal(envelopeData, envelope)
dnsLog.Infof("Envelope Type = %#v RespID = %#v", envelope.Type, envelope.ID)
dnsSession.Session.UpdateCheckin()
// Response Envelope or Handler
handlers := serverHandlers.GetSessionHandlers()
if envelope.ID != 0 {
dnsSession.Session.RespMutex.Lock()
defer dnsSession.Session.RespMutex.Unlock()
if resp, ok := dnsSession.Session.Resp[envelope.ID]; ok {
resp <- envelope
}
} else if handler, ok := handlers[envelope.Type]; ok {
handler.(func(*core.Session, []byte))(dnsSession.Session, envelope.Data)
}
return []string{"0"}, nil
}
dnsLog.Infof("Invalid session id '%#v'", sessionID)
return []string{"1"}, errors.New("Invalid session ID")
}
// Client should have sent all of the data, attempt to reassemble segments
func dnsSegmentReassemble(nonce string) ([]byte, error) {
dnsSegmentReassemblerMutex.Lock()
defer dnsSegmentReassemblerMutex.Unlock()
if reasm, ok := (*dnsSegmentReassembler)[nonce]; ok {
var keys []int
for k := range *reasm {
keys = append(keys, k)
}
sort.Ints(keys)
orderedSubdata := []string{}
for _, k := range keys {
orderedSubdata = append(orderedSubdata, (*reasm)[k]...)
}
data, err := dnsDecodeString(strings.Join(orderedSubdata, ""))
if err != nil {
dnsLog.Infof("Failed to decode session init: %v", err)
return nil, err
}
//BUG - in the event that the session init msg does not make it to the client, this will fail. This has been
//observed consistently (Namely due to the race condition bug that is also being fixed in dnsSessionPoll).
//Currently a memory leak. Consider tracking moving the cleanup code to after polling has
//begun to ensure the initial handshake was successful.
//
//delete((*dnsSegmentReassembler), nonce)
return data, nil
}
return nil, fmt.Errorf("Invalid nonce '%#v' (session init reassembler)", nonce)
}
// The domain is only a segment of the startDNSSession message, so we just store the data
func dnsSegment(fields []string) ([]string, error) {
dnsSegmentReassemblerMutex.Lock()
defer dnsSegmentReassemblerMutex.Unlock()
nonce, _ := getFieldNonce(fields)
index, err := getFieldSeq(fields)
if err != nil {
return []string{"1"}, err
}
subdata, err := getFieldSubdata(fields)
if err != nil {
return []string{"1"}, err
}
if _, ok := (*dnsSegmentReassembler)[nonce]; !ok {
(*dnsSegmentReassembler)[nonce] = &map[int][]string{}
}
if reasm, ok := (*dnsSegmentReassembler)[nonce]; ok {
(*reasm)[index] = subdata
return []string{"0"}, nil
}
dnsLog.Infof("Invalid nonce (session segment): %#v", nonce)
return []string{"1"}, errors.New("Invalid nonce (session segment)")
}
// TODO: Avoid double-fetch
func getDomainKeyFor(domain string) ([]string, error) {
_, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, domain)
if err != nil {
certs.C2ServerGenerateRSACertificate(domain)
}
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, domain)
if err != nil {
return nil, err
}
return dnsSendOnce(certPEM)
}
// --------------------------- DNS SESSION SEND ---------------------------
// Send all response data in a single TXT record, limited to 65535 bytes
func dnsSendOnce(rawData []byte) ([]string, error) {
if 65535 <= base64.RawStdEncoding.EncodedLen(len(rawData)) {
return nil, errors.New("Response too large to encode into one TXT record")
}
data := base64.RawStdEncoding.EncodeToString(rawData)
dnsLog.Infof("Encoding single resp: %#v", data)
txts := []string{}
size := int(math.Ceil(float64(len(data)) / 255.0))
for index := 0; index < size; index++ {
start := index * 255
stop := start + 255
if len(data) < stop {
stop = len(data)
}
txts = append(txts, data[start:stop])
}
return txts, nil
}
func dnsSessionPoll(domain string, fields []string) ([]string, error) {
sessionID, err := getFieldSessionID(fields)
if err != nil {
return []string{"1"}, errors.New("invalid session id (session poll)")
}
dnsSessionsMutex.Lock()
dnsSession, found := (*dnsSessions)[sessionID]
if !found {
dnsSessionsMutex.Unlock()
b64SessionId := base64.RawStdEncoding.EncodeToString([]byte(sessionID))
return []string{b64SessionId}, errors.New("invalid session (nil pointer)")
}
dnsSessionsMutex.Unlock()
isDrained := false
envelopes := []*sliverpb.Envelope{}
for !isDrained {
select {
case envelope := <-dnsSession.Session.Send:
dnsLog.Infof("New message from send channel ...")
envelopes = append(envelopes, envelope)
default:
isDrained = true
}
}
dnsSession.Session.UpdateCheckin()
if 0 < len(envelopes) {
dnsLog.Infof("%d new message(s) for session id %#v", len(envelopes), sessionID)
dnsPoll := &sliverpb.DNSPoll{}
for _, envelope := range envelopes {
data, err := proto.Marshal(envelope)
if err != nil {
dnsLog.Infof("Failed to encode envelope %v", err)
continue
}
encryptedEnvelopeData, err := cryptography.GCMEncrypt(dnsSession.Key, data)
if err != nil {
dnsLog.Infof("Failed to encrypt poll data %v", err)
return []string{"1"}, errors.New("Failed to encrypt dns poll data")
}
blockID, size := storeSendBlocks(encryptedEnvelopeData)
dnsPoll.Blocks = append(dnsPoll.Blocks, &sliverpb.DNSBlockHeader{
ID: blockID,
Size: uint32(size),
})
}
pollData, err := proto.Marshal(dnsPoll)
if err != nil {
dnsLog.Infof("Failed to encode envelope %v", err)
return []string{"1"}, errors.New("Failed to encode dns poll data")
}
encryptedPollData, err := cryptography.GCMEncrypt(dnsSession.Key, pollData)
if err != nil {
dnsLog.Infof("Failed to encrypt poll data %v", err)
return []string{"1"}, errors.New("Failed to encrypt dns poll data")
}
return dnsSendOnce(encryptedPollData)
}
dnsLog.Infof("No new message for session id %#v", sessionID)
return []string{"0"}, nil
}
// Send blocks of data via multiple DNS TXT responses
func dnsSendBlocks(blockID string, startIndex string, stopIndex string) []string {
start, err := strconv.Atoi(startIndex)
if err != nil {
return []string{}
}
stop, err := strconv.Atoi(stopIndex)
if err != nil {
return []string{}
}
if stop < start {
return []string{}
}
dnsLog.Infof("Send blocks %d to %d for ID %s", start, stop, blockID)
sendBlocksMutex.Lock()
defer sendBlocksMutex.Unlock()
respBlocks := []string{}
if block, ok := (*sendBlocks)[blockID]; ok {
for index := start; index < stop; index++ {
if index < len(block.Data) {
respBlocks = append(respBlocks, block.Data[index])
}
}
dnsLog.Infof("Sending %d response block(s)", len(respBlocks))
return respBlocks
}
dnsLog.Infof("Invalid block ID: %#v", blockID)
return []string{}
}
// Clear send blocks of data from memory
func clearSendBlock(blockID string) bool {
sendBlocksMutex.Lock()
defer sendBlocksMutex.Unlock()
if _, ok := (*sendBlocks)[blockID]; ok {
delete(*sendBlocks, blockID)
return true
}
return false
}
// Stores encoded blocks fo data into "sendBlocks"
func storeSendBlocks(data []byte) (string, int) {
blockID := generateBlockID()
sendBlock := &SendBlock{
ID: blockID,
Data: []string{},
}
for index := 0; index < len(data); index += byteBlockSize {
start := index
stop := index + byteBlockSize
if len(data) < stop {
stop = len(data)
}
encoded := base64.RawStdEncoding.EncodeToString(data[start:stop])
dnsLog.Infof("Encoded block is %d bytes", len(encoded))
sendBlock.Data = append(sendBlock.Data, encoded)
}
sendBlocksMutex.Lock()
(*sendBlocks)[sendBlock.ID] = sendBlock
sendBlocksMutex.Unlock()
return sendBlock.ID, len(sendBlock.Data)
}
// --------------------------- HELPERS ---------------------------
// Unique IDs, no need for secure random
func generateBlockID() string {
insecureRand.Seed(time.Now().UnixNano())
blockID := []rune{}
for i := 0; i < blockIDSize; i++ {
index := insecureRand.Intn(len(dnsCharSet))
blockID = append(blockID, dnsCharSet[index])
}
return string(blockID)
}
func fingerprintSHA256(block *pem.Block) string {
hash := sha256.Sum256(block.Bytes)
b64hash := base64.RawStdEncoding.EncodeToString(hash[:])
return strings.TrimRight(b64hash, "=")
}
// TODO: Add a geofilter to make it look like we're in various regions of the world.
// We don't really need to use srand but it's just easier to operate on bytes here.
func randomIP() net.IP {
randBuf := make([]byte, 4)
secureRand.Read(randBuf)
// Ensure non-zeros with various bitmasks
return net.IPv4(randBuf[0]|0x10, randBuf[1]|0x10, randBuf[2]|0x1, randBuf[3]|0x10)
}
// --------------------------- ENCODER ---------------------------
var base32Alphabet = "ab1c2d3e4f5g6h7j8k9m0npqrtuvwxyz"
var sliverBase32 = base32.NewEncoding(base32Alphabet)
// EncodeToString encodes the given byte slice in base32
func dnsEncodeToString(input []byte) string {
return strings.TrimRight(sliverBase32.EncodeToString(input), "=")
}
// DecodeString decodes the given base32 encoded bytes
func dnsDecodeString(raw string) ([]byte, error) {
pad := 8 - (len(raw) % 8)
padded := []byte(raw)
if pad != 8 {
padded = make([]byte, len(raw)+pad)
copy(padded, raw)
for index := 0; index < pad; index++ {
padded[len(raw)+index] = '='
}
}
// dnsLog.Infof("[base32] %#v", string(padded))
return sliverBase32.DecodeString(string(padded))
}
// SessionIDs are public parameters in this use case
// so it's only important that they're unique
func dnsSessionID() string {
@@ -823,5 +508,23 @@ func dnsSessionID() string {
index := insecureRand.Intn(len(dnsCharSet))
sessionID = append(sessionID, dnsCharSet[index])
}
return "_" + string(sessionID)
return string(sessionID)
}
func blockID() uint32 {
randBuf := make([]byte, 4)
secureRand.Read(randBuf)
return binary.LittleEndian.Uint32(randBuf)
}
func getDomainKeyFor(domain string) ([]byte, error) {
_, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, domain)
if err != nil {
certs.C2ServerGenerateRSACertificate(domain)
}
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, domain)
if err != nil {
return nil, err
}
return certPEM, nil
}
+4
View File
@@ -27,6 +27,8 @@ var (
srcFiles = []string{
"constants/constants.go",
"encoders/base32.go",
"encoders/base62.go",
"encoders/base64.go",
"encoders/combos.go",
"encoders/encoders.go",
@@ -165,6 +167,8 @@ var (
genericSrcFiles = []string{
"constants/constants.go",
"encoders/base32.go",
"encoders/base62.go",
"encoders/base64.go",
"encoders/combos.go",
"encoders/encoders.go",
+44
View File
@@ -0,0 +1,44 @@
package encoders
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/base32"
)
// Base32EncoderID - EncoderID
const Base32EncoderID = 65
// Base32 Encoder
type Base32 struct{}
// Missing chars: i, l, o, s
const base32Alphabet = "ab1c2d3e4f5g6h7j8k9m0npqrtuvwxyz"
var sliverBase32 = base32.NewEncoding(base32Alphabet)
// Encode - Base32 Encode
func (e Base32) Encode(data []byte) []byte {
return []byte(sliverBase32.EncodeToString(data))
}
// Decode - Base32 Decode
func (e Base32) Decode(data []byte) ([]byte, error) {
return sliverBase32.DecodeString(string(data))
}
+80
View File
@@ -0,0 +1,80 @@
package encoders
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestBase32(t *testing.T) {
sample := randomData()
b32 := new(Base32)
output := b32.Encode(sample)
data, err := b32.Decode(output)
if err != nil {
t.Errorf("b32 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
implantBase32 := new(implantEncoders.Base32)
output2 := implantBase32.Encode(sample)
data2, err := implantBase32.Decode(output2)
if err != nil {
t.Errorf("implant b32 decode returned an error %v", err)
}
if !bytes.Equal(sample, data2) {
t.Logf("sample = %#v", sample)
t.Logf("output2 = %#v", output2)
t.Logf(" data2 = %#v", data2)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
output = b32.Encode(sample)
data, err = implantBase32.Decode(output)
if err != nil {
t.Errorf("b32 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
output = implantBase32.Encode(sample)
data, err = b32.Decode(output)
if err != nil {
t.Errorf("b32 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
}
+210
View File
@@ -0,0 +1,210 @@
package encoders
/*
MIT License
Copyright (c) 2019 Shawn Wang <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------------------------------------------------------------
Modifications by moloch to work with Sliver
*/
import (
"math/bits"
"strconv"
)
const (
base = 62
compactMask = 0x1E // 00011110
mask5bits = 0x1F // 00011111
)
// Base62EncoderID - EncoderID
const Base62EncoderID = 43
// Base62 Encoder
type Base62 struct{}
const base62Alphabet = "aBCDEFGHIJKLMNOPQRSTUVWXYZAbcdefghijklmnopqrstuvwxyz0123456789"
// Encode - Base62 Encode
func (e Base62) Encode(data []byte) []byte {
return []byte(NewEncoding(base62Alphabet).EncodeToString(data))
}
// Decode - Base62 Decode
func (e Base62) Decode(data []byte) ([]byte, error) {
return NewEncoding(base62Alphabet).DecodeString(string(data))
}
type Base62Encoder struct {
encode [base]byte
decodeMap [256]byte
}
func NewEncoding(encoder string) *Base62Encoder {
if len(encoder) != base {
panic("encoding alphabet is not 62-bytes long")
}
for i := 0; i < len(encoder); i++ {
if encoder[i] == '\n' || encoder[i] == '\r' {
panic("encoding alphabet contains newline character")
}
}
e := new(Base62Encoder)
copy(e.encode[:], encoder)
for i := 0; i < len(e.decodeMap); i++ {
e.decodeMap[i] = 0xFF
}
for i := 0; i < len(encoder); i++ {
e.decodeMap[encoder[i]] = byte(i)
}
return e
}
func (enc *Base62Encoder) Encode(src []byte) []byte {
if len(src) == 0 {
return []byte{}
}
encoder := newEncoder(src)
return encoder.encode(enc.encode[:])
}
func (enc *Base62Encoder) EncodeToString(src []byte) string {
ret := enc.Encode(src)
return string(ret)
}
type encoder struct {
b []byte
pos int
}
func newEncoder(src []byte) *encoder {
return &encoder{
b: src,
pos: len(src) * 8,
}
}
func (enc *encoder) next() (byte, bool) {
var i, pos int
var j, blen byte
pos = enc.pos - 6
if pos <= 0 {
pos = 0
blen = byte(enc.pos)
} else {
i = pos / 8
j = byte(pos % 8)
blen = byte((i+1)*8 - pos)
if blen > 6 {
blen = 6
}
}
shift := 8 - j - blen
b := enc.b[i] >> shift & (1<<blen - 1)
if blen < 6 && pos > 0 {
blen1 := 6 - blen
b = b<<blen1 | enc.b[i+1]>>(8-blen1)
}
if b&compactMask == compactMask {
if pos > 0 || b > mask5bits {
pos++
}
b &= mask5bits
}
enc.pos = pos
return b, pos > 0
}
func (enc *encoder) encode(encTable []byte) []byte {
ret := make([]byte, 0, len(enc.b)*8/5+1)
x, hasMore := enc.next()
for {
ret = append(ret, encTable[x])
if !hasMore {
break
}
x, hasMore = enc.next()
}
return ret
}
type CorruptInputError int64
func (e CorruptInputError) Error() string {
return "illegal base62 data at input byte " + strconv.FormatInt(int64(e), 10)
}
func (enc *Base62Encoder) Decode(src []byte) ([]byte, error) {
if len(src) == 0 {
return []byte{}, nil
}
dec := decoder(src)
return dec.decode(enc.decodeMap[:])
}
func (enc *Base62Encoder) DecodeString(src string) ([]byte, error) {
return enc.Decode([]byte(src))
}
type decoder []byte
func (dec decoder) decode(decTable []byte) ([]byte, error) {
ret := make([]byte, len(dec)*6/8+1)
idx := len(ret)
pos := byte(0)
b := 0
for i, c := range dec {
x := decTable[c]
if x == 0xFF {
return nil, CorruptInputError(i)
}
if i == len(dec)-1 {
b |= int(x) << pos
pos += byte(bits.Len8(x))
} else if x&compactMask == compactMask {
b |= int(x) << pos
pos += 5
} else {
b |= int(x) << pos
pos += 6
}
if pos >= 8 {
idx--
ret[idx] = byte(b)
pos %= 8
b >>= 8
}
}
if pos > 0 {
idx--
ret[idx] = byte(b)
}
return ret[idx:], nil
}
+80
View File
@@ -0,0 +1,80 @@
package encoders
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestBase62(t *testing.T) {
sample := randomData()
b62 := new(Base62)
output := b62.Encode(sample)
data, err := b62.Decode(output)
if err != nil {
t.Errorf("b62 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
implantBase62 := new(implantEncoders.Base62)
output2 := implantBase62.Encode(sample)
data2, err := implantBase62.Decode(output2)
if err != nil {
t.Errorf("implant b62 decode returned an error %v", err)
}
if !bytes.Equal(sample, data2) {
t.Logf("sample = %#v", sample)
t.Logf("output2 = %#v", output2)
t.Logf(" data2 = %#v", data2)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
output = b62.Encode(sample)
data, err = implantBase62.Decode(output)
if err != nil {
t.Errorf("b62 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
output = implantBase62.Encode(sample)
data, err = b62.Decode(output)
if err != nil {
t.Errorf("b62 decode returned an error %v", err)
}
if !bytes.Equal(sample, data) {
t.Logf("sample = %#v", sample)
t.Logf("output = %#v", output)
t.Logf(" data = %#v", data)
t.Errorf("sample does not match returned\n%#v != %#v", sample, data)
}
}
+2 -2
View File
@@ -1,7 +1,5 @@
package encoders
import "encoding/base64"
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -20,6 +18,8 @@ import "encoding/base64"
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "encoding/base64"
// Base64EncoderID - EncoderID
const Base64EncoderID = 13
+7 -7
View File
@@ -1,12 +1,5 @@
package encoders
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -25,6 +18,13 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestBase64(t *testing.T) {
sample := randomData()
+7 -7
View File
@@ -1,12 +1,5 @@
package encoders
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -25,6 +18,13 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestGzipEnglish(t *testing.T) {
sample := randomData()
+7 -7
View File
@@ -1,12 +1,5 @@
package encoders
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -25,6 +18,13 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestEnglish(t *testing.T) {
sample := randomData()
+7 -7
View File
@@ -1,12 +1,5 @@
package encoders
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -25,6 +18,13 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestGzip(t *testing.T) {
sample := randomData()
+7 -7
View File
@@ -1,12 +1,5 @@
package encoders
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -25,6 +18,13 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"testing"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
)
func TestHex(t *testing.T) {
sample := randomData()