Загружаем каталог…
Загружаем каталог…
[CTF/웹해킹] PHP 스트림 래퍼(php://) 핵심 정리 및 실전 공격 기법 1. php:// 스트림 래퍼란? http://나 file://처럼 URL 스키마 형식을 띠고 있지만, 네트워크 통신을 하지 않고 PHP 엔진 내부 메모리에서 동작하는 가상의 입출력 통로(파이프)입니다. PHP의 파일 입출력 함수(include, require, file_get_contents, fopen 등)는 일반 로컬 파일 경로뿐만 아니라 스트림 주소도 동일하게 받아들입니다. 웹 해킹 및 CTF에서는 주로 LFI(Local File Inclusion) 취약점과 연계하여 소스코드 유출이나 RCE(원격 코드 실행)를 달성하는 핵심 도구로 쓰입니다. 왜 주소(URI) 형태로 만들었을까요? (인터페이스 통일) PHP 설계자들이 프로그래머에게 편의를 주기 위해서입니다. 다른 프로그래밍 언어(C, Python, Java 등)에서는 로컬 파일 읽기(open), 네트워크 통신(requests/socket), 메모리 파이프(BytesIO)를 다룰 때 각각 완전히 다른 문법과 라이브러리를 써야 합니다. 하지만 PHP는 file_get_contents()나 fopen() 같은 함수 이름은 그대로 두고, 괄호 안에 전달하는 문자열 앞머리(http://, file://, php://)만 바꿔 끼우면 PHP 엔진이 알아서 네트워크, 디스크, 내부 메모리 파이프로 분기하도록 인터페이스를 하나로 통일해 둔 것입니다. 이것이 공격자에게는 취약점이 됩니다. 개발자가 include($_GET['page']); 처럼 로컬 파일 이름만 들어올 줄 알고 짰더라도, 공격자가 php:// 나 data:// 같은 스트림 주소를 주입하면 함수가 이를 그대로 해석해 버리기 때문입니다. 2. http:// vs file:// vs php:// 핵심 차이점 겉모습만 비슷한 주소 형태(URL 스키마)를 띨 뿐, 내부 동작 방식은 완전히 다릅니다. 네트워크 통신 vs 디스크 접근 vs 프로그램 내부 파이프 http:// (네트워크 통신) 실제로 인터넷/LAN 카드를 거쳐 외부 원격 서버로 패킷을 보냅니다. 프로토콜 규격에 따라 요청 헤더, 응답 헤더, 바디(본문) 등을 주고받는 네트워크 과정이 필요합니다. allow_url_fopen이 켜져 있을 때 RFI(원격 파일 포함) 공격에 주로 쓰입니다. file:// (로컬 파일 시스템 직접 접근) 네트워크를 타지 않고, OS의 파일 시스템 API를 직접 호출해 하드디스크에 저장된 파일을 그대로 읽어옵니다. 헤더나 변환 과정 없이 순수한 파일 내용만 읽어오며, file:///etc/passwd 는 /etc/passwd 를 직접 읽는 것과 사실상 동일합니다. php:// (메모리 내부 파이프) 네트워크를 전혀 타지 않습니다. 랜선이나 와이파이와 아무 상관이 없습니다. PHP 프로그램 자체(메모리) 안에서만 작동하는 가상의 소프트웨어 통로입니다. 리눅스 CLI에서 쓰는 파이프(|) 명령어를 PHP 엔진 내부 메모리로 옮겨놓은 것과 같습니다. 비교 요약 표 스키마 동작 영역 통신 방식 헤더/패킷 유무 보안 및 공격 관점 http:// 외부 원격 서버 TCP 네트워크 소켓 통신 HTTP 요청/응답 헤더 및 바디 존재 RFI (외부 서버의 악성 코드 원격 실행) file:// 로컬 디스크 (OS) OS 파일 시스템 시스템 콜 없음 (원시 바이트 직접 읽기) LFI (로컬 설정 파일, 시스템 파일 단순 열람) php:// PHP 프로세스 메모리 PHP 내부 C 스트림 처리기 없음 (메모리 버퍼 및 필터 체인 통과) LFI 소스코드 유출, 필터 체인 바이트 위조, POST 본문 RCE 3. 표준 입출력 래퍼 (stdin, stdout, stderr) 상세 분석 운영체제에서 모든 프로세스는 생성될 때 3개의 표준 입출력 채널(File Descriptor, FD)을 부여받습니다: FD 0번: 표준 입력 (STDIN) - 키보드 또는 파이프 입력 FD 1번: 표준 출력 (STDOUT) - 모니터 터미널 일반 출력 FD 2번: 표준 에러 (STDERR) - 모니터 터미널 에러 출력 PHP는 이 3개 통로를 파일처럼 읽고 쓸 수 있도록 php://stdin, php://stdout, php://stderr 래퍼를 제공합니다. (1) php://stdin (표준 입력 래퍼) 운영체제의 0번 통로(STDIN)에 직접 접근하는 읽기 전용 스트림입니다. 문법 구조 분해: 주소: php://stdin php:// : PHP 스트림 래퍼 선언 stdin : 운영체제의 표준 입력(FD 0번) 버퍼 채널을 지정 실제 PHP 개발 코드 예시: <?php // 방법 A: 한 줄씩 인터랙티브하게 읽기 $handle = fopen('php://stdin', 'r'); echo "이름을 입력하세요: "; $name = trim(fgets($handle)); echo "입력받은 값: " . $name . "\n"; // 방법 B: 버퍼 끝까지 통째로 읽기 (파이프나 파일 리디렉션용) $all_data = file_get_contents('php://stdin'); echo "전체 전달받은 데이터:\n" . $all_data; ?> - 실제로 데이터를 전달하는 3가지 방법: 1. 키보드로 직접 입력 (인터랙티브 모드) 터미널에서 `php test.php` 실행 후 키보드로 타이핑하고 엔터 입력 2. 리눅스 파이프(|) 연결 (가장 흔함) 다른 프로그램의 출력 결과를 stdin으로 꽂아 넣음: `echo "test_payload" | php test.php` 3. 파일 리디렉션(<) 연결 디스크에 있는 파일 내용을 통째로 표준 입력으로 밀어 넣음: `php test.php < payload.txt` - 웹 환경 vs CLI 환경: php://stdin 과 php://input 의 결정적 차이 1. 웹(Apache, Nginx + PHP-FPM) 환경: 웹 서버 프로세스는 클라이언트와 소켓으로 통신합니다. 이 환경에서 프로세스의 표준 입력(stdin)은 비어 있거나 닫혀 있어, 웹 요청 도중 php://stdin 을 호출하면 아무 데이터가 들어오지 않거나 프로그램이 무한 대기(블로킹)에 빠질 수 있습니다. → 따라서 웹 환경에서는 HTTP POST 본문을 안전하게 추출하기 위해 별도의 전용 통로인 php://input 을 사용해야 합니다. 2. CLI 환경 및 CTF 소켓 챌린지: 반면, 서버 터미널에서 실행되는 배치 도구이거나, CTF에서 `nc target.com 1337` 로 특정 포트를 열어두고 사용자의 터미널 입력을 백엔드 PHP 프로세스로 파이프 연결해 둔 환경에서는 원격 사용자가 입력하는 데이터가 그대로 프로세스의 0번(stdin)으로 들어옵니다. 따라서 이러한 CLI/소켓 문제에서는 php://stdin 이 공격자의 직접적인 입력 진입점이 됩니다. ### (2) php://stdout 및 php://stderr (표준 출력 및 에러 래퍼) 운영체제의 1번 통로(STDOUT)와 2번 통로(STDERR)에 직접 데이터를 쓰는 쓰기 전용 스트림입니다. - 문법 구조 분해: 주소: php://stdout 또는 php://stderr 1. php:// : PHP 스트림 래퍼 선언 2. stdout / stderr : 표준 출력 또는 표준 에러 버퍼 채널 지정 - 실제 PHP 개발 코드 예시: ```php <?php // 일반 화면 출력 $out = fopen('php://stdout', 'w'); fwrite($out, "정상 처리 결과입니다.\n"); // 에러 로그 출력 (표준 에러로 분리) $err = fopen('php://stderr', 'w'); fwrite($err, "경고: 오류가 발생했습니다.\n"); ?> 활용 및 특징: 리눅스 CLI에서 일반 출력과 에러 출력을 분리해서 파일로 저장할 때 쓰입니다: php test.php 1> result.log 2> error.log 4. php://filter 의 구조와 동작 원리 php:// 뒤에 오는 여러 기능 중, 데이터 변환(필터링) 전용 파이프라인 기능을 켜겠다는 의미입니다. 쉽게 말해 "데이터를 있는 그대로 주지 말고, 중간에 필터(변환기)를 통과시켜서 가져와라" 하고 명령하는 모드입니다. 주소 구조 분해 (정수기 비유) 예시 주소: php://filter/read=convert.base64-encode/resource=/etc/passwd php:// PHP 내부 스트림을 사용하겠다는 선언 filter/ 그중에서도 '필터 변환 파이프라인' 기능을 켜겠다는 선언 (정수기 본체 역할) read= (또는 write=) 데이터를 읽을 때 필터를 걸지, 쓸 때 필터를 걸지 지정 (읽기 작업 시 read= 는 생략 가능) convert.base64-encode 어떤 필터를 통과시킬지 지정하는 부분 (정수기 필터 카트리지 종류) | (파이프 기호) 여러 필터를 연속으로 꽂아 넣을 때 사용하는 구분자 (예: read=string.rot13|convert.base64-encode) /resource=/etc/passwd 변환을 통과시킬 대상 원본 데이터 (정수기에 통과시킬 원수 / 원본 파일) 내부에서 일어나는 일 (스텝 바이 스텝) 1단계: 접두사 확인 PHP 엔진은 주소 맨 앞의 php:// 를 보고 "외부 인터넷으로 나가지 말고 내장된 PHP 스트림 처리기(C 언어 모듈)로 보내라"고 내부 라우팅합니다. 2단계: 원본 데이터 로드 resource= 뒤에 적힌 로컬 파일(/etc/passwd)을 디스크에서 읽어 PHP 프로세스의 메모리 버퍼에 담습니다. (헤더 같은 부가 정보 없이 순수한 원본 바이트만 담깁니다.) 3단계: 메모리 상에서 필터 적용 지정된 필터(convert.base64-encode 또는 convert.iconv...) 함수를 호출해, 메모리에 올려둔 바이트들을 차례대로 실시간 변환합니다. 4단계: 최종 결과 반환 변환이 끝난 바이트 덩어리를 호출한 함수(예: file_get_contents 또는 mPDF의 이미지 로더)에게 반환합니다. 5. CTF 필수 스트림 래퍼 7선 (1) php://filter (소스코드 유출 및 체이닝) CTF에서 가장 압도적인 빈도로 등장하는 래퍼입니다. 파일을 읽거나 쓸 때 중간에서 데이터를 실시간으로 변환(필터링)하는 소프트웨어 파이프 역할을 합니다. 문법 구조 분해: 기본 형태: php://filter/read=필터명/resource=대상파일 php:// : 스트림 래퍼 선언 filter/ : 필터 파이프라인 모드 활성화 read= : 읽기 시점 필터 적용 선언 (생략 가능) convert.base64-encode : 필터 이름 | : 연속 필터 체이닝 구분자 /resource= : 대상 원본 파일 경로 꿀팁: 필터 이름을 전부 외워야 하나요? 외울 필요 없습니다. 규칙이 매우 직관적입니다: 카테고리.기능 형태: convert.base64-encode, convert.base64-decode, string.rot13, string.toupper 등 CTF에서 손으로 직접 입력하는 필터의 90% 이상은 오직 convert.base64-encode 하나뿐입니다. 현재 서버에서 지원하는 전체 필터 목록을 확인하고 싶다면 아래 명령어로 즉시 조회 가능합니다: php -r "print_r(stream_get_filters());" 핵심 활용 1: PHP 소스코드 열람 (LFI) 일반적으로 include 'config.php';를 하면 서버에서 코드가 실행되어 화면에 빈 화면이나 HTML 결과만 보입니다. 이때 base64 인코딩 필터를 거치게 만들면 코드가 실행되지 않고 base64 텍스트로 치환되어 출력됩니다. 페이로드: php://filter/convert.base64-encode/resource=config.php 출력된 base64 문자열을 디코딩하면 원래 PHP 소스코드를 그대로 볼 수 있습니다. 핵심 활용 2: 기타 문자열 조작 필터 string.rot13 : 텍스트를 ROT13 암복호화 처리 string.toupper / string.tolower : 대소문자 변환 convert.iconv.* : 문자셋 인코딩 변환 (UTF-8, UTF-16 등) 핵심 활용 3: PHP Filter Chain (고급 RCE 기법) iconv 변환 과정에서 발생하는 바이트 변형 특성을 수십~수백 번 체이닝하여, 임의의 바이트(웹쉘 코드나 가짜 이미지 헤더)를 메모리 상에서 직접 조립해 내는 기법입니다. (2) php://input (POST 본문을 통한 코드 실행) 클라이언트가 보낸 HTTP 요청 본문(Raw POST Data)을 직접 스트림으로 읽어오는 래퍼입니다. 문법 구조 분해: 요청 주소: http://target.com/index.php?page=php://input 요청 본문: php:// : 스트림 래퍼 선언 input : 클라이언트가 전송한 HTTP 요청 바디(Body)의 원시 데이터를 읽는 입력 채널 HTTP 요청 본문 (Body) : 파일 대신 include() 함수로 전달되어 즉시 실행될 PHP 페이로드 요구 조건: php.ini 설정에서 allow_url_include = On 상태여야 함 동작 원리: include($_GET['page']); 와 같은 취약한 코드에 ?page=php://input 을 넣고, 요청 바디에 PHP 코드를 전송하면 서버가 해당 코드를 그대로 include하여 실행합니다. (3) data:// (인라인 코드 주입) 엄밀히는 php://가 아닌 별도의 스키마이지만, CTF에서 세트로 묶여서 자주 나오는 스트림 래퍼입니다. 외부 파일을 참조할 필요 없이, URL 자체에 직접 실행할 코드를 담아 보낼 수 있습니다. 문법 구조 분해 (평문 형태): 주소: data://text/plain, data:// (또는 data:) : 인라인 데이터 스키마 선언 (RFC 2397) text/plain : 데이터 MIME 타입 , (쉼표) : 메타데이터와 실제 데이터 본문의 시작을 가르는 필수 구분자 : 서버에서 실행될 실제 코드 문법 구조 분해 (Base64 인코딩 형태): 주소: data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg== data:// : Data URI 스키마 선언 text/plain : MIME 타입 ;base64 : 뒤따라오는 데이터가 Base64로 인코딩되어 있음을 알리는 선언 , (쉼표) : 구분자 PD9waHAg... : Base64 인코딩된 실제 코드 (공백, 따옴표, 괄호 등 WAF 필터링 우회용) 요구 조건: allow_url_include = On 및 allow_url_fopen = On (4) php://memory 및 php://temp (메모리 스트림) 디스크를 사용하지 않고 프로세스 메모리에 임시로 데이터를 읽고 쓸 수 있는 가상 스트림입니다. 문법 구조 분해: 기본 형태: php://memory 용량 제한 형태: php://temp/maxmemory:2097152 php://memory : 순수하게 RAM(메모리)에만 임시 입출력 스트림 생성 (디스크 I/O 없음) php://temp : 기본적으로 메모리에 쓰되, 용량이 초과되면 디스크의 임시 파일로 자동 전환 /maxmemory:2097152 : 메모리에 보관할 최대 크기를 바이트 단위로 지정 (기본값 2MB) CTF 활용: 디스크에 흔적을 남기지 않는 파일리스(Fileless) 공격이나 메모리 버퍼 조작 관련 문제에서 다뤄집니다. (5) phar:// (PHAR 아카이브 역직렬화 - RCE) PHP 아카이브(phar) 파일 내부의 메타데이터를 파싱할 때 발생하는 객체 역직렬화(Unserialize) 취약점을 이용하는 고난도 공격 기법입니다. 문법 구조 분해: 기본 형태: phar://디스크경로/아카이브내부파일 실제 페이로드 예시: phar:///var/www/uploads/evil.phar/test.txt phar:// : PHAR 스트림 래퍼 선언 /var/www/uploads/evil.phar : 서버 디스크에 업로드된 악성 phar 아카이브 파일의 실제 경로 /test.txt : phar 아카이브 내부에 존재하는 가상 파일명 (메타데이터 파싱 트리거용 더미 파일) 핵심 원리: include, require 같은 실행 함수뿐만 아니라, file_exists(), filesize(), md5_file(), is_dir() 같은 단순 파일 검사 함수에 phar:// 주소가 들어가기만 해도, PHP 엔진이 phar 파일의 메타데이터를 읽어들이면서 자동으로 unserialize()를 실행해 버립니다. 공격자가 악성 직렬화 객체(POP Chain)를 심어둔 phar 파일을 업로드한 뒤 파일 검사 함수에 경로를 찔러 넣으면 곧바로 RCE가 터집니다. (6) zip:// (압축 파일 내부 스크립트 실행 - 업로드 우회 + LFI) 서버가 .php 파일 업로드는 막고 .jpg 같은 이미지 파일만 허용할 때, 압축 파일로 우회하여 LFI로 실행시키는 테크닉입니다. 문법 구조 분해: 기본 형태: zip://압축파일절대경로#내부실행파일 URL 페이로드: ?page=zip:///var/www/uploads/avatar.jpg%23shell.php zip:// : ZIP 스트림 래퍼 선언 /var/www/uploads/avatar.jpg : 웹쉘(shell.php)을 zip으로 압축한 뒤 확장자만 .jpg로 변경하여 업로드한 파일 경로 (URL에서는 %23 으로 인코딩) : 압축 파일 경로와 압축 내부의 대상 파일을 구분하는 앵커 기호 shell.php : 압축 파일 안에 들어있는 실제 실행 대상 웹쉘 파일명 공격 흐름: 공격자가 로컬에서 shell.php 를 zip 압축하여 shell.zip 생성 확장자를 avatar.jpg 로 변경하여 서버의 프로필 이미지 업로드 기능으로 업로드 LFI 취약점이 있는 파라미터에 zip:///var/www/uploads/avatar.jpg%23shell.php 호출 PHP 엔진이 zip 파일의 압축을 풀고 내부의 shell.php 코드를 include하여 실행 (7) expect:// (즉시 쉘 명령어 실행) PHP의 PECL expect 확장 모듈이 활성화되어 있을 때 사용 가능한 래퍼로, 스트림 주소 자체가 곧바로 리눅스 쉘 명령어가 됩니다. 문법 구조 분해: 주소: expect://id 또는: expect://whoami expect:// : Expect 스트림 래퍼 선언 id : 운영체제에서 즉시 실행할 쉘 명령어 요구 조건: 기본 PHP 내장 기능이 아니며, php.ini에 extension=expect.so 가 활성화되어 있어야 함 (CTF 문제 출제자가 일부러 켜두는 경우가 많음) 공격 효과: include($_GET['page']); 에 ?page=expect://ls -la 를 넘기면 다른 우회 기법 없이 그 자리에서 쉘 명령어가 실행되고 결과가 화면에 출력됩니다. 6. 실전 공격 시나리오 요약 공격 상황 사용 래퍼 공격 목적 필요 환경 조건 include() 취약점 존재 php://filter 소스코드 유출 (.php 열람) 기본 설정에서 동작 가능 include() 취약점 존재 php://input RCE (POST 본문으로 코드 주입) allow_url_include = On include() 취약점 존재 data:// RCE (URL 내 인라인 코드 주입) allow_url_include = On 파일 검사 함수(file_exists 등) 존재 phar:// RCE (메타데이터 역직렬화 공격) 파일 업로드 가능 + POP Chain 존재 이미지 업로드만 허용 + LFI 존재 zip:// RCE (jpg 위장 압축 파일 내 웹쉘 실행) 로컬 파일 포함 취약점 include() 취약점 존재 expect:// RCE (직접 쉘 명령어 실행) PECL expect 확장 설치 필요 파일 파서/렌더러 악용 php://filter 체인 위조 헤더 주입 및 데이터 유출 특정 파서(mPDF 등) 존재 7. 방어 가이드 php.ini 설정 강화: allow_url_fopen = Off allow_url_include = Off 안전한 파일 호출: 사용자 입력값을 include, require, file_get_contents 등의 파일 함수에 직접 대입하지 말고, 화이트리스트 기반으로 지정된 파일만 열 수 있도록 통제해야 합니다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
php://. [CTF/웹해킹] PHP 스트림 래퍼(php://) 핵심 정리 및 실전 공격 기법 1. php:// 스트림 래퍼란? http://나 file://처럼 URL 스키마 형식을 띠고 있지만, 네트워크 통신을 하지 않고 PHP 엔진 내부 메모리에서 동작하는 가상의 입출력 통로(파이프)입니다. PHP의 파일 입출력 함수(include, require, file_get_contents, fopen 등)는 일반 로컬 파일 경로뿐만 아니라 스트림 주소도 동일하게 받아들입니다. 웹 해킹 및 CTF에서는 주로 LFI(Local File Inclusion) 취약점과 연계하여 소스코드 유출이나 RCE(원격 코드 실행)를 달성하는 핵심 도구로 쓰입니다. 왜 주소(URI) 형태로 만들었을까요? (인터페이스 통일)…
Открыть источник