פרצת אבטחה קריטית ב-WordPress: קריסה מרחוק דרך xmlrpc.php
אמ;לק
התגלתה חולשת אבטחה ב-WordPress המאפשרת לתוקפים להפיל את שרת האתר בקלות באמצעות שליחת בקשה מיוחדת לקובץ xmlrpc.php. החולשה מנצלת את הטיפול בדחיסת נתונים ועלולה לשבש את פעילות האתר. מומלץ לבטל את xmlrpc אם הוא אינו בשימוש פעיל או לוודא שהאתר מוגן באמצעות WAF מעודכן.
סיכום בעברית: מערכת VibeSec · מדיניות המקורות
נוסף ל-VibeSec:
כותרת מקורית: WordPress doesn’t ship Brotli. It’s still exposed to CVE-2026-111497

התוכן המלא בעברית
תרגום אוטומטי מהמקור; מונחים טכניים נשארו באנגלית. לנוסח המחייב — הכתבה המקורית.
בקשת POST אחת לא מאומתת ל-xmlrpc.php יכולה להפיל עובד (worker) מסוג PHP בהתקנת WordPress סטנדרטית, ולא מדובר בבקשה חריגה במיוחד.
בקשת POST אחת לא מאומתת ל-xmlrpc.php יכולה להפיל עובד (worker) מסוג PHP בהתקנת WordPress סטנדרטית, ולא מדובר בבקשה חריגה במיוחד. התוקף שולח pingback שכתובת המקור (URL) שלו מצביעה על שרת שבבעלותו. ה-WordPress ניגש להביא את ה-URL הזה, השרת משיב עם גוף המקודד ב-Brotli, וספריית libcurl פורקת אותו עוד לפני ש-WordPress רואה ממנו ולו בייט אחד. בגרסאות נוכחיות, פעולת הפריקה הזו גורמת לכתיבה מעבר לגבולות ה-buffer.
הפגם הוא CVE-2026-111497, כתיבה מחוץ לתחום (out-of-bounds write) במפענח Brotli של Google. גוגל הקצתה את ה-CVE ודירגה אותו בציון 9.2. הפגם מכסה את גרסאות Brotli מ-1.0.3 עד 1.2.0 ותוקן ב-21 באוקטובר 2026 בגרסאות 1.0.10, 1.1.1 ו-1.2.1. סוכנות CISA הוסיפה אותו לקטלוג ה-Known Exploited Vulnerabilities ב-27 באוקטובר והעניקה לסוכנויות פדרליות ארכה עד ה-10 בנובמבר לטיפול בו.
WordPress אינה מגיעה עם Brotli מובנה. היא מקשרת לכל מה שמערכת ההפעלה מספקת דרך libcurl עבור כל בקשה יוצאת שהיא מבצעת. לכן, אין מה לעדכן ב-WordPress, אין תוסף לשדרג, ושום דבר בבקשה הנכנסת אינו מסמן את ההתקפה, כיוון שהמטען (payload) לעולם לא עובר בתוך הבקשה עצמה. הוא חוזר בתגובה מהשרת של התוקף.
מה שניתן לעשות הוא לסגור את הנתיבים המגיעים למפענח ולשנות את הדרך שבה WordPress מבקש תגובות מלכתחילה. שתי הדרכים מפורטות מטה. כיבוי של XML-RPC סוגר את הדלת הקלה ביותר ומשאיר את השאר פתוחות.
כיצד WordPress מגיע למפענח
כל בקשה יוצאת ש-WordPress מבצעת עוברת דרך WP_Http, ספריית ה-Requests המצורפת וה-transport מסוג cURL שלה. ה-transport הזה מגדיר את CURLOPT_ENCODING למחרוזת ריקה בקונסטרקטור שלו, עבור כל handle, ללא דרך לבטל זאת. ערך ריק הוא הדרך של libcurl לומר "קבל כל דחיסה ופרוק אותה עבורי", כך שתשובה המסומנת כ-Content-Encoding: br נכנסת למפענח ה-Brotli בתוך curl_exec(). הגדרת decompress => false לא תעזור לך, כי היא רק אומרת ל-WordPress מה לעשות עם גוף שכבר פורק על ידי libcurl.
זרם (stream) פגום גורם למפענח לכתוב מעבר ל-buffer שלו לתוך זיכרון ה-heap הנמצא בסמוך. גרימת קריסה היא עניין פשוט שעובד על כל גרסה, כך שניתן להפיל עובד עם בקשה אחת, ומספר מאות בקשות יכולות להשאיר אתר מושבת. ביצוע קוד הוא עניין אחר ויש לבנות אותו ספציפית לכל יעד.
חוקרים מחזיקים בשיטות להרצת קוד (shells) נגד קבצים בינאריים של Node.js ו-Bun, וקיימות הוכחות היתכנות (PoC) ציבוריות, אך השרשראות הללו נשענות על היסט (offset) קבוע בבנייה ספציפית שאינה PIE ועל ניחוש מספר socket, והן נכשלות בכל תרחיש אחר. PHP מאחורי Apache או FPM אינו יעד זה, ועדיין לא קיימת שרשרת פומבית המכוונת אליו. כלי עבודה ציבוריים עבור הנתיב של WordPress אכן קיימים, אך הם נעצרים בשלב הקריסה.
מעבדה שפרסמה ב-27 באוקטובר מדגימה ניצול של הבאג דרך pingback ומתעדת שתי תוצאות: עובד שקורס מיידית, ועובד שנתקע ב-100% ניצול מעבד (CPU) בעקבות מטען של פחות מ-100 בתים. כותב המעבדה מציין במפורש שלא כלולה הרצת קוד ושהשגת RCE מרחוק בצורה עיוורת טרם הושגה. לכן, מניעת שירות (DoS) היא כעת עניין של העתק-הדבק, אך החלק הקשה נותר קשה. עם זאת, קשה אינו אומר בלתי ניתן להשגה. יש לחשב קיזוזים (offsets) פעם אחת לכל גרסה ולא פעם אחת לכל קורבן, ואיש אינו מחויב לפרסם את עבודתו.
הדבר שיש להיערך אליו כיום הוא קריסת עובדים והשבתת השירות הנלווית לכך. חשיפת זיכרון והרצת קוד הם יעדים קשים יותר, והם אלו שבגינם ניתן הציון 9.2.
הנתיב ללא אימות (Unauthenticated Path)
השיטה pingback.ping קיימת במפת השיטות של XML-RPC כברירת מחדל ואינה דורשת חשבון. היא מקבלת כתובת URL של מקור, ו-pingback_ping() מביאה אותה באמצעות wp_safe_remote_get(). בהתקנה סטנדרטית, XML-RPC מופעל, default_ping_status מוגדר כ-open, ויש לפחות פוסט אחד שניתן לבצע לו ping, כך שהתוקף צריך רק להפנות את המקור לשרת שלו ולענות עם זרם פגום. העובד שמטפל בבקשה מפענח אותה וקורס.
באתרים רבים נתיב זה מעולם לא היה פתוח מלכתחילה. מארחים מנוהלים חוסמים לרוב את xmlrpc.php בקצה (edge), וקטעי קוד להקשחת אבטחה מכבים אותו כבר שנים, כך שיש סיכוי סביר שהוא כבר סגור בהתקנה שלך. ודא זאת במקום להניח, שכן "XML-RPC מושבת" יכול להתייחס לשני דברים שונים. חסימה בשרת ה-Web סוגרת את הנתיב. מסנן ה-xmlrpc_enabled בשורה אחת, המשמש בתוספי אבטחה רבים, אינו עושה זאת: הוא מגביל רק את השיטות הדורשות התחברות, ו-pingback אינה אחת מהן. אתר המריץ מסנן זה עונה ל-pingback בדיוק כמו אתר שלא עבר שום שינוי.
בקשה אחת תחשוף איזה מהשניים קיים אצלך. כתובת המקור שלהלן היא כתובת מקומית ש-WordPress דוחה לפני שהיא מנסה להביא דבר מה, לכן בטוח להפנות אותה לאתר שלך:
curl -s -i -X POST https://example.com/xmlrpc.php \ -H 'Content-Type: text/xml' \ --data ' <?xml version= "1.0" ?> < methodCall > < methodName > pingback.ping </ methodName > < params > < param > < value > < string > http://127.0.0.1/ </ string > </ value > </ param > < param > < value > < string > https://example.com/?p=1 </ string > </ value > </ param > </ params > </ methodCall > '
תגובת 403, 404, או כל דבר שאינו XML, אומר שהנתיב חסום ופרצה זו לא רלוונטית אליך. שגיאה בנוסח reading requested method pingback.ping does not exist אומרת שהשיטה בוטלה, מה שגם סוגר את הפרצה. כל methodResponse אחר, עם שגיאה או בלעדיה, אומר ש-pingback_ping() הורץ והנתיב פתוח. אף אחד מאלו לא נוגע בנתיבי התוספים, oEmbed או ה-feed המוזכרים לעיל, כך שסגירת נתיב ה-pingback רק מצמצמת את שטח החשיפה שלך במקום להסיר אותו.
נתיבי התוספים
מכיוון שהבעיה נמצאת בשכבת ה-transport, היא אינה מוגבלת ל-XML-RPC. כל תוסף שמביא כתובת שסופקה על ידי מבקר בצד השרת עלול להגיע לאותו מפענח: תצוגות מקדימות לקישורים, מייבאי URL ותמונות, מאמתי webhook, ופרוקסי אווטארים. ה-wrapper הבטוח לא עוזר כאן. כל מה ש-wp_safe_remote_get() מוסיף על wp_remote_get() הוא reject_unsafe_urls, מסנן SSRF שדוחה IPs פנימיים וכל פורט מלבד 80, 443 ו-8080. הוא מעולם לא בודק את ה-Content-Encoding, כך ששרת תוקף רגיל בפורט 443 עובר דרכו ללא הפרעה.
מדוע WAF לא יכול לראות זאת בבקשה
WAF מקבל החלטות על בסיס מה שהמבקר שלח, וזה המקום היחיד שבו המתקפה הזו לא נמצאת. קריאת pingback.ping, או קריאת תוסף, נושאת בתוכה URL ומעט מאוד מעבר לכך, וה-URL נראה כמו כל אחד אחר. החלק המעניין מגיע מאוחר יותר, מהשרת של התוקף. כדי להבדיל בין בקשה זו ללגיטימית, WAF היה צריך להביא את ה-URL בעצמו ולפענח את תגובת ה-Brotli, וזהו בדיוק השלב שגורם לקריסה. חומת אש מסוג PHP אינה יכולה להגיע לשם בכל מקרה: היא רצה בתוך WordPress, והפענוח מתרחש מתחתיה בתוך libcurl, לפני שקוד ה-PHP מתבצע. להלן תהליך התקשורת והשלב שבו כלל עשוי לפעול:
השלב היחיד שבו כלל יכול לגעת הוא הראשון, ושם לא ניתן להבחין בין ההתקפה לפינגבק אמיתי. לכן, הכלל חייב להסיר את השיטה כולה ולא רק את ההתקפה. זהו צעד כדאי, והוא האופציה השנייה להלן, אך הוא סוגר נתיב אחד בלבד. הוא אינו מגיע לבאג ואינו עושה דבר לגבי נתיבי התוספים.
תיקון הבעיה
עדכן את Brotli ובנה מחדש את האימג'. זהו השינוי היחיד שמגיע לקוד הפגיע, ולכן הוא הדבר היחיד שבאמת מתקן את הבעיה. עברו לגרסאות 1.0.10, 1.1.1 או 1.2.1, או החילו את עדכון האבטחה של ההפצה שלכם עבור libbrotli1. שני דברים הופכים את הבדיקה לפחות פשוטה ממה שהיא נשמעת.
משיכה חוזרת של האימג' הרשמי לא תעזור כרגע. wordpress:latest עדיין מתקין היום גרסה פגיעה של libbrotli1. עד שאימג' הבסיס ייבנה מחדש, שדרגו את החבילה בשכבה משלכם:
FROM wordpress:latest RUN apt - get update \ && apt - get install - y --no-install-recommends --only-upgrade libbrotli1 \ && rm - rf / var / lib / apt / lists /*
ומספר הגרסה ב-upstream לא יגיד לכם אם אתם מתוקנים. Debian ו-Ubuntu מבצעות backport לתיקון לתוך חבילת ה-1.1.0 הקיימת שלהן במקום לעבור ל-1.1.1, כך ששרת מתוקן לחלוטין עדיין ידווח על Brotli 1.1.0 ועדיין יחזיק libbrotlidec.so.1.1.0 על הדיסק. בדקו את גרסת החבילה במקום זאת:
dpkg-query -W -f= '${Version} ' libbrotli1 # vulnerable: 1.1.0-2+b7 # patched: 1.1.0-2+deb13u1 (Debian 13 "trixie"; the suffix # differs by distribution and release)
אם אינכם יכולים לעדכן עדיין
שום דבר מהבאים אינו מתקן את הבאג. אלו רק דרכים לחסום את הגישה אליו בזמן שאתם מתארגנים לעדכון האמיתי. מאחר שאין שום דבר שימושי להתבסס עליו בבקשה הנכנסת, המנוף היחיד הוא הקוד שלכם, ובמיוחד האופן שבו WordPress מבקש את התגובה. האופציה הראשונה היא הגורפת, והיא עוצרת את כל הנתיבים בבת אחת.
הפסיקו את פענוח ה-Brotli ב-WordPress לחלוטין. הכניסו את הקוד הבא לתוך תוסף must-use (תיקיית wp-content/mu-plugins/) או לקובץ functions.php של ערכת הנושא שלכם. הוא מורה ל-libcurl לא לפרוק דבר, כך שהגוף ששלח התוקף לעולם לא מגיע למפענח ה-Brotli, והוא מכסה את כל נתיבי ה-pingback, oEmbed, פידים ושאילתות תוספים במכה אחת. המחיר הוא ש-WordPress תפסיק לבקש תגובות דחוסות בבקשות היוצאות שלה, וזהו מחיר זניח. זה גם נכשל בצורה בטוחה: אם שרת שולח בכל זאת גוף דחוס, WordPress עדיין תפרוק אותו בעצמה באמצעות gzip ב-PHP, הרחק מהקוד הפגיע.
add_action ( 'http_api_curl' , function ( $handle ) { // Don't let libcurl auto-decompress responses, so the Brotli // decoder is never handed an attacker-controlled body. curl_setopt ( $handle , CURLOPT_ENCODING, 'identity' ); curl_setopt ( $handle , CURLOPT_HTTP_CONTENT_DECODING, 0 ); }, 10 , 1 );
כבו את ה-pingback. כשלעצמו, הדבר מסיר את הנתיב ללא אימות. שימו לב שהפקודה הרגילה add_filter( 'xmlrpc_enabled', '__return_false' ) אינה מספיקה: היא רק משביתה את שיטות ה-XML-RPC שדורשות התחברות, ו-pingback אינה דורשת כזו. בטלו את השיטה במקום זאת. פעולה זו מנצחת גם התאמת מחרוזות מבוססת כללים, כיוון ש-WordPress מפענחת הפניות תווים ו-CDATA ב-XML לפני העיבוד, ותוקף יכול לאיית pingback.ping בדרכים שמחרוזת חיפוש רגילה לא תזהה:
add_filter( 'xmlrpc_methods' , function ( $methods ) { unset ( $methods [ 'pingback.ping' ] ); unset ( $methods [ 'pingback.extensions.getPingbacks' ] ); return $methods ; } );
או חסמו את xmlrpc.php בשרת ה-Web, מה שדוחה את הבקשה עוד לפני ש-PHP מתחיל לרוץ. ב-Apache 2.4:
< Files xmlrpc.php > Require all denied </ Files >
location = /xmlrpc.php { deny all; }
חסימת xmlrpc.php סוגרת רק את נתיב ה-pingback. נתיבי oEmbed, פידים וכל תוסף שמביא URL עדיין יכולים להגיע למפענח, לכן שלבו זאת עם פילטר ה-Brotli לעיל. מעבר לכך, בצעו ביקורת על תוספים שמביאים URLs שמסופקים על ידי מבקרים, והיכן שניתן, העבירו את התעבורה היוצאת של WordPress דרך פרוקסי המאפשר גישה רק למארחים מוכרים. אף אחד מאלו לא מחליף את עדכון הספרייה.
זיהוי ההתקפה
תעבורה יוצאת היא המקום שבו זה בולט בצורה הברורה ביותר. התקנת WordPress שמבצעת בקשה שחוזרת עם Content-Encoding: br היא כבר חריגה, כיוון ש-Brotli הוא קידוד לדפדפנים ושאילתות של WordPress עצמה אינן מקבלות אותו בדרך כלל. גוף קטנטן ודחוס שמתנפח בפריקה, או תגובה שנתקעת באמצע, הם חשודים עוד יותר. פרוקסי קדימה (forward proxy) עם לוגים יציג את כל אלו.
בקצה (edge), חפשו בקשות POST לא מאומתות ל-/xmlrpc.php הנושאות pingback.ping שכתובת המקור (URL) שלו מצביעה למקום שלאתר אין שום עסקים איתו, במיוחד אם מדובר בפרצים של בקשות או לכיוון דומיינים שנרשמו בשבוע האחרון. עקבו באותו אופן אחר נקודות קצה של תוספים שמקבלות URL.
קריאה וכלים קשורים
אבטחת WordPress- Jetpack Protect — סריקת חולשות ב-WordPress
- Limit Login Attempts Reloaded — הגבלת ניסיונות התחברות
- MalCare — סריקה וניקוי נוזקות
- פרצה קריטית בתוסף וורדפרס CryptoPayment חושפת פרטי ארנקים
- חולשת Brotli הקריטית: מה זה אומר עבור אתרי WordPress?
- חולשת PHP2Shell: כך קוד שנכתב ב-AI עלול לסכן את האתר שלכם