- Add YARA guide, WAF integration docs, and webshell rules - Ignore *.lock files in .gitignore Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
492 lines
10 KiB
Plaintext
492 lines
10 KiB
Plaintext
/*
|
|
* 웹쉘 탐지 YARA 규칙
|
|
*
|
|
* 사용법:
|
|
* yara -r webshell_rules.yar /var/www/html/
|
|
* yara -s webshell_rules.yar suspicious.php
|
|
*
|
|
* Author: Security Team
|
|
* Last Updated: 2025-02-02
|
|
*/
|
|
|
|
// ============================================
|
|
// PHP 웹쉘 - 기본 패턴
|
|
// ============================================
|
|
|
|
rule PHP_Webshell_Eval {
|
|
meta:
|
|
description = "eval()을 이용한 PHP 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$eval = /eval\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)/ nocase
|
|
|
|
condition:
|
|
$eval
|
|
}
|
|
|
|
rule PHP_Webshell_System {
|
|
meta:
|
|
description = "시스템 명령 실행 PHP 웹쉘"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$func1 = "system(" nocase
|
|
$func2 = "exec(" nocase
|
|
$func3 = "shell_exec(" nocase
|
|
$func4 = "passthru(" nocase
|
|
$func5 = "popen(" nocase
|
|
$func6 = "proc_open(" nocase
|
|
|
|
$input1 = "$_GET"
|
|
$input2 = "$_POST"
|
|
$input3 = "$_REQUEST"
|
|
|
|
condition:
|
|
any of ($func*) and any of ($input*)
|
|
}
|
|
|
|
rule PHP_Webshell_Assert {
|
|
meta:
|
|
description = "assert()를 이용한 PHP 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$assert = /assert\s*\(\s*\$_(GET|POST|REQUEST)/ nocase
|
|
|
|
condition:
|
|
$assert
|
|
}
|
|
|
|
rule PHP_Webshell_Preg_Replace {
|
|
meta:
|
|
description = "preg_replace /e 플래그 악용"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$preg = /preg_replace\s*\(\s*['"][^'"]*\/e['"]/ nocase
|
|
|
|
condition:
|
|
$preg
|
|
}
|
|
|
|
rule PHP_Webshell_Create_Function {
|
|
meta:
|
|
description = "create_function()을 이용한 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$cf = /create_function\s*\([^)]*\$_(GET|POST|REQUEST)/ nocase
|
|
|
|
condition:
|
|
$cf
|
|
}
|
|
|
|
rule PHP_Webshell_Backtick {
|
|
meta:
|
|
description = "백틱을 이용한 명령 실행"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$bt = /`[^`]*\$_(GET|POST|REQUEST)[^`]*`/
|
|
|
|
condition:
|
|
$bt
|
|
}
|
|
|
|
// ============================================
|
|
// PHP 웹쉘 - 난독화
|
|
// ============================================
|
|
|
|
rule PHP_Webshell_Base64_Eval {
|
|
meta:
|
|
description = "Base64 인코딩된 eval"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$pattern = /eval\s*\(\s*base64_decode\s*\(/ nocase
|
|
$b64_eval = "ZXZhbC" // base64("eval")
|
|
|
|
condition:
|
|
$pattern or $b64_eval
|
|
}
|
|
|
|
rule PHP_Webshell_Gzinflate {
|
|
meta:
|
|
description = "gzinflate 압축 난독화"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$gz1 = /eval\s*\(\s*gzinflate\s*\(/ nocase
|
|
$gz2 = /eval\s*\(\s*gzuncompress\s*\(/ nocase
|
|
$gz3 = /eval\s*\(\s*gzdecode\s*\(/ nocase
|
|
|
|
condition:
|
|
any of them
|
|
}
|
|
|
|
rule PHP_Webshell_Str_Rot13 {
|
|
meta:
|
|
description = "str_rot13 난독화"
|
|
severity = "medium"
|
|
|
|
strings:
|
|
$rot = /eval\s*\(\s*str_rot13\s*\(/ nocase
|
|
|
|
condition:
|
|
$rot
|
|
}
|
|
|
|
rule PHP_Webshell_Chr_Obfuscation {
|
|
meta:
|
|
description = "chr() 함수를 이용한 난독화"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$chr = /chr\s*\(\s*\d+\s*\)\s*\.\s*chr\s*\(\s*\d+\s*\)/
|
|
|
|
condition:
|
|
#chr > 5
|
|
}
|
|
|
|
rule PHP_Webshell_Hex_Obfuscation {
|
|
meta:
|
|
description = "16진수 문자열 난독화"
|
|
severity = "medium"
|
|
|
|
strings:
|
|
$hex = /\\x[0-9a-fA-F]{2}/
|
|
|
|
condition:
|
|
#hex > 10
|
|
}
|
|
|
|
rule PHP_Webshell_Variable_Function {
|
|
meta:
|
|
description = "변수를 함수로 호출"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$vf1 = /\$\w+\s*\(\s*\$_(GET|POST|REQUEST)/
|
|
$vf2 = /\$\{\s*\$\w+\s*\}\s*\(/
|
|
|
|
condition:
|
|
any of them
|
|
}
|
|
|
|
rule PHP_Webshell_Multi_Encoding {
|
|
meta:
|
|
description = "다중 인코딩 레이어"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$enc1 = "base64_decode" nocase
|
|
$enc2 = "gzinflate" nocase
|
|
$enc3 = "gzuncompress" nocase
|
|
$enc4 = "str_rot13" nocase
|
|
$enc5 = "convert_uudecode" nocase
|
|
|
|
condition:
|
|
3 of them
|
|
}
|
|
|
|
// ============================================
|
|
// 유명 웹쉘 시그니처
|
|
// ============================================
|
|
|
|
rule Webshell_C99 {
|
|
meta:
|
|
description = "C99 웹쉘"
|
|
severity = "critical"
|
|
family = "c99"
|
|
|
|
strings:
|
|
$s1 = "c99shell" nocase
|
|
$s2 = "c99_buff_prepare"
|
|
$s3 = "c99_sess_put"
|
|
$s4 = "c99ftpbrutecheck"
|
|
$s5 = "c99fsearch"
|
|
|
|
condition:
|
|
2 of them
|
|
}
|
|
|
|
rule Webshell_R57 {
|
|
meta:
|
|
description = "R57 웹쉘"
|
|
severity = "critical"
|
|
family = "r57"
|
|
|
|
strings:
|
|
$s1 = "r57shell" nocase
|
|
$s2 = "r57_pwd_hash"
|
|
$s3 = "Safe_Mode Bypass"
|
|
$s4 = "| Encoder"
|
|
|
|
condition:
|
|
2 of them
|
|
}
|
|
|
|
rule Webshell_WSO {
|
|
meta:
|
|
description = "WSO (Web Shell by oRb)"
|
|
severity = "critical"
|
|
family = "wso"
|
|
|
|
strings:
|
|
$s1 = "Web Shell by oRb" nocase
|
|
$s2 = "wso_version"
|
|
$s3 = "FilesMan"
|
|
$s4 = "WSO" wide ascii
|
|
|
|
condition:
|
|
2 of them
|
|
}
|
|
|
|
rule Webshell_B374k {
|
|
meta:
|
|
description = "B374k 웹쉘"
|
|
severity = "critical"
|
|
family = "b374k"
|
|
|
|
strings:
|
|
$s1 = "b374k" nocase
|
|
$s2 = "b374k_config"
|
|
$s3 = "b374k 2"
|
|
|
|
condition:
|
|
any of them
|
|
}
|
|
|
|
rule Webshell_Weevely {
|
|
meta:
|
|
description = "Weevely 백도어"
|
|
severity = "critical"
|
|
family = "weevely"
|
|
|
|
strings:
|
|
$pattern = /\$\w=\$\w\(\'\',\$\w\(\$\w\(\$\w/
|
|
|
|
condition:
|
|
$pattern and filesize < 5KB
|
|
}
|
|
|
|
rule Webshell_China_Chopper {
|
|
meta:
|
|
description = "China Chopper 웹쉘"
|
|
severity = "critical"
|
|
family = "china_chopper"
|
|
|
|
strings:
|
|
$cp1 = /@eval($_POST[/ nocase
|
|
$cp2 = /<%eval request\(/ nocase
|
|
$cp3 = /<%@ Page Language="Jscript"%><%eval(/ nocase
|
|
|
|
condition:
|
|
any of them and filesize < 1KB
|
|
}
|
|
|
|
rule Webshell_Ani_Shell {
|
|
meta:
|
|
description = "Ani-Shell 웹쉘"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$s1 = "Ani-Shell"
|
|
$s2 = "ani_shell"
|
|
|
|
condition:
|
|
any of them
|
|
}
|
|
|
|
rule Webshell_PHPSpy {
|
|
meta:
|
|
description = "PHPSpy 웹쉘"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$s1 = "phpspy" nocase
|
|
$s2 = "Php Spy"
|
|
|
|
condition:
|
|
any of them
|
|
}
|
|
|
|
// ============================================
|
|
// 파일 업로드 공격
|
|
// ============================================
|
|
|
|
rule PHP_File_Upload_Attack {
|
|
meta:
|
|
description = "파일 업로드를 통한 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$upload = "move_uploaded_file" nocase
|
|
$write1 = "file_put_contents" nocase
|
|
$write2 = "fwrite" nocase
|
|
|
|
$exec1 = "eval(" nocase
|
|
$exec2 = "system(" nocase
|
|
$exec3 = "exec(" nocase
|
|
|
|
condition:
|
|
($upload or $write1 or $write2) and any of ($exec*)
|
|
}
|
|
|
|
// ============================================
|
|
// 숨겨진 백도어
|
|
// ============================================
|
|
|
|
rule PHP_Hidden_Backdoor {
|
|
meta:
|
|
description = "HTTP 헤더를 통한 숨겨진 백도어"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$hdr1 = /\$_SERVER\s*\[\s*['"]HTTP_/ nocase
|
|
$hdr2 = "getallheaders()" nocase
|
|
|
|
$exec = /(eval|assert|system|exec)\s*\(/ nocase
|
|
|
|
condition:
|
|
any of ($hdr*) and $exec
|
|
}
|
|
|
|
rule PHP_Long_Encoded_String {
|
|
meta:
|
|
description = "긴 인코딩된 문자열 (난독화 의심)"
|
|
severity = "medium"
|
|
|
|
strings:
|
|
$long_b64 = /['"][a-zA-Z0-9+\/=]{500,}['"]/
|
|
|
|
condition:
|
|
$long_b64
|
|
}
|
|
|
|
// ============================================
|
|
// ASP/ASPX 웹쉘
|
|
// ============================================
|
|
|
|
rule ASPX_Webshell_Generic {
|
|
meta:
|
|
description = "ASP.NET 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$asp1 = "Request.Form" nocase
|
|
$asp2 = "Request.QueryString" nocase
|
|
$asp3 = "Request.Item" nocase
|
|
|
|
$exec1 = "Process.Start" nocase
|
|
$exec2 = "cmd.exe" nocase
|
|
$exec3 = "powershell" nocase
|
|
|
|
condition:
|
|
any of ($asp*) and any of ($exec*)
|
|
}
|
|
|
|
rule ASP_Eval {
|
|
meta:
|
|
description = "ASP Eval 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$eval = /Eval\s*\(\s*Request/ nocase
|
|
$exec = /Execute\s*\(\s*Request/ nocase
|
|
|
|
condition:
|
|
$eval or $exec
|
|
}
|
|
|
|
// ============================================
|
|
// JSP 웹쉘
|
|
// ============================================
|
|
|
|
rule JSP_Webshell_Generic {
|
|
meta:
|
|
description = "JSP 웹쉘"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$param = "request.getParameter" nocase
|
|
$exec1 = "Runtime.getRuntime().exec" nocase
|
|
$exec2 = "ProcessBuilder" nocase
|
|
|
|
condition:
|
|
$param and ($exec1 or $exec2)
|
|
}
|
|
|
|
rule JSP_Cmd_Shell {
|
|
meta:
|
|
description = "JSP 명령 실행 쉘"
|
|
severity = "critical"
|
|
|
|
strings:
|
|
$rt = "Runtime.getRuntime()"
|
|
$cmd1 = "cmd.exe" nocase
|
|
$cmd2 = "/bin/sh"
|
|
$cmd3 = "/bin/bash"
|
|
|
|
condition:
|
|
$rt and any of ($cmd*)
|
|
}
|
|
|
|
// ============================================
|
|
// 일반 의심 패턴
|
|
// ============================================
|
|
|
|
rule Suspicious_PHP_Code {
|
|
meta:
|
|
description = "의심스러운 PHP 코드 패턴"
|
|
severity = "medium"
|
|
|
|
strings:
|
|
$s1 = "$GLOBALS[" nocase
|
|
$s2 = "call_user_func_array" nocase
|
|
$s3 = "array_map" nocase
|
|
$s4 = "array_filter" nocase
|
|
$s5 = "ReflectionFunction" nocase
|
|
|
|
$input = /\$_(GET|POST|REQUEST|COOKIE)/
|
|
|
|
condition:
|
|
2 of ($s*) and $input
|
|
}
|
|
|
|
rule Suspicious_Error_Suppression {
|
|
meta:
|
|
description = "에러 억제 + 위험 함수"
|
|
severity = "medium"
|
|
|
|
strings:
|
|
$suppress = /@(eval|assert|system|exec|shell_exec|passthru)\s*\(/
|
|
|
|
condition:
|
|
$suppress
|
|
}
|
|
|
|
// ============================================
|
|
// 이미지 위장 웹쉘
|
|
// ============================================
|
|
|
|
rule PHP_In_Image {
|
|
meta:
|
|
description = "이미지 파일 내 PHP 코드"
|
|
severity = "high"
|
|
|
|
strings:
|
|
$gif = { 47 49 46 38 } // GIF header
|
|
$jpg = { FF D8 FF } // JPEG header
|
|
$png = { 89 50 4E 47 } // PNG header
|
|
|
|
$php1 = "<?php"
|
|
$php2 = "<?"
|
|
$php3 = "<%"
|
|
|
|
condition:
|
|
($gif at 0 or $jpg at 0 or $png at 0) and
|
|
any of ($php*)
|
|
}
|