https://www.openwall.com/lists/oss-security/2019/12/19/2
날짜 : 수요일, (18) 년 12 월 2019 년 21 : 21 : (19) - 0600 에서 : 매트 아프고 <mattsicker @ ... che.org> 받는 사람 : OSS - 보안 @ ... ts.openwall.com 제목 : [CVE - 2019 년 - 1만7천5백71 ] 아파치 Log4j는 1.2 신뢰할 수없는 데이터의 직렬화 에 SocketServer CVE - 2019 - 1만7천5백71 : 신뢰할 수없는 데이터의 직렬화 에 SocketServer 심각도 : 긴급 CVSS : 3.0 / AV : N / AC : L / PR : N / UI : N / S : U / C : H / I : H / A : H / RL : W 제품 : 아파치의 Log4j 버전 영향을받는 : 아파치의 log4j에 포함 최대 1.2 . (27) . 별도로 고정 에 의해 CVE - 2017 년 - 5645 년 의 log4j 2.8 . 2 . 문제 유형 : CWE - 502 : 신뢰할 수없는 데이터를 역 직렬화 설명 : 포함됨 에 Log4j는 1.2 이다 SocketServer의 클래스 이다 취약 원격으로 이용 될 수있는 신뢰할 수없는 데이터의 직렬화 역 직렬화 가제트과 결합 될 때, 임의의 코드를 실행할 신뢰할 수없는 네트워크 트래픽을들을 때 에 대한 로그 데이터. 완화 : 아파치의 log4j 1.2은 수명이 다한 에 년 8 월 2015 년 . 사용자해야 Log4j를로 업그레이드 2 .x를을하는 두 주소 취약점 그 뿐만 아니라 같은 많은 다른 문제 에 이전 버전. 신용 : 이 문제는 처음 발견 된 에 CVE- 2017 년 - 5645 마르시오 알메이다에 의해 텔스트라 (Telstra)에서 레드 팀의 드 마세. 링크 : HTTPS : // logging.apache.org/log4j/1.2/ HTTPS : // issues.apache.org/jira/browse/LOG4J2-1863 - 매트 아프고 장관, 아파치 소프트웨어 재단 부사장 서비스 로깅, ASF
SocketServer 클래스의 취약점
Log4j의 항아리 패키지를 다운로드합니다. 나는 1.2.14 버전을 선택했다.
SocketServer.class에 글로벌 검색
클래스 코드를 볼 수있는 코드의 주요 기능은, 코드가 아주 잘 이해 오래되지 않습니다.
void main(String argv[]) { if(argv.length == 3) init(argv[0], argv[1], argv[2]); else usage("Wrong number of arguments."); try { cat.info("Listening on port " + port); ServerSocket serverSocket = new ServerSocket(port); while(true) { cat.info("Waiting to accept a new client."); Socket socket = serverSocket.accept(); InetAddress inetAddress = socket.getInetAddress(); cat.info("Connected to client at " + inetAddress); LoggerRepository h = (LoggerRepository) server.hierarchyMap.get(inetAddress); if(h == null) { h = server.configureHierarchy(inetAddress); } cat.info("Starting new socket node."); new Thread(new SocketNode(socket, h)).start(); } } catch(Exception e) { e.printStackTrace(); } }
init初始化了参数,传入的参数是三个。
new一个serverSocket对象,最后new Thread(new SocketNode(socket, h)).start();
把收到的socket流内容,用SocketNode实例化这个流内容。我们看下SocketNode这个类是怎么实现的吧。全局搜索SocketNode。
public class SocketNode implements Runnable { Socket socket; LoggerRepository hierarchy; ObjectInputStream ois; static Logger logger = Logger.getLogger(SocketNode.class); public SocketNode(Socket socket, LoggerRepository hierarchy) { this.socket = socket; this.hierarchy = hierarchy; try { ois = new ObjectInputStream( new BufferedInputStream(socket.getInputStream())); } catch(Exception e) { logger.error("Could not open ObjectInputStream to "+socket, e); } } //public //void finalize() { //System.err.println("-------------------------Finalize called"); // System.err.flush(); //} public void run() { LoggingEvent event; Logger remoteLogger; try { while(true) { // read an event from the wire event = (LoggingEvent) ois.readObject(); // get a logger from the hierarchy. The name of the logger is taken to be the name contained in the event. remoteLogger = hierarchy.getLogger(event.getLoggerName()); //event.logger = remoteLogger; // apply the logger-level filter if(event.getLevel().isGreaterOrEqual(remoteLogger.getEffectiveLevel())) { // finally log the event as if was generated locally remoteLogger.callAppenders(event); } } } catch(java.io.EOFException e) { logger.info("Caught java.io.EOFException closing conneciton."); } catch(java.net.SocketException e) { logger.info("Caught java.net.SocketException closing conneciton."); } catch(IOException e) { logger.info("Caught java.io.IOException: "+e); logger.info("Closing connection."); } catch(Exception e) { logger.error("Unexpected exception. Closing conneciton.", e); } try { ois.close(); } catch(Exception e) { logger.info("Could not close connection.", e); } } }
代码不是很长,执行的是run的代码。可以看到
event = (LoggingEvent) ois.readObject();
直接执行了readObject操作。ois是ObjectInputStream ois;定义了ois是输入流。
因此只要传入的字节流是反序列化的内容即可。