From 707fe698f500354dcb2041e6c0702e0289c0ef65 Mon Sep 17 00:00:00 2001 From: ildyria Date: Mon, 12 Jun 2023 21:20:01 +0200 Subject: [PATCH 1/4] add support for 418 for dubious queries --- app/Exceptions/Handler.php | 1 + app/Exceptions/HttpHoneyPotException.php | 25 ++++++ app/Http/Controllers/HoneyPotController.php | 84 +++++++++++++++++++++ config/app.php | 1 - config/honeypot.php | 64 ++++++++++++++++ routes/web.php | 3 + tests/Feature/HoneyPotTest.php | 50 ++++++++++++ 7 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 app/Exceptions/HttpHoneyPotException.php create mode 100644 app/Http/Controllers/HoneyPotController.php create mode 100644 config/honeypot.php create mode 100644 tests/Feature/HoneyPotTest.php diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index e5aa5c5deec..1468a23b307 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -78,6 +78,7 @@ class Handler extends ExceptionHandler * @var array */ public const EXCEPTION2SEVERITY = [ + HttpHoneyPotException::class => SeverityType::NOTICE, // In theory this is a 404, but because it touches honey we don't really care. PhotoResyncedException::class => SeverityType::WARNING, PhotoSkippedException::class => SeverityType::WARNING, ImportCancelledException::class => SeverityType::NOTICE, diff --git a/app/Exceptions/HttpHoneyPotException.php b/app/Exceptions/HttpHoneyPotException.php new file mode 100644 index 00000000000..6f64931086f --- /dev/null +++ b/app/Exceptions/HttpHoneyPotException.php @@ -0,0 +1,25 @@ + $honeypot_paths_array */ + $honeypot_paths_array = config('honeypot.paths', []); + + /** @var array $honeypot_xpaths_array_prefix */ + $honeypot_xpaths_array_prefix = config('honeypot.xpaths.prefix', []); + + /** @var array $honeypot_xpaths_array_suffix */ + $honeypot_xpaths_array_suffix = config('honeypot.xpaths.suffix', []); + + foreach ($honeypot_xpaths_array_prefix as $prefix) { + foreach ($honeypot_xpaths_array_suffix as $suffix) { + $honeypot_paths_array[] = $prefix . '.' . $suffix; + } + } + + // Check if Honey is available + if (config('honeypot.enabled', true) !== true) { + $this->throwNotFound($path); + } + + // Turn the path array into a regex pattern. + // We escape . and / to avoid confusions with other regex characters + $honeypot_paths = '/^(' . str_replace(['.', '/'], ['\.', '\/'], implode('|', $honeypot_paths_array)) . ')/i'; + + // If the user tries to access a honeypot path, fail with the teapot code. + if (preg_match($honeypot_paths, $path) !== 0) { + $this->throwTeaPot($path); + } + + // Otherwise just display our regular 404 page. + $this->throwNotFound($path); + } + + /** + * using abort(404) does not give the info which path was called. + * This could be very useful when debugging. + * By throwing a proper exception we preserve this info. + * Not that this will generate a log line of type ERROR. + * + * @param string $path called + * + * @return never + * + * @throws NotFoundHttpException + */ + public function throwNotFound(string $path) + { + throw new NotFoundHttpException(sprintf('The route %s could not be found.', $path)); + } + + /** + * Similar to abort(404), abort(418) does not give info. + * It is more interesting to raise a proper exception. + * By having a proper exception we are also able to decrease the severity. + * + * @param string $path called + * + * @return never + * + * @throws HttpHoneyPotException + */ + public function throwTeaPot(string $path) + { + throw new HttpHoneyPotException($path); + } +} \ No newline at end of file diff --git a/config/app.php b/config/app.php index 5477dfea729..a9ad42350de 100644 --- a/config/app.php +++ b/config/app.php @@ -30,7 +30,6 @@ 'env' => env('APP_ENV', 'production'), - /* |-------------------------------------------------------------------------- | Application Debug Mode diff --git a/config/honeypot.php b/config/honeypot.php new file mode 100644 index 00000000000..a3b0dab0ba4 --- /dev/null +++ b/config/honeypot.php @@ -0,0 +1,64 @@ + true, + + /** + * Honey. + * + * Set of possible path. + * Those will be concatenated into a regex. + */ + 'paths' => [ + '.env', + '.git/config', + '.git/HEAD', + '.well-known/security.txt', + '.well-known/traffic-advice', + + 'readme.txt', + 'pools', + 'pools/default/buckets', + '__Additional', + + 'wp-login.php', + 'Portal/Portal.mwsl', + 'Portal0000.htm', + + 'aQQY', + 'nmaplowercheck1686252089', + 'sdk', + ], + + /** + * Because of all the combinations, it is more interesting to do a cross product. + */ + 'xpaths' => [ + 'prefix' => [ + 'admin', + 'base', + 'default', + 'home', + 'indice', + 'inicio', + 'localstart', + 'main', + 'menu', + 'start', + ], + + 'suffix' => [ + 'asp', + 'aspx', + 'cgi', + 'html', + 'jhtml', + 'php', + 'pl', + 'shtml', + ], + ], +]; \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 1ffac7437f2..b53327184e2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -40,3 +40,6 @@ Route::get('/view', [IndexController::class, 'view'])->name('view')->middleware(['redirect-legacy-id']); Route::get('/frame', [IndexController::class, 'frame'])->name('frame')->middleware(['migration:complete']); + +// This route must be defined last because it is a catch all. +Route::match(['get', 'post'], '{path}', HoneyPotController::class)->where('path', '.*'); diff --git a/tests/Feature/HoneyPotTest.php b/tests/Feature/HoneyPotTest.php new file mode 100644 index 00000000000..5866f039553 --- /dev/null +++ b/tests/Feature/HoneyPotTest.php @@ -0,0 +1,50 @@ +get($path); + $this->assertStatus($response, Response::HTTP_I_AM_A_TEAPOT); + $response = $this->post($path); + $this->assertStatus($response, Response::HTTP_I_AM_A_TEAPOT); + } + + // We check one of the version from the xpaths cross product + $response = $this->get('admin.asp'); + $this->assertStatus($response, Response::HTTP_I_AM_A_TEAPOT); + } + + public function testRoutesWithoutHoney(): void + { + $response = $this->get('/something'); + $this->assertStatus($response, Response::HTTP_NOT_FOUND); + } + + public function testDisabled(): void + { + config(['honeypot.enabled' => false]); + foreach (config('honeypot.paths') as $path) { + $response = $this->get($path); + $this->assertStatus($response, Response::HTTP_NOT_FOUND); + $response = $this->post($path); + $this->assertStatus($response, Response::HTTP_NOT_FOUND); + } + } +} \ No newline at end of file From 17c7a48341e8f2d8744a34bab3a4bf4f4c6ebb15 Mon Sep 17 00:00:00 2001 From: ildyria Date: Mon, 12 Jun 2023 21:27:06 +0200 Subject: [PATCH 2/4] typos --- app/Http/Controllers/HoneyPotController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/HoneyPotController.php b/app/Http/Controllers/HoneyPotController.php index 64829a2f8bd..5dcf96c56aa 100644 --- a/app/Http/Controllers/HoneyPotController.php +++ b/app/Http/Controllers/HoneyPotController.php @@ -53,7 +53,7 @@ public function __invoke(string $path = ''): void * using abort(404) does not give the info which path was called. * This could be very useful when debugging. * By throwing a proper exception we preserve this info. - * Not that this will generate a log line of type ERROR. + * This will generate a log line of type ERROR. * * @param string $path called * @@ -69,7 +69,7 @@ public function throwNotFound(string $path) /** * Similar to abort(404), abort(418) does not give info. * It is more interesting to raise a proper exception. - * By having a proper exception we are also able to decrease the severity. + * By having a proper exception we are also able to decrease the severity to NOTICE. * * @param string $path called * From ebd6fef99c53b5b3fb395b95db51fbce1d10875d Mon Sep 17 00:00:00 2001 From: ildyria Date: Mon, 12 Jun 2023 21:29:30 +0200 Subject: [PATCH 3/4] do not modify this file From abf5c42429edc243676d5dee40a7f658f0298464 Mon Sep 17 00:00:00 2001 From: ildyria Date: Mon, 19 Jun 2023 21:58:45 +0200 Subject: [PATCH 4/4] skip if honeypot is not enabled --- app/Http/Controllers/HoneyPotController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/HoneyPotController.php b/app/Http/Controllers/HoneyPotController.php index 5dcf96c56aa..ca41bcfcb21 100644 --- a/app/Http/Controllers/HoneyPotController.php +++ b/app/Http/Controllers/HoneyPotController.php @@ -16,6 +16,11 @@ class HoneyPotController extends Controller { public function __invoke(string $path = ''): void { + // Check if Honey is available + if (config('honeypot.enabled', true) !== true) { + $this->throwNotFound($path); + } + /** @var array $honeypot_paths_array */ $honeypot_paths_array = config('honeypot.paths', []); @@ -31,11 +36,6 @@ public function __invoke(string $path = ''): void } } - // Check if Honey is available - if (config('honeypot.enabled', true) !== true) { - $this->throwNotFound($path); - } - // Turn the path array into a regex pattern. // We escape . and / to avoid confusions with other regex characters $honeypot_paths = '/^(' . str_replace(['.', '/'], ['\.', '\/'], implode('|', $honeypot_paths_array)) . ')/i'; @@ -81,4 +81,4 @@ public function throwTeaPot(string $path) { throw new HttpHoneyPotException($path); } -} \ No newline at end of file +}