1

I have a script that outputs some information on my shell, like this:

field1: value1
field2: value2
...
fieldn: valuen

one such field/value can be:

    CmdLine: C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe -EncodedCommand <base64_encodedcommand>

I've been looking for a way to deal with that, since the encoded command can be pretty big (like, 2 terminal screens big or more). One of the things I got around to do was:

command_that_generates_strings | awk '{for (i=1;i<=NF;i++) if (tolower($i) ~/^-encodedcommand/) {i++; "echo "$i" | base64 -d | iconv -f UTF-16LE -t UTF8 | grep -i <specific_identifier_here> | sed -E \"s#.+#<better_identifier_here#g\"" | getline x; print x} else {print $i}}'

for readability:

command_that_generates_strings | awk '{
    for (i=1;i<=NF;i++) 
    if (tolower($i) ~/^-encodedcommand/) {
      i++; "echo "$i" | 
      base64 -d | 
      iconv -f UTF-16LE -t UTF8 | 
      grep -i <specific_identifier_here> | 
      sed -E \"s#.+#<better_identifier_here#g\"" | 
      getline x; print x
    } else {print $i}}'

(yeah, the iconv in there is because, to add insult to injury, the base64 content is in utf16le which grep dislikes. I have run this "semi-succesfully" in both bash and zsh)

But this doesn't quite work as expected... instead of replacing the CmdLine: yaddayadda for the <better_identifier_here>, it's replace just the encoded command and also break everything else apart, and also somehow it seems to be sorting the output, like this:

A-string
B-string
C-string
CmdLine:
C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe
D-string
EncodedCommand
...
N-string
<better_identifier_here>
...
Y-string
Z-string
0-string
1-string
...

("EncodedCommand" up there is the actual string, not the base64 encoded command from that command line)

Perhaps there's a different better way to do what I'm looking for? The expected result would be just:

field1: value1
field2: value2
...
<better_identifier_here>
...
fieldn: valuen

-- let's try to include a more useful example here:

input:

    field1: value1
    field2: value2
    ...
    CmdLine: C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe -EncodedCommand PABpAG4AcwBlAHIAdAAgAHAAbwB3AGUAcgBzAGgAZQBsAGwACgBtAHUAbAB0AGkAbABpAG4AZQAKAHMAYwByAGkAcAB0AAoAaABlAHIAZQAgAC0AIABpAHQAJwBsAGwAIABiAGUAIABlAG4AYwBvAGQAZQBkACAAYQBzACAAdQB0AGYAMQA2AGwAZQAKAGEAbgBkACAAdABoAGUAbgAgAGUAbgBjAG8AZABlAGQAIABpAG4AIABiAGEAcwBlADYANAA+AA==
    ...
    fieldn: valuen

and from that, I want to be able to identify that the string "be encoded" is part of that encoded command, and replace the whole "CmdLine" line with "Encoded payload found."

Example output would be:

    field1: value1
    field2: value2
    ...
    Encoded payload found.
    ...
    fieldn: valuen
8
  • the code you've provided does not replace anything; the code you've provided actually generates an awk syntax error message; net result: the code you've provided does not generate the output you've provided; please update the question to include the actual, working code
    – markp-fuso
    Commented Nov 23, 2023 at 21:12
  • please update the question with a few lines of input (the field/value pairs generated by your script), preference would be to pick a couple of your smallest based64 examples; also include the expected output; be sure to provide an explanation of what to do if the grep does not find a match
    – markp-fuso
    Commented Nov 23, 2023 at 21:16
  • just do it in your scripting language of choice, PowerShell, Python, etc. why bother with the command prompt?
    – Junaga
    Commented Nov 23, 2023 at 23:25
  • @markp-fuso weird, on my end the base64 encoded command after the -EncodedCommand switch is gone,I don't get any awk syntax errors. Could it be because the actual awk command on my code doesn't have linebreaks, like the ones I've added here for readability? I'll try to generate a few safe examples later. @Junaga I'm doing this in bash/zsh, whatever gave the idea of cmd? Commented Nov 24, 2023 at 8:19
  • 1
    Is the data in YAML or JSON or some other structured document format that have well known parsers? There are parsers for both YAML and JSON that know how to decode base64. Using such a parser would solve your problem more or less trivially.
    – Kusalananda
    Commented Nov 24, 2023 at 8:37

2 Answers 2

1

Assuming you want to:

  • find base64 encoded data after any occurrence of -encodedcommand (case insensitive) followed by whitespace
  • decode it into something assumed to be a multiline UTF16-LE encoded string
  • decode that UTF16-LE to get the actual text
  • replace any of the lines in the decoded text matching a pattern (case insensitively) with a new one
  • encode the result back to UTF16-LE
  • and back to base64

Then you could do:

your-cmd | perl -MMIME::Base64 -MEncode -pe '
  s{\s-encodedcommand\s+\K\S+}{
    encode_base64(
      encode(
        "UTF16LE",
        decode(
          "UTF16LE",
          decode_base64($&)
        ) =~ s/^.*bad content.*$/new better content/mgir
      ), ""
    )
  }gie'

Example. With your-cmd being:

echo 'CmdLine: ... -EncodedCommand MQANAAoAMgANAAoAMwANAAoANAANAAoA'

Where MQANAAoAMgANAAoAMwANAAoANAANAAoA is the base64 encoding of the UTF16-LE encoding of the output of seq -f $'%g\r' 4.

And the substitution being s/^3\r$/new\r/mgir, to replace that 3 line (with MSDOS line delimiter), with a new one, that gives:

CmdLine: ... -EncodedCommand MQANAAoAMgANAAoAbgBlAHcADQAKADQADQAKAA==

Where:

echo MQANAAoAMgANAAoAbgBlAHcADQAKADQADQAKAA== |
  base64 -d |
  hexdump -C

Gives:

00000000  31 00 0d 00 0a 00 32 00  0d 00 0a 00 6e 00 65 00  |1.....2.....n.e.|
00000010  77 00 0d 00 0a 00 34 00  0d 00 0a 00              |w.....4.....|
0000001c

Where you can recognise the Microsoft version of

1
2
new
4

If as per your clarified requirements, you just want to detect a string in the encoded text and if present replace the full line with the decoded contents (presumably encoded in UTF-8¹)

your-cmd | perl -MMIME::Base64 -MEncode -pe '
  if (/^\s*cmdline:.*\s-encodedcommand\s+(\S+)/i) {
    $text = decode("UTF16LE", decode_base64($1));
    if ($text =~ /encoded as/) {
      $_ = encode("UTF-8", "$text\n");
      # or "$text\r\n" if meant to be DOS-style or:
      # $_ = "Encoded payload found\n";
    }
  }'

(assuming there's only one -encodedcommand per line; if there are more, only the last one will be considered. Replace .* with .*? to consider the first one instead).

On your sample, that gives:

    field1: value1
    field2: value2
    ...
<insert powershell
multiline
script
here - it'll be encoded as utf16le
and then encoded in base64>
    ...
    fieldn: valuen

¹ perl by default will consider the input as if encoded in ISO8859-1 (aka latin1). Since the part you want to deal with is ASCII-only, that won't make any difference, and at least with latin1, you won't run into decoding issues. If it's meant to be UTF-8, you can add the -C option to perl and skip the encode("UTF-8"...) part.

Beware that though byte-order-marks don't belong in UTF-8, they're sometimes found in Microsoft files. If that's the case and CmdLine is on the first line, the /^\s*cmdline.../i regexp won't match as there will be that U+FEFF BOM character at the start. Then you could pass -C and change the regex to /^\x{feff}?\s*cmdline.../i to allow an optional U+FEFF character at the start of the line.

6
  • I can see where this goes... not sure if it'll do what I want. One base64 encoded example would be PABpAG4AcwBlAHIAdAAgAHAAbwB3AGUAcgBzAGgAZQBsAGwACgBtAHUAbAB0AGkAbABpAG4AZQAKAHMAYwByAGkAcAB0AAoAaABlAHIAZQAgAC0AIABpAHQAJwBsAGwAIABiAGUAIABlAG4AYwBvAGQAZQBkACAAYQBzACAAdQB0AGYAMQA2AGwAZQAKAGEAbgBkACAAdABoAGUAbgAgAGUAbgBjAG8AZABlAGQAIABpAG4AIABiAGEAcwBlADYANAA+AA== (decode the b64 then the utf16le) Commented Nov 25, 2023 at 3:14
  • no need for the re-encoding, but +1 for the effort and teaching me something new with perl, thanks! Commented Nov 25, 2023 at 3:25
  • @JoãoCiocca see edit. Commented Nov 25, 2023 at 8:26
  • the assumption of only one -encodedCommand (I used tolower on awk because they sometimes change the case for E and C) per line is correct. There can be multiple CmdLine, but only one encoded command per line. But I don't want to return the whole unencoded command, because that's usually even bigger than the encoded command. I want to replace it with an identifier, a marker. Commented Nov 25, 2023 at 13:52
  • 1
    @JoãoCiocca see edit. It should hopefully be easy to adapt to whatever you want to do which is not very clear to me. Commented Nov 25, 2023 at 14:08
0

So far, the only working solution seems to be to just accept that the encoded command is UTF16LE and manually include the null bytes \u0000 on the search in the jq script that generates the content...

For the example given above, to account for the possibility of upper and lowercase -EncodedCommand or -encodedcommand, that'd be like (inside jq):

restofjq | 
 if(if((split(" ")[1] | ascii_downcase)=="-encodedcommand") 
    then (
        split(" ")[2] | 
        @base64d | 
        test("\u0000b\u0000e\u0000 \u0000e\u0000n\u0000c\u0000o\u0000d\u0000e\u0000d\u0000")
    ) else empty end
  ) then "Encoded payload found!" else empty end

(line breaks added to make reading it easier)

split(" ") will make it so the powershell command line is split(" ")[0], while -EncodedCommand is split(" ")[1] and the actual encoded command goes to split(" ")[2]

Because the content is in UTF16LE, if I try to @base64d | test("be encoded") it'll fail, because of the nulls from UTF16LE. That way, if the be encoded content is found in the encoded command, the field will be output as Encoded payload found!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .