Бегло пробежался.
Оф.сайт - s9y.org
Уязвимы 2.3.3 - 2.3.5, возможно более ренние версии.
Reflected XSS в админке #1
XSS в поле поиска Media файлов - Media Library.
Пример запроса:
Код:
Code:
http://localhost.com/serendipity/serendipity_admin.php?serendipity[token]=86ff44208384e04d098c1ee7b69761e0&serendipity[adminModule]=media&serendipity[action]=&serendipity[adminAction]=&serendipity[toggle_dir]=no&serendipity[only_path]=&serendipity[only_filename]=&serendipity[filter]=&serendipity[only_path]=&serendipity[filter][fileCategory]=&serendipity[filter][i.date][from]=&serendipity[filter][i.date][to]=&serendipity[filter][i.name]=&serendipity[keywords]=&serendipity[sortorder][order]=i.date&serendipity[sortorder][ordermode]=DESC&serendipity[sortorder][perpage]=8&go=Go!
1)
Уязвимые поля:
serendipity[filter][fileCategory]
serendipity[filter][i.name]
serendipity[filter][i.date][from]
serendipity[filter][i.date][to]
serendipity[sortorder][order]
serendipity[sortorder][ordermode]
serendipity[sortorder][perpage]
serendipity[hideSubdirFiles]
2)
Наша страница содержится в templates/2k11/admin/media_items.tpl
Добираясь, наши данные проходят три функции в include/functions_images.inc.php
showMediaLibrary -> serendipity_displayImageList -> serendipity_showMedia -> media_pane.tpl
Несмотря на то, что с формой отправляется токен, ни в одной из этих функций нет проверки
CSRF токена, форма работает как с токеном так и без него.
3)
Некоторые элементы массива фильтруются еще до попадания в media_items.tpl,
например serendipity[only_path] или serendipity[only_filename], другие
средставами шаблонизатора Smarty в самом файле, но указанные выше -нет
и попадают они вот сюда:
Код:
Code:
$(document).ready(function() {
// write: is plain "foo", read: is "serendipity[foo]"!
{foreach $media.sortParams AS $sortParam}
serendipity.SetCookie("sortorder_{$sortParam}","{$media.sortorder.{$sortParam}}");
{/foreach}
{foreach $media.filterParams AS $filterParam}
serendipity.SetCookie("{$filterParam}", "{$media.{$filterParam}}");
{/foreach}
serendipity.SetCookie("only_path", "{$media.only_path}");
serendipity.SetCookie("only_filename", "{$media.only_filename}");
serendipity.SetCookie("hideSubdirFiles", "{$media.hideSubdirFiles}");
{foreach $media.filter AS $k => $v}
{if !is_array($media.filter[{$k}])}
serendipity.SetCookie("[filter][{$k}]", "{$media.filter[{$k}]}");
{else}
{foreach $media.filter[{$k}] AS $key => $val}
serendipity.SetCookie("[filter][{$k}][{$key}]", "{$media.filter[{$k}][{$key}]}");
{/foreach}
{/if}
{/foreach}
$('#media_pane_filter').find('.reset_media_filters').addClass('reset_filter');
$('#media_pane_sort').find('.reset_media_filters').addClass('reset_sort');
$('.reset_filter').click(function() {
$('#media_filter').find('input[type=text], input[type=date]').each(function() {
$(this).attr('value', '');
});
});
$('.reset_sort').click(function() {
$("#serendipity_sortorder_order option:selected").removeAttr("selected");
$("#serendipity_sortorder_order option[value='i.date']").attr('selected', 'selected');
$("#serendipity_sortorder_perpage option:selected").removeAttr("selected");
$("#serendipity_sortorder_perpage option[value='8']").attr('selected', 'selected');
});
});
Пройдет простой alert()
Reflected XSS в админке #2
Тот же файл, те же функции, но теперь необходимо обратить внимание на самое начало
шаблона media_items.tpl:
Код:
Code:
{$CONST.MEDIA_LIBRARY}
{$media.token}
{if empty($media.form_hidden)}
{else}{$media.form_hidden}{/if}
Тут понятно, элемент form_hidden массива media пустой - выводим шаблон, не пустой -
выводим form_hidden.
Откуда он берется?
include/functions_images.inc.php:serendipity_showMedia()
PHP код:
PHP:
[COLOR="#000000"][COLOR="#0000BB"]$form_hidden[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"];
[/COLOR][COLOR="#FF8000"]// do not add, if not for the default media list form
[/COLOR][COLOR="#007700"]if (([/COLOR][COLOR="#0000BB"]$serendipity[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'GET'[/COLOR][COLOR="#007700"]][[/COLOR][COLOR="#DD0000"]'adminAction'[/COLOR][COLOR="#007700"]] ==[/COLOR][COLOR="#DD0000"]'default'[/COLOR][COLOR="#007700"]|| empty([/COLOR][COLOR="#0000BB"]$serendipity[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'GET'[/COLOR][COLOR="#007700"]][[/COLOR][COLOR="#DD0000"]'adminAction'[/COLOR][COLOR="#007700"]])) && ![/COLOR][COLOR="#0000BB"]$serendipity[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'GET'[/COLOR][COLOR="#007700"]][[/COLOR][COLOR="#DD0000"]'fid'[/COLOR][COLOR="#007700"]]) {
foreach([/COLOR][COLOR="#0000BB"]$serendipity[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'GET'[/COLOR][COLOR="#007700"]] AS[/COLOR][COLOR="#0000BB"]$g_key[/COLOR][COLOR="#007700"]=>[/COLOR][COLOR="#0000BB"]$g_val[/COLOR][COLOR="#007700"]) {
[/COLOR][COLOR="#FF8000"]// do not add token, since this is assigned separately to properties and list forms
[/COLOR][COLOR="#007700"]if (![/COLOR][COLOR="#0000BB"]is_array[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$g_val[/COLOR][COLOR="#007700"]) &&[/COLOR][COLOR="#0000BB"]$g_key[/COLOR][COLOR="#007700"]!=[/COLOR][COLOR="#DD0000"]'page'[/COLOR][COLOR="#007700"]&&[/COLOR][COLOR="#0000BB"]$g_key[/COLOR][COLOR="#007700"]!=[/COLOR][COLOR="#DD0000"]'token'[/COLOR][COLOR="#007700"]) {
[/COLOR][COLOR="#0000BB"]$form_hidden[/COLOR][COLOR="#007700"].=[/COLOR][COLOR="#DD0000"]' '[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#DD0000"]"\n"[/COLOR][COLOR="#007700"];
}
}
}
[/COLOR][COLOR="#FF8000"]// далее $form_hidden попадает в $media
[/COLOR][/COLOR]
Ключи массива не фильтруются, в отличии от значений и попадают в файл шаблона.
Можно передать serendipity[хоть_что], например serendipity["/>alert()]=XSS.
XSS в имени ключа также справедливо и для serendipity[filter][*] несмотря на то что
попадает в код, указанный для XSS #1.
Insecure File Upload в админке
Загрузка файлов - это наверное первое на что стоит обратить внимание.
Нам разрешается загрузка широкого диапозона файлов, за исключением
файлов "with active content". А проверяется он по расширению - black list.
include/functions_images.inc.php:serendipity_isActiveFile( )
PHP код:
PHP:
[COLOR="#000000"][COLOR="#0000BB"][/COLOR][COLOR="#007700"]function[/COLOR][COLOR="#0000BB"]serendipity_isActiveFile[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$file[/COLOR][COLOR="#007700"]) {
if ([/COLOR][COLOR="#0000BB"]preg_match[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]'@^\.@'[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$file[/COLOR][COLOR="#007700"])) {
return[/COLOR][COLOR="#0000BB"]true[/COLOR][COLOR="#007700"];
}
[/COLOR][COLOR="#0000BB"]$core[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]preg_match[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]'@\.(php.*|[psj]html?|pht|aspx?|cgi|jsp|py|pl)$@i'[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$file[/COLOR][COLOR="#007700"]);
if ([/COLOR][COLOR="#0000BB"]$core[/COLOR][COLOR="#007700"]) {
return[/COLOR][COLOR="#0000BB"]true[/COLOR][COLOR="#007700"];
}
[/COLOR][COLOR="#0000BB"]$eventData[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]false[/COLOR][COLOR="#007700"];
[/COLOR][COLOR="#0000BB"]serendipity_plugin_api[/COLOR][COLOR="#007700"]::[/COLOR][COLOR="#0000BB"]hook_event[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]'backend_media_check'[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$eventData[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$file[/COLOR][COLOR="#007700"]);
return[/COLOR][COLOR="#0000BB"]$eventData[/COLOR][COLOR="#007700"];
}
[/COLOR][/COLOR]
И мы спокойненько грузим phpinfo.phar с содержимым: