Skip to content

Commit 745bf22

Browse files
authored
feat: add aes encryption method. (#10)
This commit refactor project to include multiple encryption methods. Add Aes encryption, loader and tests Part of: #2 Signed-off-by: Vasek - Tom C <[email protected]>
1 parent 6e5ad93 commit 745bf22

File tree

12 files changed

+314
-30
lines changed

12 files changed

+314
-30
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ go.work
2222

2323
221b
2424
*.sh
25+
payload_usage/
2526

2627
.idea

cli/bake.go

+22-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"path/filepath"
88
"strings"
99

10-
"github.com/cmepw/221b/encryption/xor"
1110
"github.com/cmepw/221b/loader"
1211
"github.com/cmepw/221b/logger"
1312
)
@@ -16,11 +15,13 @@ var (
1615
shellPath string
1716
key string
1817
output string
18+
method string
1919
)
2020

2121
var (
22-
ErrMissingShellPath = fmt.Errorf("missing shellPath argument")
23-
ErrMissingKey = fmt.Errorf("missing key argument")
22+
ErrMissingShellPath = fmt.Errorf("missing shellPath argument")
23+
ErrMissingKey = fmt.Errorf("missing key argument")
24+
ErrMethodNotSupported = fmt.Errorf("provided encryption method isn't supported, please choose: 'aes', 'xor'")
2425
)
2526

2627
var bake = &cobra.Command{
@@ -40,6 +41,12 @@ var bake = &cobra.Command{
4041
logger.Warn(fmt.Sprintf("output path set to default: %s", output))
4142
}
4243

44+
loader, ok := loader.Method[method]
45+
if !ok {
46+
logger.Fatal(ErrMethodNotSupported)
47+
}
48+
49+
logger.Info(fmt.Sprintf("use %s encryption method", method))
4350
logger.Debug(fmt.Sprintf("baking %s with key %s", shellPath, key))
4451

4552
logger.Debug(fmt.Sprintf("reading %s", shellPath))
@@ -48,13 +55,16 @@ var bake = &cobra.Command{
4855
logger.Fatal(err)
4956
}
5057

51-
logger.Debug(fmt.Sprintf("encrypting %s", shellPath))
52-
encryptedShell := xor.Encrypt(file, []byte(key))
58+
logger.Info(fmt.Sprintf("encrypting %s", shellPath))
5359

54-
logger.Debug(fmt.Sprintf("injecting encrypted shell into payload"))
60+
encryptedShell, err := loader.Encrypt(file, []byte(key))
61+
if err != nil {
62+
logger.Fatal(err)
63+
}
64+
65+
logger.Info(fmt.Sprintf("loading encrypted shell into payload"))
5566

56-
xorLoader := loader.NewXorLoader([]byte(key))
57-
content, err := xorLoader.Load(encryptedShell)
67+
content, err := loader.Load(encryptedShell, []byte(key))
5868
if err != nil {
5969
logger.Fatal(err)
6070
}
@@ -64,7 +74,9 @@ var bake = &cobra.Command{
6474
fmt.Println(string(content))
6575
}
6676

67-
if err := xorLoader.Compile(output, content); err != nil {
77+
logger.Info("compiling binary")
78+
79+
if err := loader.Compile(output, content); err != nil {
6880
logger.Fatal(err)
6981
}
7082
},
@@ -74,4 +86,5 @@ func init() {
7486
bake.Flags().StringVarP(&shellPath, "shellPath", "s", "", "path to the shell scrypt")
7587
bake.Flags().StringVarP(&key, "key", "k", "", "key to use for the xor")
7688
bake.Flags().StringVarP(&output, "output", "o", "", "output path (e.g., /home/bin.exe)")
89+
bake.Flags().StringVarP(&method, "method", "m", "xor", "encryption method")
7790
}

encryption/aes.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package encryption
2+
3+
import (
4+
"crypto/aes"
5+
"crypto/cipher"
6+
"crypto/rand"
7+
"errors"
8+
"io"
9+
)
10+
11+
var ErrCipherTextTooShort = errors.New("ciphertext too short")
12+
13+
type Aes struct{}
14+
15+
func (a Aes) Decrypt(content, key []byte) ([]byte, error) {
16+
c, err := aes.NewCipher(key)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
gcm, err := cipher.NewGCM(c)
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
nonceSize := gcm.NonceSize()
27+
if len(content) < nonceSize {
28+
return nil, ErrCipherTextTooShort
29+
}
30+
31+
nonce, ciphertext := content[:nonceSize], content[nonceSize:]
32+
33+
return gcm.Open(nil, nonce, ciphertext, nil)
34+
}
35+
36+
func (a Aes) Encrypt(content, key []byte) ([]byte, error) {
37+
c, err := aes.NewCipher(key)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
gcm, err := cipher.NewGCM(c)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
nonce := make([]byte, gcm.NonceSize())
48+
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
49+
return nil, err
50+
}
51+
52+
return gcm.Seal(nonce, nonce, content, nil), nil
53+
}

