Как переписать эту функцию, чтобы избежать инъекции аргументов

У меня была такая же проблема. У меня есть 2 джейла, ssh и recidive. Тюрьма ssh прекрасно отправляла мне электронные письма, но ничего от recidive. Я запускаю:

Fail2Ban v0.8.13

Моя соответствующая глобальная конфигурация идентична той, что вы опубликовали:

 banaction = iptables-multiport
 mta = sendmail

 action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
                   %(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s", sendername="%(sendername)s"]

 action = %(action_mwl)s

Моя начальная конфигурация джейла recidive (который не отправлял электронные письма):

[recidive]

enabled  = true
filter   = recidive
logpath  = /var/log/fail2ban.log
#action   = iptables-allports[name=recidive]
#           sendmail-whois-lines[name=recidive, logpath=/var/log/fail2ban.log]

bantime  = 604800  ; 1 week
findtime = 604800
#findtime = 86400   ; 1 day
maxretry = 3

Обратите внимание, что я закомментировал действие recidive по умолчанию. По крайней мере, для меня это было то, что пришло по умолчанию. Я сделал это, потому что хотел, чтобы recidive email работал как ssh, который также использовал действие по умолчанию. С этой конфигурацией... я ничего не получил. Сверчки. Но recidive jail работал. Я не знаю точно, в чем была проблема, но я знал, что должен ее решить. Поэтому я сначала попробовал откомментировать действие. Затем оно все еще работало, но писем не было. Или я так думал. Оказалось, что письма отправлялись на локальную учетную запись root. Я обнаружил их в почтовом ящике /var/mail/[user], на который была перенаправлена почта root. И тогда я понял, что это потому, что destemail не используется в этом действии по умолчанию. В итоге я просто скопировал глобальное действие по умолчанию action_mwl и подправил его, чтобы убрать порт.

Итак, моя окончательная конфигурация jail, которая сработала:

enabled  = true
filter   = recidive
logpath  = /var/log/fail2ban.log
#action   = iptables-allports[name=recidive]
#           sendmail-whois-lines[name=recidive, logpath=/var/log/fail2ban.log]
action = %(banaction)s[name=%(__name__)s, protocol="%(protocol)s", chain="%(chain)s"]
               %(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s", sendername="%(sendername)s"]

Возможно, все это сделано специально или это старая ошибка - я не знаю. Но вот как я заставил его работать. Это может быть слишком поздно, чтобы помочь вам, но, возможно, это поможет следующему в той же лодке...

5
03.04.2017, 02:37
2 ответа

Как избежать возможной инъекции аргумента?

Узнайте, какие входные данные вы хотите передать, и убедитесь, что аргументы содержат только их.

По крайней мере, убедитесь, что они не содержат символов, характерных для удаленной оболочки.


$1 не представляет особой проблемы, поскольку вы сравниваете его с известными значениями. Хотя вам нужно заключить его в двойные кавычки, иначе он может расшириться до нескольких слов при сравнении, и это может позволить передавать через него забавные значения (что-то вроде 1 -o x пройдет сравнение).

Используйте if [ "$1" = "s3" ] ; тогда ... вместо этого.

Что касается $2 и $3, передача их через ssh в основном аналогична передаче их в eval или ш-с. Любые замены внутри них будут расширены на удалении.

Допустим, мы запустим ssh somehost "echo $var".Теперь, если var содержит $(ls -ld /tmp), удаленная командная строка будет echo $(ls -ld /tmp), а ls выполняется на удаленном компьютере. Двойные кавычки переменной не помогут с расширением команды, но одинарные кавычки помогут. С командой, написанной как ssh somehost "echo '$var'", расширение команды не происходит.

Одинарные кавычки внутри переменной по-прежнему представляют собой проблему, так как они прервут одинарные кавычки, так что, по крайней мере, нам нужно проверить это:

case "$var" in *"'"*) echo var has a single-quote; exit 1 ;; esac

Хотя мы могли бы также проверить наличие специальных символов мы не хотим проходить. Знаки доллара начинают большинство расширений, обратные кавычки начинают расширение команд, а кавычки, которым я тоже не доверяю:

case "$var" in *[\'\"\$\`]*) echo var has something funny; exit 1 ;; esac

Но я не уверен, что это все, поэтому лучше просто внести в белый список символы, через которые мы хотим пройти. Если достаточно букв, цифр, тире и подчеркивания:

case "$var" in *[!-_a-zA-Z0-9]*) echo var has something funny; exit 1 ;; esac

Итак, это может быть началом:

function runMyScript {
    case "$2" in *[!-_a-zA-Z0-9]*) exit 1 ;; esac
    case "$3" in *[!-_a-zA-Z0-9]*) exit 1 ;; esac

    if [ "$1" = "s3" ] ; then
        ssh somehost "script.sh '$1' '$2' '$3'"
    elif [ "$1" = "ec2" ] ; then 
        ssh somehost "script.sh '$1' '$2'"
    else
        echo "** Run with s3 or ec2 options"
    fi
}

Заметив, что вы использовали функцию runMyScript вместо runMyScript(), вы не используете обычную оболочку POSIX. Если это Bash, вы можете переписать совпадения с шаблоном с помощью теста [[...]], который поддерживает совпадения с регулярными выражениями.

Новые версии Bash также имеют расширение ${var@Q}, которое должно создавать содержимое var в кавычках в формате, который можно повторно использовать в качестве входных данных. Это также может быть полезно, но у меня нет под рукой достаточного количества нового bash, чтобы проверить его.


Кроме того, не верьте мне слепо, что я помню все возможные причуды языка оболочки.

3
27.01.2020, 20:38

В качестве дополнения к прекрасному ответу @ikkachu:

По сравнению с передачей произвольных строк в eval или sh -c, здесь проблема усугубляется:

  • тот факт, что вы не знаете, какая оболочка будет использоваться на удаленном хосте для анализа этой командной строки.
  • Какой будет локаль и, в частности, какая кодировка будет использоваться на удаленном хосте. То же самое, что и в локальной системе, или нет.

Ваш вопрос в основном является подмножеством Как выполнить произвольную простую команду по ssh, не зная оболочки входа в систему удаленного пользователя?

Таким образом, ответы здесь применимы.

В частности, если sshd на удаленном хосте позволяет передавать переменные среды, начинающиеся с LC_* (как это делают многие развертывания openssh), вы можете сделать следующее:

LC_ARG1=$1 LC_ARG2=$2 LC_ARG3=$3 ssh -o SendEnv='LC_*' host exec sh -c \
   \''exec my-script "$LC_ARG1" "$LC_ARG2" "$LC_ARG3"'\'

Очистка ввода это хорошая идея, но обратите внимание, что вы не хотите использовать диапазоны, такие как [AZ], если вы не находитесь в локали C. Например, в большинстве локалей системы GNU Ǒ находится в этом диапазоне, а, например, в локали zh_HK.big5hkscs этот символ кодируется как 0x88 0x60. и вы можете распознать 0x60 как кодировку ASCII (и BIG5HKSCS) символа обратной кавычки!

Лучше всего указывать разрешенные символы по отдельности:

is_safe() case $1 in
  *[!-_.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]*) false;;
esac

Также остерегайтесь пустой строки, которую нужно будет заключать в кавычки ('' поддерживается всеми распространенными оболочками). И остерегайтесь аргументов, начинающихся с -, которые некоторые инструменты могут принять как опции.

3
27.01.2020, 20:38

Теги

Похожие вопросы