Semgrep結合GitLab實現程式碼審計實踐-服務端

tdaxia發表於2021-06-03

一、背景

前段時間在做程式碼審計,發現很多專案都存在安全隱患,大多數是來自於引數未過濾所造成的;為了解決這個問題,我將Web安全開發規範手冊V1.0進行了培訓,但是效果並不是太理想,原因是培訓後開發者的關注點主要在功能完成度上,安全編碼對於他們來說並不是核心指標;

 

為了能讓開發者時時刻刻關注安全問題,我在gitlab服務端放了一個鉤子,這個鉤子主要是將本次提交的程式碼檔案進行了檢測,遇到可能存在安全風險的問題將其輸出出來,這樣開發者能夠對培訓的內容有更深的感受,更注重編碼時候的安全問題。

二、操作步驟

  1. 搭建環境
  2. 建立專案
  3. 建立鉤子
  4. 鉤子實驗

三、搭建環境

3.1 安裝gitlab

在正式部署到伺服器之前,我需要在本地搭建一個gitlab服務,用於鉤子的開發和測試,這裡我用docker搭建速度比較快,執行的命令如下

1
docker run --detach  --publish 443:443 --publish 80:80  --name gitlab --restart always  gitlab/gitlab-ce

命令執行之後,返回的資訊如下所示

 

 

在上圖中可以看到容器已經執行成功,使用瀏覽器訪問gitlab的地址

1
http://127.0.0.1

訪問之後需要設定一個管理員的密碼,如下圖所示

 

 

填寫密碼之後,確認修改密碼,會跳轉到gitlab的主頁,如下圖所示

 

這gitlab中建立一個專案用於鉤子測試,如下圖所示

 

建立專案成功之後,注意留意頁面中的Project ID:2,把這個2記錄一下,後續會使用到;接下來需要開始鉤子的開發和部署,鉤子可以使用各種語言開發,這裡我比較熟悉php,因此採用php開發。

3.2 安裝依賴

gitlab的容器預設不支援php語言,需要先安裝php,安裝命令如下所示

1
apt update -y && apt install php -y

命令執行之後,返回的資訊如下所示

 

在上圖中可以看到php已經安裝成功,為了驗證php命令是否可以執行,這裡我使用如下命令進行驗證

1
php -v

命令執行之後,返回的資訊如下所示

 

 

在上圖中可以看到php的版本是7.4.3 ,說明php已經安裝成功。

3.3 安裝semgrep

鉤子程式中需要呼叫semgrep,這個程式gitlab中也沒有安裝,需要安裝一下,這裡採用pip安裝,不過需要先升級pip的版本才行,升級的命令如下所示

1
pip3 install --upgrade pip

命令執行之後,返回的資訊如下所示

在上圖中可以看到pip的版本已經升級到21.1.2,說明升級成功了

 

semgrep還依賴setuptools模組,需要用pip先升級一下,升級的命令如下所示

1
pip3 install --upgrade setuptools

命令執行之後,返回的資訊如下所示

在上圖中可以看到setuptools模組已經升級成功

 

接下來就可以正式安裝semgrep了,安裝的命令如下所示

1
cd /usr/local/bin/ && python3 -m pip install semgrep

命令執行之後,返回的資訊如下所示

 

在上圖中可以看到semgrep已經安裝完成,這裡我需要再次使用semgrep命令來驗證一下,執行的命令如下所示

1
semgrep --version

命令執行之後,返回的資訊如下所示

在上圖中可以看到semgrep的版本資訊為0.52.0,確認安裝成功了。

3.4 檢視hash

現在我們需要在剛才建立的專案中新增鉤子,這裡需要找到專案的存放路徑,在專案頁中

1
echo -n 2 | sha256sum

命令執行之後,返回的資訊如下所示

1
find / -iname d4

命令執行之後,返回的資訊如下所示

在上圖中可以看到專案存放的位置,返回了兩個路徑,這兩個路徑其中有一個是軟連線,透過cd命令進入進入專案的存放位置

1
cd /var/opt/gitlab/git-data/repositories/@hashed/d4

命令執行之後,再次執行ls命令,得到的資訊如下所示

 

在上圖中可以看到有一個73的資料夾,這是gitlab的命名規則,進入此資料夾,命令如下所示

1
cd 73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git/

命令執行之後,返回的資訊如下所示

 

在上圖中可以看到此專案的所有檔案,我需要在這個位置開發鉤子檔案

五、建立鉤子

自定義鉤子需要存放在custom_hooks目錄下,預設沒有此資料夾所以需要建立此資料夾,執行命令如下所示

1
mkdir custom_hooks  && cd custom_hooks

