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.