"편의 기능"이 사용자 입력을 코드로 착각하게 만든 순간 — Struts2 S2-032

“편의 기능”이 사용자 입력을 코드로 착각하게 만든 순간 — Struts2 S2-032

분류: CVE · 웹·서버 · RCE · CVE-2016-3081 [정보성]

덱. 오래된 CVE지만 지금도 반복되는 취약점 클래스의 교과서 같은 사례입니다. Apache Struts2의 “동적 메서드 호출” 편의 기능이, 요청 파라미터 하나를 데이터가 아니라 실행 가능한 코드로 취급하면서 무인증 원격 코드 실행으로 이어졌습니다.

리드

웹 프레임워크가 개발 편의를 위해 붙여 놓은 기능이 뜻밖의 신뢰 경계를 열어버리는 사례는 반복해서 나타납니다. Struts2의 S2-032(CVE-2016-3081)는 그 초기 대표 사례 중 하나입니다. 이 글이 다루는 “사용자 입력이 표현식(expression)으로 평가되는” 문제 구조는 이후 여러 언어·프레임워크에서 SSTI(서버 사이드 템플릿 인젝션)라는 이름으로 계속 재등장합니다.

핵심 사실 요약

  • CVE-2016-3081(Apache Struts2 S2-032)은 Apache Struts 2.3.19–2.3.28 버전에 영향을 준 취약점입니다. NVD 등록 기준 CWE-77(명령어 인젝션 계열)로 분류돼 있습니다.
  • 이 취약점은 Struts2의 Dynamic Method Invocation(DMI, 동적 메서드 호출) 기능이 활성화돼 있을 때 발생합니다. DMI는 URL에 method: 접두어가 붙은 요청 파라미터로 특정 액션의 메서드를 직접 지정해 호출할 수 있게 해주는 개발 편의 기능입니다.
  • 문제는 이 파라미터 값이 단순 문자열로 처리되지 않고, Struts2 내부에서 쓰는 OGNL(Object-Graph Navigation Language) 표현식으로 해석·실행될 수 있었다는 데 있습니다. OGNL은 원래 객체 그래프를 탐색하기 위한 표현식 언어인데, 공격자가 제어하는 문자열이 이 언어의 실행 대상으로 들어가면 OGNL이 제공하는 리플렉션·런타임 호출 능력까지 통째로 열립니다.
  • 알려진 공격 흐름은 크게 두 단계입니다. 먼저 OGNL의 멤버 접근 제한 장치(_memberAccess)를 완화하는 표현식을 태워 정적 메서드·클래스에 대한 접근 가드를 해제하고, 이어서 같은 표현식 체인에서 시스템 명령 실행 API를 호출합니다. 결과는 별도 콜백 없이 HTTP 응답에 그대로 실려 돌아올 수 있어, 공격자가 즉시 명령 실행 결과를 확인할 수 있는 구조였습니다.
  • 이 결함은 무인증으로 트리거됩니다. 로그인이나 별도 권한 없이, DMI가 활성화된 Struts2 애플리케이션에 요청을 보내는 것만으로 성립합니다.
  • 벤더 권고에 따르면 근본 대응은 Struts 2.3.28.1 이상으로 업그레이드하는 것이며, 이 버전부터는 DMI가 기본적으로 비활성화됩니다. 이미 예전 버전을 쓰고 있다면 struts.enable.DynamicMethodInvocation 설정값을 명시적으로 false로 두는 것도 완화책으로 안내됩니다.
  • ⚠️미확인(팩트체크 대상) — CVSS 수치·CISA KEV(악용 확인 취약점 카탈로그) 등재 여부는 출처마다 표기가 엇갈리는 경우가 있어, 이 초안에서는 구체적인 점수·KEV 등재 여부를 단정하지 않습니다. 정확한 수치는 발행 전 NVD 1차출처 대조가 필요합니다.

대응방안

  1. 근본. Apache Struts를 DMI가 기본 비활성화된 버전(2.3.28.1 이상, 이후 메이저 버전은 별도 확인) 이상으로 업그레이드합니다.
  2. 완화(업그레이드가 당장 어려울 때). struts.enable.DynamicMethodInvocation=false를 명시적으로 설정하고, method:/!method 형태의 액션 매핑 사용을 코드베이스 전반에서 제거합니다.
  3. 탐지 관점. 요청 파라미터나 URL 경로에 method: 접두어, 또는 OGNL 특유의 문법(예: _memberAccess, @java.lang.Runtime@getRuntime() 류 패턴)이 섞인 트래픽은 이 클래스 공격의 강한 신호입니다.

짧은 논평

이 취약점 클래스의 근본 원인은 결국 하나입니다 — 사용자 입력이 “값”이 아니라 “평가할 코드”로 처리되는 경계를 열어준 것. 같은 원리가 Jinja2·Freemarker 같은 템플릿 엔진의 SSTI, 최근 여러 애플리케이션에서 반복되는 표현식 언어 인젝션에서도 그대로 나타납니다. “개발 편의를 위해 넣은 강력한 기능”이 신뢰 경계를 몰래 확장하는 패턴은, 저희가 다른 CVE 분석에서도 반복해서 짚는 주제입니다.

References / 참고자료

  1. NVD, CVE-2016-3081 — 취약 버전·CWE 분류 등 공식 메타데이터. https://nvd.nist.gov/vuln/detail/CVE-2016-3081
  2. Apache Struts 공식 보안 권고, S2-032 — 영향 범위·DMI 비활성화 권고. https://cwiki.apache.org/confluence/display/WW/S2-032

이 글은 위 1차출처를 바탕으로 이미 공개된 사실을 우리 관점에서 다시 정리한 정보성 초안입니다. 소스코드 라인 대조·패치 diff 분석은 하지 않았으며, 작동하는 페이로드는 싣지 않았습니다. ⚠️미확인 표시 항목은 발행 전 독립 팩트체크에서 확증·정정되어야 합니다.