词法分析在安全产品中的应用

问题背景

最近分析了一些waf、rasp等安全产品,发现核心检测逻辑非常依赖词法分析。

总结一点东西:

  • 词法分析是什么?
  • 举个例子:OpenRASP怎么用到词法分析做安全策略?
  • 怎么实现词法分析?
  • 还有哪些安全防护产品用到词法分析?

分析过程

词法分析是什么?

词法分析可以将字符串按照自己的规则变成一串token。

比如一个算术表达式 1+2*3,按照一定规则做词法分析后可以变成 NUM ADD NUM MUL NUM 一串token。

编程语言在执行前也要做词法分析,比如Python词法分析结果是这样子的

1
2
3
4
5
6
7
➜  ~ python -m tokenize
a=1

1,0-1,1: NAME 'a'
1,1-1,2: OP '='
1,2-1,3: NUMBER '1'
1,3-1,4: NEWLINE '\n'

安全产品怎么用到词法分析做安全策略?

拿OpenRASP举例,OpenRASP检测SQL注入、命令执行时核心检测算法非常依赖词法分析。

  • 先看看请求是怎么到检测插件的

    OpenRASP可以作为PHP扩展保护PHP应用,在扩展被加载时hook了很多执行sql的函数,比如openrasp项目agent/php5/hook/openrasp_mysqli.cc文件中可以看到hook了mysqli模块的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * mysqli相关hook点
    */

    ...

    PRE_HOOK_FUNCTION(mysqli_query, SQL);
    POST_HOOK_FUNCTION(mysqli_query, SQL_ERROR);
    ...

    当业务代码在做数据库查询时,如果调用mysqli_query等函数执行SQL,就会在执行SQL前经过OpenRASP的检测策略。

    检测策略文件是JavaScript写的,代码位置在plugins/official/plugin.js

    在执行SQL注入检查时,会传入即将执行的SQL(如 “select * from users where a=1”)和完整的请求信息(包含请求参数)。

    1
    2
    3
    ...
    plugin.register('sql', function (params, context) { //params.query包含完整sql,context包含完整请求
    ...
  • OpenRASP怎么做SQL注入检测?

    SQL注入检测策略默认开启了三种:

    * 词法分析后,判断用户输入是否改变SQL执行逻辑
    * 词法分析后,判断SQL语句是否有写文件操作、使用黑名单函数等
    * sql server执行异常

    第一类SQL注入检测策略:对即将执行的sql语句做分词,判断用户的输入占用了几个token,正常情况下,用户输入应该只占有一个token。 比如最终执行sql是select id from user where name=”user_input_name”, 正常请求时 “user_input_name” 会变成一个字符串token。如果碰到恶意请求闭合了双引号,用户输入就至少占用两个token。

    详细可见官方文档 OpenRASP v0.20 发布 | 性能提升与零规则漏洞检测

    实现的代码在 plugins/official/plugin.js文件

    1
    2
    3
    4
    5
    6
    7
    // raw_tokens包含有词法分析得到的token串
    // userinput_idx是用户输入在最终执行的sql的开始位置

    if (is_token_changed(raw_tokens, userinput_idx, value.length, distance, is_sql=true)) {
    reason = _("SQLi - SQL query structure altered by user input, request parameter name: %1%, value: %2%", [name, value])
    return true
    }

    第二类SQL注入检测策略:检查分词后的token串中是否命中规则。

    比如 为了对抗”利用SQL注入中写webshell”这种攻击方式,就检查最终执行的sql语句是否包含 [“into”,”outfile”]和[“into”,”dumpfile”] 两类token

    select "webshell.txt" into outfile '/var/www/webshell.php' 可以用来写入Webshell

    检测策略的实现代码也在 plugin.js 中可以看到

    1
    2
    3
    4
    5
    6
    7
    8
    else if (features['into_outfile'] && i < tokens_lc.length - 2 && tokens_lc[i] == 'into')
    {
    if (tokens_lc[i + 1] == 'outfile' || tokens_lc[i + 1] == 'dumpfile')
    {
    reason = _("SQLi - Detected INTO OUTFILE phrase in sql query")
    break
    }
    }

    命令执行检测同样也依赖词法分析。

    可以看出来OpenRASP中词法分析在sql注入、命令执行检测算法中目前很重要。

怎么实现词法分析?

OpenRASP有两个JS接口可以获取分析后的token串,见sql_tokenize

实现接口的代码在openrasp-v8项目中base/flex/flex.cc文件,这个文件是flex工具生成的,原始的规则文件并没有开源出来。

1
2
3
4
5
6
7
static sql_tokenize(query) {
return tokenize(query, "sql")
}

static cmd_tokenize(query) {
return tokenize(query, 'bash')
}

从实现角度上看,词法分词可以分为三类:

  • 借助工具,如flex、antlr。openrasp就是用借助flex做的
  • 纯手写实现,比如 libinjection
  • combinator库等半自动

哪些安全产品用到了词法分析?

  • openrasp
  • waf
    • libinjection
    • 命令执行防护
    • xss防护
    • sql注入防护
  • hids
    • webshell检测
  • db防火墙
  • 代码混淆与反混淆

上面的部分产品除了用到词法分析,还会用到语法分析、语义分析。

总结

主要还是围绕OpenRASP来写的这篇文章,没有细讲词法分析的实现、做项目时应该用工具还是完全手写词法分析、语法分析这类检测引擎的误报和漏报场景等问题。

关于词法分析、语法分析的原理、算法和实现,推荐看 编译原理之美 这个课程,一级棒。

参考