encryption/aes_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package encryption
2+
3+
import (
4+
"bytes"
5+
"github.com/stretchr/testify/require"
6+
"testing"
7+
)
8+
9+
func TestAes_EncryptDecrypt(t *testing.T) {
10+
type TestCase struct {
11+
content []byte
12+
key []byte
13+
}
14+
15+
testsCases := map[string]TestCase{
16+
"encrypt a simple string": {
17+
content: []byte("hello world"),
18+
key: []byte("0123456789ABCDEF"),
19+
},
20+
"encrypt a golang file": {
21+
content: []byte(`
22+
package main
23+
24+
import "fmt"
25+
26+
func main() {
27+
fmt.Println("hello Go!")
28+
}
29+
`),
30+
key: []byte("0123456789ABCDEF"),
31+
},
32+
}
33+
34+
for name, tt := range testsCases {
35+
t.Run(name, func(t *testing.T) {
36+
aes := Aes{}
37+
38+
encryptedContent, err := aes.Encrypt(bytes.Clone(tt.content), tt.key)
39+
require.NoError(t, err)
40+
require.NotEqual(t, tt.content, encryptedContent)
41+
42+
decryptedContent, err := aes.Decrypt(encryptedContent, tt.key)
43+
require.NoError(t, err)
44+
require.Equal(t, tt.content, decryptedContent)
45+
})
46+
}
47+
}

encryption/encryption.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package encryption
22

33
type Encryption interface {
4-
Decrypt(content, key []byte) []byte
5-
Encrypt(content, key []byte) []byte
4+
Decrypt(content, key []byte) ([]byte, error)
5+
Encrypt(content, key []byte) ([]byte, error)
66
}
+8-6
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
// Package xor implements xor encryption and decryption.
2-
package xor
1+
package encryption
2+
3+
// Xor implements xor encryption and decryption.
4+
type Xor struct{}
35

46
// Decrypt apply xor on the content with the given key.
57
// Note that it will actually update the given content.
6-
func Decrypt(content, key []byte) []byte {
8+
func (x Xor) Decrypt(content, key []byte) ([]byte, error) {
79
keyLen := len(key)
810

911
for i := 0; i < len(content); i++ {
1012
content[i] ^= key[i%keyLen]
1113
}
12-
return content
14+
return content, nil
1315
}
1416

1517
// Encrypt apply xor on the content with the given key.
1618
// Note that it will actually update the given content.
17-
func Encrypt(content, key []byte) []byte {
19+
func (x Xor) Encrypt(content, key []byte) ([]byte, error) {
1820
keyLen := len(key)
1921

2022
for i := 0; i < len(content); i++ {
2123
content[i] ^= key[i%keyLen]
2224
}
2325

24-
return content
26+
return content, nil
2527
}

encryption/xor/xor_test.go encryption/xor_test.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package xor
1+
package encryption
22

33
import (
44
"bytes"
@@ -33,10 +33,14 @@ func main() {
3333

3434
for name, tt := range testsCases {
3535
t.Run(name, func(t *testing.T) {
36-
encryptedContent := Encrypt(bytes.Clone(tt.content), tt.key)
36+
xor := Xor{}
3737

38+
encryptedContent, err := xor.Encrypt(bytes.Clone(tt.content), tt.key)
39+
require.NoError(t, err)
3840
require.NotEqual(t, tt.content, encryptedContent)
39-
decryptedContent := Decrypt(encryptedContent, tt.key)
41+
42+
decryptedContent, err := xor.Decrypt(encryptedContent, tt.key)
43+
require.NoError(t, err)
4044
require.Equal(t, tt.content, decryptedContent)
4145
})
4246
}

loader/aes.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package loader
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
"text/template"
8+
9+
"github.com/cmepw/221b/encryption"
10+
"github.com/cmepw/221b/templates"
11+
)
12+
13+
type Aes struct {
14+
baseLoader
15+
encryption.Aes
16+
}
17+
18+
func (a Aes) Load(content, key []byte) ([]byte, error) {
19+
tmpl, err := template.New("loader").Funcs(template.FuncMap{
20+
"key": func() string {
21+
return string(key)
22+
},
23+
"shellcode": func() string {
24+
result := []string{}
25+
26+
for _, b := range content {
27+
result = append(result, fmt.Sprintf("0x%02x", b))
28+
}
29+
30+
return strings.Join(result, ", ") + ","
31+
},
32+
}).Parse(templates.AesTmpl)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
result := new(bytes.Buffer)
38+
err = tmpl.Execute(result, nil)
39+
40+
return result.Bytes(), err
41+
}

loader/loader.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@ import (
66
"os/exec"
77
"path/filepath"
88

9+
"github.com/cmepw/221b/encryption"
910
"github.com/cmepw/221b/logger"
1011
"github.com/cmepw/221b/templates"
1112
)
1213

14+
// Method gather all loading and encryption method.
15+
var Method = map[string]Loader{
16+
"xor": Xor{},
17+
"aes": Aes{},
18+
}
19+
1320
type Loader interface {
14-
Load(content []byte) ([]byte, error)
21+
Load(content, key []byte) ([]byte, error)
1522
Compile(path string, content []byte) error
23+
encryption.Encryption
1624
}
1725

1826
type baseLoader struct{}

loader/xor.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,19 @@ import (
66
"strings"
77
"text/template"
88

9+
"github.com/cmepw/221b/encryption"
910
"github.com/cmepw/221b/templates"
1011
)
1112

1213
type Xor struct {
13-
key []byte
1414
baseLoader
15+
encryption.Xor
1516
}
1617

17-
func NewXorLoader(key []byte) *Xor {
18-
return &Xor{key: key}
19-
}
20-
21-
func (x Xor) Load(content []byte) ([]byte, error) {
18+
func (x Xor) Load(content, key []byte) ([]byte, error) {
2219
tmpl, err := template.New("loader").Funcs(template.FuncMap{
2320
"key": func() string {
24-
return string(x.key)
21+
return string(key)
2522
},
2623
"shellcode": func() string {
2724
result := []string{}

0 commit comments

Comments
 (0)