1 điểm bởi GN⁺ 2024-04-03 | 1 bình luận | Chia sẻ qua WhatsApp
  • Cuộc tấn công xz được chia thành chèn mã shell ở giai đoạn configure và chèn tệp đối tượng ở giai đoạn make; phân tích này lần theo cách các script trong tarball phân phối xz 5.6.0 và 5.6.1 cài lén tệp đối tượng backdoor vào bản build
  • Mã độc và tệp đối tượng được nén, mã hóa và che giấu như các tệp đầu vào kiểm thử nhị phân trong tests/files; phần mô tả README hiện có rằng đây là “các tệp kiểm thử làm thủ công” giúp việc ngụy trang trở nên dễ dàng
  • Mã được thêm vào m4/build-to-host.m4 tìm bad-3-corrupt_lzma2.xz, khôi phục bằng trxz -d rồi chạy bằng /bin/sh; trong 5.6.1 còn có cơ chế mở rộng để tìm script bổ sung trong tệp kiểm thử mới
  • Script configure sửa src/liblzma/Makefilelibtool để chạy lại script ẩn cả trong quá trình make, đồng thời điều chỉnh -z,now và các flag liên quan đến PIC để ifunc resolver được chạy tại thời điểm liên kết động ban đầu
  • Ở giai đoạn make, sau khi trích xuất và giải mã đối tượng độc hại từ good-large_compressed.lzma, nó tráo các sản phẩm build của crc64_fast.ccrc32_fast.c, qua đó chặn lời gọi RSA_public_decrypt thông qua _get_cpuid

Cấu trúc tổng thể của cuộc tấn công

  • Andres Freund đã thông báo sự tồn tại của cuộc tấn công xz trên mailing list công khai oss-security@openwall vào ngày 2024-03-29
    • Hôm trước đó cũng đã thông báo cho Debian security và danh sách riêng tư distros@openwall
    • Nguyên nhân là khi cài Debian sid, ông thấy các hiện tượng bất thường liên quan đến liblzma, cụ thể là CPU tăng cao khi đăng nhập SSH và lỗi Valgrind
  • Cuộc tấn công gồm hai giai đoạn lớn
    • Mã shell được chèn trong lúc configure
    • Mã shell này lại chèn mã shell vào make, và trong lúc make thêm tệp đối tượng độc hại vào bản build
  • Nếu đặt trực tiếp tệp đối tượng độc hại vào kho lưu trữ như evil.o thì rất dễ gây nghi ngờ, nên kẻ tấn công đã nén và mã hóa mã shell cùng tệp đối tượng, rồi giấu chúng bên trong các tệp đầu vào kiểm thử nhị phân
  • Thư mục tests/files đã tồn tại từ trước khi Jia Tan xuất hiện, và README giải thích rằng đây là các tệp dùng để kiểm thử việc triển khai decoder .xz, .lzma, .lz; một số tệp được tạo trực tiếp bằng hex editor vì “không có mã nguồn nào tốt hơn chính tệp đó”
  • Kẻ tấn công lợi dụng bối cảnh này để khiến việc thêm một vài tệp kiểm thử mới trông có vẻ bình thường

Điểm thực thi mà backdoor nhắm tới

  • Hiệu ứng cuối cùng của script là khiến hàm _get_cpuid trong tệp đối tượng độc hại được gọi như một phần của GNU indirect function(ifunc) resolver
  • ifunc resolver thường có thể được gọi trễ trong khi chương trình chạy, nhưng vì lý do bảo mật, cách phổ biến là gọi tất cả ở giai đoạn liên kết động ban đầu và ánh xạ GOT·PLT thành chỉ đọc
  • ifunc resolver độc hại có thể chạy ở thời điểm ban đầu khi GOT·PLT vẫn còn ghi được
  • Backdoor tìm RSA_public_decrypt trong bảng và thay bằng phiên bản của kẻ tấn công
  • Hàm bị thay thế sẽ chạy mã của kẻ tấn công khi một chứng chỉ SSH hợp lệ được đưa ra

Giai đoạn configure: ngụy trang build-to-host.m4

  • xz-utils dùng GNU autoconf, và thông thường configure cùng các tệp m4 hỗ trợ được đưa vào bản phân phối tarball chứ không nằm trong kho mã nguồn
  • Cuộc tấn công bắt đầu bằng việc thêm thư viện hỗ trợ ngoài dự kiến m4/build-to-host.m4 vào tarball xz 5.6.0 và xz 5.6.1
  • build-to-host.m4 độc hại chứa những thay đổi trông hợp lý khi so với tệp chuẩn
    • Tăng serial từ 3 lên 30
    • Đổi năm bản quyền từ 2023 thành 2023-2024
    • Thêm một số thay đổi shell·m4 khó đọc
  • Phần cốt lõi thật sự là đoạn mã tìm một mẫu cụ thể trong bản phân phối bằng grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/
    • Tệp khớp mẫu này là ./tests/files/bad-3-corrupt_lzma2.xz
    • Kết quả là gl_am_configmake trỏ tới tệp kiểm thử này
  • gl_path_map được đặt thành tr "\t \-_" " \t_\-", hoán đổi tab·khoảng trắng và gạch dưới·gạch nối cho nhau

