-1

Description

I asked for help on freelancing, for the finalization of the project in PHP. All this worked well until I needed to edit the authorization mechanism. Having opened the file, I found that it was encrypted. Tried to contact him, but unsuccessfully. Help me find the decoder, the files are flooded with pastest.

Samples:

The original file is covered with pattern: https://pastebin.com/etEWDu2S eval(gzuncompress(base64_decode('

The decrypted file (but the lines are obfuscated): https://pastebin.com/AWr6zGg1 $GLOBALS['1867101966'][round(0)]

Another file is route.php, there are generally three lines: https://pastebin.com/FmbSyYLZ multiple eval(gzuncompress(base64_decode('

Happyness day!

1
  • 1
    I unfortunelly can't edit my post, maybe can i automatized decoding and deobf this code? May 23, 2018 at 16:43

4 Answers 4

1

Here's your deobfuscated file, note that I do not give guarantees, there was some manual work done and I may have removed important lines, more on that below:

<?

if (isset($_SESSION['id'])) {
    header('Location: ../');
    exit();
}

$message = '';
if (isset($_GET['data']) && isset($_GET['a'])) {
    $id  = check($_GET['a'], "int");
    $STH = $pdo->prepare("SELECT `id`, `login`, `email` FROM `users` WHERE `id`=:id LIMIT 1");
    $STH->setFetchMode(PDO::FETCH_OBJ);
    $STH->execute(array(
        ':id' => $id
    ));
    $row = $STH->fetch();
    if (empty($row->id)) {
        show_error_page();
    }
    if ($_GET['data'] != md5($row->id . $conf->code . $row->email . date("Y-m-d"))) {
        $message = '<p class=\'text-danger\'>' . $messages['Link_not_active'] . '</p>';
    } else {
        $password  = crate_pass(7, 1);
        $password2 = convert_password($password, $conf->salt);
        $STH = $pdo->prepare("UPDATE `users` SET `password`=:password WHERE `id`=:id LIMIT 1");
        if ($STH->execute(array(
            ':password' => $password2,
            ':id' => $id
        )) == '1') {
            include_once "inc/notifications.php";
            $letter = recovery_letter($conf->name, $row->login, $password);
            sendmail($row->email, $letter['subject'], $letter['message'], $pdo);
            $message = '<p class=\'text-success\'>' . $messages['Recovery_pass1'] . $row->email . $messages['Recovery_pass2'] . '</p>';
            write_log("User reinstated pass #" . $row->email);
        } else {
            $message        = '<p class=\'text-danger\'>' . $messages['Error_page'] . '</p>';
        }
    }
}

$tpl->load_template('elements/title.tpl');
$tpl->set("{title}", $page->title);
$tpl->set("{name}", $conf->name);
$tpl->compile('title');
$tpl->clear();
$tpl->load_template('head.tpl');
$tpl->set("{title}", $tpl->result['title']);
$tpl->set("{site_name}", $conf->name);
$tpl->set("{image}", $page->image);
$tpl->set("{robots}", $page->robots);
$tpl->set("{type}", $page->kind);
$tpl->set("{description}", $page->description);
$tpl->set("{keywords}", $page->keywords);
$tpl->set("{url}", $page->full_url);
$tpl->set("{other}", '');
$tpl->set("{token}", $token);
$tpl->set("{cache}", $conf->cache);
$tpl->set("{template}", $conf->template);
$tpl->set("{site_host}", $site_host);
$tpl->compile('content');
$tpl->clear();

$menu = $tpl->get_menu($pdo);
$nav                 = array(
    $PI->to_nav('main', 0, 0),
    $PI->to_nav('recovery', 1, 0)
);

$nav                 = $tpl->get_nav($nav, 'elements/nav_li.tpl');
include_once "inc/not_authorized.php";
$tpl->load_template('/index/recovery.tpl');
$tpl->set("{site_host}", $site_host);
$tpl->set("{template}", $conf->template);
$tpl->set("{message}", $message);
$tpl->compile('content');
$tpl->clear();

I wrote a small Python script to get rid of the annoying round() calls. It simply uses a regex to find all non-nested round(...) calls, dumps a small PHP script, runs PHP on it and retrieves the output because I didn't trust Python's round to be perfectly equal in corner cases.

The script:

#!/usr/bin/python

import subprocess
import re
import sys

def php_eval(code):
    code = "<?php echo(" + code + "); ?>"
    open("/tmp/eval","w").write(code)
    return subprocess.check_output(["php","/tmp/eval"]).decode("ascii")

def deob(fname):
    obf = open(fname,"r").read()

#    while True:
#        m = re.search(r'\[round.+?\]',obf)
#        if m:
#            s = m.group(0)
#            print("Matched %s" % s)
#            repl = php_eval(s[1:-1])
#            print("Replacing %s with %s" % (s,repl))
#            obf = obf.replace(s,"["+repl+"]")
#        else:
#            break

#    while True:
#        m = re.search(r'\$GLOBALS\[\'_174350175_\'\]\[\d+?\]',obf)
#        if m:
#            s = m.group(0)
#            obf = obf.replace(s,"round")
#        else:
#            break

    while True:
        m = re.search(r'round\([^\(]+?\)',obf)
        if m:
            s = m.group(0)
            repl = php_eval(s)
            print("Replacing %s with %s" % (s,repl))
            obf = obf.replace(s,repl)
        else:
            break


    return obf

plain = deob(sys.argv[1])
open(sys.argv[2],"w").write(plain)

Then I threw the code into a PHP beautifier: http://www.phpformatter.com/

Then the manual part.

There are some patterns the obfuscator creates:

while (3171 - 3171)
    bin2hex($seneiuhtrbbit, $likgfowocfbgko, $_SESSION);

while loops that never execute.

if ((3165 ^ 3165) && preg_split($seneiuhtrbbit))
    fgetss($pdo, $nav);

if clauses that never execute because the 1st part evaluates to false already.

(3253 - 3253 + 3158 - 3158) ? strnatcmp($pdo, $letter, $password) : mt_rand(806, 3253);

ternary operator usage that effectively is a nop (condition evaluates to false, making it call mt_rand)

And some others. I removed those manually, which gives the above script.

If you want to deobfuscate the route.php, you may want to uncomment the blocks in above script, as it adds another layer of rounds but I didn't continue. You would also need to text-replace the function calls back in from the 2nd GLOBAL array.

2
  • You is genious, f*cking great!!! Can i use this script on Windows system? May 24, 2018 at 13:43
  • Yes, a basic Python 3.x installation will run the script but you need to adjust some things. The php_eval function also expects PHP to be installed and callable as just php somefile.php from the command line. It also uses a Linux path /tmp/eval for its temporary file which you would need to adjust, just point it somewhere it may write. May 25, 2018 at 6:25
2

use a script like this to get a beautified php code

edit do not use jsbeautier install pear and use PHP_Beautifier instead there are lot of white space differences between PHP_Beautifier and jsbeautifier some of them are fatal like below or better use one of the online services like unphp

