Drupal SA-CORE-2019-010 .開頭文件名(如.htaccess) 文件上傳

drupal .開頭文件名 文件上傳

經過diff 8.8.1的補丁,很容易發現修復點,位於core\modules\file\file.module前端

補丁在文件名兩側進行了trim(..., '.'),結合漏洞通告能夠知道應該是文件名過濾不嚴致使.開頭的文件上傳。node

原生模塊分析

漏洞點位於_file_save_upload_single函數sql

function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $validators = [], $destination = FALSE, $replace = FileSystemInterface::EXISTS_REPLACE) {
  ...
  // Begin building file entity.
  $values = [
    'uid' => $user->id(),
    'status' => 0,
    'filename' => $file_info->getClientOriginalName(),//
    'uri' => $file_info->getRealPath(),
    'filesize' => $file_info->getSize(),
  ];
  $values['filemime'] = \Drupal::service('file.mime_type.guesser')->guess($values['filename']);
  $file = File::create($values);

  ...

  // If we made it this far it's safe to record this file in the database.
  $file->save();

  ...
  return $file;
}

全局搜索調用本函數的地方,發現只在core/modules/file/file.module:file_save_upload()中被調用。函數

因爲此處不是控制器,沒法直接調用,所以繼續反向追蹤調用此函數的位置。在多處找到調用,好比位於core/modules/update/src/Form/UpdateManagerInstall.php:submitForm(),這是update模塊的updatemanagerinstall表單。ui

public function submitForm(array &$form, FormStateInterface $form_state) {
  $local_cache = NULL;
  $all_files = $this->getRequest()->files->get('files', []);
  if ($form_state->getValue('project_url')) {
    ...
  }
  elseif (!empty($all_files['project_upload'])) {
    $validators = ['file_validate_extensions' => [$this->archiverManager->getExtensions()]];
    if (!($finfo = file_save_upload('project_upload', $validators, NULL, 0, FileSystemInterface::EXISTS_REPLACE))) {
      // Failed to upload the file. file_save_upload() calls
      // \Drupal\Core\Messenger\MessengerInterface::addError() on failure.
      return;
    }
    $local_cache = $finfo->getFileUri();
  }

這裏的$validators經過$this->archiverManager->getExtensions()調用archiver管理器進行取值,因爲這裏設計不少內部成員變量,所以經過調試的方式來分析會快一些。下面就開始嘗試構造路由到這個update模塊。this

經過在update模塊根目錄下的update.routing.yml路由文件能夠發現相應的路由:url

嘗試上傳.htaccess設計

果真受到了限制,下面調試跟進這個$validators是如何取值的。最終跟進core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php:getDefinitions()方法,這裏經過遍歷全部module目錄下的src/plugin/archiver/下的全部php文件,而後解析這個php文件的annotation。調試後發現只有system模塊下存在這個目錄:3d

能夠看到這裏的annotation中限定了後綴名爲{"tar", "tgz", "tar.gz", "tar.bz2"}

到這裏就能夠中止調試了,這個update模塊因爲限制了後綴名,沒法知足咱們的條件。下面再找一些$validators的值不是$this->archiverManager->getExtensions()的模塊。

發現core/modules/image/src/Controller/QuickEditImageController.php:upload()

public function upload(EntityInterface $entity, $field_name, $langcode, $view_mode_id) {
    $field = $this->getField($entity, $field_name, $langcode);
    $field_validators = $field->getUploadValidators();
    $field_settings = $field->getFieldDefinition()->getSettings();
    $destination = $field->getUploadLocation();

    // Add upload resolution validation.
    if ($field_settings['max_resolution'] || $field_settings['min_resolution']) {
      $field_validators['file_validate_image_resolution'] = [$field_settings['max_resolution'], $field_settings['min_resolution']];
    }

    // Create the destination directory if it does not already exist.
    if (isset($destination) && !$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) {
      return new JsonResponse(['main_error' => $this->t('The destination directory could not be created.'), 'errors' => '']);
    }

    // Attempt to save the image given the field's constraints.
    $result = file_save_upload('image', $field_validators, $destination);
    ...

這裏的$validators是經過$field->getUploadValidators()來取值的,跟以前的module一樣的思路,先構造路由而後進行調試跟進。

訪問quickedit/image/upload/node/1/field_image/en/full映射到本控制器,而後跟進getUploadValidators()。通過一系列跟進以後發現配置在config表中,sql語句大概是SELECT name, data FROM config WHERE collection = '' AND name ='field.field.node.article.field_image';

剩下的調用file_save_upload的地方也都作了驗證,沒有能夠利用的地方,都限制了文件後綴名。

第三方模塊分析

看完全部調用點以後有點懷疑人生,會不會是相似於CVE-2018-7600那樣,durpal6中雖然有漏洞,可是沒找到錯誤的寫法從而沒法利用?經過再次閱讀官方通告以後發現也許真是這樣,可是幸運的是...

意思大概是經過第三方contributed模塊可能致使.htaccess文件上傳?

而後我嘗試在第三方模塊中尋找與文件上傳相關的模塊,找到了一個名爲imce的文件/圖片管理模塊

IMCE is an image/file uploader and browser that supports personal directories and quota.

安裝完畢以後直接訪問路徑/imce/public便可得到一個管理界面(後臺)。

上傳.php文件會自動在後面加上.txt後綴。嘗試上傳.htaccess

然而這只是個前端過濾而已,經過抓包修改文件名便可成功上傳。

經過閱讀源碼發現opUpload()方法調用了file_save_upload()進行文件上傳。

public function opUpload(ImceFM $fm) {
    ...
    $validators = [];
    // Extension validator
    $exts = $fm->getConf('extensions', '');
    $validators['file_validate_extensions'] = [$exts === '*' ? NULL : $exts];
    ...
    // Save files
    if ($files = file_save_upload('imce', $validators, $destination, NULL, $replace)) {

其中後綴名$validators從$fm->getConf('extensions','')獲取。跟蹤源碼後發現,也是從config表中找到imce 模塊的一些配置

因爲$validators是*,即爲不限制後綴名,從而形成.htaccess文件上傳。

補丁

經過diff 8.8.1的補丁,很容易發現修復點,位於core\modules\file\file.module

補丁在文件名兩側進行了trim(..., '.')。

上傳以後變成

參考

https://www.drupal.org/sa-core-2019-010

相關文章
相關標籤/搜索