Decoding eval/gzinflate/base64 hacker code

I recently had a case of a hacked website. The hacker gained access to templates and injected the following PHP code:

eval(gzinflate(base64_decode('HJ3HkqNQEkU/ZzqCBd4t8V4YAQI2E..........................NIAWIIgeFb//eeff/79z/8A')));

To evaluate the possible damage the hacker has done, you’ll have to decode the string. The problem is that decoding the string leads to a new encoded string that has to be decoded again, leading to a new encoded string, decoding it again and so forth.

So I wrote some code to decode it.
(My first attempt was to try it in Python, but quickly got encoding problems and ugly Python errors such as “Type str doesn’t support the buffer API”. In the end I always tend to fall back to solid static typed languages, Java in this case.)

Here is the Java code:

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;

import org.apache.commons.codec.binary.Base64;

import com.google.common.io.CharStreams;
import com.google.common.io.Files;

public class GzipBase64Decoder {
	
	public String decode(String gzipBase64Input)  throws Exception {
		String currentDecodedString = gzipBase64Input;
		String previousDecodedString = null;
        int count = 0;
        while (true) {
        	//System.out.println(count);
            InputStream inflInstream = new InflaterInputStream(
                    new ByteArrayInputStream(new Base64().decode(currentDecodedString)),
            		//new ByteArrayInputStream(BaseEncoding.base64().decode(currentDecodedString)),
                    new Inflater(true));
        	try {
                currentDecodedString = CharStreams.toString(new InputStreamReader(inflInstream, "UTF-8"));
                previousDecodedString = currentDecodedString;
                int begin = currentDecodedString.indexOf('\'');
                int end = currentDecodedString.lastIndexOf('\'');

                //System.out.println(currentDecodedString);
                
                if (begin == -1 || begin >= end) {
                	break;
                } else {
                    currentDecodedString = currentDecodedString.substring(begin + 1, end);
                }
                
                
                count++;
        	} catch (ZipException ex) {
        		return previousDecodedString;
        	}
        }
        
        throw new IllegalStateException("Unable to decode input");
	}
	
	public static void main(String[] args) throws Exception {
		String result = new GzipBase64Decoder().decode(InputData.INPUT);
		System.out.println(result);
		
		Files.append(result, new File("x:/db/result.txt"), Charset.defaultCharset());
		
	}
}

class InputData {
	public static final String INPUT = "PASTE THE ENCODED STRING HERE";
}

If uses both Google Guava and Apache Commons as third party utility libraries.
The Java code simply tries to decoded the strings until it cannot be decoded any further.
The input string is put in a separate class for compilation performance reasons. (Due to the length of the encoded string in my case.)
Note that this assumes that all child encoded strings have the same pattern: eval > gzinflate > base64. It also assumes that the encoded string is enclosed by single quotes.

In my case the encoded string was encoded 10 times. In such a low number of encodes, you can also try manually using: www.whitefirdesign.com/tools/deobfuscate-php-hack-code.html
The decoded hacker code resulted in “c99madshell v.2.0 madnet edition”, which can be a big security thread on a server. (More info about cc99madshell, see: www.derekfountain.org/security_c99madshell.php )

Leave a Reply

Your email address will not be published. Required fields are marked *