diff --git a/protobuf/clientpb/client.pb.go b/protobuf/clientpb/client.pb.go index abb1c59ca9..6603612f5b 100644 --- a/protobuf/clientpb/client.pb.go +++ b/protobuf/clientpb/client.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.26.0 // protoc v3.21.12 // source: clientpb/client.proto @@ -4172,8 +4172,7 @@ func (x *MsfStager) GetFile() *commonpb.File { } // GetSystemReq - Client request to the server which is translated into -// -// InvokeSystemReq when sending to the implant. +// InvokeSystemReq when sending to the implant. type GetSystemReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4238,8 +4237,7 @@ func (x *GetSystemReq) GetRequest() *commonpb.Request { } // MigrateReq - Client request to the server which is translated into -// -// InvokeMigrateReq when sending to the implant. +// InvokeMigrateReq when sending to the implant. type MigrateReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/protobuf/commonpb/common.pb.go b/protobuf/commonpb/common.pb.go index 9e6ce0c34b..e069b44a2d 100644 --- a/protobuf/commonpb/common.pb.go +++ b/protobuf/commonpb/common.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.26.0 // protoc v3.21.12 // source: commonpb/common.proto @@ -131,9 +131,8 @@ func (x *Request) GetSessionID() string { } // Response - Common fields used in all gRPC responses. Note that the Err field -// -// only used when the implant needs to return an error to the server. -// Client<->Server comms should use normal gRPC error handling. +// only used when the implant needs to return an error to the server. +// Client<->Server comms should use normal gRPC error handling. type Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/protobuf/dnspb/dns.pb.go b/protobuf/dnspb/dns.pb.go index cac0f3edde..d55d806eec 100644 --- a/protobuf/dnspb/dns.pb.go +++ b/protobuf/dnspb/dns.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.26.0 // protoc v3.21.12 // source: dnspb/dns.proto @@ -87,10 +87,12 @@ func (DNSMessageType) EnumDescriptor() ([]byte, []int) { return file_dnspb_dns_proto_rawDescGZIP(), []int{0} } -// NOTE: DNS is very space sensitive so certain fields are re-purposed -// depending on the DNSMessageType as noted below: // -// [Type TOTP]: ID field is used for the TOTP code +//NOTE: DNS is very space sensitive so certain fields are re-purposed +//depending on the DNSMessageType as noted below: +// +//[Type TOTP]: ID field is used for the TOTP code +// type DNSMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/protobuf/rpcpb/services.pb.go b/protobuf/rpcpb/services.pb.go index b17e5ea5e0..a572a568fa 100644 --- a/protobuf/rpcpb/services.pb.go +++ b/protobuf/rpcpb/services.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.26.0 // protoc v3.21.12 // source: rpcpb/services.proto diff --git a/protobuf/rpcpb/services_grpc.pb.go b/protobuf/rpcpb/services_grpc.pb.go index 1012f61793..589afd303e 100644 --- a/protobuf/rpcpb/services_grpc.pb.go +++ b/protobuf/rpcpb/services_grpc.pb.go @@ -1,8 +1,4 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.21.12 -// source: rpcpb/services.proto package rpcpb diff --git a/protobuf/sliverpb/sliver.pb.go b/protobuf/sliverpb/sliver.pb.go index 9908c442d3..f4ef72ec5b 100644 --- a/protobuf/sliverpb/sliver.pb.go +++ b/protobuf/sliverpb/sliver.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.26.0 // protoc v3.21.12 // source: sliverpb/sliver.proto @@ -173,8 +173,7 @@ func (PeerFailureType) EnumDescriptor() ([]byte, []int) { } // Envelope - Used to encode implant<->server messages since we -// -// cannot use gRPC due to the various transports used. +// cannot use gRPC due to the various transports used. type Envelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -747,8 +746,7 @@ func (x *CloseSession) GetRequest() *commonpb.Request { } // Ping - Not ICMP, just sends a rount trip message to an implant to -// -// see if it's still responding. +// see if it's still responding. type Ping struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2936,8 +2934,7 @@ func (x *CurrentTokenOwner) GetResponse() *commonpb.Response { } // InvokeGetSystemReq - Implant-side version of GetSystemReq, this message -// -// contains the .Data based on the client's req.Config +// contains the .Data based on the client's req.Config type InvokeGetSystemReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/server/assets/assets.go b/server/assets/assets.go index 0508a0e00a..596c10a313 100644 --- a/server/assets/assets.go +++ b/server/assets/assets.go @@ -21,8 +21,13 @@ package assets import ( "archive/zip" "bytes" + "encoding/hex" "errors" "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" "io" insecureRand "math/rand" "os" @@ -256,6 +261,7 @@ func SetupGoPath(goPathSrc string) error { setupLog.Info("Static asset not found: constants.go") return err } + sliverpbGoSrc = xorPBRawBytes(sliverpbGoSrc) sliverpbGoSrc = stripSliverpb(sliverpbGoSrc) sliverpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "sliverpb") os.MkdirAll(sliverpbDir, 0700) @@ -268,6 +274,7 @@ func SetupGoPath(goPathSrc string) error { setupLog.Info("Static asset not found: common.pb.go") return err } + commonpbSrc = xorPBRawBytes(commonpbSrc) commonpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "commonpb") os.MkdirAll(commonpbDir, 0700) os.WriteFile(filepath.Join(commonpbDir, "common.pb.go"), commonpbSrc, 0600) @@ -278,6 +285,7 @@ func SetupGoPath(goPathSrc string) error { setupLog.Info("Static asset not found: dns.pb.go") return err } + dnspbSrc = xorPBRawBytes(dnspbSrc) dnspbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "dnspb") os.MkdirAll(dnspbDir, 0700) os.WriteFile(filepath.Join(dnspbDir, "dns.pb.go"), dnspbSrc, 0600) @@ -306,6 +314,208 @@ func stripSliverpb(src []byte) []byte { return out } +func xorPBRawBytes(src []byte) []byte { + var ( + fileAst *ast.File + err error + sliverpbVarName = "file_sliverpb_sliver_proto_rawDesc" + sliverPbProtoInitFuncName = "file_sliverpb_sliver_proto_init" + fset = token.NewFileSet() + parserMode = parser.ParseComments + ) + fileAst, err = parser.ParseFile(fset, "", src, parserMode) + if err != nil { + // Panic because this is mandatory for the agent to work + panic(err) + } + var xorKey [8]byte + // generate random xor key + if _, err := insecureRand.Read(xorKey[:]); err != nil { + // Panic because this is mandatory for the agent to work + panic(err) + } + + ast.Inspect(fileAst, func(n ast.Node) bool { + switch node := n.(type) { + // Look for the protobuf init function and decode the XORed + // data at the top of the function + case *ast.FuncDecl: + // add an ast.AssignStmt at the top of the function Body + // that calls the xor function on the file_sliverpb_sliver_proto_rawDesc object + if node.Name.Name == sliverPbProtoInitFuncName { + newStmt := &ast.AssignStmt{ + Lhs: []ast.Expr{ + ast.NewIdent(sliverpbVarName), + }, + Tok: token.ASSIGN, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: ast.NewIdent("xor"), + Args: []ast.Expr{ + ast.NewIdent(sliverpbVarName), + ast.NewIdent("xorKey"), + }, + }, + }, + } + node.Body.List = append([]ast.Stmt{newStmt}, node.Body.List...) + } + + // Look for the protobuf rawDesc variable and XOR each byte + case *ast.GenDecl: + for _, spec := range node.Specs { + switch spec := spec.(type) { + case *ast.ValueSpec: + for _, id := range spec.Names { + if id.Name == sliverpbVarName { + values := spec.Values[0].(*ast.CompositeLit).Elts + // XOR each value of the slice + for i, v := range values { + elt := v.(*ast.BasicLit) + elt.Value = xorByte(elt.Value, xorKey[i%len(xorKey)]) + } + + } + } + default: + } + } + default: + } + return true + }) + + // Add the XOR function to the AST + fileAst.Decls = append(fileAst.Decls, ast.Decl(&ast.FuncDecl{ + Name: ast.NewIdent("xor"), + Type: &ast.FuncType{ + Params: &ast.FieldList{ + List: []*ast.Field{ + { + Names: []*ast.Ident{ast.NewIdent("input")}, + Type: ast.NewIdent("[]byte"), + }, + { + Names: []*ast.Ident{ast.NewIdent("key")}, + Type: ast.NewIdent("[]byte"), + }, + }, + }, + Results: &ast.FieldList{ + List: []*ast.Field{ + { + Type: ast.NewIdent("[]byte"), + }, + }, + }, + }, + Body: &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.AssignStmt{ + Lhs: []ast.Expr{ + ast.NewIdent("out"), + }, + Tok: token.DEFINE, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: ast.NewIdent("make"), + Args: []ast.Expr{ + &ast.ArrayType{ + Elt: ast.NewIdent("byte"), + }, + &ast.CallExpr{ + Fun: ast.NewIdent("len"), + Args: []ast.Expr{ + ast.NewIdent("input"), + }, + }, + }, + }, + }, + }, + &ast.RangeStmt{ + Key: ast.NewIdent("i"), + Value: ast.NewIdent("_"), + Tok: token.DEFINE, + X: ast.NewIdent("input"), + Body: &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.AssignStmt{ + Lhs: []ast.Expr{ + &ast.IndexExpr{ + X: ast.NewIdent("out"), + Index: ast.NewIdent("i"), + }, + }, + Tok: token.ASSIGN, + Rhs: []ast.Expr{ + &ast.BinaryExpr{ + X: &ast.IndexExpr{ + X: ast.NewIdent("input"), + Index: ast.NewIdent("i"), + }, + Op: token.XOR, + Y: &ast.IndexExpr{ + X: ast.NewIdent("key"), + Index: &ast.BinaryExpr{ + X: &ast.Ident{ + Name: "i", + }, + Op: token.REM, + Y: &ast.CallExpr{ + Fun: ast.NewIdent("len"), + Args: []ast.Expr{ + ast.NewIdent("key"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.ReturnStmt{ + Results: []ast.Expr{ + ast.NewIdent("out"), + }, + }, + }, + }, + })) + + xorTokens := make([]ast.Expr, len(xorKey)) + // map xorKey to a slice of ast.BasicLit + for i, b := range xorKey { + xorTokens[i] = &ast.BasicLit{ + Kind: token.INT, + Value: fmt.Sprintf("0x%x", b), + } + } + + // add the global xorKey variable to the AST + fileAst.Decls = append(fileAst.Decls, ast.Decl(&ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{ast.NewIdent("xorKey")}, + Values: []ast.Expr{ + &ast.CompositeLit{ + Type: ast.NewIdent("[]byte"), + Elts: xorTokens, + }, + }, + }, + }, + })) + + outBuff := bytes.Buffer{} + // Render the AST as Go code + printer.Fprint(&outBuff, fset, fileAst) + return outBuff.Bytes() +} + func setupCodenames(appDir string) error { nouns, err := assetsFs.ReadFile("fs/nouns.txt") if err != nil { @@ -426,3 +636,18 @@ func pseudoRandStringRunes(n int) string { } return string(b) } + +func xorByte(raw string, key byte) string { + // strip 0x + raw = raw[2:] + if len(raw) == 1 { + // Because we got `0x8` at some point + raw = fmt.Sprintf("0%s", raw) + } + hexByte, err := hex.DecodeString(raw) + if err != nil { + panic(err) + } + newByte := hex.EncodeToString([]byte{hexByte[0] ^ key}) + return fmt.Sprintf("0x%s", newByte) +}