對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("消息發送完成")
測試
用釘釘賬號創建一個群聊,設置自定義機械人
,獲取到webhook
和secretKey
填充到main.yml
里,目前main.yml
文件內容如下,因為是測試所以時間設置的5分鐘執行一次,在實際執行過程中是大於五分鐘的,前面已經說過理由了。另外webhook
和secretKey
是明文存放在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 * * * *'
然後就能愉快使用了
參考鏈接
- //www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html
- //segmentfault.com/a/1190000039241099
- //blog.csdn.net/qq_40748336/article/details/110749375
- //juejin.cn/post/7038588768189677604
- //hdcola.medium.com/github-action定時任務作弊條-b44f81184a34
- //blog.imkasen.com/github-action-practice.html
- //blog.csdn.net/cumi7754/article/details/108103697
- //abigeater.com/archives/153
- //so.muouseo.com/qa/r464mqrqx6eg.html
END
建了一個微信的安全交流群,歡迎添加我微信備註進群
,一起來聊天吹水哇,以及一個會發佈安全相關內容的公眾號,歡迎關注 😃

