Об опасности использования 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
? Да в том, что эта команда преобразует любую переданную ей строку в команду или команды 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
Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2020/01/25/ob-o