Рубрики
Блог

Об опасности использования eval в bash-скриптах.

Преамбула
В недавнем примере я допустил, что называется, «детский мат», грубую и далеко неочевидную новичку ошибку, о которой, впрочем, все знают. Но прописные истины неплохо повторять, так что не стоит кидаться на эту заметку с криками «боян!!!111пыщ». Для кого-то может и боян, а кому-то это может быть неизвестно. Или вот я, зная в теории об ошибке, все равно ее допустил, и даже не сразу понял, что я так грубо наебался, и мой скрипт стал небезопасным от слова «совсем».
Исходная задача
Имелся следующий набор данных в виде форматированного текста (таблицы в текстовом файле): verb666,Misha Verbitsky,+415314499922,42,11:00-16:00 ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00 dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00 vfurry1,Veniamin Furman,+415314499900,99,12:20-19:25 tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00 Исходная задача состояла в том, чтобы раскидать вывод awk в переменные bash, так, чтоб значение из соответствующей колонки таблицы попало в указанную переменную bash, и изначальный код был таким:
# ... цикл по строкам
for TMPSTRING in $(cat "testfile.txt")
do
    eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" dq $1 dq;
    print "FULLNAME=" dq $2 dq; print "PHONE=" dq $3 dq; print"ROOM=" dq $4 dq;
    print "WORKTIME=" dq $5 dq}' dq='"')
# ... делаем что-то с данными в переменных
done
# ...
Т.е. тут мы сначала получаем строчку текстового файла, передаем ее awk, awk, в свою очередь, возвращает строки: ... LOGIN="vfurry1" FULLNAME="Veniamin Furman" PHONE="+415314499922" ROOM="99" WORKTIME="12:20-19:25" ... Похоже на команду присваивания значений переменным. Да так оно и есть! Далее, полученная строка передается команде eval.
Опасность eval
В чем опасность eval? Да в том, что эта команда преобразует любую переданную ей строку в команду или команды bash, и немедленно их выполняет. Любую, абсолютно любую строку. И тут возникает ошибка безопасности. Мы получаем данные из внешнего источника. Это может быть файл с внешнего носителя, или скачанный из Интернета. В нем в худшем случае, который всегда надо держать в голове, работая с внешними источниками данных, может содержаться все, что угодно. И злоумышленнику не надо получать доступ к скрипту, достаточно изменить входные данные: verb666,Misha Verbitsky,+415314499922,42,11:00-16:00 ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00 dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00 vfurry1,Veniamin Furman `rm -rf ./testdir`,+415314499900,99,12:20-19:25 tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00 Тут, после Veniamin Furman вписана команда удаления директории ./testdir в текущем каталоге, а могло быть вписано и $HOME или даже /. Строки, взятые в двойные кавычки, интерпретируются bash’ем не только, как строки с чисто тестовыми данными. В двойных кавычках можно и нужно сделать подстановку, если это возможно. А символы `` (обратные кавычки) значат, что нужно выполнить команду внктри них, а потом подставить результат в то место, где указано выражение в обратных кавычках. Таким образом, команда FULLNAME="Veniamin Furman `rm -rf ./testdir`" выполнится так: 1. В переменную записывается строка Veniamin Furman 2. Выполняется команда rm -rf ./testdir 3. Вывод команды дописывается в переменную Соответственно, каталог ./testdir в процессе будет удален.
Общие рекомендации
Всегда держите в голове, что данные внутри eval$() будут исполнены bash’ем, как код (команды оболочки).
Исправление
1. Заменить двойные кавычки (") на одинарные ('). Строки в одинарных кавычках будут интерпретированы просто как текстовые последовательности, и без всякого выполнения и изменения записаны в переменные, так как есть: eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'") Вывод: vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25 2. Но и это еще не конец, злоумышленник может обмануть и одинарные кавычки, обернув свою команду в …одинарные кавычки: vfurry1,Veniamin Furman '`rm -rf ./testdir`',+415314499900,99,12:20-19:25 Тогда выполнение будет происходить так. Когда очередь дойдет до FULLNAME awk добросовестно выведет: FULLNAME='Veniamin Furman '`rm -rf ./testdir`' eval же это также добросовестно интерпретирует. В $FULLNAME будет записано Veniamin Furman, далее закроется одинарная кавычка, т.е. больше ничего в переменную писаться не будет, а далее выполнится rm -rf ./testdir. Решение таково — удалить или заменить на что-либо безобидное, одинарные кавычки из входной строки перед передачей ее awk. Можно удалить sed‘ом: sed -e 's~'\''~~g' Код целиком: eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'") Вывод зловредной сткроки будет: vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25
Тестовый пример
Файлы: maketestdir — скрипт для создания тестовых каталогов. testfile.txt — Тестовый файл. Ошибку безопасности можно обойти, заменив двойные кавычки одинарными в коде. testfile2.txt — Замена двойных кавычек одинарными не поможет. Необходимо дополнительно удалять одинарные кавычки во входной строке. wrongcode — файл без исправлений кода (в коде используются двойные кавычки) wrongcode2 — только первое исправление (в коде используются одинарные кавычки) goodcode — окончательно исправленный код. Вызов скриптов: <скрипт> <тестовый файл> Например: wrongcode testfile2.txt Пример на GitHub]]>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *