Decrypt obfuscated perl script

2019-01-23 14:32发布

问题:

Had some spam issues on my server and, after finding out and removing some Perl and PHP scripts I'm down to checking what they really do, although I'm a senior PHP programmer I have little experience with Perl, can anyone give me a hand with the script here:

http://pastebin.com/MKiN8ifp

(It was one long line of code, script was called list.pl)


The start of the script is:

$??s:;s:s;;$?::s;(.*); ]="&\%[=.*.,-))'-,-#-*.).<.'.+-<-~-#,~-.-,.+,~-{-,.<'`.{'`'<-<--):)++,+#,-.{).+,,~+{+,,<)..})<.{.)-,.+.,.)-#):)++,+#,-.{).+,,~+{+,,<)..})<*{.}'`'<-<--):)++,+#,-.{).+:,+,+,',~+*+~+~+{+<+,)..})<'`'<.{'`'<'<-}.<)'+'.:*}.*.'-|-<.+):)~*{)~)|)++,+#,-.{).+:,+,+,',~+*+~+~+{+<+,)..})

It continues with precious few non-punctuation characters until the very end:

0-9\;\\_rs}&a-h;;s;(.*);$_;see;

回答1:

Replace the s;(.*);$_;see; with print to get this. Replace s;(.*);$_;see; again with print in the first half of the payload to get this, which is the decryption code. The second half of the payload is the code to decrypt, but I can't go any further with it, because as you see, the decryption code is looking for a key in an envvar or a cookie (so that only the script's creator can control it or decode it, presumably), and I don't have that key. This is actually reasonably cleverly done.



回答2:

For those interested in the nitty gritty... The first part, when de-tangled looks like this:

$?  ?  s/;s/s;;$?/ :
       s/(.*)/...lots of punctuation.../;

The $? at the beginning of the line is the pre-defined variable containing the child error, which no doubt serves only as obfuscation. It will be undefined, as there can be no child error at this point.

The questionmark following it is the start of a ternary operator

CONDITION ? IF_TRUE : IF_FALSE

Which is also added simply to obfuscate. The expression returned for true is a substitution regex, where the / slash delimiter has been replaced with colon s:pattern:replacement:. Above, I have put back slashes. The other expression, which is the one that will be executed is also a substitution regex, albeit an incredibly long one. The delimiter is semi-colon.

This substitution replaces .* in $_ - the default input and pattern-searching space - with a rather large amount of punctuation characters, which represents the bulk of the code. Since .* matches any string, even the empty string, it will simply get inserted into $_, and is for all intents and purposes identical to simply assigning the string to $_, which is what I did:

$_ = q;]="&\%[=.*.,-))'-,-# .......;;

The following lines are a transliteration and another substitution. (I inserted comments to point out the delimiters)

y; -"[%-.:<-@]-`{-}#~\$\\;{\$()*.0-9\;\\_rs}&a-h;;
#^                       ^           ^          ^
#1                       2                      3

(1,2,3 are delimiters, the semi-colon between 2 and 3 is escaped)

The basic gist of it is that various characters and ranges -" (space to double quote), and something that looks like character classes (with ranges) [%-.:<-@], but isn't, get transliterated into more legible characters e.g. curly braces, dollar sign, parentheses,0-9, etc.

s;(.*);$_;see;

The next substitution is where the magic happens. It is also a substitution with obfuscated delimiters, but with three modifers: see. s does nothing in this case, as it only allows the wildcard character . to match newline. ee means to evaluate the expression twice, however.

In order to see what I was evaluating, I performed the transliteration and printed the result. I suspect that I somewhere along the line got some characters corrupted, because there were subtle errors, but here's the short (cleaned up) version:

s;(.*);73756220656e6372797074696f6e5f6 .....;;  # very long line of alphanumerics
s;(..);chr(hex($1));eg;
s;(.*);$_;see;
s;(.*);704b652318371910023c761a3618265 .....;;  # another long line
s;(..);chr(hex($1));eg; 
&e_echr(\$_);
s;(.*);$_;see;

The long regexes are once again the data containers, and insert data into $_ to be evaluated as code.

The s/(..)/chr(hex($1))/eg; is starting to look rather legible. It is basically reading two characters at the time from $_ and converting it from hex to corresponding character.

The next to last line &e_echr(\$_); stumped me for a while, but it is a subroutine that is defined somewhere in this evaluated code, as hobbs so aptly was able to decode. The dollar sign is prefixed by backslash, meaning it is a reference to $_: I.e. that the subroutine can change the global variable.

After quite a few evaluations, $_ is run through this subroutine, after which whatever is contained in $_ is evaluated a last time. Presumably this time executing the code. As hobbs said, a key is required, which is taken from the environment %ENV of the machine where the script runs. Which we do not have.



回答3:

Ask the B::Deparse module to make it (a little more) readable.