Skip to content

Commit

Permalink
Show some serious content for more exploit attempts (#471)
Browse files Browse the repository at this point in the history
Requests for `/etc/passwd` will now trigger _zi egg_ even on non-404 pages (think `/?file=/etc/passwd` where `/` is a regular existing page) and also on PHP CGI CVE-2024-4577 😈
  • Loading branch information
spaze authored Jan 15, 2025
2 parents f4f6a43 + 61f4673 commit 5c4200a
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 29 deletions.
1 change: 1 addition & 0 deletions app/disallowed-calls.neon
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ parameters:
allowIn:
- src/Application/ServerEnv.php
- tests/Application/ServerEnvTest.phpt
- tests/EasterEgg/FourOhFourButFoundTest.phpt
disallowedClasses:
-
class:
Expand Down
29 changes: 15 additions & 14 deletions app/src/EasterEgg/FourOhFourButFound.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,48 @@

namespace MichalSpacekCz\EasterEgg;

use MichalSpacekCz\Application\ServerEnv;
use MichalSpacekCz\Http\Robots\Robots;
use MichalSpacekCz\Http\Robots\RobotsRule;
use Nette\Application\Responses\TextResponse;
use Nette\Application\UI\Presenter;
use Nette\Http\IRequest;

readonly class FourOhFourButFound
{

private const array TEMPLATES = [
'?%ad' => __DIR__ . '/templates/phpCve20244577.html',
'/etc/passwd' => __DIR__ . '/templates/etcPasswd.html',
];


public function __construct(
private IRequest $httpRequest,
private readonly Robots $robots,
private Robots $robots,
) {
}


public function sendItMaybe(Presenter $presenter): void
{
$url = $this->httpRequest->getUrl();
$url = ServerEnv::tryGetString('REQUEST_URI');
if ($url === null) {
return;
}
foreach (self::TEMPLATES as $request => $template) {
if (str_contains($url->getPath(), $request)) {
$this->sendIt($presenter, $template);
} else {
$query = $url->getQuery();
if ($query && str_contains(urldecode($query), $request)) {
$this->sendIt($presenter, $template);
}
if (str_contains($url, $request) || str_contains(urldecode($url), $request)) {
$this->robots->setHeader([RobotsRule::NoIndex, RobotsRule::NoFollow]);
$presenter->sendResponse(new TextResponse(file_get_contents($template)));
}
}
}


private function sendIt(Presenter $presenter, string $template): never
/**
* @return list<string>
*/
public function getRequestSubstrings(): array
{
$this->robots->setHeader([RobotsRule::NoIndex, RobotsRule::NoFollow]);
$presenter->sendResponse(new TextResponse(file_get_contents($template)));
return array_keys(self::TEMPLATES);
}

}
12 changes: 12 additions & 0 deletions app/src/EasterEgg/templates/phpCve20244577.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prase error</title>
</head>
<body>
<br>
<b>Parse error</b>: syntax error, unexpected token &quot;&lt;?php&quot;, expecting &quot;&lt;?gif87a&quot; or &quot;&lt;?gif89a&quot; in <b>php://input</b> on line <b>-1</b>°C<br>
</body>
</html>
6 changes: 5 additions & 1 deletion app/src/Form/FormValidators.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace MichalSpacekCz\Form;

use Contributte\Translation\Translator;
use MichalSpacekCz\EasterEgg\FourOhFourButFound;
use Nette\Forms\Controls\TextInput;
use Nette\Forms\Form;

Expand All @@ -12,13 +13,16 @@

public function __construct(
private Translator $translator,
private FourOhFourButFound $fourOhFourButFound,
) {
}


public function addValidateSlugRules(TextInput $input): void
{
$input->addRule(Form::Pattern, $this->translator->translate('messages.forms.validateSlugParamsError'), '[a-z0-9.,_-]+');
$input
->addRule(Form::Pattern, $this->translator->translate('messages.forms.validateSlugParamsError'), '[a-z0-9.,_-]+')
->addRule(Form::IsNotIn, $this->translator->translate('messages.forms.validateSlugParamsEasterEgg'), $this->fourOhFourButFound->getRequestSubstrings());
}

}
13 changes: 13 additions & 0 deletions app/src/Www/Presenters/BasePresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use MichalSpacekCz\Application\Locale\LocaleLinkGenerator;
use MichalSpacekCz\Css\CriticalCss;
use MichalSpacekCz\Css\CriticalCssFactory;
use MichalSpacekCz\EasterEgg\FourOhFourButFound;
use MichalSpacekCz\Form\ThemeFormFactory;
use MichalSpacekCz\Form\UiForm;
use MichalSpacekCz\User\Manager;
Expand Down Expand Up @@ -38,6 +39,8 @@ abstract class BasePresenter extends Presenter

private ComponentParameters $componentParameters;

private FourOhFourButFound $fourOhFourButFound;


/**
* @internal
Expand Down Expand Up @@ -93,6 +96,15 @@ public function injectComponentParameters(ComponentParameters $componentParamete
}


/**
* @internal
*/
public function injectFourOhFourButFound(FourOhFourButFound $fourOhFourButFound): void
{
$this->fourOhFourButFound = $fourOhFourButFound;
}


#[Override]
protected function startup(): void
{
Expand All @@ -101,6 +113,7 @@ protected function startup(): void
if ($this->authenticator->isForbidden() && $this->getRequest()?->getMethod() !== Request::FORWARD) {
$this->forward(':Www:Forbidden:', ['message' => 'messages.forbidden.spam']);
}
$this->fourOhFourButFound->sendItMaybe($this);
}


Expand Down
3 changes: 0 additions & 3 deletions app/src/Www/Presenters/ErrorPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use MichalSpacekCz\Application\Exceptions\ParameterNotStringException;
use MichalSpacekCz\Application\Locale\LocaleLink;
use MichalSpacekCz\Application\Locale\LocaleLinkGenerator;
use MichalSpacekCz\EasterEgg\FourOhFourButFound;
use MichalSpacekCz\ShouldNotHappenException;
use Nette\Application\BadRequestException;
use Nette\Application\UI\InvalidLinkException;
Expand All @@ -32,7 +31,6 @@ class ErrorPresenter extends BaseErrorPresenter

public function __construct(
private readonly LocaleLinkGenerator $localeLinkGenerator,
private readonly FourOhFourButFound $fourOhFourButFound,
private readonly AppRequest $appRequest,
private readonly Translator $translator,
) {
Expand Down Expand Up @@ -70,7 +68,6 @@ protected function getLocaleLinksGeneratorParams(): array

public function actionDefault(BadRequestException $exception): void
{
$this->fourOhFourButFound->sendItMaybe($this);
$code = (in_array($exception->getCode(), $this->statuses) ? $exception->getCode() : IResponse::S400_BadRequest);
$this->template->errorCode = $code;
$this->template->pageTitle = $this->translator->translate("messages.title.error{$code}");
Expand Down
1 change: 1 addition & 0 deletions app/src/lang/messages.cs_CZ.neon
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,4 @@ httpHeaders:
headerNotSent: "hlavička neposlána"
forms:
validateSlugParamsError: "%label musí odpovídat formátu %d"
validateSlugParamsEasterEgg: "%label nemůže být '%value', protože to spustí Easter egg"
1 change: 1 addition & 0 deletions app/src/lang/messages.en_US.neon
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,4 @@ httpHeaders:
headerNotSent: "header not sent"
forms:
validateSlugParamsError: "%label must match %d"
validateSlugParamsEasterEgg: "%label can't be '%value', because that's used by an Easter egg"
16 changes: 5 additions & 11 deletions app/tests/EasterEgg/FourOhFourButFoundTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ namespace MichalSpacekCz\EasterEgg;

use MichalSpacekCz\Test\Application\ApplicationPresenter;
use MichalSpacekCz\Test\Application\UiPresenterMock;
use MichalSpacekCz\Test\Http\Request;
use MichalSpacekCz\Test\TestCaseRunner;
use Nette\Application\Responses\TextResponse;
use Nette\Http\UrlScript;
use Tester\Assert;
use Tester\TestCase;

Expand All @@ -22,7 +20,6 @@ class FourOhFourButFoundTest extends TestCase

public function __construct(
private readonly FourOhFourButFound $fourOhFourButFound,
private readonly Request $request,
private readonly ApplicationPresenter $applicationPresenter,
) {
}
Expand All @@ -42,6 +39,8 @@ class FourOhFourButFoundTest extends TestCase
['/etc/foo?file=..%2F..%2F..%2Fetc%2Fpasswd', 'rick:x:1337:1337:Astley'],
['/etc/foo?file=../../../etc/passwd&foo/bar', 'rick:x:1337:1337:Astley'],
['/etc/foo?file=..%2F..%2F..%2Fetc%2Fpasswd&foo/bar', 'rick:x:1337:1337:Astley'],
['/?%adfoo', 'Parse error'],
['/?%ad=/etc/passwd&bar', 'Parse error'],
];
}

Expand All @@ -50,7 +49,7 @@ class FourOhFourButFoundTest extends TestCase
public function testSendItMaybe(string $url, ?string $contains): void
{
$presenter = new UiPresenterMock();
$this->request->setUrl(new UrlScript($url));
$_SERVER['REQUEST_URI'] = $url;
if ($contains === null) {
Assert::false($this->applicationPresenter->expectSendResponse(function () use ($presenter): void {
$this->fourOhFourButFound->sendItMaybe($presenter);
Expand All @@ -60,13 +59,8 @@ class FourOhFourButFoundTest extends TestCase
$this->fourOhFourButFound->sendItMaybe($presenter);
}));
$response = $presenter->getResponse();
if (!$response instanceof TextResponse) {
Assert::fail('Response is of a wrong type ' . get_debug_type($response));
} elseif (!is_string($response->getSource())) {
Assert::fail('Source should be a string but is ' . get_debug_type($response->getSource()));
} else {
Assert::contains($contains, $response->getSource());
}
assert($response instanceof TextResponse && is_string($response->getSource()));
Assert::contains($contains, $response->getSource());
}
}

Expand Down

0 comments on commit 5c4200a

Please sign in to comment.