5.1 新建鉤子

建立custom_hooks資料夾並進入之後,使用vim建立一個鉤子檔案,命令如下所示

1
vim  pre-receive

進入vim編輯器介面之後,將如下鉤子程式碼新增進去,程式碼如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/php
<?php
 
 
fwrite(STDOUT, 'please input:');
list($oldVer, $newVer, $ref_name) = explode(" ", fgets(STDIN));
 
//ob_start();
$cmd = "git diff --name-only  {$oldVer}..{$newVer}";
echo $cmd . PHP_EOL;
exec($cmd, $result);
 
$rand = date("Y-m-d-H-i-s");
$baseDir = "/tmp/11/$rand/";
$ruleFile = "/semgrep-rule.yaml";
 
 
 
 
foreach ($result as $value) {
    if (strstr($value, ".php") !== false) {
        $randName = $baseDir . $value;
        if (!is_dir(dirname($randName))) {
#        if (file_exists($randName) == false) {
            mkdir(dirname($randName), 0777, true);
        }
        $cmd = "git show {$newVer}:$value > $randName";
 
#        echo $cmd . PHP_EOL;
 
 
        exec($cmd, $result);
    }
}
 
 
$cmd = "/opt/gitlab/embedded/bin/semgrep  -f '$ruleFile' $baseDir  -o /tmp/11.txt";
 
exec($cmd, $result);
 
 
//ob_clean();
 
 
$notice = file_get_contents("/tmp/11.txt");
echo $notice . PHP_EOL;
 
 
file_put_contents("/tmp/11.txt", "");
exec("rm -rf $baseDir");
 
echo 0;

儲存並推出此鉤子檔案,接著需要給自定義鉤子目錄設定許可權,這裡我簡單粗暴的把許可權設定為777,命令如下所示

1
chmod -R 777 ../

許可權設定好之後,我還需要建立一個semgrep的掃描規則檔案,用於判斷程式碼是否正確。

 

執行的命令如下所示

1
vim /semgrep-rule.yaml

進入vim編輯器之後,需要將如下規則內容複製進去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
rules:
  - id: assert-use
    patterns:
      - pattern: assert($ASSERT, ...);
      # - pattern-not: assert(<... $ASSERT ...>, ...); - https://github.com/returntocorp/semgrep/issues/2035
      - pattern-not: assert("...", ...);
    message: |
      使用使用者輸入呼叫assert等價於eval'。
    metadata:
      references:
        - https://www.php.net/manual/en/function.assert
        - https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/AssertsSniff.php
    languages: [ php ]
    severity: ERROR
 
  - id: backticks-use
    pattern: '`...`;'
    message: |
      使用反勾號可能導致命令注入漏洞。
    metadata:
      references:
        - https://www.php.net/manual/en/language.operators.execution.php
        - https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/BackticksSniff.php
    languages: [ php ]
    severity: ERROR

儲存並推出規則檔案後,需要修改此規則檔案的許可權,這裡我以777許可權距離,命令如下所示

1
chmod 777 /semgrep-rule.yaml

設定完規則檔案許可權之後,還有兩個快取地方需要設定許可權,否則會在執行過程當中報錯,首先是semgrep的快取檔案,設定許可權命令如下

1
mkdir -p /var/opt/gitlab/.cache  && chmod -R 777 /var/opt/gitlab/.cache

另外一處是鉤子本身的快取檔案,同樣需要設定許可權,執行的命令如下所示

1
echo '' > /tmp/11.txt  && chmod 777 /tmp/11.txt

5.2 測試鉤子

現在可以正式測試鉤子的可用性,首先需要拉取剛才建立的專案程式碼,命令如下所示

1
git clone http://127.0.0.1/root/test.git

執行命令之後,返回的資訊如下所示

 

 

在上圖中可以看到專案已經拉取下來,接下來我需要編輯一個php檔案,命令如下所示

1
vim index.php

命令執行完畢之後,將測試的程式碼存放進去

1
2
3
4
5
6
7
8
<?php
 
phpinfo();
 
 
$cmd = "ls {$_GET['x']}";
 
exec($cmd);

儲存並退出之後,將程式碼提交到gitlab中去,命令如下所示

1
echo ' ' >> index.php && git add . && git commit . -m 'init' && git push

但git往gitlab伺服器推送之後,gitlab就會呼叫鉤子,並將鉤子返回的資訊輸出出來,如下圖所示

 

 

在上圖中可以看到鉤子提示了 index.php檔案第8行不安全,此致整個部署完畢。


 

作者:湯青松

 

日期:2021-06-03

 

微信:songboy8888

相關文章