Decrypt obfuscated perl script
Asked Answered
C

3

15

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;
Cornflakes answered 6/3, 2012 at 20:48 Comment(5)
Looks like normal Perl to me... are you sure this script isn't a commercially licensed purposely-obfuscated one? We're not here to help you violate licensing agreements.Judyjudye
I would be interested to know if that does something, but I don't really want to run it at work. It reminds me of one of theseCollinsworth
Even a fairly experienced Perl programmer would cringe faced with trash like that. It doesn't much matter what it does; it is horrible code.Polygamy
@MarcB that would be really sneaky wouldn't it? Make it plain if you can and don't tell me the result if it looks "commercial"Cornflakes
@JonathanLeffler that was used to send spam from my server I'm interested to find out if it's connected to others, yes it's horrible, like I said I de-obfuscated the PHP ones.Cornflakes
M
25

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.

Mazzola answered 6/3, 2012 at 21:15 Comment(4)
A co-worker points out that if the code was still running, one could do cat /proc/<pid>/env and probably find out what the encryption key was (via E_KEY or HTTP_COOKIE).Michellemichels
@The Coder - was this script visible under your server root? If you want to pursue this further, maybe you want to put a new script in its place to capture the key.Corniculate
I'd say it's quite clever if not very helpful. ;-)Octahedral
Looks similar to Mirai to me, an ukrainian perl trojan, wandering through small web devices. Definitely very trickyGaia
G
17

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.

Godfry answered 7/3, 2012 at 0:24 Comment(0)
P
6

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

Puga answered 6/3, 2012 at 20:52 Comment(3)
I tried that on the code (which Perl said was OK when I checked with perl -c list.pl); DeParse didn't want to have anything to do with me - or maybe it was the code it complained about. (perl -MO=DeParse list.pl and some minor variants. The error was Undefined subroutine &B::DeParse::compile called at (eval 1) line 24. CHECK failed--call queue aborted..)Polygamy
@JonathanLeffler that's because Deparse is spelled Deparse, not DeParse.Mazzola
Thanks - I'm an idiot for not spotting that. The Deparse'd output isn't a lot easier to understand, though some of the regexes stand out more clearly.Polygamy

© 2022 - 2024 — McMap. All rights reserved.