반응형
PHP, Windows, IIS) PHP(PHP2Shell)취약점 위험성과 대응 방안
최근(2026-10-22 기준) 이슈가 되는 PHP 신규 보안 취약점(CVE-2026-109618, CVE-2026-109619)과 관련하여, 8.5~8.2 버전은 패치가 제공 되나, 지원이 종료(EOL, End-of-Life)된 PHP 7 버전 등은 문제가 있으며, 특히 Windows기반의 IIS를 활용하고 있어 일단 해결책이 필요하여 Gemini의 지원을 받아 해결하여 해당 정보를 공휴하고자 한다.
보안 취약점
- PHP 보안 취약 점 : CVE-2026-109618, CVE-2026-109619
- allow_url_include,- allow_url_fopen, expose_php 설정(PHP: Description of core php.ini directives - Manual)의 취약점
- 취약점 이용 방법이 공개됨에 따라 보안 취약은 알고 있었어도, 방법을 몰랐던 악성 사용자(해커)들도 알게 됨에 따라 취약점 방어는 필수
대응 방법 (Windows 환경에서 )
- Apache + PHP-CGI(또는 XAMPP 기본 설정) : 모듈 방식(mod_php)이나 FastCGI(php-fpm) 전용 안전 설정으로 전환
- IIS + PHP : 처리기 매핑에서 모듈 방식 FastCgiModule으로 변경
Windows/IIS 환경 기준 (서비스/사이트별 적용)
직접 수정
- web.config 기반 차단 룰 적용 (http://localhost/~~~.php 로 끝나는 경우만 적용 할 경우)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Block PHP-CGI Argument Injection (CVE-2024-4577)" stopProcessing="true">
<match url=".*\.php$" ignoreCase="true" />
<conditions logicalGrouping="MatchAny">
<!-- 소프트 하이픈 인코딩 및 하이픈 옵션 주입 패턴 차단 -->
<add input="{UNENCODED_URL}" pattern=".*(%ad|%AD|-d|-s|-w).*" />
<add input="{QUERY_STRING}" pattern=".*(%ad|%AD|-d|-s|-w).*" />
</conditions>
<action type="CustomResponse" statusCode="403" subStatusCode="0" statusReason="Forbidden" statusDescription="Blocked by Security Policy" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
or(또는)
web.config기반 긴급 차단 룰 적용 (http://localhost/?~~~로 PHP확장자와 기본페이지 기준 적용)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Block-PHP-CGI-Arg-Injection-Global" stopProcessing="true">
<!-- 루트 빈 경로(^$), 디렉터리 슬래시 끝(/$), .php 파일만 검사 대상 지정 -->
<match url="(^$|.*\/$|.*\.php$)" />
<conditions logicalGrouping="MatchAny">
<!-- 1) 소프트 하이픈(%ad, %AD) 차단 -->
<add input="{QUERY_STRING}" pattern=".*(%ad|%AD).*" />
<!-- 2) 쿼리 스트링 내 독립된 옵션 플래그(-d, -s, -w) 차단 -->
<add input="{QUERY_STRING}" pattern="(^|&|\+|%20)-[dswDSW](\b|=|\+|%20)" />
<!-- 3) 인코딩 우회 주입 차단 -->
<add input="{UNENCODED_URL}" pattern="(%ad|%AD|(^|&|\+|%20)-[dswDSW](\b|=|\+|%20))" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Blocked by Security Policy" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
PowerShell 자동화 스크립트 실행
web.confing직접 수정이 아닌 스크립트로 할 경우
- PowerShell 자동화 스크립트 (
http://localhost/~~~.php로 끝나는 경우만 적용 할 경우)
# ==============================================================================
# PHP CGI Argument Injection Mitigation for IIS on Windows Server 2022
# ==============================================================================
# 1. 관리자 권한 확인 및 IIS 모듈 로드
Import-Module WebAdministration
$SiteName = "Default Web Site" # 적용할 IIS 사이트명으로 변경
$RuleName = "Block-PHP-CGI-Arg-Injection-extPHP"
Write-Host "[+] IIS URL Rewrite 모듈 설치 여부 확인 중..." -ForegroundColor Cyan
$rewriteModule = Get-WebConfiguration -pspath "MACHINE/WEBROOT/APPHOST" -filter "system.webServer/globalModules/add[@name='RewriteModule']"
if (-not $rewriteModule) {
Write-Warning "[!] IIS URL Rewrite 모듈이 설치되어 있지 않습니다."
Write-Warning " https://www.iis.net/downloads/microsoft/url-rewrite 에서 다운로드 후 설치하십시오."
return
}
Write-Host "[+] '$SiteName'에 CVE-2024-4577 완화 규칙 적용 중..." -ForegroundColor Cyan
# 중복 룰 방지를 위해 기존 동일 이름 규칙 삭제
Clear-WebConfiguration -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']" -ErrorAction SilentlyContinue
# 새 규칙 추가
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules" -name "." -value @{
name = $RuleName
stopProcessing = "True"
}
# Match URL 설정 (*.php)
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/match" -name "url" -value ".*\.php$"
# Action 설정 (403 Custom Response)
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" -name "type" -value "CustomResponse"
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" -name "statusCode" -value 403
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" -name "statusReason" -value "Forbidden"
# Conditions 설정 (%ad, -d 등 주입 탐지)
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" -filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/conditions" -name "." -value @{
input = "{QUERY_STRING}"
pattern = ".*(%ad|%AD|-d|-s).*"
}
Write-Host "[+] URL Rewrite 보안 차단 규칙이 성공적으로 배포되었습니다." -ForegroundColor Green
or(또는)
- PowerShell 자동화 스크립트 (
http://localhost/?~~~로 PHP확장자와 기본페이지 기준 적용)
Import-Module WebAdministration
$SiteName = "Default Web Site" # 실제 IIS 사이트 이름
$RuleName = "Block-PHP-CGI-Arg-Injection-Global"
Write-Host "[+] '$SiteName'의 URL 재작성 규칙을 정밀 필터링으로 갱신 중..." -ForegroundColor Cyan
# 1. 기존 규칙 삭제
Clear-WebConfiguration -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']" -ErrorAction SilentlyContinue
# 2. 새 규칙 등록
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules" -name "." -value @{
name = $RuleName
stopProcessing = "True"
}
# 3. Match URL: *.php 또는 디렉터리 경로(^$, .*\/$)만 매칭 (.js, .css 등 정적 파일 완전 제외)
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/match" `
-name "url" -value "(^$|.*\/$|.*\.php$)"
# 4. Action: 403 Forbidden 응답 반환
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" `
-name "type" -value "CustomResponse"
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" `
-name "statusCode" -value 403
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/action" `
-name "statusReason" -value "Forbidden"
# 5. Conditions: MatchAny
Set-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/conditions" `
-name "logicalGrouping" -value "MatchAny"
# 6. Condition 1: 소프트 하이픈 공격 벡터 차단 (%ad, %AD)
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/conditions" -name "." -value @{
input = "{QUERY_STRING}"
pattern = ".*(%ad|%AD).*"
}
# 7. Condition 2: PHP CLI 옵션 플래그 정밀 탐지 (경계값 기준 -d, -s, -w 매칭)
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/conditions" -name "." -value @{
input = "{QUERY_STRING}"
pattern = "(^|&|\+|%20)-[dswDSW](\b|=|\+|%20)"
}
# 8. Condition 3: 인코딩 우회 원본 URL(UNENCODED_URL) 정밀 검사
Add-WebConfigurationProperty -pspath "IIS:\Sites\$SiteName" `
-filter "system.webServer/rewrite/rules/rule[@name='$RuleName']/conditions" -name "." -value @{
input = "{UNENCODED_URL}"
pattern = "(%ad|%AD|(^|&|\+|%20)-[dswDSW](\b|=|\+|%20))"
}
Write-Host "[+] 사이트 수준 정밀 차단 규칙이 성공적으로 배포되었습니다." -ForegroundColor Green
Windows/IIS 환경 기준 (전체/전역 적용 & 확장자 상관 없이)
서비스/사이트별 적용또는전체 적용및 PHP확장자만 적용할지는 관리자의 선택 영역
applicationHost.config기반 차단 룰 적용
C:\Windows\System32\inetsrv\config\applicationHost.config 파일에 포함
<system.webServer>
<rewrite>
<globalRules>
<rule name="Global-Block-PHP-CGI-Arg-Injection" stopProcessing="true">
<!-- 1) 루트 빈 문자열(^$), 2) 슬래시로 끝나는 디렉터리(/$), 3) .php 로 끝나는 파일만 검사 -->
<match url="(^$|.*\/$|.*\.php$)" />
<conditions logicalGrouping="MatchAny">
<!-- 소프트 하이픈 공격 벡터 차단 -->
<add input="{QUERY_STRING}" pattern=".*(%ad|%AD).*" />
<!-- 독립된 CGI 파라미터 플래그(-d, -s, -w)만 엄격히 차단 -->
<add input="{QUERY_STRING}" pattern="(^|&|\+|%20)-[dswDSW](\b|=|\+|%20)" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Blocked by Security Policy" />
</rule>
</globalRules>
</rewrite>
</system.webServer>
- PowerShell을 자동화 스크립트 (전역 설정)
Import-Module WebAdministration
$GlobalRuleName = "Global-Block-PHP-CGI-Arg-Injection"
Write-Host "[+] IIS 전역 규칙 정밀 필터링으로 갱신 중..." -ForegroundColor Cyan
# 1. 기존 전역 규칙 삭제
Clear-WebConfiguration -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']" -ErrorAction SilentlyContinue
# 2. 전역 규칙 생성
Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules" -name "." -value @{
name = $GlobalRuleName
stopProcessing = "True"
}
# 3. Match URL: *.php 또는 디렉터리 경로(/ 또는 빈 문자열)만 매칭 (.js, .css, 이미지 등 제외)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/match" `
-name "url" -value "(^$|.*\/$|.*\.php$)"
# 4. Action: 403 Forbidden
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/action" `
-name "type" -value "CustomResponse"
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/action" `
-name "statusCode" -value 403
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/action" `
-name "statusReason" -value "Forbidden"
# 5. Conditions: MatchAny
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/conditions" `
-name "logicalGrouping" -value "MatchAny"
# 6. Condition 1: 소프트 하이픈 인코딩 탐지 (%ad, %AD)
Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/conditions" -name "." -value @{
input = "{QUERY_STRING}"
pattern = ".*(%ad|%AD).*"
}
# 7. Condition 2: PHP 실행 옵션 플래그 정밀 탐지 (쿼리 시작(^), 공백(+), 또는 구분자(&) 바로 뒤의 -d, -s, -w 만 매칭)
Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.webServer/rewrite/globalRules/rule[@name='$GlobalRuleName']/conditions" -name "." -value @{
input = "{QUERY_STRING}"
pattern = "(^|&|\+|%20)-[dswDSW](\b|=|\+|%20)"
}
Write-Host "[+] 오탐이 방지된 전역 인바운드 보안 규칙이 적용되었습니다." -ForegroundColor Green
※ .php만 적용할지, 전체 모든 입력에 대하셔 적용할지는 관리자의 선택 영역
PHP.INI 수정
위의
URL 재작성룰로 우선 방어 가능하니, 확인 후 진행 필요.
특히allow_url_fopen의 경우 클롤링 및 외부 API호출을 위하여 편의상fopen(),file_get_contents()사용되었을 수 있으니, 꼭 확인 후 적용을 추천
php.iniFastCGI 매핑 추가 점검 사항 (소스 확인 후 적용)- 기본 환경 설정
allow_url_include = Off
allow_url_fopen = Off
expose_php = Off
- 세션 환경 설정
- session.upload_progress.enabled : 실시간 업로드 진행률 설정 (기본적으로 Off)
- session.use_strict_mode : 세선 공격을 방어하기 위한 설정
session.upload_progress.enabled = 0
session.use_strict_mode = 1
점검절차 (curl 이용)
- 정상 요청 확인 (기대값: HTTP 200 OK)
curl -I -s -o /dev/null -w "%{http_code}\n" "http://localhost/index.php?test=normal"
- CVE-2024-4577 악용 패턴 테스트 (기대값: HTTP 403 Forbidden)
# 1) 소문자 소프트 하이픈(%ad) 주입 테스트
curl -i "http://localhost/index.php?%add+allow_url_include%3don"
# 2) 대문자 소프트 하이픈(%AD) 주입 테스트
curl -i "http://localhost/index.php?%ADd+allow_url_include%3don"
# 3) 직접 인자 플래그(-d) 주입 테스트
curl -i "http://localhost/index.php?-d+allow_url_include=1"
진단 스크립트 (PowerShell)
- 접속 확인
# 실제 환경에 맞게 주소 변경 (포트나 HTTPS 여부 확인)
$baseUrl = "http://localhost/index.php"
$tests = @(
@{ Name = "정상 요청"; Query = "?page=1" },
@{ Name = "소프트 하이픈(%ad)"; Query = "?%add+allow_url_include=1" },
@{ Name = "대문자 하이픈(%AD)"; Query = "?%ADd+auto_prepend_file=php://input" },
@{ Name = "직접 인자 주입(-d)"; Query = "?-d+expose_php=0" }
)
foreach ($test in $tests) {
$targetUrl = $baseUrl + $test.Query
try {
$response = Invoke-WebRequest -Uri $targetUrl -Method Head -UseBasicParsing -TimeoutSec 5
Write-Host "[$($response.StatusCode)] $($test.Name) - 통과 (200 OK)" -ForegroundColor Yellow
} catch {
if ($_.Exception.Response) {
$statusCode = [int]$_.Exception.Response.StatusCode
if ($statusCode -eq 403) {
Write-Host "[$statusCode] $($test.Name) - 정상 차단됨 (보안 룰 작동)" -ForegroundColor Green
} else {
Write-Host "[$statusCode] $($test.Name) - HTTP 오류 발생" -ForegroundColor DarkYellow
}
} else {
Write-Host "[연결 실패] $($test.Name) - 서버에 도달하지 못함: $($_.Exception.Message)" -ForegroundColor Red
}
}
}
PowerShell 보안 오류 발생시
- 1회성 Bypass 실행 (가장 권장)
powershell -ExecutionPolicy Bypass -File .\[파일명]
- 현재 열려 있는 Powershell창에서만
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -Force
.\[파일명]
- 시스템 전역 허용 (관리자 권한)
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force
.\[파일명]
※ 본 글은 직접 작성하였으며, 점검 절차 및 PowerShell 소스는 제미나이(Gemini) 3.8 Flash의 도움을 받았습니다.
반응형
'Language > PHP' 카테고리의 다른 글
| PHP,Windows,IIS)PHP(PHP2Shell)관련 로그(Log) 분석 스크립트 (PowerShell 스크립트 포함) (0) | 2026.10.23 |
|---|---|
| [PHP] 워드·아웃룩·엑셀에서 복사한 유니코드 특수문자 처리 오류와 PHP 변환 함수 (0) | 2026.07.02 |
| [PHP]구글드라이브를 이용한 OCR 기능 (0) | 2021.09.09 |
| [알고리즘] 날짜 - 이번주에 시작일과 마지막일 구하기 (0) | 2009.12.08 |
| array_walk를 이용한 배열 함수 (0) | 2008.07.18 |