امروز درگیر بررسی امنیت یکی از سایتهای WordPress شدم؛ سایتی که در ظاهر بدون مشکل کار میکرد، اما با کمی بررسی مشخص شد مهاجم مدتها قبل به آن دسترسی پیدا کرده و چندین مکانیزم برای حفظ دسترسی خود ایجاد کرده است.
این نوشته گزارش تجربه امروز من است؛ از اولین بررسیها تا پیدا کردن Administratorهای جعلی، Backdoorها، فایلهای مخفی و در نهایت فهمیدن اینکه چرا یک اسکن ساده Antivirus نمیتواند برای اطمینان از سلامت WordPress کافی باشد.
#همهچیز از کجا شروع شد؟
اولین مرحله بررسی Logهای سرور بود.
در لاگهای Apache و PHP درخواستهایی به:
wp-login.phpوجود داشت.
در نگاه اول چنین چیزی میتواند نشانه حمله Brute Force باشد، اما هنگام بررسی متوجه شدم بخشی از این درخواستها از IP خودم بوده است.
بنابراین صرف وجود یک درخواست مشکوک در Log، دلیل کافی برای اعلام Incident نیست.
در بررسی امنیتی باید همیشه سه چیز را کنار هم گذاشت:
IP
+
Timestamp
+
نوع درخواست و رفتار آن#اولین مورد مشکوک؛ MU Plugin
در ادامه به مسیر زیر رسیدم:
wp-content/mu-plugins/Must-Use Pluginها با Pluginهای عادی تفاوت دارند.
آنها بدون نیاز به فعالسازی از طریق پنل WordPress بارگذاری میشوند.
در این پوشه فایلهایی با نامهایی شبیه Pluginهای معروف وجود داشت:
akismet.php
contact-form-7.php
wp-fastest-cache.php
wp-fixplugin.php
wp-yoast-seo.phpاما نکته عجیب این بود که این Pluginها اصلاً در سایت نصب نبودند.
یعنی مهاجم از نام Pluginهای معتبر برای پنهان کردن فایلهای خودش استفاده کرده بود.
#Contact Form 7 تقلبی
موردی که ابتدا فقط مشکوک بود، با بررسی کد کاملاً مشخص شد.
فایلی با نام:
wp-content/mu-plugins/contact-form-7.phpخودش را به شکل Contact Form 7 معرفی میکرد:
/*
Plugin Name: Contact Form 7
Version: 6.1.5
*/اما این Contact Form 7 واقعی نبود.
داخل فایل مکانیزمی وجود داشت که با یک پارامتر مخفی در HTTP Request فعال میشد:
wp_acrionsسپس داده رمزگذاریشده را دریافت و Decode میکرد.
کد از تکنیکهایی مثل:
Base64
ROT13
AES-256-CBCاستفاده میکرد.
اما مسئله اصلی بعد از Decode اتفاق میافتاد.
#دریافت کد PHP از سرور مهاجم
Backdoor میتوانست یک URL را از داده رمزگذاریشده استخراج کند و با CURL به آن متصل شود:
curl_init( $url );
curl_exec( $ch );سپس Response دریافتشده را بهعنوان PHP بررسی میکرد.
بعد آن را در فایلی با پسوند .jpg مینوشت:
wp-content/uploads/wpcf7-module-cache/xxxxxxxx_xxxxx.jpgاما این فایل واقعاً تصویر نبود.
کد PHP بود.
و سپس:
include_once $payload_path;اجرا میشد.
یعنی مهاجم میتوانست کد PHP دلخواه خود را از راه دور دریافت کرده و روی سایت اجرا کند.
بعد از اجرای کد نیز فایل حذف میشد.
این دقیقاً یکی از روشهای رایج برای کاهش ردپا در Backdoorهاست.
ساختار کلی تقریباً چنین بود:
HTTP Request
↓
پارامتر مخفی
↓
Decode / Decrypt
↓
استخراج URL
↓
CURL
↓
دریافت PHP
↓
ذخیره موقت با پسوند .jpg
↓
include_once()
↓
اجرای کد
↓
حذف فایلدر این مرحله دیگر مشخص بود که با یک Plugin خراب یا کد معمولی WordPress طرف نیستیم.
با یک Backdoor واقعی مواجه بودیم.
#سرقت رمز عبور Administrator
اما قسمت بدتر هنوز باقی مانده بود.
این فایل فقط امکان اجرای کد از راه دور را فراهم نمیکرد.
در Hook مربوط به Authentication، رمز عبوری که کاربر هنگام Login وارد میکرد دریافت میشد:
$GLOBALS['_wfc_login_pwd'] = $password;و سپس هنگام Login:
'pwd' => $pwdداخل Log نوشته میشد.
مسیر Log:
wp-content/uploads/logs/wpcf7-integration-debug.txtبنابراین این Backdoor عملاً قابلیت Credential Logging نیز داشت.
یعنی مهاجم میتوانست رمز عبور کاربران را جمعآوری کند.
این موضوع اهمیت پاکسازی را چند برابر میکند.
#تلاش برای نوشتن فایل .htaccess
در همین فایل یک رفتار دیگر هم وجود داشت.
کد تلاش میکرد در:
wp-content/uploads/logs/فایل .htaccess ایجاد کند تا دسترسی مستقیم به Logها کنترل شود.
در Log سرور نیز خطایی شبیه این دیده شد:
file_put_contents(.../uploads/logs/.htaccess)
Permission deniedاین اتفاق در ابتدا شاید یک خطای ساده به نظر برسد، اما وقتی کل کد را بررسی کردم مشخص شد این عملیات بخشی از همان Backdoor بوده است.
#پیدا شدن Administratorهای جعلی
بعد از بررسی فایلها، سراغ Database و کاربران WordPress رفتم.
دستور:
wp user list --role=administrator --allow-rootچند Administrator ناشناس نشان داد.
نامهایی شبیه:
wp_3cojlz
wp_5jkf4j
wp_63f312
wp_amjfno
wp_eycjyd
wp_qy2rjt
wp_skbrjs
wp_t5nagt
wp_59qejuوجود داشت.
تاریخ ایجاد این حسابها نیز عمدتاً در بازه 17 تا 19 آگوست 2026 بود.
در حالی که Administrator اصلی سایت سالها قبل ایجاد شده بود.
این کاربران پس از بررسی بهعنوان حسابهای غیرمجاز شناسایی و حذف شدند.
مثلاً:
wp user delete 23 --allow-rootو در نهایت Administratorهای غیرمجاز حذف شدند.
#چرا ClamAV چیزی پیدا نکرد؟
برای بررسی بیشتر ClamAV را اجرا کردم.
نتیجه:
Scanned files: 11096
Infected files: 0اما در همان زمان ما چند فایل PHP مخرب واقعی پیدا کرده بودیم.
این تجربه یک نکته مهم را نشان داد:
نتیجه Clean بودن یک Antivirus به معنی Clean بودن WordPress نیست.
ClamAV یک Antivirus عمومی است و بسیاری از Backdoorهای PHP که اختصاصی برای یک سایت ساخته شدهاند ممکن است در Signatureهای آن وجود نداشته باشند.
برای همین بررسی امنیتی WordPress بهتر است چند لایه داشته باشد:
Antivirus
+
YARA
+
Code Review
+
WordPress Integrity Check
+
User Audit
+
Plugin/Theme Audit
+
Log Analysis#بررسی WordPress Core
برای بررسی فایلهای اصلی WordPress میتوان از WP-CLI استفاده کرد:
wp core verify-checksums --allow-rootاین یکی از تستهای مفید برای اطمینان از سالم بودن فایلهای Core است.
#بررسی Pluginها
برای مشاهده Pluginهای نصبشده:
wp plugin list --allow-rootاما در یک Incident نباید فقط به همین لیست اکتفا کرد.
باید مسیرهایی مثل:
wp-content/plugins/
wp-content/mu-plugins/
wp-content/themes/
wp-content/uploads/نیز بررسی شوند.
خصوصاً:
mu-pluginsچون فایلهای PHP این پوشه میتوانند بدون فعالسازی معمول WordPress اجرا شوند.
#پیدا کردن فایلهای PHP
یک دستور ساده برای پیدا کردن فایلهای PHP:
find wp-content -type f -name "*.php"و برای فایلهای PHP مخفی:
find wp-content -type f -name ".*.php"#جستجوی کدهای مشکوک
برای یک بررسی سریع میتوان از grep استفاده کرد:
grep -RInE \
'eval\s*\(|base64_decode\s*\(|gzinflate\s*\(|gzdecode\s*\(|str_rot13\s*\(|shell_exec\s*\(|system\s*\(|passthru\s*\(|proc_open\s*\(|popen\s*\(' \
wp-contentالبته این دستور فقط برای Triage مناسب است.
مثلاً base64_decode() میتواند در یک Plugin سالم نیز وجود داشته باشد.
بنابراین:
Suspicious ≠ Maliciousو باید Context کد بررسی شود.
#بررسی Cronهای WordPress
Backdoorها گاهی از Cron برای Persistence استفاده میکنند.
برای بررسی:
wp cron event list --allow-root#بررسی PHP Syntax
برای بررسی Syntax یک فایل:
php -l file.phpمثلاً:
php -l wp-content/plugins/example/example.phpاین دستور Malware را تشخیص نمیدهد؛ فقط Syntax PHP را بررسی میکند.
#یک نکته مهم درباره MU Pluginها
در جریان بررسی امروز مشخص شد که صرفاً نگاه کردن به صفحه Plugins کافی نیست.
ممکن است در پنل WordPress هیچ Plugin مشکوکی مشاهده نشود، ولی در:
wp-content/mu-plugins/فایل PHP مخربی وجود داشته باشد که WordPress آن را بهصورت خودکار Load کند.
به همین دلیل در بررسی Incident باید این پوشه همیشه بررسی شود.
#مرحله بعد؛ اسکن Plugin قبل از Upload
بعد از این اتفاق به این نتیجه رسیدم که فقط بررسی سایت Production کافی نیست.
بهتر است Plugin قبل از Upload شدن روی Server نیز بررسی شود.
در Linux میتوان YARA را نصب کرد:
sudo pacman -S yaraو سپس Ruleهای مخصوص PHP Malware را روی فایلها اجرا کرد.
یک اسکنر مناسب میتواند ترکیبی از این موارد باشد:
YARA
+
PHP suspicious functions
+
Obfuscation detection
+
Hidden PHP files
+
Remote code loading
+
Dynamic execution
+
PHP syntax checkاین روش میتواند قبل از ورود یک فایل مشکوک به Production، حداقل یک مرحله دفاعی اضافه ایجاد کند.
#Credentialها؛ چیزی که نباید فراموش شود
بعد از پیدا شدن Backdoor، حذف فایل بهتنهایی کافی نیست.
وقتی مشخص شد Backdoor قابلیت ثبت Password داشته، باید فرض را بر این گذاشت که Credentialهای مورد استفاده در آن سایت ممکن است افشا شده باشند.
بنابراین پس از پاکسازی باید Credentialهای مرتبط بررسی و در صورت نیاز تعویض شوند:
WordPress Administrator
Hosting Panel
FTP/SFTP
SSH
Database
API Keys
SMTP Credentials
Third-party Servicesهمچنین Sessionها و Loginهای فعال نیز باید بررسی شوند.
#جمعبندی
این Incident برای من یک یادآوری مهم بود:
امنیت WordPress با نصب یک Security Plugin یا اجرای یک Antivirus تمام نمیشود.
یک مهاجم میتواند:
Administrator جعلی ایجاد کند
↓
MU Plugin ایجاد کند
↓
Backdoor نصب کند
↓
Credential سرقت کند
↓
Remote Code Execution داشته باشد
↓
Persistence ایجاد کندو در عین حال سایت برای کاربر عادی کاملاً سالم به نظر برسد.
مهمترین چیزی که از بررسی امروز برای من باقی ماند این است:
در امنیت، Clean بودن یک Scan به معنی Clean بودن سیستم نیست.
باید چند منبع مختلف را کنار هم قرار داد:
Logs
+
Filesystem
+
Database
+
WordPress Core
+
Plugins
+
Themes
+
MU Plugins
+
Cron
+
Users
+
Network Activityو مهمتر از همه، بعد از حذف Malware باید دنبال روش ورود و مکانیزم Persistence نیز گشت.
چون اگر فقط فایل آلوده را حذف کنیم اما راه ورود مهاجم را نبندیم، احتمال بازگشت آلودگی همچنان وجود دارد.
این دقیقاً چیزی بود که امروز در بررسی این سایت با آن مواجه شدم.