Language/PHP
PHP, Windows, IIS) PHP(PHP2Shell)관련 로그(Log) 분석 스크립트 (PowerShell 스크립트 포함)
소소한 늙은 개발자의 메모장
2026. 10. 23. 21:07
반응형
PHP, Windows, IIS) PHP(PHP2Shell)관련 로그(Log) 분석 스크립트 (PowerShell 스크립트 포함)
방어 방법
이전글 참조 : https://userpark.net/175
이전 기록을 확인하기 위한 PowerShell 스크립트
윈도우 서버 기준 경로가 동일할것 같으나, 필요시 경로 변경
- Root이하 Sub폴더 전체 검색
- 옵션
- DaysBack : 최근 기준 보여줄 이전 일수
- ShowAll : 기록을 화면에 다 보여 줄지 여부?
- MaxDisplayCount : ShowAll이 아닌 경우 보여줄 최대 개수
# ==============================================================================
# IIS 로그 정밀 분석 스크립트 (전수 출력 / 요약 출력 선택 옵션 버전)
# ==============================================================================
$RootLogDir = "C:\inetpub\logs\LogFiles"
if (-not (Test-Path $RootLogDir)) {
Write-Warning "[-] 로그 디렉터리를 찾을 수 없습니다: $RootLogDir"
return
}
# ------------------------------------------------------------------------------
# [사용자 설정 옵션]
# 1) 날짜 조건: 최근 N일
$DaysBack = 3
$StartDate = (Get-Date).Date.AddDays(-$DaysBack)
# 2) 전체 출력 여부 설정:
# $true -> 탐지된 모든 건수를 생략 없이 100% 화면에 출력
# $false -> 파일당 최근 $MaxDisplayCount 건수만큼만 요약 출력
$ShowAll = $true
$MaxDisplayCount = 15
# ------------------------------------------------------------------------------
# 1. 검사 대상 URI 패턴: 루트(/), 서브디렉터리(/.../), 또는 .php 파일만 대상
$TargetUriPattern = "(^\/$|^.*\/$|\.php$)"
# 2. 정밀 공격 쿼리 패턴 (도면번호/한글 오탐 완벽 제외)
$AttackQueryPattern = "(%[aA][dD][dswDSW\+]|(^|&|\+|%20)-[dswDSW](=|\+|%20|$)|allow_url_include|auto_prepend_file|php:\/\/|expose_php)"
$modeText = if ($ShowAll) { "전체 출력 모드" } else { "요약 출력 모드 (파일당 최근 $MaxDisplayCount 건)" }
Write-Host "[+] 검색 시작 날짜: $($StartDate.ToString('yyyy-MM-dd 00:00:00')) 이후" -ForegroundColor Cyan
Write-Host "[+] 출력 설정 상태: $modeText" -ForegroundColor Cyan
Write-Host "[+] 대상 디렉터리 : $RootLogDir" -ForegroundColor Cyan
$targetFiles = Get-ChildItem -Path $RootLogDir -Filter "*.log" -Recurse -File |
Where-Object { $_.LastWriteTime -ge $StartDate } |
Sort-Object LastWriteTime -Descending
if ($targetFiles.Count -eq 0) {
Write-Warning "[-] 해당 기간 내에 수정된 로그 파일이 없습니다."
return
}
$grandTotal = 0
foreach ($file in $targetFiles) {
$siteFolderName = $file.Directory.Name
$relativeName = "$siteFolderName\$($file.Name)"
$lines = Get-Content -Path $file.FullName
if (-not $lines) { continue }
# #Fields 헤더 동적 매핑
$fieldLine = $lines | Where-Object { $_ -like "#Fields:*" } | Select-Object -First 1
if (-not $fieldLine) { continue }
$headers = ($fieldLine -replace "^#Fields:\s*", "") -split "\s+"
$idxDate = [array]::IndexOf($headers, "date")
$idxTime = [array]::IndexOf($headers, "time")
$idxIP = [array]::IndexOf($headers, "c-ip")
$idxMethod = [array]::IndexOf($headers, "cs-method")
$idxURI = [array]::IndexOf($headers, "cs-uri-stem")
$idxQuery = [array]::IndexOf($headers, "cs-uri-query")
$idxStatus = [array]::IndexOf($headers, "sc-status")
$idxSubStatus = [array]::IndexOf($headers, "sc-substatus")
if ($idxDate -eq -1 -or $idxStatus -eq -1 -or $idxQuery -eq -1 -or $idxURI -eq -1) { continue }
$validMatches = @()
foreach ($line in $lines) {
if ($line.StartsWith("#")) { continue }
$cols = $line -split "\s+"
if ($cols.Length -le $idxStatus) { continue }
$rawURI = $cols[$idxURI]
$rawQuery = $cols[$idxQuery]
if ($rawQuery -eq "-" -or ($rawURI -notmatch $TargetUriPattern)) { continue }
if ($rawQuery -match $AttackQueryPattern) {
$logDateTimeStr = "$($cols[$idxDate]) $($cols[$idxTime])"
$logDate = [datetime]::MinValue
if ([datetime]::TryParse($logDateTimeStr, [ref]$logDate)) {
if ($logDate -ge $StartDate) {
$statusCode = $cols[$idxStatus]
$subStatusCode = if ($idxSubStatus -ne -1) { $cols[$idxSubStatus] } else { "0" }
$shortQuery = if ($rawQuery.Length -gt 45) { $rawQuery.Substring(0, 42) + "..." } else { $rawQuery }
$validMatches += [PSCustomObject]@{
Time = $logDateTimeStr
Status = "$statusCode.$subStatusCode"
Method = if ($idxMethod -ne -1) { $cols[$idxMethod] } else { "-" }
ClientIP = if ($idxIP -ne -1) { $cols[$idxIP] } else { "-" }
URI = $rawURI
Query = $shortQuery
}
}
}
}
}
if ($validMatches.Count -gt 0) {
$grandTotal += $validMatches.Count
Write-Host "----------------------------------------------------------------------------------------------------" -ForegroundColor DarkGray
if ($ShowAll -or ($validMatches.Count -le $MaxDisplayCount)) {
Write-Host "[!] 실제 공격 탐지: $relativeName (기간 내 총 $($validMatches.Count)건 - 전체 표시)" -ForegroundColor Red
$validMatches | Format-Table -Property Time, Status, Method, ClientIP, URI, Query -AutoSize
} else {
Write-Host "[!] 실제 공격 탐지: $relativeName (기간 내 총 $($validMatches.Count)건 중 최근 $MaxDisplayCount 건만 표시)" -ForegroundColor Red
$validMatches | Select-Object -Last $MaxDisplayCount |
Format-Table -Property Time, Status, Method, ClientIP, URI, Query -AutoSize
}
}
}
Write-Host "====================================================================================================" -ForegroundColor Cyan
if ($grandTotal -eq 0) {
Write-Host "[결과] 설정된 기간 내 탐지된 공격 시도가 없습니다." -ForegroundColor Green
} else {
Write-Host "[결과] 실제 공격 시도 총 $grandTotal 건이 확인되었습니다. ($modeText)" -ForegroundColor Yellow
}
※ 본 글은 직접 작성하였으며, 점검 절차 및 PowerShell 소스는 `제미나이(Gemini) 4 Argon`의 도움을 받았습니다.
반응형