| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> PHP知识库 -> SQL注入漏洞-SQL注入原理与实践 -> 正文阅读 |
|
[PHP知识库]SQL注入漏洞-SQL注入原理与实践 |
什么是SQL注入?:所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 ????????MySQL基础语法:MySQL 教程_w3cschool???? ? ?浏览器打开sqli.com/sqli-1.php,其源码如下: 代码已经写了注释,如果没有给这个页面传参数,则默认传一个id参数,它的值为1。可以看到,在13行,获取到用户传过来的参数后,直接进入了SQL语句,带进了查询语句,没有任何其他操作,很明显的存在SQL注入。在sql语句中,new被反引号包起来了,关于反引号的作用,请参考预备知识中的“mysql创建表时反引号的作用”。 先在URL后面加一个单引号,可以看到页面报错了,这个警告的意思是:mysql_fetch_array()这个函数第一个参数期望的参数类型是一个资源集,但是给的是一个布尔值,出现该警告的位置在代码的第15行。 ?查看代码,发现它的第一个参数$result这个参数的值来自第14行,它是mysql_query执行后的返回值。 前面的语句在MySQL里执行出错了,于是返回了false,这说明我们传进去的参数1’破坏了SQL语句,我们可以稍微修改下代码,在第14行后面输出一下当前执行的sql语句。修改后的代码如下: 我们可以直接复制该语句到MySQL中去执行。由于数据库编码是utf8的,直接在mysql客户端中进行查询,中文会显示乱码,所以可以到phpmyadmin中来执行这个sql语句。 访问http://localhost/phpmyadmin/,如果需要输入密码,账号输入root,密码为空,然后点击执行登录。 复制刚才输出的语句:select * from `new` where id = 1',然后点击执行。 这说明我们输入的参数被mysql服务器当作SQL命令执行了!再来看下通过and 1=1和and 1=2来判断网页是否存在SQL注入的原理。首先传个1 and 1=1,页面显示正常,而且最终执行的SQL语句是: select * from `new` where id = 1 and 1=1 再次尝试1 and 1=2,页面除了输出最终执行的SQL页面,没有其他任何输出。 其实这里SQL语句执行并没有出错,问题出在and 1=2上,再来一下这条语句,它从new这个表里面查询数据,但是有限制条件,where指查询满足后面的数据,也就是说需要查找id = 1的数据,并且需要 1=2,但是1=2这是用于不可能满足的。所以,这个语句被数据库准确无误地执行了,但是它并没有找到符合条件的数据,所以没有任何数据输出。而前面加 1 and 1=1能正常返回数据,是因为1=1这个永远为真,所以这个条件可以说是跟没加一样。 通过and 1=1 、 and 1=2来判断页面是否存在注入,是因为我们修改了原本的SQL语句逻辑,当添加and 1=1的时候,这个条件永远成立,所以页面一定返回正常(需要该页面存在注入),但是当添加and 1=2的时候,这个条件永远不成立,所以如果该页面存在注入,必然不会返回任何数据。所以就会说,如果添加and 1=1页面返回正常,添加and 1=2页面返回不正常或者错误,就说明页面存在SQL注入。当然这只能大概判断,而不是说明该页面一定存在注入,其他情况在后面的实验里说。 那万能密码又是怎么回事呢? 网上找到的万能密码都是类似’ or 1=1这种,为什么输入这个字符串就可以绕过登录了呢? 我们也用or 1=1试试,看在这个页面提交这个值是什么效果。 访问http://sqli.com/sqli-1.php?id=1 or 1=1 发现居然输出了多条记录,这里其实输出了new表中所有的数据,所有的建表语句都在sql.sql文件中,可以打开看下sqli数据库中有哪些表,有什么内容。 它这里只要满足id =1 或者1=1任意一个条件,而1=1永远为真,所以返回了所有的数据,其效果与select * from `new`一样。 但是如果仅仅只是读取new这个表里面的数据,对我们来说,其实意义不大,因为即使不存在注入,我们也可以增加ID的值来查看其中所有的信息。比如访问http://sqli.com/sqli-1.php?id=2 可以查看id=2的内容。 我们希望的是能够通过注入,获取一些网站管理员不希望我们知道的数据,比如管理员的账号密码。 在本页面的源码中,注入点在where后面,也就说,从哪个表中查询什么数据是写死的,那么怎么才能通过注入获取其他表中的内容呢? 比如获取user表中的数据。 通过union操作符就可以达到我们的目的,union可以合并两条或者多条select语句的查询结果,它的语法如下: Select column-1,column-2,…,column-N from table-1 union select column-1,column-2,…,column-N from table-2 它的返回值是两个select语句查询结果组成的表。我们可以通过在第一个查询后面注入一个union运算符,并添加另外一个任意查询,就可以读取到数据库中用户可以访问的任何表。但是,使用union有一些限制: 1.两个查询返回的列数必须型相同。 2.两个select语句对应列所返回的数据类型必须是相同或者是兼容的。 当前的两个select 他们查询的列的个数必须一致,如果第一个select查询了5列,则第二个select也必须查询5列。 如:select title, content from new union select username, password from user; 像上面的查询,列数相同,会返回这2个查询的结果集。 来到phpmyadmin中验证一下,如图: 重点关注怎么知道查询中列的数量,代码中,写的是select * from `new`,直接在代码中看不出来new这个表中有多少列,而需要去查看建表语句,而在实际应用中,除非是开源的CMS,否则你不可能准确知道它查询了多少列,这就需要我们想个办法来获取当前查询的列数。 一个简单的办法是通过order by 子句来确定。Order by 是根据指定的列名进行排序。 Order by 子句可以接受一个列名作为参数,也可以接受一个简单的、能表示特定列的数字,所以可以通过增大order by 子句中代表列的数字来识别查询中的列数。 来到phpmyadmin测试。执行show create table new来查看建表语句 ?可以看到这个表有3列,然后来测试用order by子句来测试列数量,从1开始,每次查询加1,加到报错为止,如: 1的时候正常,继续增加该值。到4的时候,发现执行出错。 因为这里一共只有3列,但是你要根据第4列来进行排序,当然就会报错了。 ?再测试一个语句,把* 改成title, content。还是从1开始测试。结果如下: select title, content from new order by 1 ,结果正常。 select title, content from new order by 3 ,报错。因为我们这里只查询了2列。 现在就来注入页面进行测试。 访问http://sqli.com/sqli-1.php?id=1 order by 1,页面正常。 依次增加order by 子句后面的值,直到4的时候,页面报错。 所以我们可以得出结论,在这个查询中,一共查询了3列,因为4的时候报错了。 然后就可以通过union 来进行查询user表中的数据啦。 根据union的语法,构造注入语句,通过前面的测试已经知道这个查询一共查询了3列,在符合union操作符的前提下,可以构造如下语句: http://sqli.com/sqli-1.php?id=1 union select null, username, password from user ? 这样就获取了user表中所有的用户名和密码。 如果你写的网站存在注入,别人很容易就能通过SQL注入拿到管理员账号密码,那么网站就很容易被人篡改,并且导致信息泄露。 这样就获取了user表中所有的用户名和密码。 如果你写的网站存在注入,别人很容易就能通过SQL注入拿到管理员账号密码,那么网站就很容易被人篡改,并且导致信息泄露。 这就需要了解mysql数据库的information_schema 这个数据库了。 构造出获取所有数据库的语句,为: http://sqli.com/sqli-1.php?id=1 union select 1,schema_name,3 from information_schema.schemata 可以看到返回了所有的数据库名: ? 其中schema_name 和 数字3的位置可以互换,没有什么影响,因为这里只需要有一个位置输出了就行,其中的数字1和3 可以被其他数字或者字符或者NULL代替,用字符代替的时候,记得用双引号或者单引号引上。虽然1和3可以被代替,但是不能缺少,因为必须要保持3列union才能正确执行。 这里后面的from 为什么是information_schema.schemata而不是schemata呢? 因为在当前执行SQL语句的环境中,它的默认是库是sqli,如果我们要跨库查询,则需要在表名前加上库名,如果不加上库名,则表示在sqli这个数据库的schemata表中查询,由于sqli这个数据库不存在schemata表,所以会报错,如下图: 如果我们输出mysql 的报错信息,就可以看到很明显的提示。 修改sqli-1.php源文件,在执行sql语句的下面加入如下代码: ??? if(!$result){????? ? ?? ?? die(mysql_error()); ??? } 再次访问上述链接: 提示表sqli.schemata 不存在。 通过前面的注入语句,我们已经获取了所有的数据库库名 在实验步骤二中,我们已经获取了sqli数据库user表中的数据,这次换一个,改成mysql。mysql这个数据库保存了mysql的账号密码信息,在注入的时候,可以读取该表中的数据来读取mysql账号密码。 现在就通过注入来获取mysql的账号和密码。 首先查看mysql这个数据库中有哪些表。构造如下语句: 1 union select 1, table_name,3 from information_schema.tables where table_schema='mysql' ? 这个表中保存了mysql服务器的账号和密码的hash值。然后构造语句获取该表中的所有列名。构造语句为: 1 union select 1, column_name, 3 from information_schema.columns where table_name ='user' and table_schema='mysql' 访问后返回该表中所有的列名。 存在User字段和Password字段,User字段保存了MySQL服务器的用户名,Password字段保存的是用户的Hash值。 由于在这里有2个位置可以输出,所以我们可以一次查询用户的账号和密码。 构造语句如下: 1 union select 1, User, Password from mysql.user ?发现页面只有输出root。进入phpmyadmin确认一下。 ? ? |
|
PHP知识库 最新文章 |
Laravel 下实现 Google 2fa 验证 |
UUCTF WP |
DASCTF10月 web |
XAMPP任意命令执行提升权限漏洞(CVE-2020- |
[GYCTF2020]Easyphp |
iwebsec靶场 代码执行关卡通关笔记 |
多个线程同步执行,多个线程依次执行,多个 |
php 没事记录下常用方法 (TP5.1) |
php之jwt |
2021-09-18 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 11:54:49- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |