PHP-Antimalware-Scanner icon indicating copy to clipboard operation
PHP-Antimalware-Scanner copied to clipboard

Scanner crash when encountering certain kind of malware

Open kdescoubes opened this issue 1 year ago • 10 comments

Hi there,

I use your scanner on multiple websites, but some times, it crashes prematurely (like described in issue #46 for example).

So I tried to run the scanner on sub directory until I find THE one which makes the scanner crashed.

I found a malware (attached), I move it to quarantine, then ran the scanner again : it didn't crash !

What's really weird is that if I run the scanner on the quarantine, it doesn't crash ...

(the file was a dot php file of course, renamed it to txt to upload it)

2308ba68.txt

kdescoubes avatar Mar 13 '23 12:03 kdescoubes

Same here on another wordpress website :

image

File responsible for the crash is here :

themes.php.txt

I move it to the quarantine, and now the scan is OK.

Again, triggering the scan on the quarantine does not crash the scanner ...

kdescoubes avatar Mar 13 '23 12:03 kdescoubes

Hi kdescoubes,

The problem is in the file: vendor/marcocesarato/amwscan/src/Deobfuscator.php The following "calc" function is defined in mentioned file:

private function calc($expr)
    {
        if (is_array($expr)) {
            $expr = $expr[0];
        }
        preg_match('~(min|max)?\(([^\)]+)\)~mi', $expr, $exprArr);
        if (!empty($exprArr[1]) && ($exprArr[1] === 'min' || $exprArr[1] === 'max')) {
            return $exprArr[1](explode(',', $exprArr[2]));
        }

        preg_match_all('~([\d\.]+)([\*\/\-\+])?~', $expr, $exprArr);
        if (!empty($exprArr[1]) && !empty($exprArr[2])) {
            if (in_array('*', $exprArr[2], true)) {
                $pos = array_search('*', $exprArr[2], true);
                $res = @$exprArr[1][$pos] * @$exprArr[1][$pos + 1];
                $expr = str_replace(@$exprArr[1][$pos] . '*' . @$exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr);
            } elseif (in_array('/', $exprArr[2], true)) {
                $pos = array_search('/', $exprArr[2], true);
                $res = $exprArr[1][$pos] / $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '/' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr);
            } elseif (in_array('-', $exprArr[2], true)) {
                $pos = array_search('-', $exprArr[2], true);
                $res = $exprArr[1][$pos] - $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '-' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr);
            } elseif (in_array('+', $exprArr[2], true)) {
                $pos = array_search('+', $exprArr[2], true);
                $res = $exprArr[1][$pos] + $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '+' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr);
            } else {
                return $expr;
            }
        }

        return $expr;

As you can see, this is a recursive function that - for some reason - has an erroneous stop condition for the file you indicated and goes into very, very deep levels of recursion (in my case, about ~281000 - until the memory on the stack is exhausted).

I haven't had time to disarm this function and analyze the stop condition, but it seems that a simple and sufficient workaround is to add an additional guard in the form:

if($level>100000) return "";

This will interrupt further nesting if it goes too far :)

So, all the correct function code will therefore look as follows:

    private function calc($expr, $level = 0)
    {
		if($level>100000) return "";
		
        if (is_array($expr)) {
            $expr = $expr[0];
        }
        preg_match('~(min|max)?\(([^\)]+)\)~mi', $expr, $exprArr);
        if (!empty($exprArr[1]) && ($exprArr[1] === 'min' || $exprArr[1] === 'max')) {
            return $exprArr[1](explode(',', $exprArr[2]));
        }

        preg_match_all('~([\d\.]+)([\*\/\-\+])?~', $expr, $exprArr);
        if (!empty($exprArr[1]) && !empty($exprArr[2])) {
            if (in_array('*', $exprArr[2], true)) {
                $pos = array_search('*', $exprArr[2], true);
                $res = @$exprArr[1][$pos] * @$exprArr[1][$pos + 1];
                $expr = str_replace(@$exprArr[1][$pos] . '*' . @$exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr, $level+1);
            } elseif (in_array('/', $exprArr[2], true)) {
                $pos = array_search('/', $exprArr[2], true);
                $res = $exprArr[1][$pos] / $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '/' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr, $level+1);
            } elseif (in_array('-', $exprArr[2], true)) {
                $pos = array_search('-', $exprArr[2], true);
                $res = $exprArr[1][$pos] - $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '-' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr,$level+1);
            } elseif (in_array('+', $exprArr[2], true)) {
                $pos = array_search('+', $exprArr[2], true);
                $res = $exprArr[1][$pos] + $exprArr[1][$pos + 1];
                $expr = str_replace($exprArr[1][$pos] . '+' . $exprArr[1][$pos + 1], $res, $expr);
                $expr = $this->calc($expr,$level+1);
            } else {
                return $expr;
            }
        }

        return $expr;
    }

This is completely sufficient (at least for my needs).

I sincerely greet you and warm hugs for the file that helped me to solve this problem, ~WB

Borcejn avatar Oct 11 '23 22:10 Borcejn

Hi, @Borcejn What should I do after changing this piece of code to execute the scanner?

AldoTapiaInnova avatar Oct 19 '23 22:10 AldoTapiaInnova

@AldoTapiaInnova Just run index.php from src

Borcejn avatar Oct 19 '23 23:10 Borcejn

I too have the same experience. Malware is present, yet the scanner fails. I had to use strace -e trace=file to discover the offending php scan file. It would be great if the scanner failed with a report, and maybe the file it was last scanning. Thank you.

stovesy avatar Jan 22 '24 12:01 stovesy

I have the same problem. The scanner "takes off" when it encounters a file. Change in code "if($level>100000) return "";" did not help After that, the scanner stopped starting at all. I changed the file name from PHP to TXT

osjtcnhr.txt

58legend avatar Apr 01 '24 00:04 58legend

@58legend my fix definitely helps (I checked it just now). If the scanner crashed, you did something wrong. Note you need to replace the entire piece of code, not just add one line...

Borcejn avatar Apr 07 '24 18:04 Borcejn

@Borcejn Thank you for your reply. This is very important to me. I appreciate your help. What I do and what my actions are:

  1. I download the scanner: wget https://raw.githubusercontent.com/marcocesarato/PHP-Antimalware-Scanner/master/dist/scanner --no-check-certificate (if I scan, the scanner "freezes" on the virus file)
  2. I change lines number 7110-7149 to the code of your function private function calc
  3. After that I started scan and I get an error:
root@ip-172-31-37-200:/home/ubuntu# php7.4 scanner ./virus -r --path-report ./virusscan_$(date +%d-%b-%y).html
PHP Fatal error:  Uncaught PharException: phar "/home/ubuntu/scanner" has a broken signature in /home/ubuntu/scanner:8
Stack trace:
#0 /home/ubuntu/scanner(8): Phar::webPhar()
#1 {main}
  thrown in /home/ubuntu/scanner on line 8

What am i doing wrong?

58legend avatar May 01 '24 09:05 58legend

@58legend oh no, you cannot edit .phar directly. It's like trying to edit a zip/tar archive in notepad :) Instead you need to download all files of this project and then edit mentioned script. After fix run awmscan/src/index.php (you can set "scanner" as an alias for this localization in your environment).

Borcejn avatar May 04 '24 23:05 Borcejn

I understand what you mean. Downloaded and edited Dist, after this build new phar file. It works!!! @Borcejn Thank you) Now I have fixed scanner here: wget https://raw.githubusercontent.com/58legend/scanner/main/scanner --no-check-certificate and here my favorite command to scan: php scanner ./ -r --path-report ./virusscan_$(date +%d-%b-%y).htm

58legend avatar May 09 '24 12:05 58legend

@58legend in my case, the scan still crashes for reasons unknown to me ((

I downloaded your version, but the flight did not stop

Xolog avatar Jul 04 '24 13:07 Xolog