代码审计工具 Semgrep

 

文章首发于先知社区:轻量级代码审计工具: Semgrep - 先知社区

Semgrep 介绍

什么是 Semgrep?

Semgrep 是一个开源的静态代码分析工具,旨在帮助开发人员和安全专家发现和修复软件代码中的安全漏洞、代码质量问题和最佳实践违规等。Semgrep 支持多种编程语言,包括但不限于 Python、JavaScript、Go、Java、PHP 等。

以下是 Semgrep 的一些主要特点和优势:

  1. 易于使用: Semgrep 的语法简单直观,使用 YAML 或者纯文本格式的规则定义,使得规则编写和理解变得容易。您可以根据特定的需求编写自定义规则,或者使用社区维护的规则库。
  2. 快速扫描: Semgrep 使用高效的静态分析算法,能够在大型代码库中进行快速扫描。它具有可扩展性,适用于小型项目和大型企业应用程序。
  3. 广泛的规则库: Semgrep 提供了一个丰富的规则库,涵盖了安全漏洞、常见的代码错误、最佳实践和性能问题等多个方面。您可以直接使用这些规则,也可以根据自己的需求进行定制和扩展。
  4. 多语言支持: Semgrep 支持多种流行的编程语言,如 Python、JavaScript、Go、Java 等。这使得它成为跨多个技术栈的团队或项目的理想选择。
  5. 集成和扩展性: Semgrep 可以与各种 CI/CD 工具和代码编辑器集成,例如 GitLab、GitHub Actions、Jenkins 等。此外,Semgrep 还提供了 API 和 CLI 接口,使您能够轻松集成到现有的工作流程中。
  6. 可定制性: Semgrep 允许您根据自己的需求创建自定义规则。您可以使用 Semgrep 提供的丰富的模式匹配语法来编写适合特定代码风格和规范的规则。
  7. 活跃的社区: Semgrep 拥有活跃的开源社区支持,社区维护了大量的规则库,并定期更新和改进 Semgrep 的功能和性能。

以上介绍来自于 ChatGPT :heart_eyes:,本文的主要目的是讲解 Semgrep 使用方法以及注意事项。

Semgrep 环境搭建

Semgrep 的官方文档提供了详细的安装教程 。可参考 Semgrep Privacy Policy - Semgrep

安装 semgrep

python3 -m pip install semgrep
semgrep --version

更新 semgrep

python3 -m pip install --upgrade semgrep 

Semgrep 语法教程

Semgrep 的语法非常简单,把官方的教程过一遍就可以入门了,学习成本比 CodeQL 要小很多。

Semgrep 使用

在终端使用

使用 semgrep 命令行操作时,可以通过 –help 查看 semgrep 支持的功能。

└─$ semgrep --help
Usage: semgrep [OPTIONS] COMMAND [ARGS]...

  To get started quickly, run `semgrep scan --config auto`

  Run `semgrep SUBCOMMAND --help` for more information on each subcommand

  If no subcommand is passed, will run `scan` subcommand by default

Options:
  -h, --help  Show this message and exit.

Commands:
  ci                   The recommended way to run semgrep in CI
  install-semgrep-pro  Install the Semgrep Pro Engine
  login                Obtain and save credentials for semgrep.dev
  logout               Remove locally stored credentials to semgrep.dev
  lsp                  [EXPERIMENTAL] Start the Semgrep LSP server
  publish              Upload rule to semgrep.dev
  scan                 Run semgrep rules on files
  shouldafound         Report a false negative in this project.

每一种模式对应不同的功能,其中最重要的是 scan 功能,用于扫描指定的源码,其他功能会在后续进行介绍。

└─$ semgrep scan --help                                                                             
Usage: semgrep scan [OPTIONS] [TARGETS]...

  Run semgrep rules on files

  Searches TARGET paths for matches to rules or patterns. Defaults to searching entire
  current working directory.

  To get started quickly, run

      semgrep --config auto .

  This will automatically fetch rules for your project from the Semgrep Registry. NOTE:
  Using `--config auto` will log in to the Semgrep Registry with your project URL.

  For more information about Semgrep, go to https://semgrep.dev.

  NOTE: By default, Semgrep will report pseudonymous usage metrics to its server if you
  pull your configuration from the Semgrep registry. To learn more about how and why
  these metrics are collected, please see https://semgrep.dev/docs/metrics. To modify
  this behavior, see the --metrics option below.

Options:
  --replacement TEXT              An autofix expression that will be applied to any
                                  matches found with --pattern. Only valid with a
                                  command-line specified pattern.
...

使用官方规则库(Registry)进行扫描

官方维护了一个规则库(Registry)以便用户提交、查找以及选择特定的规则去扫描项目:Explore - Semgrep。其中包含了社区规则以及专业规则(Pro),社区规则存储在 github 仓库中 returntocorp/semgrep-rules: Semgrep rules registry

Registry,根据常见的扫描需求,定制了一些扫描集合,例如 OWASP-TOP-10、CWE-TOP-25 等。

20230710050409

或是根据语言进行分类:

20230710050628

假如我们需要扫描 PHP,可以在 Registry 中进行搜索,查找结果包括 PHP 相关的规则集合,以及其中的所有规则:

20230710050759

可以看到官方为 PHP 归纳了三个规则集合,分别是 php、php-laravel、phpcs-security-audit。

└─$ semgrep --config "p/phpcs-security-audit" /mnt/share/project/ctf_archives/test/baijiacms
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2787 files tracked by git with 10 Code rules:
  Scanning 748 files with 10 php rules.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:03                                                                                                                     
                    
                    
┌──────────────────┐
│ 89 Code Findings │
└──────────────────┘
                           
    /mnt/share/project/ctf_archives/test/baijiacms/includes/baijiacms/common.inc.php 
       php.lang.security.exec-use.exec-use                          
          Executing non-constant commands. This can lead to command injection.
          Details: https://sg.run/5Q1j                                        
                                                                              
          654┆ system('convert'.$quality_command.' '.$file_full_path.' '.$file_full_path);
            ⋮┆----------------------------------------
       php.lang.security.weak-crypto.weak-crypto                             
          Detected usage of weak crypto function. Consider using stronger alternatives.
          Details: https://sg.run/KlBn                                                 
                                                                                       
          372┆ $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
            ⋮┆----------------------------------------
         1049┆ $package['sign'] = strtoupper(md5($string1));
...
┌──────────────┐
│ Scan Summary │
└──────────────┘
Some files were skipped or only partially analyzed.
  Partially scanned: 2 files only partially analyzed due to parsing or internal Semgrep errors

Ran 10 rules on 748 files: 89 findings.

A new version of Semgrep is available. See https://semgrep.dev/docs/upgrading

使用本地规则库进行扫描

离线使用时,可以将 semgrep 官方维护的社区规则库下载到本地,使用 --config 参数指定规则库的路径进行扫描。

└─$ semgrep --config /mnt/share/Tools/web/code_audit/semgrep-rules/php /mnt/share/project/ctf_archives/test/baijiacms
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2788 files tracked by git with 57 Code rules:
  Scanning 748 files with 40 php rules.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:12                                                                                                                     
                
                
┌──────────────┐
│ Scan Summary │
└──────────────┘
Some files were skipped or only partially analyzed.
  Partially scanned: 2 files only partially analyzed due to parsing or internal Semgrep errors
  Scan skipped: 5 files larger than 1.0 MB, 195 files matching .semgrepignore patterns
  For a full list of skipped files, run semgrep with the --verbose flag.

Ran 57 rules on 748 files: 257 findings.

A new version of Semgrep is available. See https://semgrep.dev/docs/upgrading

semgrep 提供了 -o 参数可以输出到文件,但无论是否加不加 --force-color / --no-force-color 参数,输出到文件中的内容都会带上颜色字符,导致在文件中查看并不方便,确实有点坑,但借助 sed 可以消除这些字符。

semgrep scan --config /mnt/share/Tools/web/code_audit/semgrep-rules/php /mnt/share/project/ctf_archives/test/baijiacms --no-force-color  --dataflow-traces | sed 's/\x1B\[[0-9;]*[mK]//g' > result.txt

在 vscode 中使用

vscode 中的 semgrep 插件主要用于配置规则文件路径、排除文件等,配置项如下:

20230710044056

其中 configuration 可以配置规则文件路径用于离线场景下的扫描,如果没有指定, semgrep 会从官方社区获取规则文件内容。

配置完后,vscode 会根据配置规则自动扫描当前 workspace,扫描结果会直接显示在 vscode 的 problem 窗口中,为了方便观察漏洞类型,可以切换树视图。

20230710043746

树视图下的第一列显示了匹配的规则名:

20230710044004

如果同时安装了对应语言的插件,Problem 窗口也会出现其他的错误信息,此时可以通过关键词过滤出 semgrep 条目。

在 Semgrep Cloud Platform (SCP) 中使用

Semgrep 云平台提供了所有 semgrep 的支持,其良好的查询以及结果展示功能是终端以及 vscode 插件所不具备的,缺点在于必须在线使用。

20230710223609

云平台支持 Locally 和 CI/CD 两种方式。

20230710235200

Locally 方式可以直接在终端中执行扫描命令,然后将结果发送到云平台。需要注意的是,这种方式需要先将源码目录初始化为一个 git 项目,否则会报错,并且无法指定特定的扫描规则,默认使用所有的规则,如下所示,这一次扫描使用了所有的 1591 条规则,其中包含了 513 条 Pro 规则。

$ semgrep ci                                                                       

┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2763 files tracked by git with 1591 Code rules, 513 Pro rules:
                                                                                                                        
  Language      Rules   Files          Origin      Rules                                                                
 ─────────────────────────────        ───────────────────                                                               
  <multilang>      76    7689          Community    1078                                                                
  php              60     748          Pro rules     513                                                                
  js              242     273                                                                                           
  html              1      72                                                                                           
  yaml             27       2                                                                                           
  bash              4       2                                                                                           
  json              4       1                                                                                           
                                                                                                                        
Semgrep Pro Engine may be slower and show different results than Semgrep OSS.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸ 100% 0:23:45 
  
  ...
┌──────────────┐
│ Scan Summary │
└──────────────┘
Some files were skipped or only partially analyzed.
  Scan was limited to files tracked by git.
  Partially scanned: 8 files only partially analyzed due to parsing or internal Semgrep errors
  Scan skipped: 196 files matching --exclude patterns, 4 files larger than 1.0 MB
  For a full list of skipped files, run semgrep with the --verbose flag.

CI scan completed successfully.
  Found 502 findings (0 blocking) from 22014 rules.
  Uploading scan results  
  Finalizing scan                                                                                                                                 
  View results in Semgrep Cloud Platform:
    https://semgrep.dev/orgs/dr34d/findings
    https://semgrep.dev/orgs/dr34d/supply-chain
  No blocking findings so exiting with code 0

扫描完成之后,就可以在云平台中查看到扫描结果:

20230711031208

云平台中的查找功能还是比较好用的。

Pro 版本与社区版本

根据官方公告:Unlocking advanced security for all: Semgrep’s latest update, Semgrep 官方将对所有用户开放团队版功能,其中就包括了 Pro 引擎、规则的使用。

我们还将免费向每个由最多10个月度贡献者组成的团队提供 Semgrep Code 的团队版功能(请参阅常见问题解答)。这包括通过 Pro 引擎进行高级代码扫描,跨文件和函数边界查找漏洞,并使用由我们的安全研究团队编写的具有高可信度的 Pro 规则。我们认为这些功能对于运行现代安全程序至关重要。现在在“设置”中打开 Pro 引擎 →

为了让供应链和Code的专业功能对所有人都可用,我们将所有账户升级到团队版,并停用社区版,以求简单明了。我们认为在不同等级之间划分功能或保留功能是不利的,并希望每个用户从一开始就体验到Semgrep的全部价值。此外,为了与我们的创业计划为小团队提供的定价保持一致,并且因为我们提供更多免费的产品和功能,我们还将贡献者限制降低到10个(从20个)。

对于我们现有的团队版用户,如果您的使用量在修改后的限制范围内,并且已经付费,我们将与您联系调整账单。如果您超过了新的限制,您将有时间到 2023 年 7 月 31 日调整使用量和/或购买额外的席位。我们希望确保每个人过渡顺利;您可以通过发送电子邮件至 support@semgrep.com 咨询或反馈问题,或阅读有关定价、计费或使用限制的文档。

Pro 规则有什么不同?

Pro 规则是 Semgrep 官方维护的高可信度、专业的规则集合,仅在团队级别及以上的功能中可用,由于官方已经对所有用户开放的团队版功能,所以现在可以直接使用了。

以 php 规则集合为例,该规则集合中有 29 条都是 Pro 规则。

20230710231558

不登陆的情况下,使用该规则集合扫描会显示仅有 16 条规则。

semgrep scan --config "p/php" /mnt/share/project/ctf_archives/test/baijiacms  | sed 's/\x1B\[[0-9;]*[mK]//g' > result.txt   
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2789 files tracked by git with 16 Code rules:
  Scanning 748 files with 16 php rules.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00 

登陆之后再进行扫描就可以使用所有规则了:

semgrep scan --config "p/php" /mnt/share/project/ctf_archives/test/baijiacms  | sed 's/\x1B\[[0-9;]*[mK]//g' > result.txt
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2789 files tracked by git with 45 Code rules, 29 Pro rules:
  Scanning 748 files with 43 php rules.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:03

对比社区规则与 Pro 规则中对命令注入的匹配规则:

社区规则:exec-use.yaml,仅仅只是匹配部分命令执行函数的调用,既不全面也没有使用污点分析。

  patterns:
  - pattern: $FUNC(...);
  - pattern-not: $FUNC('...', ...);
  - metavariable-regex:
      metavariable: $FUNC
      regex: exec|passthru|proc_open|popen|shell_exec|system|pcntl_exec

Pro 规则:tainted-command-injection,其中使用 pattern-sources 匹配来自 $_REQUEST 等输入源, pattern-sinks 配置了部分命令执行函数,但相比之下也并不是十分完备。

    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: $_REQUEST
              - pattern: $_GET
              - pattern: $_POST
              - pattern: $_FILES
              - pattern: $_COOKIE
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: exec(...)
              - pattern: system(...)
              - pattern: passthru(...)
              - pattern: shell_exec(...)
              - pattern: |
                  `...`
              - patterns:
                  - focus-metavariable: $COMMAND
                  - pattern: proc_open($COMMAND, ...)
              - patterns:
                  - focus-metavariable: $COMMAND
                  - pattern: popen($COMMAND, ...)
              - pattern: pcntl_exec(...)

Pro 引擎有什么不同?

想要在云平台中使用 Pro 引擎,需要按照如下的步骤进行开启:

20230710225638

根据开启的提示,Pro 引擎引入了文件间分析可以消除误报、引入新的结果,并且可以增加扫描时间和内存使用量。但仅在完整扫描时启用,且仅在 Java 和 Javascript 文件上运行,但在实际测试时发现其他语言也同样有效。

终端中使用 Pro 引擎需要先进行登陆:

semgrep login

根据输出访问对应的链接进行登陆。

Login enables additional proprietary Semgrep Registry rules and running custom policies from Semgrep Cloud Platform.
Login at: xxxx

终端获得 token 之后,就可以安装 pro 引擎了。

semgrep install-semgrep-pro

安装完后可以使用 --pro 参数调用 pro 引擎。

semgrep --pro --config "p/default" 

Pro 规则 + Pro 引擎扫描

Pro 引擎具备跨文件分析能力,Pro 规则更为精准,所以同时使用 Pro 引擎与 Pro 规则进行扫描效果是最好的,实际测试时可以先进行登陆,扫描时添加 --pro 参数。

semgrep scan --config "p/php" /mnt/share/project/ctf_archives/test/baijiacms --pro 
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2789 files tracked by git with 45 Code rules, 29 Pro rules:
  Scanning 748 files with 43 php rules.
Semgrep Pro Engine may be slower and show different results than Semgrep OSS.
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:15   

扫描到的结果确实相对更多,但输出的格式确实一言难尽,十分不方便查看,而 vscode 插件中也没有内置 pro 参数。为了良好的展示效果似乎只能通过 Semgrep 云平台了。