$tpl - > $pdo  instead of $tpl->$pdo (notice the space between - > which   
would not let php compile the script 

commented out the jsbeautifier part in the script below

import sys
import base64
import zlib
import jsbeautifier

f1 = open(sys.argv[1],"rb")
f2 = open(sys.argv[2],"wb")
f3 = open(sys.argv[3],"wb")
#f4 = open(sys.argv[4],"wb")
base64.decode(f1,f2)
f1.close()
f2.close()
f2 = open(sys.argv[2],"rb")
dat = f2.read()
f2.close()
decom = zlib.decompress(dat)
f3.write(decom)
f3.close()
#f3 = open(sys.argv[3],"rb")
#uglyjs=f3.read()
#f3.close()

#beaujs = jsbeautifier.beautify(uglyjs)
#f4.write(beaujs)
#f4.close()

usage copy the base64 encoded string to a file say b64.dat

:\>xxd -l 10 b64.dat
0000000: 6546 3746 572f 7554 3238                 eF7FW/uT28

and excute the script like

:\>python decodebase64.py b64.dat B64.DEC B64.UJS B64.PHP


:\>HEAD B64.PHP -n 5
if (isset($_SESSION['id'])) {
    header('Location: ../');
    exit();
}
$message = '';

you can also setup an apache and run the file in phpdbg to evaluate things

hope you have other template files in proper directories it tries to load some some template files like elements\xxx.tpl

foodown.php is the content of first link in your query split into multiple lines
like $a = base64_xxx
$b = gzunzip($a)
eval($b)

:\>phpdbg -p* foodown.php
function name: (null)
L1-5 {main}() C:\Users\XXX\Desktop\foodown.php - 0x1a7e000 + 10 ops
 L2    #0     INIT_FCALL<1>           64                   "base64_decode"
 L2    #1     SEND_VAL                "eF7FW/uT28aR/lcYZ"+ 1
 L2    #2     DO_ICALL                                                          @0
 L2    #3     ASSIGN                  $a                   @0
 L3    #4     INIT_FCALL<1>           64                   "gzuncompress"
 L3    #5     SEND_VAR                $a                   1
 L3    #6     DO_ICALL                                                          @2
 L3    #7     ASSIGN                  $b                   @2
 L4    #8     INCLUDE_OR_EVAL<1>      $b
 L5    #9     RETURN<-1>              1
[Script ended normally]

:\>
2
  • Thank's u! It's unreal amazing! You is best php guy! Can i automatic this calculating, like round(round(0) + 1.75 + 1.75 + 1.75 + 1.75) and deobf this string to real code? May 23, 2018 at 16:38
  • use notepad++ hi ctrl+alt+b to select everything between braces make it a variable like $deob1= (round(.......ro...ro...) and evaluate that and replace the original
    – blabb
    May 23, 2018 at 17:13
1

This is not that hard task as one can image, only a bit tedious.

First, you should reformat those files to have some of the structure reconstructed

https://gist.github.com/pawlos/bd3838d04cbbe094e534c5bd8b532b69

With only such small task you can see some SQL queries that go to the DB for user and password data.

From that, you can start renaming the obfuscated variables or converting those round(round(0) + 1.75 + 1.75 + 1.75 + 1.75) to actual numerical values (7 in this case).

2
  • Thank`s! Can i make automatic this process? May 23, 2018 at 16:35
  • I guess you could try to automate this somehow but probably it would require more time than calculating it once and then find&replace May 23, 2018 at 17:05
0

adding another answer because this contains substantially more info and might mess up the earlier answer

contents of directory pre experiment
32xxxx.*php.txt is from unphp.net
etE.*txt is content of original posts first link
b64.dat is the base64 string without quotes from the etE.*txt file

the other 2 are python scripts

:\>ls
317ca73f142f83e85c24c22cb66c59c7_php.txt  decodebase64.py  findround.py
b64.dat                                   etEWDu2S.txt

contents of python script that recursively replaces round() functions
uses subprocess module and evaluates the round() using PHP -r

:\>cat findround.py
import sys
import subprocess

if( len(sys.argv) != 2):
    print("usage "+ sys.argv[0] + " \"path to infile\"")
    sys.exit();

print "opening " + sys.argv[1]
infile = open(sys.argv[1] , "r")
print "reading all the lines"
data = infile.readlines();
print "closing the input file"
infile.close();
print "total number of lines read = %d" % len(data)

for i in range(0,len(data),1):
    pos = data[i].rfind("round(")
    if(pos == -1):
        continue
    else:
        substrobrace= data[i][pos:]
        cbpos = substrobrace.find(")")
        #print substrobrace[:cbpos+1]
        commandline = "php -r " +"\"print " + substrobrace[:cbpos+1] +  ";\""
        repl = subprocess.check_output( commandline )
        data[i] = data[i].replace(substrobrace[:cbpos+1] , repl)
        #print data[i]
        while (pos != -1):
            pos = data[i].rfind("round(")
            if(pos == -1):
                break
            else:
                substrobrace= data[i][pos:]
                cbpos = substrobrace.find(")")
                #print substrobrace[:cbpos+1]
                commandline = "php -r " +"\"print " + substrobrace[:cbpos+1] +  ";\""
                repl = subprocess.check_output( commandline )
                data[i] = data[i].replace(substrobrace[:cbpos+1] , repl)
                #print data[i]
outfilename = sys.argv[1] +".deob"
outfile = open(outfilename,"w")
outfile.writelines(data)
outfile.close()

contents of python scripts that de-obfuscates the etE.*.txt file
again uses supprocess to beautify the single line eval();

:\>cat decodebase64.py
import sys
import base64
import zlib
import time
import subprocess

f1 = open(sys.argv[1],"rb")
f2 = open(sys.argv[2],"wb")
f3 = open(sys.argv[3],"wb")
base64.decode(f1,f2)
f1.close()
f2.close()
f2 = open(sys.argv[2],"rb")
dat = f2.read()
f2.close()
decom = zlib.decompress(dat)
tagdecom = "<?php " + decom + "?>"
f3.write(tagdecom)
f3.close()
time.sleep(5)
commandline = "c:\\php\\php_beautifier.bat " + sys.argv[3] + " "+ sys.argv[3] +".php"
res = subprocess.check_output( commandline )
deobcmdline = "python findround.py " + sys.argv[3] +".php"
res = subprocess.check_output( deobcmdline )

executing the decodebase64.py with arguments
(this file calls the other script after some timeout so you get a deobfuscated file in one go with all the round() replaced) also diffed with output from unphp.net for sanity check
(js beautifier module in earlier my answer messes ugly php with white space error)

:\>decodebase64.py b64.dat b64.dec b64.ugly

:\>diff b64.ugly.php 317ca73f142f83e85c24c22cb66c59c7_php.txt
0a1,2
> /* Decoded by unphp.net */
>

checking if the deobfuscated php file is compilable or not

:\>phpdbg b64.ugly.php.deob
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of C:\b64.ugly.php.deob]
prompt> q

contents of the deobfuscated file with round() replaced with actual values

:\>head b64.ugly.php.deob
<?php if (isset($_SESSION['id'])) {
    header('Location: ../');
    exit();
}
$message = '';
if ((3165 ^ 3165) && preg_split($seneiuhtrbbit)) fgetss($pdo, $nav);
(3253 - 3253 + 3158 - 3158) ? strnatcmp($pdo, $letter, $password) : mt_rand(806, 3253);
if (isset($_GET['data']) && isset($_GET['a'])) {
    $id = check($_GET['a'], "int");
    $STH = $pdo->prepare("SELECT `id`, `login`, `email` FROM `users` WHERE `id`=:id LIMIT 1");

:\>

as you can see and as john posted in his answer this now contains
fake if() / while() / ternary operators / which will never execute notice if((3165 ^ 3165) && where the first condition evaluates to 0 so will never execute the fgetss =()
try php -a to open an interactive php shell and try evaluating the lines one by one
notice i modified the if condition to show the execution of second condition which has a wrong numbers of argument
prototype of preg_split is preg_split(regex,string,flag);

:\>php -a
Interactive shell

php > if ((3165 ^ 3165) && preg_split($seneiuhtrbbit)) fgetss($pdo, $nav);
php > if ((3165 ^ 3164) && preg_split($seneiuhtrbbit)) fgetss($pdo, $nav);

Warning: preg_split() expects at least 2 parameters, 1 given in php shell code on line 1
php >    

so you need to pluck them out manually

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.