ค้นหาเว็บไซต์

ป้องกันการโจมตีด้วยการแทรกสคริปต์


สคริปต์ PowerShell เช่นเดียวกับภาษาการเขียนโปรแกรมอื่นๆ อาจเสี่ยงต่อการถูกโจมตีแบบฉีดได้ การโจมตีแบบฉีดเกิดขึ้นเมื่อผู้ใช้ป้อนข้อมูลให้กับฟังก์ชันที่มีช่องโหว่ซึ่งรวมถึงคำสั่งพิเศษ ฟังก์ชันที่มีช่องโหว่จะรันคำสั่งพิเศษซึ่งอาจเป็นช่องโหว่ด้านความปลอดภัยที่ร้ายแรง ตัวอย่างเช่น ผู้ใช้ที่ประสงค์ร้ายอาจใช้ฟังก์ชันที่มีช่องโหว่ในการเรียกใช้โค้ดที่กำหนดเองบนคอมพิวเตอร์ระยะไกลในทางที่ผิด ซึ่งอาจทำให้คอมพิวเตอร์เครื่องนั้นเสียหายและเข้าถึงเครื่องอื่น ๆ บนเครือข่ายได้

เมื่อคุณทราบปัญหานี้แล้ว มีหลายวิธีในการป้องกันการโจมตีจากการฉีดยา

ตัวอย่างโค้ดที่มีช่องโหว่

ช่องโหว่การแทรกโค้ด PowerShell เกี่ยวข้องกับการป้อนข้อมูลของผู้ใช้ที่มีโค้ดสคริปต์ การป้อนข้อมูลของผู้ใช้จะถูกเพิ่มลงในสคริปต์ที่มีช่องโหว่ซึ่งมีการแยกวิเคราะห์และเรียกใช้โดย PowerShell

function Get-ProcessById
{
    param ($ProcId)

    Invoke-Expression -Command "Get-Process -Id $ProcId"
}

ฟังก์ชัน Get-ProcessById ค้นหากระบวนการภายในเครื่องด้วยค่า Id ใช้อาร์กิวเมนต์พารามิเตอร์ $ProcId ประเภทใดก็ได้ จากนั้น $ProcId จะถูกแปลงเป็นสตริงและแทรกลงในสคริปต์อื่นที่แยกวิเคราะห์และรันโดยใช้ Intake-Expression cmdlet ฟังก์ชันนี้ทำงานได้ดีเมื่อมีการส่งผ่านจำนวนเต็ม Id กระบวนการที่ถูกต้อง

Get-ProcessById $pid

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    50.09     132.72       1.20   12528   3 pwsh

อย่างไรก็ตาม พารามิเตอร์ $ProcId ไม่ได้ระบุประเภท ยอมรับค่าสตริงใดๆ ที่สามารถรวมคำสั่งอื่นๆ ได้

Get-ProcessById "$pid; Write-Host 'pwnd!'"

ในตัวอย่างนี้ ฟังก์ชันดึงข้อมูลกระบวนการที่ระบุโดย $pid ได้อย่างถูกต้อง แต่ยังเรียกใช้สคริปต์ที่แทรกไว้ Write-Host 'pwnd!'

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     92    45.66     122.52       1.06   21736   3 pwsh
pwnd!

วิธีป้องกันการโจมตีด้วยการฉีด

มีหลายวิธีในการป้องกันการโจมตีด้วยการฉีด

ใช้อินพุตแบบพิมพ์

คุณสามารถระบุประเภทสำหรับอาร์กิวเมนต์ $ProcId

function Get-ProcessById
{
    param ([int] $ProcId)

    Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Get-ProcessById "$pid; Write-Host 'pwnd!'"
Get-ProcessById:
Line |
   7 |  Get-ProcessById "$pid; Write-Host 'pwnd!'"
     |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot process argument transformation on parameter 'ProcId'. Cannot convert value
"8064; Write-Host 'pwnd!'" to type "System.Int32". Error: "The input string '8064; Write-Host 'pwnd!'
was not in a correct format."

ในที่นี้ พารามิเตอร์อินพุต $ProcId ถูกจำกัดไว้ที่ประเภทจำนวนเต็ม ดังนั้นจึงเกิดข้อผิดพลาดขึ้นเมื่อมีการส่งสตริงที่ไม่สามารถแปลงเป็นจำนวนเต็มได้

อย่าใช้ Inrigger-Expression

แทนที่จะใช้ Inrigger-Expression ให้เรียก Get-Process โดยตรง และปล่อยให้ตัวเชื่อมโยงพารามิเตอร์ของ PowerShell ตรวจสอบอินพุต

function Get-ProcessById
{
    param ($ProcId)

    Get-Process -Id $ProcId
}
Get-ProcessById "$pid; Write-Host 'pwnd!'"
Get-Process:
Line |
   5 |      Get-Process -Id $ProcId
     |                      ~~~~~~~
     | Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host 'pwnd!'" to type
"System.Int32". Error: "The input string '8064; Write-Host 'pwnd!' was not in a correct
format."

ตามแนวทางปฏิบัติที่ดีที่สุด คุณควรหลีกเลี่ยงการใช้ Inrigger-Expression โดยเฉพาะอย่างยิ่งเมื่อจัดการกับอินพุตของผู้ใช้ Inrigger-Expression เป็นอันตรายเนื่องจากจะแยกวิเคราะห์และเรียกใช้เนื้อหาสตริงใดๆ ก็ตามที่คุณระบุ ซึ่งทำให้เสี่ยงต่อการโจมตีแบบฉีด ควรใช้การเชื่อมโยงพารามิเตอร์ PowerShell ดีกว่า

ตัดสตริงด้วยเครื่องหมายคำพูดเดี่ยว

อย่างไรก็ตาม มีหลายครั้งที่การใช้ Inrigger-Expression เป็นสิ่งที่หลีกเลี่ยงไม่ได้ และคุณยังต้องจัดการอินพุตสตริงของผู้ใช้ด้วย คุณสามารถจัดการอินพุตของผู้ใช้ได้อย่างปลอดภัยโดยใช้เครื่องหมายคำพูดเดี่ยวรอบตัวแปรอินพุตสตริงแต่ละตัว เครื่องหมายคำพูดเดี่ยวช่วยให้แน่ใจว่า parser ของ PowerShell ถือว่าอินพุตของผู้ใช้เป็นสตริงเดี่ยว

function Get-ProcessById
{
    param ($ProcId)

    Invoke-Expression -Command "Get-Process -Id '$ProcId'"
}

Get-ProcessById "$pid; Write-Host 'pwnd!'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host " to type
"System.Int32". Error: "The input string '8064; Write-Host' was not in a correct format."

อย่างไรก็ตาม ฟังก์ชันเวอร์ชันนี้ยังไม่ปลอดภัยอย่างสมบูรณ์จากการโจมตีแบบฉีด ผู้ใช้ที่ประสงค์ร้ายยังคงสามารถใช้เครื่องหมายคำพูดเดี่ยวในการป้อนข้อมูลเพื่อแทรกโค้ดได้

Get-ProcessById "$pid'; Write-Host 'pwnd!';'"

ตัวอย่างนี้ใช้เครื่องหมายคำพูดเดี่ยวในการป้อนข้อมูลของผู้ใช้เพื่อบังคับให้ฟังก์ชันรันคำสั่งแยกกันสามคำสั่ง ซึ่งหนึ่งในนั้นคือโค้ดที่กำหนดเองที่ผู้ใช้แทรกเข้าไป

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    46.08     183.10       1.08    2524   3 pwsh
pwnd!

ใช้เมธอด EscapeSingleQuotedStringContent()

เพื่อป้องกันผู้ใช้แทรกอักขระเครื่องหมายคำพูดเดี่ยวของตนเองเพื่อใช้ประโยชน์จากฟังก์ชัน คุณต้องใช้ API EscapeSingleQuotedStringContent() นี่เป็นวิธีการสาธารณะแบบคงที่ของคลาส PowerShell System.Management.Automation.Language.CodeGeneration วิธีการนี้ทำให้อินพุตของผู้ใช้ปลอดภัยโดยการหลีกเลี่ยงเครื่องหมายคำพูดเดี่ยวใดๆ ที่รวมอยู่ในอินพุตของผู้ใช้

function Get-ProcessById
{
    param ($ProcId)

    $ProcIdClean = [System.Management.Automation.Language.CodeGeneration]::
        EscapeSingleQuotedStringContent("$ProcId")
    Invoke-Expression -Command "Get-Process -Id '$ProcIdClean'"
}
Get-ProcessById "$pid'; Write-Host 'pwnd!';'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064'; Write-Host 'pwnd!';'" to type
"System.Int32". Error: "The input string '8064'; Write-Host 'pwnd!';'' was not in a correct format."

สำหรับข้อมูลเพิ่มเติม โปรดดู EscapeSingleQuotedStringContent()

การตรวจจับโค้ดที่มีช่องโหว่ด้วย Injection Hunter

Injection Hunter เป็นโมดูลที่เขียนโดย Lee Holmes ซึ่งมีกฎ PowerShell Script Analyzer สำหรับการตรวจจับช่องโหว่ของการแทรกโค้ด ใช้คำสั่งใดคำสั่งหนึ่งต่อไปนี้เพื่อติดตั้งโมดูลจากแกลเลอรี PowerShell:

# Use PowerShellGet v2.x
Install-Module InjectionHunter

# Use PowerShellGet v3.x
Install-PSResource InjectionHunter

คุณสามารถใช้สิ่งนี้เพื่อทำให้การวิเคราะห์ความปลอดภัยเป็นอัตโนมัติในระหว่างการสร้าง กระบวนการบูรณาการอย่างต่อเนื่อง การปรับใช้ และสถานการณ์อื่น ๆ

$RulePath = (Get-Module -list InjectionHunter).Path
Invoke-ScriptAnalyzer -CustomRulePath $RulePath -Path .\Invoke-Dangerous.ps1
RuleName                            Severity     ScriptName Line  Message
--------                            --------     ---------- ----  -------
InjectionRisk.InvokeExpression      Warning      Invoke-Dan 3     Possible script injection risk via the
                                                 gerous.ps1       Invoke-Expression cmdlet. Untrusted input can cause
                                                                  arbitrary PowerShell expressions to be run.
                                                                  Variables may be used directly for dynamic parameter
                                                                  arguments, splatting can be used for dynamic
                                                                  parameter names, and the invocation operator can be
                                                                  used for dynamic command names. If content escaping
                                                                  is truly needed, PowerShell has several valid quote
                                                                  characters, so  [System.Management.Automation.Languag
                                                                  e.CodeGeneration]::Escape* should be used.

สำหรับข้อมูลเพิ่มเติม โปรดดูที่ PSScriptAnalyzer

บทความที่เกี่ยวข้อง