
Liferay CE 7.0.3 GA4 보안 연구: 사전 인증 RCE as root(CVE-2020-7961 계열)를 엔드투엔드로 재현했으며, 추가로 16건의 발견 사항 포함 — 그중 8건 이상은 알려진 CVE가 없음. 에이전트 기반 루프 헌팅: Docker에서 25개 생성기, 22개 판정기, 9개 실시간 검증기 사용. 증거 추적 및 원샷 체커 포함.
Liferay Portal CE 7.0.3 GA4(git 태그 7.0.3-ga4, 커밋 b2d7cc68a)에 대해 RAPTOR loop-hunt 방법론으로 수행된 보안 감사: 결정적 정찰 → 25개의 격리된 생성 에이전트가 원시 소스 읽기 → 22개의 독립 판정 에이전트가 원시 소스로부터 반박 → 9개의 라이브 검증기가 Docker의 실제 GA4 인스턴스 공격.
핵심 소식:
root권한의 인증 없는 RCE(CVE-2020-7961 계열)가 엔드투엔드로 재현되었으며, 라이브 검증된 취약점 8건 추가 확인 — 그중 여러 건은 알려진 CVE가 없습니다.
⚠️ 법적/윤리적 고지: 여기의 모든 내용은 자체 호스팅 테스트 인스턴스를 대상으로 합니다. Liferay CE 7.0은 수명 종료(EOL) 상태이며(마지막 릴리스 7.0.6 GA7, 2018), 이러한 문제는 업스트림에서 패치되지 않았습니다. 이 자료는 본인이 소유하거나 명시적으로 테스트 권한을 부여받은 시스템에만 사용하십시오.
상태: ✅ Docker GA4 인스턴스에 대해 라이브 검증됨 · 🔬 코드로 확인됨(독립 판정 에이전트가 원시 소스에서 전체 추적 경로를 재도출) · 🧪 추가 검증 필요/구성 조건부.
거부된 항목을 포함한 전체 큐레이션 목록: FINDINGS.md · 상세 보고서: REPORT.md
선행 연구로 사용된 약 50개의 알려진 Liferay 7.0.x 이슈 카탈로그: PRIOR-ART.md(OSV [email protected], NVD 조사, Code White / CERT-EU 권고).
"신규" = 2026년 7월 기준 OSV/NVD/Liferay 권고에서 일치하는 CVE를 찾지 못함; 발견 사항은 CVE 배정을 위해 제출되지 않았음(제품 라인이 EOL이기 때문).
CVE-2020-15840(이중 인코딩 URL 필터 우회), CVE-2024-8980 + CVE-2025-43748(CSRF → 스크립트 콘솔 RCE), CVE-2021-33335(회사 관리자 탈취 — GA4는 영향 범위의 하한), CVE-2017-1000425(flash.jsp XSS), CVE-2021-29040(상세 JSONWS 오류 메시지), CVE-2022-41414 / CVE-2023-33949(열거/등록 기본값), CVE-2025-62261(평문 재설정 토큰 — NVD가 7.0.3 GA4를 직접 명시). PRIOR-ART.md 참조.
원장: TRIED.md(시도한 모든 것, 문서화된 3개의 dry 슬라이스 포함) · INVENTORY.md(커버리지 분모) · 환경/픽스처: ENV.md
공식 liferay/portal:7.0.3-ga4 이미지가 없으므로 공식 번들에서 빌드합니다:
# Dockerfile
FROM openjdk:8-jdk
RUN apt-get update && apt-get install -y --no-install-recommends unzip curl \
&& rm -rf /var/lib/apt/lists/*
RUN curl -sSL -o /tmp/liferay.zip \
"https://downloads.sourceforge.net/project/lportal/Liferay%20Portal/7.0.3%20GA4/liferay-ce-portal-tomcat-7.0-ga4-20170613175008905.zip" \
&& unzip -q /tmp/liferay.zip -d /opt \
&& mv /opt/liferay-ce-portal-7.0-ga4 /opt/liferay \
&& rm /tmp/liferay.zip
EXPOSE 8080 11311
CMD ["/opt/liferay/tomcat-8.0.32/bin/catalina.sh", "run"]
docker build -t liferay-ga4 .
docker run -d --name liferay-ga4 -p 8080:8080 liferay-ga4
# first boot takes several minutes — wait for it:
until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/)" = 200 ]; do sleep 10; done
그런 다음 http://localhost:8080/에서 일회성 설정 마법사를 완료합니다(기본 HSQL DB; 마법사가 [email protected]의 관리자 비밀번호를 설정합니다).
tools/liferay_ga4_check.py는 단일 파일, 표준 라이브러리만 사용하는 Python 3 스크립트입니다 — 임의의 GA4 인스턴스를 지정하면 라이브 검증된 모든 발견 사항을 테스트합니다:
# passive validation of all findings (safe, no payloads detonated)
python3 tools/liferay_ga4_check.py http://localhost:8080
# with an account (self-registration is on by default) for the poller sink test
python3 tools/liferay_ga4_check.py http://localhost:8080 --user [email protected] --password '***'
# full RCE proof (opt-in): drops a SELF-DELETING JSP via the deserialization file-write
# gadget and fetches the command output; adjust --web-root to your install
python3 tools/liferay_ga4_check.py http://localhost:8080 --rce \
--web-root /opt/liferay/tomcat-8.0.32/webapps/ROOT
기본 GA4 설치에 대한 예상 출력: 9개 발견 사항 VULNERABLE(F-1, F-1+RCE, F-2, F-4, F-5*, F-8, F-11, F-12, F-13*) 및 F-10은 취약하지 않음으로 확인(터널 장벽 유지). *F-5와 F-13은 픽스처 파라미터(--leg-uuid/--version-id, --wcd-page/--group-id/--article-id)가 필요합니다 — --help 참조.
역직렬화 가젯은 스크립트 내부에서 순수 Python으로 생성됩니다(엔게이지먼트에서 사용된 ysoserial AspectJWeaver 페이로드와 바이트 단위 검증됨) — Java, jar, ysoserial이 필요 없습니다.
비밀번호 재설정 티켓(128비트 UUID, 일회용, 24시간), 티켓 로그인 시 세션 갱신, 회사별 SecureRandom 키를 사용한 remember-me 쿠키 AES-128, 기본 PBKDF2 해싱, 포털 전체 XML 스택(기본적으로 외부 엔티티 비활성화), WebDAV/SharePoint 다이제스트 게이트, /api/axis|/api/liferay|/api/atom localhost IP 잠금, 업로드 경로 검증, /combo /sprite 경로 탐색 방어, 스크립트 콘솔 및 Gogo 웹 UI의 옴니 관리자 게이팅.
커버리지는 전체 인벤토리(TRIED.md)를 기준으로 컴포넌트별로 추적되었습니다. 미포함: 전체 4GB 트리에 대한 함수별 고도 분석(Semgrep 앵커만 해당), 파일 고도의 foundation/web-experience 외부 modules/apps 대부분, F-2 가젯 연구, 비기본 구성 라이브 테스트(F-9/F-14), PDFBox 2.0.3 의존성 CVE 도달 가능성. 루프는 고갈되어서가 아니라 보고를 위해 일시 중지되었습니다.
| ID | 발견 사항 | 심각도 | 인증 | 상태 | 증거 |
|---|
| F-1 | JSONWS 타입 오버라이드 → 임의 클래스 인스턴스화 + 세터 호출 → root 권한 RCE (c3p0 HexAsciiSerializedMap → ObjectInputStream → AspectJWeaver 파일 쓰기 → JSP) | 치명적(Critical) | 없음 | ✅ | verified/f1-jsonws-deser-rce.md |
| F-2 | /poller/* jabsorb javaClass 힌트 → 임의 클래스 인스턴스화 + 필드 주입, userId 검증 이전에 발생 | 높음(잠재적) | 모든 계정(기본적으로 자기 등록 활성화) | ✅ 싱크 확인 / RCE 경로 열림 | verified/F-2-poller.md |
| F-4 | XML-RPC pingback.ping 검증 전 가져오기 → 블라인드 SSRF + 내부 포트 오라클 (127.0.0.1:11311의 Gogo 셸에 도달) | 중간(Medium) | 없음 | ✅ | verified/f4-xmlrpc-pingback-ssrf.md |
| F-5 | SyncDownloadServlet versionId IDOR → 임의의 DL 파일 버전 읽기 (크로스 사이트, 스트리밍 객체에 VIEW 검사 없음) | 높음(High) | 없음(게스트가 볼 수 있는 "leg" 경유) | ✅ | verified/F-5.md |
| F-6 | XSL 템플릿 엔진이 샌드박싱을 무시함(secureProcessing=false, restricted 제거) → document() SSRF 및 XSLTC rt:exec 명령 실행 | 치명적(Critical) | 사이트 템플릿 작성자 | ✅ | verified/F-6-F-7-template-engines.md |
| F-7 | Velocity 및 FreeMarker 제한 템플릿이 원시 propsUtil / saxReaderUtil을 노출 → 게스트에게 포털 속성 + file:// 정보 노출 | 높음(High) | 템플릿 작성자 | ✅ | verified/F-6-F-7-template-engines.md |
| F-8 | /image/*?img_id=N을 통한 Image_ 테이블 IDOR — 토큰 없음, 접두사 범위 검사를 접두사 교체로 우회, 순차적 ID 열거 가능 | 중간(Medium) | 없음 | ✅ | verified/f-8-image-idor.md |
| F-11 | SimpleCaptcha = 4자리 PIN, 잘못된 추측에도 무효화되지 않으며 게스트 카운터가 증가하지 않음 → 약 5,000회 요청으로 돌파 | 중간(Medium) | 없음 | ✅ | verified/f11-f12-captcha-reminder.md |
| F-12 | 비밀번호 찾기: 캡차 없이 사용자 열거 + 힌트 답변 오라클(새 세션 우회); 빈 답변으로 질문 없는 계정에 실제 재설정 티켓 발급 | 중간-높음(Medium-High) | 없음 | ✅ | verified/f11-f12-captcha-reminder.md |
| F-13 | Web Content Display 요청 파라미터 오버라이드(groupId/articleId/ddmTemplateKey) → 비게스트 기사의 크로스 사이트 읽기 (기본값 journal.article.view.permission.check.enabled=false) | 높음(High) | 없음(게스트 페이지에 WCD 필요) | ✅ | verified/F-13-wcd-param-override.md |
| F-3 | OSGi axis-extender가 모듈 SOAP 서비스에서 ServiceAccessPolicy / 원격 액세스 표시를 건너뜀 | 중간(Medium)(WSDD 모듈 시 높음) | 없음 | 🔬 | judge/axis-spring-remoting.md |
| F-9 | OpenID가 어설션 검증 전에 피해자 계정을 공격자의 OpenID에 연결 → 계정 탈취 | 높음(High) | 없음(OpenID 활성화 시, 비기본 설정) | 🔬 🧪 | judge/sso-autologin.md |
| F-10 | TunnelServlet readObject가 클래스 블랙리스트로만 보호됨; 클래스패스에 ROME/JdbcRowSetImpl/c3p0/AspectJWeaver 가젯 존재 | 잠재적 치명적(Critical) | 신뢰된 피어(공유 시크릿 + 허용 호스트) | ✅ 인증 전 장벽 유지 확인 | verified/f10-f20-tunnel-auth-pipeline.md |
| F-14 | TokenAutoLogin이 단순 SM_USER 헤더를 신뢰함(검증 없음, IP 허용 목록 없음) | 중간(Medium) | 없음(토큰 SSO 활성화 시, 비기본 설정) | 🔬 🧪 | judge/sso-autologin.md |
| F-15 | 사이트 관리자 LAR 가져오기가 RolesAdminPortletDataHandler를 밀반입 → 대량 회사 역할 삭제 / 권한 제거 | 중간(Medium) | 사이트 관리자 | 🔬 | judge/export-import-lar.md |
| F-16 | OpenSocial(기본 번들에 없음): 인증 전 makeRequest SSRF, /gadgets/ifr 오리진 JS, 위조 가능한 보안 토큰 → 가장, 저장형 XSS | 높음(High) ×4 | 없음 / 사이트 멤버 | 🔬(배포 조건부) | judge/opensocial-shindig.md |
| F-17 | WSRP(기본 번들에 없음): 인증 전 ProxyServlet SSRF; 마크업 엔드포인트 → 임의 포틀릿 렌더링 + 레이아웃 typeSettings 영속화 | 중간(Medium) | 없음 | 🔬(배포 조건부) | judge/wsrp.md |
| 발견 사항 | 알려진 CVE? | 관계 |
|---|
| F-1 JSONWS 역직렬화 RCE | CVE-2020-7961(CISA KEV, 실제 공격에 악용됨) | 재현 + 독립 재도출. GA4에서 패치되지 않음을 확인; AspectJWeaver 파일 쓰기 가젯으로 전체 체인 입증(공개 PoC는 일반적으로 JdbcRowSetImpl JNDI 사용). |
F-10 TunnelServlet readObject | CVE-2020-15842(클래스 일치) | 알려진 클래스. 블랙리스트 전용 역직렬화 방어 확인; 인증 전 장벽 라이브 검증(공유 시크릿/허용 호스트가 없으면 악용 불가). |
F-7 템플릿 propsUtil 이스케이프 | CVE-2020-13445(템플릿 주입, FreeMarker/Velocity) | 변형. 동일한 버그 클래스(템플릿 API가 민감한 객체를 제한하지 못함), 다른 메커니즘: 원시 PropsImpl/saxReaderUtil 객체가 제한된 컨텍스트 내부에서 노출됨. |
| F-12 (열거 부분) | CVE-2021-33321(login.secure.forgot.password=false) | 알려진 안전하지 않은 기본값 — 라이브로 독립 확인됨. |
| F-12 (오라클 + 빈 답변 재설정 티켓) | 발견된 것 없음 | 신규. 2단계에서 새 JSESSIONID 캡차 우회; 빈 answer로 비밀 정보 없이 질문 없는 계정에 유효한 24시간 재설정 티켓 생성. |
| F-11 SimpleCaptcha 무차별 대입 | 발견된 것 없음(CVE-2019-6588은 XSS라는 다른 캡차 문제) | 신규. 4자리 PIN + 무효화 없음 + 게스트 카운터 우회. |
| F-2 poller jabsorb 역직렬화 | 발견된 것 없음 | 신규. 싱크 라이브 확인; LiferaySerializer 필드 주입으로 무기화가 차단됨(공개 연구 과제). |
| F-4 pingback SSRF 오라클 | 발견된 것 없음 | 신규. |
F-5 sync versionId IDOR | 발견된 것 없음 | 신규. |
| F-6 XSL 엔진 RCE | 발견된 것 없음(CVE-2020-13445는 FreeMarker/Velocity만 포함) | 신규. XSL 엔진은 이전 모든 템플릿 주입 수정 범위 밖이었음. |
F-8 Image_ IDOR | 발견된 것 없음 | 신규. |
| F-13 WCD 크로스 사이트 읽기 | 발견된 것 없음 | 신규. 근본적인 안전하지 않은 기본값(journal.article.view.permission.check.enabled=false) 및 새로운 전달 경로(포틀릿 요청 파라미터 오버라이드). |
| F-3 axis-extender SAP 우회 | 발견된 것 없음 | 신규. |
| F-9 OpenID 검증 전 연결 | 발견된 것 없음 | 신규. |
F-14 SM_USER 헤더 신뢰 | 발견된 것 없음 | 신규. |
| F-15 LAR 역할 데이터 핸들러 밀반입 | 발견된 것 없음 | 신규. |
| F-16 / F-17 OpenSocial / WSRP | 발견된 것 없음 | 신규, 단 해당 모듈은 기본 GA4 번들에 포함되지 않음(Liferay-Releng-Bundle: false). |
| 단계 | 실행 내용 | 산출물 |
|---|
| 0 — 결정적 | raptor-sca 의존성 감사(727개 의존성), Semgrep 규칙 17개(앵커 1,098개), 위협 모델, web.xml 기반 엔트리포인트 매니페스트, 선행 연구 CVE 카탈로그 | SCA.md SEMGREP.md THREAT-MODEL.md ENTRYPOINTS.md PRIOR-ART.md |
| 1 — 생성 | 컴포넌트 슬라이스 × 버그 클래스 렌즈별 1개씩, 총 25개의 격리된 에이전트가 원시 소스만으로 추론(이전 요약 없음 — 앵커링 방지) | 45개 후보 — candidates/ 참조 |
| 2 — 판정 | 원시 소스에서 각 후보를 반박하는 22개의 독립 에이전트; 기각하려면 코드에서 확인 가능한 증거가 필요("보이지 않는 계층이 안전하다고 가정하지 말 것") | judge/ |
| 3 — 라이브 검증 | Docker GA4 인스턴스를 공격하는 9개 에이전트; 모든 주장은 캡처된 요청/응답으로 뒷받침됨; 필수 정리 수행 | verified/ |