對Github指定類目的內容進行監控和推送

很久之前看到HACK學習呀有一個Github 安全搬運工的系列文章,個人覺得很不錯,想要在自己的公眾號上也做這方面的內容,內容的編輯排版相對來說比較容易,這樣問題就回歸到Github安全內容的獲取上

這篇文章整體比較長,如果想直接使用的話可以跳轉去倉庫 //github.com/Cl0udG0d/YunP4n_Monitor/

代碼監控

實際上這部分在Github上也已經有人做過了,不過大部分安全人員實現的項目是代碼監控、信息泄露相關的,例如

我們可以基於他們來進行魔改,直到看到了…
//github.com/yhy0/github-cve-monitor

實時監控github上新增的cve、自定義關鍵字和安全工具更新,並多渠道推送通知

其中就有自定義關鍵字這個功能,我們可以藉此來實現我們想要的功能

本來現在可以開始部署了,但是基礎條件就是需要一個VPS,然後通過screen之類的後台運行命令把python腳本運行在後台,定時對內容進行爬取和監控
這樣還是比較麻煩(我看起來像缺VPS的人嗎
所以我們可以考慮用之前github開放的action來實現這個功能

Github Action

Github Action官方文檔中對自身的定義

在Github Actions的倉庫中自動化、自定義和執行軟件開發工作流程。您可以發現、創建和共享操作以執行您喜歡的任何作業(包括CI/CD),並將操作合併到完全自定義的工作流程中。

也就是說我們可以給自己的代碼倉庫部署一系列自動化腳本,在我們進行了提交、合併分支等操作後,自動執行腳本。
阮一峰Github Action指南中的介紹:

大家知道,持續集成由很多操作組成,比如抓取代碼、運行測試、登錄遠程服務器,發佈到第三方服務等等。GitHub 把這些操作就稱為 actions。

很多操作在不同項目裏面是類似的,完全可以共享。GitHub 注意到了這一點,想出了一個很妙的點子,允許開發者把每個操作寫成獨立的腳本文件,存放到代碼倉庫,使得其他開發者可以引用。

如果你需要某個 action,不必自己寫複雜的腳本,直接引用他人寫好的 action 即可,整個持續集成過程,就變成了一個 actions 的組合。這就是 GitHub Actions 最特別的地方。

也就是說當我們編寫好項目對應的腳本並上傳到Github上後,Github會運行腳本幫我們做一系列動作,而做這些動作所需要消耗的計算資源Github會慷慨地提供給我們(當然也有防止濫用的機制)。如果我們計算資源消耗不是很大,用Github Action就綽綽有餘了
所以這裡我們可以通過Github Action編寫定時任務來運行python腳本,從而達成白嫖
官方文檔在此
其中Github Actions的一些術語:

  • workflow 工作流程:持續集成一次運行的過程,就是一個workflow
  • job 任務:一個workflow由一個或多個jobs構成,一次持續集成的運行,可以完成多個任務
  • step 步驟:每個job由多個step構成,一步步完成
  • action 動作:每個step可以依次執行一個或多個命令action

感覺是在一直套娃 😃

牛刀小試

先把原來的倉庫fork一份到自己這裡

第一次操作直接在web界面上進行吧

接下來GitHub自動給我們生成了一個小型的main.yml文件,其中填充了運行一個工作流所必須的基本要素

Github Actions默認的位置是.github/workflows/目錄中,下面對main.yml進行解釋

# name屬性用來指定這個工作流的名字

name: CI

# 這個部分用來指定能夠觸發工作流執行的事件
on:
  # 當對分支master進行push操作的時候,這個工作流就被觸發了
  push:
    branches: [ "master" ]
  # 當對分支master進行pull request操作的時候,同樣也會觸發這個工作流
  pull_request:
    branches: [ "master" ]

  # 允許你手動在Github網頁上執行該workflow
  workflow_dispatch:

# 一個workflow可以由多個job組成,可以並行或者分離運行,在jobs裏面來說明要交給Github action執行的任務
jobs:
  # 這個jobs中的一個任務,名字叫做build(任務的名字是自己取的)
  build:
    # 用來指定這個任務在什麼操作系統上面跑,此處運行在最新版本的ubuntu鏡像上(服務器是Github免費提供的)
    runs-on: ubuntu-latest

    # 指出這個build任務的步驟
    steps:
      # 將你的job指向你的文件目錄$GITHUB_WORKSPACE,用來運行你的腳本
      - uses: actions/checkout@v3

      # 步驟 運行單行命令 輸出Hello, world!
      - name: Run a one-line script
        run: echo Hello, world!

      # 步驟 運行一組命令
      - name: Run a multi-line script
        run: |
          echo Add other actions to build,
          echo test, and deploy your project.

點擊Start commit,這樣一個最基本的Github action工作流就寫好了,當進行push或者pull request時會觸髮腳本執行
為了檢測是否成功,push一個test.txt上去,可以看到

點擊可以查看運行詳情

測試定時任務

我們先測試一下Github Actions的定時任務功能

on:
  schedule:
    - cron: '*/5 * * * *'

上面表示每五分鐘執行一次任務,Crontab的設置規則可以查看
有關cron語法的五個字段的說明
┌───────────── minute (0 – 59)
│ ┌───────────── hour (0 – 23)
│ │ ┌───────────── day of the month (1 – 31)
│ │ │ ┌───────────── month (1 – 12 or JAN-DEC)
│ │ │ │ ┌───────────── day of the week (0 – 6 or SUN-SAT)
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │


註:這裡的定時任務指定的時間和中國的時區有時差,北京時間比Github所使用的時區快8個小時
比如7月1號23點,github時間是7月1號15點。所以我們可以配置成15點或16點執行(對應北京時間的夜裡11點、12點)

Github Actions定時任務最短的時間間隔也是5分鐘
如果不是很熟悉crontab可以在網站上編寫規則之後查看

這裡可以看到執行時間之間相差了5分鐘
修改github main.yml文件為5分鐘後發現

任務執行時間相差挺大的 orz
官方對此有一個非常確定的說法如下

注意: schedule 事件在Github Actions 工作流程運行期間負載過高時可能會延遲。高負載時間包括每小時的開始時間。為了降低延遲的可能性,將您的工作流程安排在不同時間運行

原因在於Github為了控制成本,在雲上的成本是固定的,這時如果對於用戶的任務完全都實時,會有一個非常高的瞬間峰值,對於資源的使用非常不合理,對於這個問題有兩個辦法解決:

  • 讓我們的定時任務對時間的要求不精確
  • 使用自己的雲資源 (又回到了原點)

所以我們不會選擇第二種方法,而我們的任務恰好對時間不精確,只需要一天給我們推送一次或者兩次就可以了,繼而我們自然要薅這個羊毛了

魔改main.yaml

修改main.yml文件中定時為每天12點一次

on:
  schedule:
    - cron: '0 4 * * *'

因為時區的問題所以我們用北京時間減 8 小時
接着修改main.yml

環境變量

使用本地時間

env:
  TZ: Asia/Shanghai

工作指令

這裡我們只需要執行python文件,所以運行單任務就行
先設置虛擬的運行環境

runs-on: ubuntu-latest

設置每個step,首先要 checkout the repository under $GITHUB_WORKSPACE

steps:
  - name: checkout
  - uses: actions/checkout@v3

安裝運行代碼需要的依賴

- name: Set up Python3
  uses: actions/setup-python@v2
  with:
    python-version: '3.6'
  
  - name: Install requirements
    run: |
      python3 -m pip install --upgrade pip
      pip3 install -r ./requirements.txt

最後是運行這個腳本

- name: Run YunP4n_Sec
  env:
    github_token: ${{ secrets.GITHUB_TOKEN }}
  run: python3 ./github_sec_monitor.py

魔改github_sec_monitor.py

雖然現在能夠運行python文件了,但是裏面還有一些東西是需要我們自己配置的

orz,寫到這一步的時候發現要改的東西還挺多的,草,直接新建一個倉庫吧

創建倉庫YunP4n_Monitor

倉庫地址 //github.com/Cl0udG0d/YunP4n_Monitor
創建main.py執行我們的主要邏輯,整體的邏輯為
加載關鍵字列表 -> 遍歷關鍵字並獲取最新結果 -> 內容清洗 -> 推送釘釘

加載關鍵字列表

使用Github Actions來實現的話,我們把關鍵字作為一個字符串存儲到main.yml的環境變量裏面,並在python文件里通過環境變量的方式加載出來就行
main.yml設置

- name: Run YunP4n_Sec
  env:
    keywords: '漏洞掃描'

python中通過下面的方式加載

import os

keywords=os.environ["keywords"]

遍歷關鍵字並獲取最新結果

首先是遍歷關鍵字,我們通過空格來區分關鍵字,即

- name: Run YunP4n_Sec
  env:
    keywords: '漏洞掃描 應急響應'

python中通過空格的切割來處理關鍵字

import os

keywords=os.environ["keywords"]

def splitKeywordList():
    return keywords.split()

獲取Github上的搜索結果通過接口來實現,記得帶上github token,同樣從環境變量中取得

github_token = os.environ.get("github_token")
def getKeywordNews(keyword):
    today_keyword_info_tmp=[]
    try:
        # 抓取本年的
        api = "//api.github.com/search/repositories?q={}&sort=updated".format(keyword)
        json_str = requests.get(api, headers=github_headers, timeout=10).json()
        today_date = datetime.date.today()
        n=20 if len(json_str['items'])>20 else len(json_str['items'])
        for i in range(0, n):
            keyword_url = json_str['items'][i]['html_url']
            try:
                keyword_name = json_str['items'][i]['name']
                description=json_str['items'][i]['description']
                pushed_at_tmp = json_str['items'][i]['pushed_at']
                pushed_at = re.findall('\d{4}-\d{2}-\d{2}', pushed_at_tmp)[0]
                if pushed_at == str(today_date):
                    today_keyword_info_tmp.append({"keyword_name": keyword_name, "keyword_url": keyword_url, "pushed_at": pushed_at,"description":description})
                    print("[+] keyword: {} ,{} ,{} ,{} ,{}".format(keyword, keyword_name,keyword_url,pushed_at,description))
                else:
                    print("[-] keyword: {} ,{}的更新時間為{}, 不屬於今天".format(keyword, keyword_name, pushed_at))
            except Exception as e:
                pass
    except Exception as e:
        logging.error(e, "github鏈接不通")
    return today_keyword_info_tmp

內容清洗

對我來說,如果給我推送過一次的內容,我就不想再獲取到了,一天我想要獲取最多10條github關鍵字內容監控信息,所以需要有一個集合來專門存儲已經爬取的內容
這裡需要提到Github的一個機制,即不能創建同名的倉庫,我們可以通過倉庫的名字來判斷是否已經被推送過了。
此外還有一個問題是Github給我們提供的運行容器在下一次加載的時候又是全新的,所以我們不能把我們在VPS上存儲到數據庫中的方法套用在這裡(會造成數據的丟失)。對於Github用戶來說,最安全的位置莫過於倉庫了,所以我們可以將數據存儲到開源倉庫一個叫做clean.txt的文件夾內
每次加載的時候
讀取txt -> 加載到環境變量 -> python更新環境變量 -> 檢查是否已推送 -> 存儲到txt裏面 -> 將環境變量中已推送的工具覆蓋掉原有文件 -> 更新倉庫
這樣就不用擔心數據丟失的問題了
具體的操作如下

  • 讀取txt並加載到環境變量
- name: clean data
  run: echo "CleanKeywords=$(cat ./clean.txt)" >> $GITHUB_ENV
  • python更新環境變量
CleanKeywords=os.environ.get("CleanKeywords")
  • 檢查是否已推送
cleanKeywords=set(splitCleanKeywords())
for tempdata in templist:
    if tempdata.get("keyword_name") in cleanKeywords:
        pass
    else:
        //如果未推送的邏輯
  • 更新環境變量
def flashCleanData(cleanKeywords):
    text=""
    for key in cleanKeywords:
        text+="{} ".format(key)
    env_file = os.getenv('GITHUB_ENV')
    with open(env_file, "a") as myfile:
        myfile.write("CleanKeywords={}".format(text))
    logging.info("環境變量刷新成功")
  • 更新倉庫原文件
- name: push clean data
  run: echo ${{ env.CleanKeywords }} > ./clean.txt
  • 更新倉庫
- name: push clean data
        run: |
          echo ${{ env.CleanKeywords }} > ./clean.txt
          REMOTE=//${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
          git config user.email "${{ github.actor }}@users.noreply.github.com"
          git config user.name "${{ github.actor }}"
          git add .
          git commit -m "Add new comment"
          git push ${REMOTE} main

這樣就不會推送重複的內容了

推送釘釘

這部分已經有很多師傅實現了,我們只需要修改為我們的格式即可,看代碼就明白了

def dingding(text, msg,webhook,secretKey):
    ding = cb.DingtalkChatbot(webhook, secret=secretKey)
    ding.send_text(msg='{}\r\n{}'.format(text, msg), is_at_all=False)
    logging.info("發送消息 title:{} msg:{}".format(text,msg))

def sendmsg(pushdata):
    text=""
    for data in pushdata:
        text+="工具名稱:{}\n工具網址:{}\n詳情:{}\n\n\n ".format(data.get("keyword_name"),data.get("keyword_url"),data.get("description"))
    dingding("新推送",text,webhook,secretKey)
    logging.info("消息發送完成")

測試

用釘釘賬號創建一個群聊,設置自定義機械人,獲取到webhooksecretKey填充到main.yml里,目前main.yml文件內容如下,因為是測試所以時間設置的5分鐘執行一次,在實際執行過程中是大於五分鐘的,前面已經說過理由了。另外webhooksecretKey是明文存放在main.yml文件中的,存在信息泄露的風險,目前沒有找到比較好的替代方法,如果有師傅知道的話麻煩帶帶

name: YunP4n_Sec

on:
  schedule:
    - cron: '*/5 * * * *'

env:
  TZ: Asia/Shanghai

jobs:
  github_monitor:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v3

      - name: Set up Python3
        uses: actions/setup-python@v2
        with:
          python-version: '3.6'

      - name: clean data
        run: echo "CleanKeywords=$(cat ./clean.txt)" >> $GITHUB_ENV

      - name: Install requirements
        run: |
          python3 -m pip install --upgrade pip
          pip3 install -r ./requirements.txt
      
      - name: Run YunP4n_Sec
        env:
          keywords: '漏洞掃描 內網滲透 代碼審計 SQL注入 XSS 漏洞挖掘'
          github_token: ${{ secrets.GITHUB_TOKEN }}
          secretKey: "SEC33f24a184e46aecd0b2e2cad1df0457d6820cc1d75430880420aca97d54c24f6"
          webhook: "//oapi.dingtalk.com/robot/send?access_token=d8c16a1142ae1e3077d05234b73826b1b65ec56c38b9ff83564e8b317f372d04"
        run: python3 ./main.py

      - name: test run
        run: echo ${{ env.CleanKeywords }}

      - name: push clean data
        run: |
          echo ${{ env.CleanKeywords }} > ./clean.txt
          REMOTE=//${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
          git config user.email "${{ github.actor }}@users.noreply.github.com"
          git config user.name "${{ github.actor }}"
          git add .
          git commit -m "Add new comment"
          git push ${REMOTE} main

等待一段時間後成功推送消息

使用

fork倉庫 //github.com/Cl0udG0d/YunP4n_Monitor 之後
只需要修改main.yml文件中的這幾個參數就行

  • keywords 你需要監控的關鍵字們,空格隔開就行
  • secretKey 釘釘推送的參數
  • webhook 釘釘推送的參數

如果想要修改推送的時間,修改這部分就行

on:
  schedule:
    - cron: '*/5 * * * *'

然後就能愉快使用了

參考鏈接

END

建了一個微信的安全交流群,歡迎添加我微信備註進群,一起來聊天吹水哇,以及一個會發佈安全相關內容的公眾號,歡迎關注 😃

GIF
GIF