- 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 tr và xz -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/Makefile và libtool để 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.c và crc32_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#### và ####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_64 và linux-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.lzma và bad-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 và .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
Ý 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 đề
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
./configure && make && make installViệ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.tomlkhông thể giải quyết đượcVà 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ớ
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
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
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
Đ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
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
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
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
.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
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
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ử
“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
Hoặc cũng có thể chỉ là lười biếng
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ùngxz --lzma2=dict=65536 -c stream_2mớ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ý doNế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
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
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
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
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 đó
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
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
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
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
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
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 đề
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
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