SQL注入语义分析库libinjection简介

SQL语句
733
0
0
2023-09-08
标签   SQL注入
目录
  • SQL注入语义分析库libinjection
  • 什么是libinjection
  • libinjection和正则表达式
  • modsecurity 如何使用libinjection
  • ModSecurity只用了libinjection防御sql注入吗?
  • WAF研发领域,语义分析相对于正则表达式先进性的研究
  • 参考文献

SQL注入语义分析库libinjection

什么是libinjection

libinjection是一款用于防御SQL注入攻击的开源软件库。它是由C语言编写的,可以嵌入到任何Web应用程序中,并可以较为准确地检测和防止恶意SQL注入语句。libinjection采用了基于正则表达式的技术来识别和拦截SQL注入攻击,同时其开放源代码的特点也使得其具备了较高的可定制性和扩展性。

libinjection是一个基于C语言的SQLi词法解析分析器,它可以通过对不同语句进行词法分析和语法分析来实现对SQL语句以及HTML语句的解析。

在此之前,市场上绝大多数的WAF都是基于正则匹配(Regex)的,很多的WAF(防火墙)以及NGWAF(下一代防火墙)出于性能方面的考虑,都会选择使用这个库来代替常规的正则表达式。

ModSecurity,它是Apache和nginx的流行WAF。ModSecurity捆绑了libinjection,mod_security从2.7.4版本开始就支持libinjection(dectectSQLi-v2.7.4、detectXSS-v2.8.0)。

libinjection和正则表达式

libinjection和正则表达式都是非常有效的防御SQL注入攻击的技术,但两者之间有一些差异和优劣。

libinjection

libinjection是一个专门用于检测SQL注入攻击的库,其使用了一个基于机器学习的算法来分析请求中的SQL语句。与正则表达式相比,它有以下优点:

  • 更准确:libinjection使用机器学习算法分析SQL语句,因此可以更准确地检测SQL注入攻击,同时减少误报率。
  • 更容易维护:libinjection的规则库相对较小,易于维护。
  • 更快:libinjection的性能要比正则表达式好,响应时间更短。

正则表达式

正则表达式是一种常见的用于检测和防御各种Web应用程序攻击(包括SQL注入攻击)的技术。与libinjection相比,它有以下优点:

更通用:正则表达式不仅可用于检测SQL注入攻击,而且还可用于检测其他类型的Web应用程序攻击。更灵活:正则表达式可以使用各种模式和操作符,从而可以灵活地处理各种复杂情况。更可配置性:正则表达式的规则库可以根据实际情况进行定制和配置。

总体而言,libinjection可能比正则表达式更准确,更容易维护,并具有更快的性能,但正则表达式更通用,更灵活,具有更高的可配置性。因此,为了获得最佳的安全性能,您可能需要考虑同时使用这两种技术。

modsecurity 如何使用libinjection

下面是使用libinjection和ModSecurity进行SQL注入检测和预防的步骤:

安装ModSecurity

安装并配置ModSecurity,使其可以与您的Web服务器一起使用。

安装libinjection

安装libinjection并将其与ModSecurity集成。如果您使用的是Debian或Ubuntu,可以使用以下命令进行安装:

sudo apt-get install libinjection-dev

在ModSecurity中使用libinjection

将ModSecurity配置为使用libinjection来检测SQL注入攻击。以下是一个示例ModSecurity规则,可用于检测SQL注入攻击:

SecRule ARGS "@detectSQLi" \
    "id:,\
    phase:,\
    t:none,\
    t:lowercase,\
    t:replaceComments,\
    t:compressWhitespace,\
    ctl:auditLogParts=+E,\
    ctl:debugLogParts=+E,\
    ctl:requestBodyProcessor=XML,\
    ctl:ruleEngine=on,\
    ctl:auditEngine=on,\
    ctl:ruleRemoveByTag=abcd \
    ctl:ruleRemoveById= \
    libinjection_check"

SecRule &TX:INJECTION @eq \
    "id:,\
    phase:,\
    t:none,\
    ctl:auditLogParts=+E,\
    ctl:debugLogParts=+E,\
    ctl:requestBodyProcessor=XML,\
    ctl:ruleEngine=on,\
    ctl:auditEngine=On,\
    ctl:ruleRemoveByTag=abcd \
    ctl:ruleRemoveById= \
    block"

这个规则中包含了两个SecRule。第一个将对输入请求进行规范化和过滤,然后使用libinjection检测SQL注入攻击。第二个SecRule将根据检测结果来做出相应的动作,例如阻止访问请求或将其记录在日志中。

ModSecurity只用了libinjection防御sql注入吗?

ModSecurity不仅使用了libinjection,还使用了正则表达式等多种技术来进行SQL注入攻击防御。实际上,在ModSecurity中使用正则表达式是非常常见的一种方法,因为这种方法可以用于检测和阻止各种类型的Web应用程序攻击,包括SQL注入攻击。

以下是一个示例ModSecurity规则,使用正则表达式检测SQL注入攻击:

SecRule ARGS "@detectSQLi" \
    "id:,\
    phase:,\
    t:none,\
    t:lowercase,\
    t:replaceComments,\
    t:compressWhitespace,\
    ctl:auditLogParts=+E,\
    ctl:debugLogParts=+E,\
    ctl:requestBodyProcessor=XML,\
    ctl:ruleEngine=on,\
    ctl:auditEngine=on,\
    ctl:ruleRemoveByTag=abcd \
    ctl:ruleRemoveById= \
    libinjection_check"

SecRule &TX:INJECTION @eq \
    "id:,\
    phase:,\
    t:none,\
    ctl:auditLogParts=+E,\
    ctl:debugLogParts=+E,\
    ctl:requestBodyProcessor=XML,\
    ctl:ruleEngine=on,\
    ctl:auditEngine=On,\
    ctl:ruleRemoveByTag=abcd \
    ctl:ruleRemoveById= \
    block"

虽然语义分析引擎能够更准确地识别和阻止恶意流量,但是正则表达式仍然是一种非常重要的检测方式,可以用来完成一些特定的任务。

例如,对于某些规则化的数据格式,如邮件地址、电话号码和身份证号码等,使用正则表达式可以快速、准确地进行匹配判断。此外,正则表达式还可以用来检测各种类型的注入攻击(如SQL注入和XSS攻击)等漏洞利用行为。

因此,在实际的安全防御中,WAF通常会同时采用多种技术手段,包括语义分析引擎和正则表达式等,以提高其检测能力,并最大程度地保护Web应用程序免受攻击威胁。

WAF研发领域,语义分析相对于正则表达式先进性的研究

多年以来,WAF对攻击的检测,通常使用正则表达式,典型的如ModSecurity。作为老牌的WAF,其拥有庞大的正则规则库。其检测率高,但也正因为规则数量庞大,正则逐一匹配,此过程速度慢,性能低。

对于同步检测的WAF产品,部署并对网站提供防护后,会带来不小的访问性能影响。

新兴的WAF产品,渐有使用语义分析引擎取代正则表达式检测。

其优势究竟何在,性能又能提升多少?

WAF研发领域,语义分析相对于正则表达式先进性的研究

直接查看原文链接: https://www.toutiao.com/article/6944906084217799207/

libinjection架构和使用

官方使用示例

#include <stdio.h>
#include <string.h>
#include "libinjection.h"

int main(int argc, const char* argv[])
{
    char fingerprint[];
    const char* input;
    size_t slen;
    int issqli;

    if (argc <) {
        fprintf(stderr, "Usage: %s inputstring\n", argv[]);
        return -;
    }

    input = argv[];
    slen = strlen(input);


    issqli = libinjection_sqli(input, slen, fingerprint);
    if (issqli) {
        printf("sqli with fingerprint of '%s'\n", fingerprint);
    } else {
        printf("not sqli\n");
    }


    return issqli;
}

具体的检查sql注入实现为 libinjection_is_sqli:

int libinjection_is_sqli(struct libinjection_sqli_state * sql_state)
{
    const char *s = sql_state->s;
    size_t slen = sql_state->slen;

    /*
     * no input? not SQLi
     */
    if (slen ==) {
        return FALSE;
    }

    /*
     * test input "as-is"
     */
    libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
    if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,
                          sql_state->fingerprint, strlen(sql_state->fingerprint))) {
        return TRUE;
    } else if (reparse_as_mysql(sql_state)) {
        libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);
        if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,
                              sql_state->fingerprint, strlen(sql_state->fingerprint))) {
            return TRUE;
        }
    }

    /*
     * if input has a single_quote, then
     * test as if input was actually '
     * example: if input if "' = 1", then pretend it's
     *   "'' = 1"
     * Porting Notes: example the same as doing
     *   is_string_sqli(sql_state, "'" + s, slen+, NULL, fn, arg)
     *
     */
    if (memchr(s, CHAR_SINGLE, slen)) {
        libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI);
        if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,
                              sql_state->fingerprint, strlen(sql_state->fingerprint))) {
            return TRUE;
        } else if (reparse_as_mysql(sql_state)) {
            libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL);
            if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,
                                  sql_state->fingerprint, strlen(sql_state->fingerprint))) {
                return TRUE;
            }
        }
    }

    /*
     * same as above but with a double-quote "
     */
    if (memchr(s, CHAR_DOUBLE, slen)) {
        libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_DOUBLE | FLAG_SQL_MYSQL);
        if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,
                              sql_state->fingerprint, strlen(sql_state->fingerprint))) {
            return TRUE;
        }
    }

    /*
     * Hurray, input is not SQLi
     */
    return FALSE;
}

这段代码是用于检测 SQL 注入攻击的,通过调用 libinjection 库对输入的 SQL 查询语句进行指纹识别,并判断是否存在于已知的 SQL 注入指纹库中。

  • 首先获取输入字符串 s 和其长度 slen。如果输入字符串长度为0,则直接返回 FALSE,不需要进行检测。
  • 接着,对输入的 SQL 查询语句进行指纹识别,并标记 FLAG_QUOTE_NONE 和 FLAG_SQL_ANSI 标志位。然后通过 sql_state->lookup 函数查找该指纹是否已经存在于数据库中,如果存在则返回 TRUE。

“test input as-is” 的意思是对输入的 SQL 语句进行原样测试,即不进行任何转义或处理。这相当于将输入的 SQL 语句作为一个整体进行指纹识别,并标记 FLAG_QUOTE_NONE 和 FLAG_SQL_ANSI 标志位,在已知的 SQL 注入指纹库中查找是否存在相同的指纹。如果存在,则说明该输入可能是 SQL 注入攻击,否则继续进行其他检测。通常情况下,如果输入字符串包含单引号或双引号字符或者 SQL 关键字,就需要进行进一步的检测和处理。

  • 如果该指纹不存在于数据库中,则尝试将查询语句解析为 MySQL 语法,通过 reparse_as_mysql 函数进行转换。如果转换成功,则再次调用 libinjection_sqli_fingerprint 函数对转换后的 SQL 查询语句进行指纹识别,并标记 FLAG_QUOTE_NONE 和 FLAG_SQL_MYSQL 标志位。最后再次通过 sql_state->lookup 函数查找该指纹是否存在于数据库中,如果存在则返回 TRUE。
  • 接下来,如果输入的字符串中包含单引号字符,则调用 libinjection_sqli_fingerprint 函数对 SQL 查询语句进行指纹识别,并标记 FLAG_QUOTE_SINGLE 和 FLAG_SQL_ANSI 标志位。然后通过 sql_state->lookup 函数查找该指纹是否已经存在于数据库中,如果存在则返回 TRUE。
  • 如果该指纹不存在于数据库中,则尝试将查询语句解析为 MySQL 语法,通过 reparse_as_mysql 函数进行转换。如果转换成功,则再次调用 libinjection_sqli_fingerprint 函数对转换后的 SQL 查询语句进行指纹识别,并标记 FLAG_QUOTE_SINGLE 和 FLAG_SQL_MYSQL 标志位。最后再次通过 sql_state->lookup 函数查找该指纹是否存在于数据库中,如果存在则返回 TRUE。
  • 最后,如果输入的字符串中包含双引号字符,则调用 libinjection_sqli_fingerprint 函数对 SQL 查询语句进行指纹识别,并标记 FLAG_QUOTE_DOUBLE 和 FLAG_SQL_MYSQL 标志位。然后通过 sql_state->lookup 函数查找该指纹是否已经存在于数据库中,如果存在则返回 TRUE。
  • 如果输入字符串没有被识别为 SQL 注入攻击,则最后返回 FALSE。

总结,这段代码是用于检测 SQL 注入攻击,通过指纹识别技术来进行检测和防御。同时支持 ANSI SQL 和 MySQL 两种语法,并能够检测包含单引号和双引号字符以及 SQL 关键字的字符串。

例如,输入常用的SQL注入的检测语句 :’ and 1=1

libinjection会将其转换为s&1,其中单引号依据定义被转换为s,and被转换为&,数字被转换为1。

libinjection在转换完后,通过二分查找算法对内置的特征进行匹配,匹配到则将SQL注入识别特征复制进fingerprint变量并返回。

转换 搜索关键字: sql_keywords ,在文件libinjection_sqli_data.h 中~!

在libinjection中,将SQL关键字转换为单个字母时使用的字符集通常是a-z或A-Z。这意味着只有26个不同的字母可以用来表示所有的SQL关键字。

为了解决这个问题**,libinjection使用了一些技巧来扩展字母表的大小。其中一个技巧是引入数字后缀。例如,对于两个关键字SELECT和SET,它们都会被转换为字母s。为了避免冲突,第二个关键字SET会被转换为字母s2。**

另一个技巧是使用大写字母表示特殊的关键字。例如,LIMIT和OFFSET是MySQL和PostgreSQL中常用的关键字,它们被分别映射为L和O。这样做的好处是,即使一个关键字被映射为小写字母,仍然可以通过使用大写字母来表示其他特殊关键字,以避免冲突。

需要注意的是,虽然仅有26个字母可以用来表示所有的SQL关键字,但由于上面提到的技巧,libinjection能够支持更多的关键字,并确保每个关键字都使用唯一的单个字母来表示。

参考文献

【技术分享】如何绕过WAF/NGWAF的libinjection实现SQL注入

参考URL: http://www.52bug.cn/hkjs/3025.html

SQL注入与libinjection分析(1)SQL注入

参考URL: https://blog.csdn.net/lqy971966/article/details/105269658