From 30ae198b898a987e0d934a0e049cc6811f500475 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Tue, 15 Mar 2022 23:42:53 +0100 Subject: CVE-2022-27257: LFI in Redbasic theme. Limit valid chars in schema names, and discard attempts at loading schemas with invalid names. This prevents a local file inclusion vulnerability where an unauthenticated attacker can include arbitrary php files readable by the server process and potentially obtain remote code execution. Valid schema names may consist of ascii letters, numbers, hyphens and underscores. Should be good enough for most cases, I think. Fixes https://framagit.org/hubzilla/core/-/issues/1665 --- view/theme/redbasic/php/style.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/view/theme/redbasic/php/style.php b/view/theme/redbasic/php/style.php index 9cb835ec6..8485c00bc 100644 --- a/view/theme/redbasic/php/style.php +++ b/view/theme/redbasic/php/style.php @@ -36,9 +36,8 @@ if(! App::$install) { // not --- like the mobile theme does instead. // Allow layouts to over-ride the schema - -if($_REQUEST['schema']) { - $schema = $_REQUEST['schema']; +if (isset($_REQUEST['schema']) && preg_match('/^[\w_-]+$/i', $_REQUEST['schema'])) { + $schema = $_REQUEST['schema']; } if (($schema) && ($schema != '---')) { -- cgit v1.2.3 From 8c19ab8f9f47a522ad2b929495f3b5821efd2f34 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Sun, 20 Mar 2022 12:57:42 +0100 Subject: Add helper to escape URLs. The escaping makes the URL safe for display and for use in HTML element attributes (such as href="..." etc), but does not guarantee that the URL itself is valid after conversion. This should be good enough for mitigating XSS issues caused by injecting html or javascript into a URL. Also probably good enough for _most_ normal URLs, but there may be devils hidden in the details somewhere. --- include/text.php | 12 ++++++++++++ tests/unit/AntiXSSTest.php | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/text.php b/include/text.php index 9a2ca1af4..0c806d009 100644 --- a/include/text.php +++ b/include/text.php @@ -114,6 +114,18 @@ function escape_tags($string) { return (htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); } +/** + * Escape URL's so they're safe for use in HTML and in HTML element attributes. + */ +function escape_url($input) { + if (empty($input)) { + return EMPTY_STR; + } + + // This is a bit crude but seems to do the trick for now. It makes no + // guarantees that the URL is valid for use after escaping. + return htmlspecialchars($input, ENT_HTML5 | ENT_QUOTES); +} function z_input_filter($s,$type = 'text/bbcode',$allow_code = false) { diff --git a/tests/unit/AntiXSSTest.php b/tests/unit/AntiXSSTest.php index b45042a1e..09642726f 100644 --- a/tests/unit/AntiXSSTest.php +++ b/tests/unit/AntiXSSTest.php @@ -24,6 +24,26 @@ class AntiXSSTest extends TestCase { $this->assertEquals("<submit type="button" onclick="alert('failed!');" />", $escapedString); } + /** + * @dataProvider urlTestProvider + */ + public function testEscapeURL($url, $expected) : void { + $this->assertEquals($expected, escape_url($url)); + } + + public function urlTestProvider() : array { + return [ + [ + "https://example.com/settings/calendar/?f=&rpath=https://example.com/cdav/calendar'>", + "https://example.com/settings/calendar/?f=&rpath=https://example.com/cdav/calendar'><script>alert('boom')</script>" + ], + [ + "settings/calendar/?f=&rpath=https://example.com'+accesskey=x+onclick=alert(/boom/);a='", + "settings/calendar/?f=&rpath=https://example.com'+accesskey=x+onclick=alert(/boom/);a='" + ], + ]; + } + /** *xmlify and unxmlify */ -- cgit v1.2.3 From d35609f33a3679043b8fa4dc3ad2570b425c06f5 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Sun, 20 Mar 2022 13:10:56 +0100 Subject: CVE-2022-27258: XSS via rpath query param. Escape URLs provided by the rpath query param in settings modules. This prevents a possible Cross-Site scripting vulnerability, where an attacker could inject web scripts and html into the settings form via the rpath query parameter, and have a user execute the script by tricking them to clicking a link. Fixes part of https://framagit.org/hubzilla/core/-/issues/1666 --- Zotlabs/Module/Settings/Calendar.php | 4 ++-- Zotlabs/Module/Settings/Channel_home.php | 8 ++++---- Zotlabs/Module/Settings/Connections.php | 4 ++-- Zotlabs/Module/Settings/Directory.php | 4 ++-- Zotlabs/Module/Settings/Editor.php | 4 ++-- Zotlabs/Module/Settings/Events.php | 4 ++-- Zotlabs/Module/Settings/Manage.php | 4 ++-- Zotlabs/Module/Settings/Network.php | 2 +- Zotlabs/Module/Settings/Photos.php | 4 ++-- Zotlabs/Module/Settings/Profiles.php | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Zotlabs/Module/Settings/Calendar.php b/Zotlabs/Module/Settings/Calendar.php index ab85eb450..e0f9b55d3 100644 --- a/Zotlabs/Module/Settings/Calendar.php +++ b/Zotlabs/Module/Settings/Calendar.php @@ -34,14 +34,14 @@ class Calendar { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Calendar Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Channel_home.php b/Zotlabs/Module/Settings/Channel_home.php index e8faa7fb2..3948563dc 100644 --- a/Zotlabs/Module/Settings/Channel_home.php +++ b/Zotlabs/Module/Settings/Channel_home.php @@ -13,7 +13,7 @@ class Channel_home { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); @@ -25,7 +25,7 @@ class Channel_home { $channel_menu = ((x($_POST['channel_menu'])) ? htmlspecialchars_decode(trim($_POST['channel_menu']),ENT_QUOTES) : ''); set_pconfig(local_channel(),'system','channel_menu',$channel_menu); - + Libsync::build_sync_packet(); if($_POST['rpath']) @@ -82,7 +82,7 @@ class Channel_home { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Channel Home Settings'), @@ -90,7 +90,7 @@ class Channel_home { '$extra_settings_html' => $extra_settings_html, '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Connections.php b/Zotlabs/Module/Settings/Connections.php index 4369deb27..4193ba11d 100644 --- a/Zotlabs/Module/Settings/Connections.php +++ b/Zotlabs/Module/Settings/Connections.php @@ -34,14 +34,14 @@ class Connections { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Connections Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Directory.php b/Zotlabs/Module/Settings/Directory.php index d1dd0677e..b36b0c318 100644 --- a/Zotlabs/Module/Settings/Directory.php +++ b/Zotlabs/Module/Settings/Directory.php @@ -34,14 +34,14 @@ class Directory { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Directory Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Editor.php b/Zotlabs/Module/Settings/Editor.php index cf6dd2807..c14883262 100644 --- a/Zotlabs/Module/Settings/Editor.php +++ b/Zotlabs/Module/Settings/Editor.php @@ -34,14 +34,14 @@ class Editor { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Editor Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Events.php b/Zotlabs/Module/Settings/Events.php index ab393c932..5b4ba460b 100644 --- a/Zotlabs/Module/Settings/Events.php +++ b/Zotlabs/Module/Settings/Events.php @@ -34,14 +34,14 @@ class Events { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Events Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Manage.php b/Zotlabs/Module/Settings/Manage.php index cbc494cf8..74e05b3f2 100644 --- a/Zotlabs/Module/Settings/Manage.php +++ b/Zotlabs/Module/Settings/Manage.php @@ -35,14 +35,14 @@ class Manage { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Channel Manager Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Network.php b/Zotlabs/Module/Settings/Network.php index 9f5bdb2e5..89b562d90 100644 --- a/Zotlabs/Module/Settings/Network.php +++ b/Zotlabs/Module/Settings/Network.php @@ -53,7 +53,7 @@ class Network { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Stream Settings'), diff --git a/Zotlabs/Module/Settings/Photos.php b/Zotlabs/Module/Settings/Photos.php index 8195d660b..bf7481e92 100644 --- a/Zotlabs/Module/Settings/Photos.php +++ b/Zotlabs/Module/Settings/Photos.php @@ -34,14 +34,14 @@ class Photos { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Photos Settings'), '$features' => process_module_features_get(local_channel(), $features), '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Profiles.php b/Zotlabs/Module/Settings/Profiles.php index a1a1b8d96..f7c7a0029 100644 --- a/Zotlabs/Module/Settings/Profiles.php +++ b/Zotlabs/Module/Settings/Profiles.php @@ -43,7 +43,7 @@ class Profiles { $tpl = get_markup_template("settings_module.tpl"); $o .= replace_macros($tpl, array( - '$rpath' => $rpath, + '$rpath' => escape_url($rpath), '$action_url' => 'settings/' . $module, '$form_security_token' => get_form_security_token('settings_' . $module), '$title' => t('Profiles Settings'), -- cgit v1.2.3 From b02f6a1dae3e3fae4af4b24e65256cdf653b2515 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Sun, 20 Mar 2022 14:35:25 +0100 Subject: Add function is_local_url() to check if url is local. --- include/network.php | 8 ++++++++ tests/unit/includes/NetworkTest.php | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/unit/includes/NetworkTest.php diff --git a/include/network.php b/include/network.php index fa408e602..a236a6f8e 100644 --- a/include/network.php +++ b/include/network.php @@ -559,6 +559,14 @@ function z_dns_check($h,$check_mx = 0) { return((@dns_get_record($h,$opts) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); } +function is_local_url($url) { + if (str_starts_with($url, z_root()) || str_starts_with($url, '/')) { + return true; + } + + return false; +} + /** * @brief Validates a given URL. * diff --git a/tests/unit/includes/NetworkTest.php b/tests/unit/includes/NetworkTest.php new file mode 100644 index 000000000..0b9b42e00 --- /dev/null +++ b/tests/unit/includes/NetworkTest.php @@ -0,0 +1,33 @@ +assertEquals($expected, is_local_url($url)); + } + + public function localUrlTestProvider() : array { + return [ + [ '/some/path', true ], + [ 'https://mytest.org/some/path', true ], + [ 'https://other.site/some/path', false ], + ]; + } +} + -- cgit v1.2.3 From b3ca31bce7ed0dd5777458005718ba96985cbdc2 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Sun, 20 Mar 2022 14:37:19 +0100 Subject: CVE-2022-27256: Open redirect via rpath query param. Don't follow urls to external sites when submitting forms from the settings modules. This mitigates an Open Redirect vulnerability where an attacker could trick a user to go to an attacker controlled destination. Fixes part of https://framagit.org/hubzilla/core/-/issues/1666 --- Zotlabs/Module/Settings/Calendar.php | 6 +++--- Zotlabs/Module/Settings/Channel_home.php | 2 +- Zotlabs/Module/Settings/Connections.php | 6 +++--- Zotlabs/Module/Settings/Directory.php | 6 +++--- Zotlabs/Module/Settings/Editor.php | 6 +++--- Zotlabs/Module/Settings/Events.php | 6 +++--- Zotlabs/Module/Settings/Manage.php | 6 +++--- Zotlabs/Module/Settings/Network.php | 6 +++--- Zotlabs/Module/Settings/Photos.php | 8 ++++---- Zotlabs/Module/Settings/Profiles.php | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Zotlabs/Module/Settings/Calendar.php b/Zotlabs/Module/Settings/Calendar.php index e0f9b55d3..65240c635 100644 --- a/Zotlabs/Module/Settings/Calendar.php +++ b/Zotlabs/Module/Settings/Calendar.php @@ -11,14 +11,14 @@ class Calendar { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Channel_home.php b/Zotlabs/Module/Settings/Channel_home.php index 3948563dc..470dbe4c3 100644 --- a/Zotlabs/Module/Settings/Channel_home.php +++ b/Zotlabs/Module/Settings/Channel_home.php @@ -28,7 +28,7 @@ class Channel_home { Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Connections.php b/Zotlabs/Module/Settings/Connections.php index 4193ba11d..52a95a3d1 100644 --- a/Zotlabs/Module/Settings/Connections.php +++ b/Zotlabs/Module/Settings/Connections.php @@ -11,14 +11,14 @@ class Connections { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Directory.php b/Zotlabs/Module/Settings/Directory.php index b36b0c318..09ea61f60 100644 --- a/Zotlabs/Module/Settings/Directory.php +++ b/Zotlabs/Module/Settings/Directory.php @@ -11,14 +11,14 @@ class Directory { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Editor.php b/Zotlabs/Module/Settings/Editor.php index c14883262..85c3e69ae 100644 --- a/Zotlabs/Module/Settings/Editor.php +++ b/Zotlabs/Module/Settings/Editor.php @@ -11,14 +11,14 @@ class Editor { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Events.php b/Zotlabs/Module/Settings/Events.php index 5b4ba460b..0a0e3516c 100644 --- a/Zotlabs/Module/Settings/Events.php +++ b/Zotlabs/Module/Settings/Events.php @@ -11,14 +11,14 @@ class Events { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Manage.php b/Zotlabs/Module/Settings/Manage.php index 74e05b3f2..6fb57eafb 100644 --- a/Zotlabs/Module/Settings/Manage.php +++ b/Zotlabs/Module/Settings/Manage.php @@ -12,14 +12,14 @@ class Manage { $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Network.php b/Zotlabs/Module/Settings/Network.php index 89b562d90..eae963a25 100644 --- a/Zotlabs/Module/Settings/Network.php +++ b/Zotlabs/Module/Settings/Network.php @@ -21,10 +21,10 @@ class Network { $network_divmore_height = 50; set_pconfig(local_channel(),'system','network_divmore_height', $network_divmore_height); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; @@ -61,7 +61,7 @@ class Network { '$extra_settings_html' => $extra_settings_html, '$submit' => t('Submit') )); - + return $o; } diff --git a/Zotlabs/Module/Settings/Photos.php b/Zotlabs/Module/Settings/Photos.php index bf7481e92..f68c8847b 100644 --- a/Zotlabs/Module/Settings/Photos.php +++ b/Zotlabs/Module/Settings/Photos.php @@ -7,18 +7,18 @@ use Zotlabs\Lib\Libsync; class Photos { function post() { - + $module = substr(strrchr(strtolower(static::class), '\\'), 1); check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module); - + $features = get_module_features($module); process_module_features_post(local_channel(), $features, $_POST); - + Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; diff --git a/Zotlabs/Module/Settings/Profiles.php b/Zotlabs/Module/Settings/Profiles.php index f7c7a0029..0ff2dfb6d 100644 --- a/Zotlabs/Module/Settings/Profiles.php +++ b/Zotlabs/Module/Settings/Profiles.php @@ -23,7 +23,7 @@ class Profiles { Libsync::build_sync_packet(); - if($_POST['rpath']) + if(isset($_POST['rpath']) && is_local_url($_POST['rpath'])) goaway($_POST['rpath']); return; -- cgit v1.2.3