Khôi phục script ẩn đầu tiên

  • gl_[$1]_prefix trích xuất phần sau dấu chấm cuối cùng trong tên tệp, nên trở thành xz
    • Tên này trông như prefix, nhưng thật ra là phần mở rộng tệp
    • Nó trở thành tên lệnh nén rất có khả năng đã được cài sẵn trên máy build
  • Chuỗi lệnh được tạo sau đó bề ngoài dùng sed "r\n", nhưng thực chất gần giống cat đã được làm rối
    • r\n của sed là lệnh cố đọc tệp không tồn tại \n, nên không làm gì cả
    • Vì không có -n, nó in nguyên đầu vào ra
  • Lệnh thực tế được chạy có dạng như sau
cat ./tests/files/bad-3-corrupt_lzma2.xz | tr "\t \-_" " \t_\-" | xz -d
  • Lệnh này được đánh giá trong quá trình chạy config.status thông qua AC_CONFIG_COMMANDS
  • Trong đầu ra được khôi phục có ####Hello########World####; theo README, stream thứ nhất và thứ ba của bad-3-corrupt_lzma2.xz là các stream xz hợp lệ, chỉ stream ở giữa có dữ liệu LZMA2 bị hỏng

Khác biệt giữa 5.6.0 và 5.6.1

  • Trong xz 5.6.0, script đầu tiên được khôi phục cắt một khoảng byte cụ thể từ good-large_compressed.lzma, biến đổi rồi giải nén thành stream LZMA thô và chuyển cho /bin/sh
  • Script đầu tiên của xz 5.6.1 chứa năm lần dòng thoát nếu không phải Linux
    • [ ! $(uname) = "Linux" ] && exit 0
    • Trong script dài được phân tích sau đó cũng có một dòng kiểm tra Linux riêng cho 5.6.1
    • Dòng đó có lỗi không hoạt động vì thiếu khoảng trắng quanh =
  • 5.6.1 khác 5.6.0 ở số byte cuối cùng của head, offset của tail, và đối số tr
    • Trong ví dụ, 5.6.1 trích xuất 16×2048+939 = 33.707 byte
    • Sau đó dùng 31.233 byte cuối cùng bằng tail -c +31233
  • Pipeline trích xuất gồm các bước sau
    • Giải nén good-large_compressed.lzma bằng xz -dc
    • Trích xuất một khoảng byte cụ thể bằng pipeline head
    • Chọn phần sau bằng tail
    • Áp dụng mã thay thế đơn giản bằng tr
    • Giải nén stream LZMA thô bằng xz -F raw --lzma1 -dc
    • Chạy kết quả bằng /bin/sh

