I monitor bots activity for a while and often see such behavior:

  • [SUCCESS] Robot comes to the page
  • [SUCCESS ..?] Robot exploits known vulnerability
  • [FAIL :(] Robot verifies access to web-shell
  • [...] Robot leaves

When bot mentions that exploit was not successful, it would loose all interest to your web-site and will go to find next victim. But what it was intended to do? Just verify existing vulnerability? Install some malware? ...or miner?

All source code is available in github repo migolovanov/sandbox

Sanbox idea

To answer the questions above we have to trick bot to believe that exploit was successful and that it has shell access to application. All we need to do is:

  • parse parameter values
  • detect scripting language
  • return expected output of the command

It is pretty much easy to say "Hey, let's parse that command and return expected output", but let's see on example. There is die(); function in PHP that should output message to console and terminate current script. But its behavior differs depending on value type:

Command stdout stderr exit code
die(1); 1
die("test"); test 0
die(test); test PHP Warning: Use of undefined constant test - assumed 'test' (this will throw an Error in a future version of PHP) in php shell code on line 1 0

stdout (Standard output) and stderr (Standard error)  - output streams that are used with process to write some data in console. In general, you should see both, but if errors are disabled in PHP settings, only stdout would be displayed. exit code shows if command was executed successful (default 0) or not (any other status), in general it is used for error handling and is not visible in script output.

As you see, it would be really hard to make parser of some language and rewrite all its functions (and probably miss some undocumented behavior). So, I decided that the best language parser is the language interpreter itself. It would be much easier and rather safe to execute untrusted scripts in Docker containers with limited resources and lifetime.

The scheme of communication with sandbox looks this way:

User sends commands to API, it creates docker container for each command, logs all system calls (open file, execute shell command, download file etc.) and returns result of execution to the user.

Intercept scripts

While executing PHP scripts I needed to somehow rewrite system functions to add logging actions there. Luckily PHP has uopz PECL module, that allows to add hooks on functions. The install procedure is simple:

apt-get install php php-dev php-pear
pecl install uopz
echo 'extension=uopz.so' >> /etc/php/7.2/cli/php.ini

By default exit-functions are blocked, but it's possible to enable them this way:

uopz_allow_exit(true);

Example of hook:

uopz_set_hook('file_get_contents', function ($str) { logger("OPEN READ {$str}"); });

For bash script it was even easier - replace original binary with bash script that logs command to file and calls original bash binary like that:

cmd=${@/eval/}
cmd="${cmd//bash/bash.original}"
eval $cmd

File download

Most likely bot will try to download exploits and execute them on target host. To track such attempts, I replaced curl and wget tools on sandbox with python script. It downloads file and prints content to output.

There is much more left to do, to make script work more accurate, save data to files etc. But at least it will save file content so it would be available even when file was removed from malicious server.

API

As API-server I used node.js, to parse GET- and POST-arguments and for each of them create docker container to execute it:

docker run --rm --cpu-quota 50000 --stop-timeout 60 -v /data/files:/data/files 2d8ru/sandbox -c ${command}

For example if we send to API request asking to list files in current dir (ls) and download some page (wget https://google.com):

GET /?php=die(system("ls%26%26wget+http%3a//google.com>/dev/null"))%3b HTTP/1.1
Host: sandbox
Connection: close

We will get following response:

{
   "error":false,
   "message":{
      "php":{
         "stdout":"bin\nboot\ndata\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\nvar",
         "stderr":"",
         "cmd":"die(system(\"ls&&wget http://google.com>/dev/null\"));",
         "log":[
            "2018-10-25T15:34:54+00:00 PHP EXEC: ls&&wget http://google.com>/dev/null",
            "2018-10-25T15:34:54+00:00 BASH EXEC -c ls&&wget http://google.com>/dev/null",
            "2018-10-25 15:34:54.977360 http://google.com 200 /data/files/87cfa963345b55fbb75fa7c11b769dac"
         ]
      }
   }
}

As you see, there is current directory file listing in stdout and no errors in stderr. In log variable we see actions performed by the analyzed code:

  • run php command (call of system functions)
  • run command in bash (bash -c command called by system function in PHP)
  • result of file download (response code - 200 and path where file was stored)

This info should be enough to investigate script behavior and for webserver to correctly response with executed code (stdout and stderr values).

Honeypot integration

Now we have working sandbox with API, where we can safely execute malicious command and get its output to display for attacker.

To integrate sandbox with honeypot I have made lua plugin, which will call API and store command execution result in redis to reduce wait time and count of calls to API. You can find source code on github in wtf-plugin-honeybot-sandbox repo.

Conclusion

Now we can trick bots that shell upload was successful or that application has known RCE vulnerability and thanks to that we can get more info regarding robot behavior.

References

  1. Sources from article migolovanov/sandbox
  2. uopz PHP module
  3. Lua module for honeypot wtf-plugin-honeybot-sandbox