Compare commits

...

2 Commits

Author SHA1 Message Date
bill-rich 542732aef6 Assume ASCII and include valid BE and LE bytes 2023-06-08 17:13:05 -07:00
Miccah Castorina cdc9198050 Use heuristic to choose the most likely UTF-16 decoded string 2023-06-05 08:51:07 -05:00
3 changed files with 40 additions and 18 deletions
+15 -14
View File
@@ -26,30 +26,31 @@ func (d *UTF16) FromChunk(chunk *sources.Chunk) *sources.Chunk {
// utf16ToUTF8 converts a byte slice containing UTF-16 encoded data to a UTF-8 encoded byte slice. // utf16ToUTF8 converts a byte slice containing UTF-16 encoded data to a UTF-8 encoded byte slice.
func utf16ToUTF8(b []byte) ([]byte, error) { func utf16ToUTF8(b []byte) ([]byte, error) {
endianness, err := guessUTF16Endianness(b) var bufBE, bufLE bytes.Buffer
if err != nil { for i := 0; i < len(b)-1; i += 2 {
return nil, err if r := rune(binary.BigEndian.Uint16(b[i:])); b[i] == 0 && utf8.ValidRune(r) {
} bufBE.WriteRune(r)
}
buf := &bytes.Buffer{} if r := rune(binary.LittleEndian.Uint16(b[i:])); b[i+1] == 0 && utf8.ValidRune(r) {
for i := 0; i < len(b); i += 2 { bufLE.WriteRune(r)
r := rune(endianness.Uint16(b[i:])) }
if utf8.ValidRune(r) { // Guard against index out of bounds for the next check.
buf.WriteRune(r) if i+1 >= len(b)-1 {
continue
} }
} }
return buf.Bytes(), nil return append(bufLE.Bytes(), bufBE.Bytes()...), nil
} }
func guessUTF16Endianness(b []byte) (binary.ByteOrder, error) { func guessUTF16Endianness(b []byte) (binary.ByteOrder, error) {
if len(b) < 2 || len(b)%2 != 0 { if len(b) < 2 {
return nil, fmt.Errorf("input length must be even and at least 2 bytes long") return nil, fmt.Errorf("input length must at least 2 bytes long")
} }
var evenNullBytes, oddNullBytes int var evenNullBytes, oddNullBytes int
for i := 0; i < len(b); i += 2 { for i := 0; i < len(b)-1; i += 2 {
if b[i] == 0 { if b[i] == 0 {
oddNullBytes++ oddNullBytes++
} }
Binary file not shown.
+25 -4
View File
@@ -2,6 +2,7 @@ package decoders
import ( import (
"bytes" "bytes"
"os"
"testing" "testing"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources" "github.com/trufflesecurity/trufflehog/v3/pkg/sources"
@@ -30,13 +31,13 @@ func TestUTF16Decoder(t *testing.T) {
name: "Invalid UTF-16 input (it's UTF-8)", name: "Invalid UTF-16 input (it's UTF-8)",
input: []byte("Hello World!"), input: []byte("Hello World!"),
expected: nil, expected: nil,
expectNil: true, expectNil: false,
}, },
{ {
name: "Invalid UTF-16 input (odd length)", name: "Invalid UTF-16 input (odd length)",
input: []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 0}, input: []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 0},
expected: nil, expected: []byte("Hello Worl"),
expectNil: true, expectNil: false,
}, },
} }
@@ -57,12 +58,32 @@ func TestUTF16Decoder(t *testing.T) {
return return
} }
if !bytes.Equal(decodedChunk.Data, tc.expected) { if !bytes.Equal(decodedChunk.Data, tc.expected) {
t.Errorf("Expected decoded data: %v, got: %v", tc.expected, decodedChunk.Data) t.Errorf("Expected decoded data: %s, got: %s", tc.expected, decodedChunk.Data)
} }
}) })
} }
} }
func TestDLL(t *testing.T) {
data, err := os.ReadFile("utf16_test.dll")
if err != nil {
t.Errorf("Failed to read test data: %v", err)
return
}
chunk := &sources.Chunk{Data: data}
decoder := &UTF16{}
decodedChunk := decoder.FromChunk(chunk)
if decodedChunk == nil {
t.Errorf("Expected chunk with data, got nil")
return
}
if !bytes.Contains(decodedChunk.Data, []byte("aws_secret_access_key")) {
t.Errorf("Expected chunk to have aws_secret_access_key")
return
}
}
func BenchmarkUtf16ToUtf8(b *testing.B) { func BenchmarkUtf16ToUtf8(b *testing.B) {
// Example UTF-16LE encoded data // Example UTF-16LE encoded data
data := []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0} data := []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0}