Cơ chế mở rộng của 5.6.1

  • Trong script dài có nhiều điểm eval các biến chưa được đặt
    • Ví dụ: eval $zrKcVq, eval $zrKcSS
    • Có thể đây là điểm mở rộng để xuất debug hoặc để một cơ chế khác thiết lập giá trị trong tương lai
  • Nếu có config.status, script đọc LD, CC, GCC, srcdir, build, enable_shared, enable_static, gl_path_map, v.v.
  • Mã mở rộng mới trong 5.6.1 tìm hai chuỗi magic trong tests/files
    • Chuỗi bắt đầu: ~!:_ W
    • Chuỗi kết thúc: |_!{ -
  • Nếu hai chuỗi nằm trong cùng một tệp, nó trích xuất các byte ở giữa, đi qua tr và giải nén LZMA2 thô, rồi eval đầu ra
  • Cơ chế này có vẻ là cấu trúc để sau này đưa script mới vào trong một tệp kiểm thử khác mà không cần tạo lý do tái tạo tệp kiểm thử nhị phân hiện có

Điều kiện thực thi ở giai đoạn configure

  • Script im lặng thoát nếu không thỏa các điều kiện cần cho backdoor
  • Phải bật hỗ trợ GNU ifunc
    • config.status phải có ["HAVE_FUNC_ATTRIBUTE_IFUNC"]=" 1"
    • config.h phải có define HAVE_FUNC_ATTRIBUTE_IFUNC 1
  • Phải bật build thư viện chia sẻ
    • enable_shared phải là yes
  • Mục tiêu build phải là x86-64 Linux
    • build phải thỏa điều kiện ^x86_64linux-gnu$
  • Các nguồn liên quan đến CRC ifunc phải còn nguyên
    • crc64_fast.c
    • crc32_fast.c
    • crc_x86_clmul.h
  • Nó cũng kiểm tra điều kiện compiler và linker
    • GCC=yes
    • CC=gcc
    • Đầu ra LD -v chứa GNU ld
  • Phải có các tệp kiểm thử chứa backdoor good-large_compressed.lzmabad-3-corrupt_lzma2.xz

Sửa Makefile và libtool

  • Nếu có debian/rules hoặc RPM_ARCH=x86_64, mã sửa src/liblzma/Makefile sẽ chạy trong giai đoạn configure
  • Trước tiên, nó kiểm tra trong Makefile có nhiều dòng sẽ dùng làm điểm chèn sau này hay không
    • ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
    • am__uninstall_files_from_dir = {
    • am__install_max =
    • am__vpath_adj_setup =
    • am__include = include
    • all: all-recursive
    • LTLIBRARIES = $(lib_LTLIBRARIES)
    • AM_V_CCLD = $(am__v_CCLD_$(V))
  • Trong libtool, nó kiểm tra có pic_flag=" -fPIC -DPIC" hay không
  • Sau đó dùng sed -i để rải các dòng có tính chất sau vào nhiều nơi trong Makefile
    • am__test = bad-3-corrupt_lzma2.xz
    • am__strip_prefix = tr ...
    • am__dist_setup = ... | xz -d ... | $(SHELL)
    • am__test_dir = $(top_srcdir)/tests/files/$(am__test)
  • am__test_dir nhìn theo tên như một thư mục, nhưng thực tế trỏ tới tệp
  • Các dòng được thêm không gom ở một chỗ mà được chèn rải rác khắp Makefile, khiến Makefile được tạo trông phức tạp một cách hợp lý

Cờ linker và thực thi đệ quy

  • Script ở giai đoạn configure thêm -Wl,--sort-section=name,-X vào liblzma_la_LDFLAGS
  • Nếu LDFLAGS hiện có không chứa -z,now hoặc -z -Wl,now, nó cũng thêm -z,now
  • -Wl,now buộc dynamic loader phân giải mọi symbol ngay khi chương trình khởi động, tạo hành vi LD_BIND_NOW
    • Trong quá trình này, ifunc resolver chạy ngay lúc khởi động
    • Backdoor có thể chạy khi GOT·PLT vẫn còn ghi được
  • Cờ PIC của libtool được đổi như sau
    • Cũ: -fPIC -DPIC
    • Mới: -fPIC -DPIC -fno-lto -ffunction-sections -fdata-sections
  • Script cũng thêm AM_V_CCLD và một đoạn shell dài vào Makefile
    • Cuối cùng nó chạy sed rpath $(am__test_dir) | $(am__dist_setup)
    • sed rpath cũng đóng vai trò cat đã được làm rối
  • Kết quả là trong quá trình make, pipeline sau được chạy lại
cat ./tests/files/bad-3-corrupt_lzma2.xz |
tr "\t \-_" " \t_\-" |
xz -d |
/bin/sh

Giai đoạn make: trích xuất tệp đối tượng

  • Ở giai đoạn make, script được chạy lại trong thư mục src/liblzma
  • Vì không có config.status trong thư mục hiện tại, phần chỉnh sửa Makefile của giai đoạn configure được bỏ qua
  • Thay vào đó, nếu có .libs/liblzma_la-crc64_fast.o.libs/liblzma_la-crc32_fast.o, script nhận định rằng đang ở trong quá trình make và chạy mã tiếp theo
  • 5.6.1 cũng mới có hook mở rộng dành cho giai đoạn make
    • Chuỗi bắt đầu: jV!.^%
    • Chuỗi kết thúc: %.R.1Z
    • Không có tệp kiểm thử nào chứa các chuỗi này; có vẻ đây là cấu trúc mở rộng cho tương lai
  • Nó lại kiểm tra nguồn liên quan đến CRC ifunc có đúng như kỳ vọng hay không
  • Nó cũng kiểm tra xem các cờ compile và linker được thêm ở giai đoạn configure đã được áp dụng chưa
    • ../../libtool phải có các cờ PIC bổ sung
    • liblzma_la_LINK phải có -z,now hoặc -z -Wl,now
    • Nếu có lazy thì thoát

Giải mã và chọn đối tượng độc hại

  • Script chọn một trong hai giá trị offset tùy theo nội dung gettext.m4
    • Trong gettext.m4 của tarball xz được phân phối không có chuỗi kiểm tra, nên trong bản build ví dụ nó lấy toàn bộ đầu vào
  • Nó lại giải nén good-large_compressed.lzma, rồi trích xuất 33.707 byte bằng pipeline head đã thiết lập trước đó là i
  • Sau đó chia đầu ra theo từng byte và chạy hàm giải mã giống RC4 bằng awk
    • Phiên bản 5.6.1 lặp vòng thứ hai tới 8192
    • Phiên bản 5.6.0 lặp vòng ở cùng vị trí tới 4096
  • Đầu ra đã giải mã được giải nén lần nữa bằng xz -dc --single-stream
    • --single-stream khiến nó dừng ở marker EOF xz đầu tiên, không đọc phần trước đó đã trích bằng tail
  • Kết quả cuối cùng được ghi vào liblzma_la-crc64-fast.o

Tráo đối tượng CRC

  • Script sao lưu sản phẩm build thật .libs/liblzma_la-crc64_fast.o thành tên gần giống .libs/liblzma_la-crc64-fast.o
    • Nó tận dụng khác biệt giữa gạch dưới (crc64_fast) và gạch nối (crc64-fast)
  • Tiếp đó, nó biên dịch nội dung crc64_fast.c đã được sửa bằng sed, đồng thời đưa đối tượng độc hại đã trích xuất liblzma_la-crc64-fast.o làm đầu vào để ghi đè .libs/liblzma_la-crc64_fast.o
  • Mã C được chèn tạo mới _is_arch_extension_supported, bên trong gọi _get_cpuid
  • crc64_resolve hiện có được đổi để gọi _is_arch_extension_supported() thay vì is_arch_extension_supported()
  • _get_cpuid là hàm do tệp đối tượng backdoor cung cấp, và thực hiện thêm hành vi trước khi trả về thông tin cpuid
    • Đặc biệt, nó sửa GOT·PLT để chặn lời gọi RSA_public_decrypt

Xử lý phía crc32 và dọn dẹp

  • Nếu việc sửa crc64 thành công, script cũng áp dụng sửa tương tự cho crc32_fast.c
  • Phía crc32 không thêm tệp đối tượng backdoor
    • Nhằm tránh đưa đối tượng backdoor vào bản build hai lần
  • Chưa rõ vì sao lại chặn cả crc32 và crc64 ifunc
    • Phân tích cho rằng chỉ một trong hai cũng có vẻ đủ
    • Có thể nhằm làm cho hai đoạn mã dispatch trông giống nhau trong debugger
  • Nếu cả hai lần biên dịch thành công, nó liên kết lại tệp .la bằng liblzma_la_LINK
  • Nếu liên kết thành công nhưng không có .libs/liblzma.so, nó coi là thất bại và khôi phục bản sao lưu
  • Dù thành công hay không, nó xóa .libs/liblzma.a, .libs/liblzma.la, .libs/liblzma.lai, .libs/liblzma.so*
    • Giả định là bước liên kết gốc của Makefile sau đó sẽ tạo lại chúng
  • Ở đường lỗi, nó đổi tên các đối tượng crc32·crc64 đã sao lưu trở lại tên ban đầu
  • Cuối cùng, nó xóa các tệp tạm và tệp sao lưu, hoạt động theo cách chèn đối tượng độc hại vào sản phẩm make mà không để lại dấu vết

1 bình luận

 
GN⁺ 2024-04-03
Ý kiến trên Hacker News
  • Với tư cách là người từ lâu đã không thích autotools, tôi muốn nói rằng chuyện lần này đã chứng minh lập trường của mình là đúng, nhưng bất kỳ hệ thống build nào đủ phức tạp để hỗ trợ đa nền tảng thì rốt cuộc cũng sẽ trở nên khó hiểu đến mức ai đó có thể nhét thứ như vậy vào
    Dù vậy, việc rất nhiều phần mềm trên thực tế được build bằng những shell script khổng lồ kiểu cargo cult mà gần như không ai hiểu toàn bộ thật sự là một vấn đề

    • Tôi cho rằng nguyên nhân gốc rễ là việc bash và những ngôn ngữ có cú pháp phức tạp, dày đặc được dùng phổ biến
      Nếu build script được viết bằng Python thì việc làm rối backdoor sẽ khó hơn nhiều. Ai cũng sẽ thấy đó là đoạn mã kỳ lạ. Còn mã bash thì có xu hướng được xem là bình thường dù không đọc nổi
    • Tôi hiểu những lời chỉ trích autotools, và cũng chưa bao giờ ghen tị với những người phải bảo trì script cấu hình, nhưng từ góc độ người dùng, tôi nhớ thời mà gần như mọi phần mềm đều cài xong chỉ với ./configure && make && make install
    • autotools nên biến mất, nhưng phần lớn các trường hợp sử dụng gốc tạo ra độ phức tạp trong hệ thống build rốt cuộc đều quy về quản lý và phát hiện dependency
      Việc hoàn toàn thiếu các best practice cho luồng đó mới là gốc rễ của sự phức tạp, nên trong điều kiện như vậy, một file cấu hình build kiểu mybuild.toml không thể giải quyết được
    • Đồng ý, cuối cùng vì cố hỗ trợ phạm vi nền tảng rộng như vậy nên cần một hệ thống phức tạp, và điều đó khiến ta phải hỏi mình đang chấp nhận rủi ro lớn đến mức nào
      Và tôi cũng thắc mắc vì sao lại cần một hệ thống phức tạp để build thứ như thư viện nén, vốn về bản chất chỉ cần làm chút toán học và cấp phát bộ nhớ
    • Tôi cũng nghĩ vậy. Rất dễ bị cám dỗ đổ lỗi toàn bộ chuyện này cho m4, nhưng đó gần như là tiếng nói của sang chấn hơn
  • Với tư cách lập trình viên thì chuyện này gây kinh ngạc, và cho thấy thứ trong mắt tôi trông như phương thức tấn công hạng nhất có lẽ chỉ là độ khó sơ-trung cấp đối với những người làm việc ở tầng này
    Trong những thứ xuất hiện trên HN còn có những thứ ở trình độ cao hơn nhiều, nên tôi nghĩ đây chỉ là khởi đầu của những gì có thể xảy ra nếu có đủ tiền hoặc động cơ khác. Nhìn vào vô số package trên GitHub và hàng triệu thư viện, vector này quá hiệu quả đến mức tôi tin chắc trong vài tháng tới sẽ lộ ra hàng trăm trường hợp tương tự
    Tôi lo cho các hãng phần cứng tiêu dùng/prosumer, từ Philips Hue đến Alexa, SumUp, các nhà sản xuất camera, Netgear, TP-Link. Sản phẩm của họ đầy các thư viện mã nguồn mở, và tôi chắc 100% rằng phần lớn đội ngũ phát triển không dành thời gian tìm những vector chèn lén lút như thế này

    • Tôi khó hiểu lập luận “phần lớn đội ngũ phát triển không dành thời gian tìm những vector chèn lén lút như thế này”, và có cảm giác phe chỉ trích dependency hell đang dùng kịch bản này để khiến maintainer mã nguồn mở trông tệ hơn
      Nếu là tổ chức thương mại đặt nặng độ tin cậy, họ sẽ làm phân tích chuỗi cung ứng trước khi chấp nhận dependency. Vì thế họ thường trả tiền cho Red Hat để duy trì một bản phân phối Linux ổn định, và các dự án như FreeBSD giới hạn rất chặt phần mềm được đưa vào cài đặt mặc định
      Nếu bạn bị ảnh hưởng bởi chuyện này thì đáng tiếc, nhưng đó là trách nhiệm của bạn. Nếu lo rằng nhà phát triển phần mềm bạn dùng miễn phí như bia miễn phí sẽ đổi tính, thì hãy tạo động lực để họ không làm vậy, tức là trả tiền, hoặc fork dự án rồi bổ sung các biện pháp bảo mật của riêng mình
      Nếu lo rằng tấn công chuỗi cung ứng sẽ xảy ra trong dependency của phần mềm thương mại, bạn nên thương lượng hợp đồng bao gồm cả bồi thường thiệt hại đó. Nếu không sẵn lòng làm vậy thì là thiếu chuyên nghiệp
      Nói thêm, tôi nghiêm túc nói rằng ngay cả những dependency cơ bản nhất như SSH cũng cần được kiểm tra chuỗi cung ứng
    • Thực ra là ngược lại. Những nhà phát triển và maintainer hiểu rõ hơn chúng ta đã mô tả sự kiện này là một cuộc tấn công cực kỳ tinh vi
      Các bài viết bảo mật thông tin ban đầu cũng chỉ giải thích được một phần mã, chứ chưa bao quát được toàn bộ chiến lược của cuộc tấn công. Đó là vì cuộc tấn công và mã rất tinh vi. Việc các phân tích ban đầu mô tả cuộc tấn công khác nhau cũng là do nó không đơn giản để hiểu, và mãi về sau mới có những bài kiểu “cuối cùng thì có vẻ đây là một cuộc tấn công thực thi mã từ xa”
      Giờ đây đã có cả scanner để phát hiện lỗ hổng trên server, nên mọi người mới có thể nói “à, cái đó là một cuộc tấn công quá đơn giản và ngớ ngẩn, sao không phát hiện sớm hơn?” mà thôi
    • Vì vậy tôi không thấy TP-Link được lợi gì khi làm firmware router dựa trên OpenWRT. Trên thiết bị của mình, tôi muốn chạy upstream nguyên bản hoặc thứ gì đó được thiết kế để bám theo upstream
      Điều này áp dụng giống nhau cho mọi thiết bị. Tôi cũng không thích việc Android phải dùng kernel cũ, và cũng không hài lòng khi macOS chạy nhánh Darwin/BSD lỗi thời. Tôi lo về công sức cần thiết cho việc backport
      Tất nhiên, điều đó không có nghĩa mã nguồn mở không có lỗ hổng
    • Một tổ chức vận hành tương đối bài bản sẽ có cơ chế nhận cập nhật khi dependency phát sinh vấn đề bảo mật. Tùy ngành, các cơ quan quản lý hoặc tổ chức chứng nhận như PCI còn yêu cầu điều đó
      Ngược lại, có lẽ ta nên sợ hơn những backdoor vô hình bên trong phần mềm độc quyền gần như sẽ không bao giờ bị phát hiện
  • Có vẻ infographic của Thomas Roccia vẫn chưa được nhắc đến ở đây: https://twitter.com/fr0gger_/status/1774342248437813525

    • Phần lớn có cảm giác là đang gượng ép nối các manh mối mà không có căn cứ
      Ví dụ oss-fuzz đã clone trực tiếp repository GitHub rồi build xz. Backdoor chỉ có trong tarball chứ không có trong repository, nên oss-fuzz không có khả năng phát hiện backdoor. Vì vậy PR oss-fuzz đó cũng có thể là một thay đổi thật sự không liên quan đến backdoor
    • Jia Tan đã yêu cầu các bản phân phối cập nhật nhanh ngay trước khi công bố
      Khả năng có một tài khoản hoặc nhân vật khác quanh Andreas Freund biết sớm hơn rằng backdoor sắp bị công bố là bao nhiêu. Điều đó cũng khiến tôi nghĩ rằng vẫn có thể còn nội gián khác ở xung quanh
    • Tài liệu này chỉ cho thấy ở mức cao một phần quá trình exploit được cài vào liblzma, chứ hoàn toàn không nói exploit hoạt động ra sao hay nội dung của nó là gì
    • Nhìn timeline thì cuộc tấn công bắt đầu bằng việc thêm mục ignore vào file .gitignore. Ngày nay những thứ như vậy rất khó phát hiện
  • Điểm nổi bật nhất từ góc nhìn của một người ngoài cuộc ngây thơ là đây
    “Vì nhiều tệp được tạo thủ công bằng trình chỉnh sửa hex, nên không có ‘mã nguồn’ nào tốt hơn chính bản thân tệp.” Tôi hiểu rằng với các thư viện phân tích cú pháp như liblzma thì chuyện đó có thể xảy ra. Với kẻ tấn công, việc này hẳn chỉ trông như thêm vài tệp kiểm thử mới
    Bản thân các tệp đúng là đáng sợ, nhưng tôi hiểu lý do. Dù vậy, chẳng lẽ ít nhất trong quá trình build không thể tách chúng ra sao
    “Thông thường các script configure và thư viện hỗ trợ chỉ được thêm vào bản phân phối tarball, chứ không nằm trong kho mã nguồn. Bản phân phối xz cũng làm như vậy.”
    Tạm gác cơn phẫn nộ mang tính nghi thức đối với autotools sang một bên, tôi không hiểu vì sao tarball lại phải chứa tệp kiểm thử. Kiểm thử độc hại có thể lây nhiễm máy của nhà phát triển, nhưng nếu tarball là để build sản phẩm cuối cùng, chẳng phải nên có chính sách chỉ bao gồm những gì cần thiết sao. Đặc biệt càng phải như vậy nếu tệp kiểm thử là một khối nhị phân không thể audit

    • Việc chạy kiểm thử trong CI để xác nhận không có vấn đề trên một số môi trường cụ thể sau khi build là khá phổ biến
      Tuy nhiên, lần gần nhất chúng tôi làm vậy thì chúng tôi ưu tiên Git upstream và tự tạo các artifact autoconf cần thiết. Tôi luôn không thích khái niệm release tarball có chứa những thứ không có trong Git
    • Ngay cả khi có một lượng mã autoconf được sinh ra, chúng vẫn là source tarball
      Cần phải có khả năng biên dịch mã trên máy đích rồi chạy kiểm thử
    • Ngoài việc xác minh chương trình được biên dịch trên đích có hoạt động đúng không, nếu muốn biên dịch bằng tối ưu hóa dựa trên profile, bạn cần các ví dụ chạy để tối ưu hóa, nên cũng cần kiểm thử
  • “Khác biệt đầu tiên là script này đảm bảo, một cách tuyệt đối chắc chắn, rằng nó sẽ thoát nếu không được chạy trên Linux.”
    Việc kiểm tra lặp đi lặp lại đúng là bí ẩn. Giả thuyết của tôi là kẻ tấn công có thể đã thêm phần lặp để nó trông có vẻ hợp lý như đầu vào kiểm thử của một thư viện nén

    • Có thể mục đích là tạo khoảng trống để chỉnh sửa script. Vì có thể ghi đè các byte ở phần đầu script
      Hoặc cũng có thể chỉ là lười biếng
    • Tôi cũng thấy lạ. Ở đầu script còn có các byte ngẫu nhiên khác nhau được thêm vào, và chúng không phải văn bản mà là byte ngẫu nhiên thực sự. Tuy nhiên, do có dấu thăng ở phía trước nên chúng bị coi là chú thích và không ảnh hưởng đến script
      Có vẻ là có chủ ý, nhưng tôi không biết vì sao chúng ở đó. Tôi từng nghĩ có lẽ xz sẽ bỏ qua nén nếu đầu vào quá ngắn hoặc không đủ phức tạp, nên họ thêm vào để lấp đầy kích thước, nhưng sau khi loại bỏ rồi nén lại bằng xz thì vẫn nén được bình thường và plaintext ban đầu không còn lại trong các byte của archive nén
      Trong lúc cố tái tạo chính xác các byte của tệp .xz đã được commit vào Git, tôi phát hiện rằng stream xz của script đó dường như không được nén bằng preset xz mặc định. Tôi phải dùng xz --lzma2=dict=65536 -c stream_2 mới tái tạo được, còn các preset đánh số mặc định đều chọn kích thước từ điển khác. Điều này cũng có vẻ có chủ ý, nhưng tôi không hiểu lý do
    • Có thể mục đích là làm phình to hoặc làm rối một phần của tệp kiểm thử đã nén chăng
      Nếu không có phần lặp, có lẽ bên trong tệp nén đã xuất hiện một nhị phân kỳ lạ kích hoạt công cụ bảo mật hoặc antivirus
    • Có thật là bí ẩn không? Kẻ tấn công có lẽ nhắm đến mục tiêu Linux x86, và hỗ trợ IFUNC không được bảo đảm sẽ hoạt động trên các nền tảng khác
    • Nếu chạy trên nơi không phải Linux, có thể nó đã va chạm do khác biệt hệ điều hành hoặc để lại dấu vết và bị phát hiện
  • Thật bi hài khi công nghệ hiện đại đã trở nên cực kỳ phức tạp và khó hiểu một cách không cần thiết, và tình hình ngày càng tệ hơn. Có vẻ các nhà phát triển đang tận hưởng điều đó một cách khổ dâm

    • Đây có lẽ là cách diễn giải tệ nhất
      Việc công cụ theo kịp mức độ phức tạp ngày càng tăng của sản phẩm chúng ta tạo ra là một điều khá đáng kinh ngạc. Thành thật mà nói, trong hầu hết trường hợp mọi thứ chỉ đang đơn giản hơn, và tôi nghĩ vấn đề gần với việc mọi người không muốn học cái mới hơn
  • Ai đó đã thuê những người này thâm nhập vào dự án này hẳn đã bỏ ra một lượng thời gian khổng lồ để khiến nó tránh bị phát hiện trong thời gian dài. May là nó quá phức tạp nên họ đã không tính hết mọi yếu tố
    Vì vậy, xét về bảo mật, tôi cho rằng mã nguồn mở sẽ luôn tốt hơn mã nguồn đóng. Dĩ nhiên vụ này đã phơi bày một lỗ hổng khổng lồ trong chuỗi cung ứng, và cũng cho thấy các thành phần nền tảng của FOSS bị đánh giá thấp đến mức nào, khiến maintainer dễ bị thao túng ra sao
    Nhưng nếu cùng kiểu tấn công đó xảy ra bên trong một công ty tư nhân thì sao? Có lẽ thậm chí không cần kỹ thuật làm rối mã cao cấp. Chỉ cần một PR đủ lớn và một deadline đang tới gần, bạn có thể lén đưa thứ này vào hệ thống vận hành với nỗ lực tối thiểu. Đến lúc công ty nhận ra chuyện gì đã xảy ra thì bạn đã bay tới một quốc gia không có hiệp ước dẫn độ, và đang bán dữ liệu bị rò rỉ trên Tor hoặc dark web rồi

    • Tôi nghĩ ở đây có thiên kiến kẻ sống sót. Làm sao ta biết trong số những nỗ lực tương tự có bao nhiêu cái đã thành công? Trong số những thứ bị phát hiện, có bao nhiêu cái bị quy đơn giản là lỗi vô ý? Chẳng hạn, làm sao ta chắc được Heartbleed không phải do ai đó cố tình đưa vào, và người đó hiện không trở nên cực kỳ giàu có
      Nếu được tuyển vào một công ty tư nhân, công ty biết bạn là ai. Bản thân điều đó đã là một rào cản tức thì ngăn các hành vi đáng ngờ. Trên GitHub thì không ai biết bạn là ai. Việc đưa backdoor vào một dự án mà không bị phát hiện có thể khó hơn, nhưng nếu bị phát hiện thì cũng không có rủi ro nào phải gánh. Bạn có thể tiếp tục thử bao nhiêu lần tùy ý. Jia Tan vẫn chưa bị bắt, và cũng không cần thiết kế cả cuộc đời để sống ở một nước không có hiệp ước dẫn độ. Trừ khi người đó vốn đã ở một nơi như vậy
    • Khó mà đồng ý. Phần lớn công ty tư nhân sẽ yêu cầu gặp mặt trực tiếp khi tuyển dụng. Kể cả làm hoàn toàn từ xa, vẫn có kỳ vọng rằng một ngày nào đó bạn sẽ gặp đồng nghiệp ngoài đời, và thường là trước khi bạn có các commit đáng kể. Nếu đó là một công ty đáng để thâm nhập, gần như chắc chắn họ sẽ yêu cầu kiểm tra lý lịch
      Và ngay cả sau khi vào được rồi, bạn cũng không thể tùy ý commit vào dự án mục tiêu. Quản lý của bạn và quản lý cấp trên của họ có những ưu tiên khác. Kiểu quản lý thường hay rối loạn chức năng của doanh nghiệp vì lợi nhuận lại trở thành một lực cản ở đây. Bạn không chỉ phải biện minh cho chính đoạn code, mà còn phải giải thích vì sao ngay từ đầu bạn lại làm việc đó
    • Hơi lạc đề một chút, nhưng xét theo chuẩn Mỹ hiện nay, thực sự còn bao nhiêu quốc gia không có hiệp ước dẫn độ
      Ngay cả những nước có quan hệ căng thẳng cũng có thể dẫn độ như một phần của đàm phán nếu điều đó thuận tiện về mặt chính trị. Nga có lẽ cũng đã không giữ Snowden lại nếu ông ấy không tiết lộ bí mật quốc gia. Nếu chỉ là một vụ rò rỉ dữ liệu bình thường, có khi họ đã đem đổi ông ấy lấy một tài phiệt bị bắt ở nơi khác vì rửa tiền
    • Sẽ khá thú vị nếu làm một đồ án môn học để khảo sát các dự án nhỏ, có vẻ tương đối vô hại nhưng được dùng gần như ở khắp nơi, xem chúng có thể bị xử lý theo cùng cách này không, hoặc liệu đã từng có chuyện tương tự xảy ra chưa
      Chỉ riêng việc lập danh sách cũng đã hữu ích rồi
  • Bài học rút ra từ vụ này là có lẽ không nên cho phép ẩn danh đối với những contributor cốt lõi của các dự án mã nguồn mở quan trọng. Cuộc tấn công này đã thành công và kẻ tấn công có lẽ sẽ thoát mà không phải trả giá gì, vì họ ẩn danh

    • Tôi phản đối
      Biện pháp đó sẽ không giúp ích, và với các actor cấp nhà nước hoặc các mối đe dọa dai dẳng nâng cao tương tự, họ có thể dễ dàng né bằng cách thêm hành vi đánh cắp danh tính vào một bước của cuộc tấn công, hoặc dùng một đặc vụ có thể được bảo vệ ngay cả khi backdoor bị phát hiện
      Trong khi đó, các rào cản kỹ thuật cần cho quy trình như vậy có khả năng gây thiệt hại lớn cho toàn bộ cộng đồng mã nguồn mở
      Cách giải quyết ở đây là học từ cuộc tấn công này và chuyển sang các thực hành khiến những cuộc tấn công tương tự khó hơn. Các tệp không có trong repository tuyệt đối không nên được đưa vào release tarball. Mọi code được sinh ra từ đó đều phải được check in, và build script phải tái sinh code dẫn xuất rồi fail nếu nó khác với code đã check in. Dữ liệu khó hiểu không nên được truy cập trong quá trình release build, và các bài test phụ thuộc vào dữ liệu nhị phân phải được build hoàn toàn tách biệt với binary phát hành
    • Có hai vấn đề
      Thứ nhất, nhiều contributor quan trọng, đặc biệt trong lĩnh vực bảo mật, thích hoạt động dưới bút danh vì những lý do chính đáng. Ép họ công khai danh tính sẽ đẩy họ ra ngoài
      Thứ hai, nếu đứng sau là một cơ quan tình báo như nhiều người suy đoán, thì các cơ quan như vậy dù sao cũng có thể tạo ra danh tính “thật”. Kết cục là ta loại bỏ những người có ích nhưng lại không loại được kẻ tấn công
    • Không chỉ không thể thực thi, mà có thể còn không phải ý hay. Vì nếu biết danh tính maintainer của các dự án mã nguồn mở quan trọng, việc gây áp lực lên họ sẽ dễ hơn
    • Nếu đây là actor cấp nhà nước, và thực sự trông có vẻ là vậy, thì có thể xác minh kiểu gì? Bằng lái xe, số an sinh xã hội, căn cước quốc gia, hộ chiếu, bất cứ giấy tờ nào họ cũng có thể tạo ra hợp pháp
      Nếu chính phủ đã nhúng tay thì không có giới hạn. Cách duy nhất là yêu cầu xuất hiện trực tiếp tại một địa điểm đáng tin cậy, rồi chỉ còn biết hy vọng nơi đó không nằm trong thẩm quyền của kẻ tấn công
    • Ai, và dựa trên tiêu chí nào, sẽ chỉ định đâu là dự án quan trọng
      Nếu ai đó tạo một thư viện rồi người khác bắt đầu dùng, họ có bị buộc phải công khai danh tính không? Maintainer có được trả tiền không?
  • Nếu từng làm phát triển phần mềm, dù đóng hay mở, bạn sẽ biết các developer thường phê duyệt PR một cách nửa lười biếng. Linus Torvalds gần như là ngoại lệ hiếm hoi khi dành cả ngày để chỉ ra vấn đề

    • Đồng ý
      Nếu ai đó đủ khó tính để thực sự chú ý kỹ, người đó rất dễ bị xem là kẻ phiền phức cản trở phát triển. Việc bị cho là hay bắt bẻ sẽ tạo căng thẳng với cả đội
      Nói thật là hiện tôi cũng đang có tình huống tương tự. Không phải tôi là người khó tính, mà là một trong những developer tôi thuê. Đáng tiếc là sự kỹ lưỡng của anh ấy không được đánh giá cao, nên tôi đã phải đưa anh ấy ra khỏi đội. Việc anh ấy đến từ Đông Âu và thường đưa feedback rất thẳng thắn cũng không giúp được gì
  • Các tiện ích Unix đã được kiểm chứng trong thời gian dài. Tôi mong kernel và các tiện ích cốt lõi được giữ cố định, đừng thay đổi, trừ khi thật sự cần thiết
    Nếu chưa hỏng thì đừng sửa. Đế chế phần mềm trông như đang mất kiểm soát

    • Tất cả đều hỏng cả. Đó là kết quả của các bản hack nhanh để khiến mọi thứ tạm chạy được hôm nay, tích tụ qua 50 năm và một triệu developer
      Thỉnh thoảng cũng có những ví dụ sáng chói được làm đúng cách, nhưng nhìn chung có thể nói những thứ đó đã bị lấn át hoàn toàn