Наткнулся на проект с SSRFиной и перегруженной функцией fsockopen с предварительной фильтрацией нежелательных портов, таких как 11211. Да, затея достаточно плохая, но что поделать...
Нашел пару способов обогнуть, но чувствую что есть еще хитрости:-) Для простоты и удобства, примеры фильтрации буду писать на PHP.
И так, что я смог придумать:
1. Сначала проверялся только второй аргумент функции, т.е. только порт:
PHP код:
function connect($host, $port) {
if ($port == 11211)
return;
return fsockopen($host, $port);
}
В общем, не правильно совсем. Т.к. php_stream_xport_create принимает только один аргумент (протокол + хост + порт) и потом парсит, то fsockopen делает конкатенацию:
PHP код:
if (port > 0) {
hostname_len = spprintf(&hostname, 0, "%s:%ld", host, port);
} else {
hostname_len = host_len;
hostname = host;
}
Таким образом, достаточно было передать нужный порт и фейковый:
PHP код:
function connect($host, $port) {
if ($port == 11211)
return;
return fsockopen($host, $port);
}
var_dump(connect("target:11211blahblah", 80));
2. Это быстренько учли и добавили валидацию, скопипастив логику из PHP. Присмотрелся к php_tcp_sockop_connect, и заметил что порт парсится в int и затем приводится к unsigned short. Получился очень простой вариант обхода:
PHP код:
function connect($host, $port) {
if ($port == 11211)
return;
// Условная проверка, что порт не будет передан в хосте. Убьет IPv6, ну да и фиг с ним
if (!preg_match("#^(?:[a-z0-9+.-]+?://)?[a-z0-9._-]+$#iD", $host))
return;
return fsockopen($host, $port);
}
var_dump(connect("target", 11211 + 65536));
Увы, это все на что меня хватило:-(
Окончательная фильтрация порта работает так:
1. Сначала делается конкатенация, потом парсится. Сделано копипастой из php, не подкопаться.
2. Проверяются границы port > 0 && port < 65535
3. Порт сверяется со списком запрещенных
Может у вас есть еще какие-нибудь варианты обхода подобных фильтраций порта или знаете какие особенности fsockopen? Может через какой-то хитрый протокол (я не нашел увы ничего походящего)?