┌──────────────────┐
│ 16 Code Findings │
└──────────────────┘
                                                                                                    
    /mnt/share/project/ctf_archives/test/baijiacms/includes/baijiacms/common.inc.php 
       php.lang.security.injection.tainted-filename.tainted-filename
          File name based on user input risks server-side request forgery.    
          Details: https://sg.run/Ayqp                                        
                                                                              
          501┆ if(is_uploaded_file($filename)) {
    
            Taint comes from:
            597┆ $filename = random(15) . ".{$extention}";
            Taint flows through these intermediate variables:
            372┆ $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 :    
  35);                                                                                                               
            382┆ $hash .= $seed{mt_rand(0, $max)};
            then reaches:
            372┆ $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 :    
  35);                                                                                                               
    
            Taint flows through these intermediate variables:
            597┆ $filename = random(15) . ".{$extention}";
            602┆ $file_tmp_name = SYSTEM_WEBROOT . $path . $extpath. $filename;
    
            This is how taint reaches the sink:
            609┆ return file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path);
            Taint flows through these intermediate variables:
            637┆ function                                                                                            
  file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path,$allownet=true)        
            then call to:
            642┆ if(!file_move($file_tmp_name, $file_full_path)) {
            Taint flows through these intermediate variables:
            499┆ function file_move($filename, $dest) {
            then reaches:
            501┆ if(is_uploaded_file($filename)) {
    
            ⋮┆----------------------------------------
          506┆ return is_file($dest);
    
            Taint comes from:
            581┆ $filename = random(15) . ".{$extention}";
            Taint flows through these intermediate variables:
            372┆ $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 :    
  35);                                                                                                               
            382┆ $hash .= $seed{mt_rand(0, $max)};
            then reaches:
            372┆ $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 :    
  35);                                                                                                               
    
            Taint flows through these intermediate variables:
            581┆ $filename = random(15) . ".{$extention}";
            584┆ $file_full_path = WEB_ROOT . $path . $extpath. $filename;
    
            This is how taint reaches the sink:
            586┆ return                                                                                              
  file_save($file['tmp_name'],$filename,$extention,$file_full_path,$file_relative_path);                   
            Taint flows through these intermediate variables:
            637┆ function                                                                                            
  file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path,$allownet=true)        
            then call to:
            642┆ if(!file_move($file_tmp_name, $file_full_path)) {
            Taint flows through these intermediate variables:
            499┆ function file_move($filename, $dest) {
            then reaches:
            506┆ return is_file($dest);
            ...

在等出的情况下使用 --pro 参数,semgrep 会提示要求登陆后使用,但扫描时间与扫描结果与不添加 --pro 参数时也不一样。,重新登陆之后再次扫描,发现结果居然与不登陆的时候一样(!!?)

└─$ semgrep scan --config /mnt/share/Tools/web/code_audit/semgrep-rules/php /mnt/share/project/ctf_archives/test/baijiacms --pro 
               
               
┌─────────────┐
│ Scan Status │
└─────────────┘
  Scanning 2767 files tracked by git with 57 Code rules:
  Scanning 748 files with 40 php rules.
!!!This is a proprietary extension of semgrep.!!!
!!!You must be logged in to access this extension!!!
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:30 

注意:隐私策略

根据官方的隐私策略 Semgrep Privacy Policy - Semgrep 的相关描述。

$ semgrep --config=myrule.yaml  # → no metrics (loading rules from local file)
$ semgrep --config=p/python     # → metrics enabled (fetching Registry)
$ semgrep login && semgrep ci   # → metrics enabled (logged in to semgrep.dev)
  1. 当仅使用本地配置文件或命令行搜索模式运行时,官方不会收集相关信息。
  2. 当使用 Registry 中的规则时,官方会收集所需数据以帮助维护人员完善规则。
  3. 当使用云平台时,官方也会收集这些数据,并且将结果存放在云平台中。

收集数据的具体内容可见:Data collected as metrics

用户也可以通过指定 --metrics 选项来控制数据的发送。

--metrics auto:(默认)每当从 Semgrep 注册表中提取规则时都会发送
--metrics on:每次 Semgrep 运行时都会发送
--metrics off:永远不会发送 

如果有隐私考虑的话,建议离线使用规则库,并添加 --metrics off 参数。

其他资源

在线测试

官方提供了一个 Playground 来辅助规则的编写

一些开源规则库

github 中有一些开源出来的规则库可供参考。

结语

本文对 semgrep 的基本使用以及注意事项进行了介绍,本人也是第一次使用,如有错误请及时指出。从结果来看,semgrep 优点在于轻量级,扫描速度快、规则定制简单,但污点分析能力并不算强,如果去定制一些规则去做危险函数匹配,配合 vscode 插件,对代码审计还是有帮助的。

编写规则如果想省事也可以交给 chatGPT :heart_eyes:

I want you to act as a code audit expert specializing in semgrep rules. Your role will involve helping me understand and utilize semgrep effectively for code review purposes. You should be proficient in semgrep’s syntax, rules, and patterns. Your tasks will include explaining the purpose and functionality of existing semgrep rules, assisting in the interpretation of semgrep scan results, and guiding me in the customization and creation of custom semgrep rules. Your expertise in code auditing and semgrep will be crucial in ensuring the thoroughness and accuracy of our code reviews, helping us identify and address potential security vulnerabilities, bugs, and best practice violations.

prompt 取自:DummyKitty/Cyber-Security-chatGPT-prompt: some prompt about cyber security

参考