XDNET
Вернуться   xakepok.ru - Ethical Hacking - Cyber Security - Penetration Testing > UNDERGROUND > Этичный Хакинг и Пентестинг

Этичный Хакинг и Пентестинг Раздел включает в себя активный анализ системы на наличие потенциальных уязвимостей, которые могут спровоцировать некорректную работу целевой системы, либо полный отказ в обслуживании. Здесь учебники, статьи о методах взлома, XSS, SQL-инъекций, а так же, рекомендации по их устранению.

Ответ
Опции просмотра Опции темы  
Старый 18.11.2017, 18:48   #1
Супер-модератор
 
Аватар для Que$t
Группа: Member
 
Регистрация: 29.10.2017
Возраст: 30
Сообщений: 346
Репутация: 127
По умолчанию Как взломать более 17 000 сайтов

Эта история о том, как я нашел уязвимость в фреймворке Webasyst и, в частности, в ecommerce-движке Shop-Script 7.

Все началось с того, что вечером я решил приобрести мерч русскогоязычного рэп-исполнителя. После оплаты мне пришло письмо, содержащее ссылку на детали моего заказа:
Просмотр информации о заказе:
Код:
https://o***yshop.com/my/order/21311/9fe684d6508769ef213111ed917d1cce94088/ 
PIN: 3302
(примечание: ID заказа был видоизменен для публикации)

В глаза сразу бросился идентификатор заказа, встречающийся в середине строки хеша:

9fe684d6508769ef213111ed917d1cce94088

Мне стало интересно, как генерируется эта строка, а для этого нужно было взглянуть на исходники движка. Изучив html source страницы, я узнал какой движок используется в магазине, а немного погуглив нашел где его скачать.

Изучаем исходники

Оказалось, что данная строка генерируется случайно и никакой закономерности не прослеживается. Но волею случая, пока я искал функцию отвечающую за этот хеш, я наткнулся на довольно любопытные участки кода.
wa-system/contact/waContact.class.php

Функция save($data, $validate)
Код:
/**
 * Saves contact's data to database.
 *
 * @param array $data Associative array of contact property values.
 * @param bool $validate Flag requiring to validate property values. Defaults to false.
 * @return int|array Zero, if saved successfully, or array of error messages otherwise
 */
public function save($data = array(), $validate = false)
{
    $add = array();
    foreach ($data as $key => $value) {
        if (strpos($key, '.')) {
            $key_parts = explode('.', $key);
            $f = waContactFields::get($key_parts[0]);
            if ($f) {
                $key = $key_parts[0];
                if ($key_parts[1] && $f->isExt()) {
                    // add next field
                    $add[$key] = true;
                    if (is_array($value)) {
                        if (!isset($value['value'])) {
                            $value = array('ext' => $key_parts[1], 'value' => $value);
                        }
                    } else {
                        $value = array('ext' => $key_parts[1], 'value' => $value);
                    }
                }
            }
        } else {
            $f = waContactFields::get($key);
        }
        if ($f) {
            $this->data[$key] = $f->set($this, $value, array(), isset($add[$key]) ? true : false);
        } else {
            if ($key == 'password') {
                $value = self::getPasswordHash($value);
            }
            $this->data[$key] = $value;
        }
    }
    $this->data['name'] = $this->get('name');
    $this->data['firstname'] = $this->get('firstname');
    $this->data['is_company'] = $this->get('is_company');
    if ($this->id && isset($this->data['is_user'])) {
        $c = new waContact($this->id);
        $is_user = $c['is_user'];
        $log_model = new waLogModel();
        if ($this->data['is_user'] == '-1' && $is_user != '-1') {
            $log_model->add('access_disable', null, $this->id, wa()->getUser()->getId());
        } else if ($this->data['is_user'] != '-1' && $is_user == '-1') {
            $log_model->add('access_enable', null, $this->id, wa()->getUser()->getId());
        }
    }

    $save = array();
    $errors = array();
    $contact_model = new waContactModel();
    foreach ($this->data as $field => $value) {
        if ($field == 'login') {
            $f = new waContactStringField('login', _ws('Login'), array('unique' => true, 'storage' => 'info'));
        } else {
            $f = waContactFields::get($field, $this['is_company'] ? 'company' : 'person');
        }
        if ($f) {
            if ($f->isMulti() && !is_array($value)) {
                $value = array($value);
            }
            if ($f->isMulti()) {
                foreach ($value as &$val) {
                    if (is_string($val)) {
                        $val = trim($val);
                    } else if (isset($val['value']) && is_string($val['value'])) {
                        $val['value'] = trim($val['value']);
                    } else if ($f instanceof waContactCompositeField && isset($val['data']) && is_array($val['data'])) {
                        foreach ($val['data'] as &$v) {
                            if (is_string($v)) {
                                $v = trim($v);
                            }
                        }
                        unset($v);
                    }
                }
                unset($val);
            } else {
                if (is_string($value)) {
                    $value = trim($value);
                } else if (isset($value['value']) && is_string($value['value'])) {
                    $value['value'] = trim($value['value']);
                } else if ($f instanceof waContactCompositeField && isset($value['data']) && is_array($value['data'])) {
                    foreach ($value['data'] as &$v) {
                        if (is_string($v)) {
                            $v = trim($v);
                        }
                    }
                    unset($v);
                }
            }
            if ($validate !== 42) { // this deep dark magic is used when merging contacts
                if ($validate) {
                    if ($e = $f->validate($value, $this->id)) {
                        $errors[$f->getId()] = $e;
                    }
                } elseif ($f->isUnique()) { // validate unique
                    if ($e = $f->validateUnique($value, $this->id)) {
                        $errors[$f->getId()] = $e;
                    }
                }
            }
            if (!$errors && $f->getStorage()) {
                $save[$f->getStorage()->getType()][$field] = $f->prepareSave($value, $this);
            }
        } elseif ($contact_model->fieldExists($field)) {
            $save['waContactInfoStorage'][$field] = $value;
        } else {
            $save['waContactDataStorage'][$field] = $value;
        }
    }

    // Returns errors
    if ($errors) {
        return $errors;
    }

    $is_add = false;
    // Saving to all storages
    try {
        if (!$this->id) {
            $is_add = true;
            $storage = 'waContactInfoStorage';

            if (wa()->getEnv() == 'frontend') {
                if ($ref = waRequest::cookie('referer')) {
                    $save['waContactDataStorage']['referer'] = $ref;
                    $save['waContactDataStorage']['referer_host'] = parse_url($ref, PHP_URL_HOST);
                }
                if ($utm = waRequest::cookie('utm')) {
                    $utm = json_decode($utm, true);
                    if ($utm && is_array($utm)) {
                        foreach ($utm as $k => $v) {
                            $save['waContactDataStorage']['utm_'.$k] = $v;
                        }
                    }
                }
            }

            $this->id = waContactFields::getStorage($storage)->set($this, $save[$storage]);
            unset($save[$storage]);
        }
        foreach ($save as $storage => $storage_data) {
            waContactFields::getStorage($storage)->set($this, $storage_data);
        }
        $this->data = array();
        $this->removeCache();
        $this->clearDisabledFields();
        wa()->event(array('contacts', 'save'), $this);

    } catch (Exception $e) {
        // remove created contact
        if ($is_add && $this->id) {
            $this->delete();
            $this->id = null;
        }
        $errors['name'][] = $e->getMessage();
    }
    return $errors ? $errors : 0;
}
Параметр $data содержит данные в формате ‘название поля’ => ‘значение поля’, в функции я не заметил защиты от Mass Assignment, но не исключал, что фильтрация аргумента происходит до вызова самой функции. Мне стало лениво просматривать все места в коде, где вызывается save() и я решил проверить теорию экспериментальным путем.

Установив на локалку движок, первым делом я решил посмотреть структуру таблицы `wa_contact`.
Структура таблицы `wa_contact`


Чтобы пользователь имел доступ к админ-панели (в движке она называется «бэкэндом») у покупателя должны быть заданы поля `login`, `password`, а поле `is_user` должно быть равно 1.

Тестируем

Добавляем товар в корзину, переходим на страницу оформления заказа, заполняем стандартные поля… и самое время добавить новые:

Отправляем запрос, пробуем зайти с нашими данными в админку (/wa/webasyst/). Авторизация проходит успешно, но… страница админки совершенно пустая: у нас нет никаких прав. Судорожно ищу поле в таблице, отвечающее за права доступа и понимаю, что такого поля нет, а все права как и подобает вынесены в отдельную таблицу.

Я уже почти смирился с фиаско, пока не заметил, что таблица `wa_contact_rights` содержит права для пользователей по id и для групп по id со знаком минус. В голову сразу же пришла идея присвоить нашему пользователю отрицательный id, тем самым получив права группы. Сказано – сделано, меняем customer[id] на -1 по аналогии с тем, как мы меняли остальные параметры ранее. Опять авторизуемся в админке и получаем все права, которые доступны группе «Администраторы»


Что имеем в итоге

Уязвимость, позволяющую на любом сайте на этом фреймворке, в любом интернет-магазине на этом движке получить полные права администратора, которые в свою очередь позволяют, например, получить конфединциальную информацию обо всех заказах и покупателях, менять статус заказов (скажем, помечать их оплаченными) и просто изменять настройки веб-сайта.

Условий для использования уязвимости нет, она работает и при отключенной регистрации на сайте (в действительности при оформлении заказа регистрация все же происходит).

По данным PublicWWW более 17 000 сайтов используют данный фреймворк.

Об уязвимости было сообщено более двух месяцев назад, сайты обновились и никто не пострадал.

Хронология событий:

8 августа, 22:30 – купил футболку
9 августа, 08:00 – сообщил об уязвимости Webasyst, прикрепил видео с Proof of Concept
9 августа, 13:00 – получил подтверждение от службы поддержки
14 августа – получил вознаграждение, уязвимость была закрыта
11 сентября – получил добро на публикацию данной статьи


автор manok
взято с хабры
Que$t вне форума   Ответить с цитированием
Ответ

Метки
как взломать , как взломать сайт , уязвимость , webasyst


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 
Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Друзья

Текущее время: 06:46. Часовой пояс GMT +4.


"(c) xakepok.ru" (c) xakepok.ru Внимание! Все материалы, находящиеся на сайте, выложены исключительно в образовательных целях. Владельцы сайта не несут ответственность за использование информации в незаконных целях.
Мы не несём никакой ответственности за предоставленные материалы. При копировании информации обратная ссылка обязательна.
Powered by: vBulletin Version 3.8.1
Copyright ©2000 - 2017, vBulletin Solutions, Inc ~ Перевод: zCarot
Remix fluid style from X1mer@ for Hacket.Ru Взлом и Безопасность (c) xakepok.ru
Время генерации страницы 0.05155 секунды с 14 запросами