In the previous article, we described the vulnerability discovered in the Yii2 Framework 2.0.35. In this piece, you’ll find out how to prevent it.  It’s a highly recommended read, especially for web developers who want to quickly check the rule settings and fix a detected vulnerability.

Yii is an object-oriented component framework that implements the MVC design pattern (learn more on Wiki). 

We used Yii2 Framework 2.0.35 as a demo configuration.How a seemingly safe Active Record call in the Yii2 framework can seriously compromise a web application?

        $name=Yii::$app->request->post('name');
        $password=Yii::$app->request->post('password');
        $user = User::findOne(['name'=>$name,'password'=>$password]);
        if(!$user){
                echo "no way!";
        }else{
                echo "Hello! $user->name";
        }

We created a sample MySQL table:

mysql> select * from user;
+-------+--------------------+
| name  | password       |
+-------+--------------------+
| admin | W0o0oO0_very_St0Ng |
+-------+--------------------+

Here is what happens during the Active Record call procedure: 

  • two POST parameters—name and password—are used to call the User model with the findOne method.

This exploitation technique is applicable to other Active Record methods, including those which use the POST parameters mentioned above as arguments.


Here is the description of the findOne method from Yii2 documentation:

findOne()Returns a single active record model instance by a primary key or an array of column values.

The database query will look like:

SELECT * FROM `user` where `name` = ‘$name’ and `password`= ‘$password’

Active Record calls, when used properly, are virtually unaffected by SQL injections

Let’s test the query with the correct password:

https://lh5.googleusercontent.com/hT_T1J4yiwcwRCAU4oYbXXNxfOn_5Hl2-Tx4NRPJFu-oo266g9vDPiSZHE0XBf8_u_Iw4dNk-ZjJDGrHnyfWMmAUOH_zojbozPhV2tri3ygeCPAKdCVIDctMtqjPuHK7ALLqvoW8

And with an incorrect password:

https://lh4.googleusercontent.com/ZtZZB50vvRXXid8LR4HxQzGPrd6bq5vRE2fMhUs_3VjnKp37qXjhbTZsnFa3s0q549wDdFKA4Nm3m7_ROeF_bsPhlPRf4PvTQUT86p6-1z7UscnbftTJyswuPqT4hvDdNUqHUQCV

Everything works as expected. So what is wrong with this code?

Maybe there is a classic comparison error similar to the strcmp function, when one of the comparison arguments is represented by an array and the function returns NULL leading to an incorrect comparison? Let’s check.

https://lh6.googleusercontent.com/rIYtDwUIWiPHVbIyTDOphttYmyBlSvL9qEanCk9P-QBzLWdy7AFK4o_AgqpR4NUpLzBakqtC_7JGYX321nw1HKKRRu3PkvtcGykRIkmC5OaidI7CynqWdSVX72K1ZKAEmEhrcfPY

No, this is not a comparison error.

For successful exploitation of the vulnerability, the following conditions are required:

  1. The web application configuration has the following component.
...
'components'          => [
        'request'    => [
            'parsers'   => [
                'application/json' => 'yii\web\JsonParser',
...

This configuration is often used for REST API applications. 

  1. The model doesn’t use rules or an incorrect rule configuration is created. Hence, the controller doesn’t validate the incoming parameters. 

This kind of rules is insufficient.

...   
     public function rules()
    {
        return [
            [['name', 'password'], 'required'],
        ];
    }
...

The existing controller doesn’t use validation according to the model rules and those rules are insufficient, which violates the application logic. 

Let’s go back to the configuration component ‘application/json’ => ‘yii\web\JsonParser’. This option enables transferring data to the application not only with Content-Type: application/x-www-form-urlencoded, but also in JSON.

Here are the example requests to the controller with correct and incorrect passwords:

https://lh5.googleusercontent.com/GyFlg10y9HBebdPZdZzQuaCYWWESvN5nQz5U04O_A7Kgord0LtYqmt4Ao4cfDTmZ-8cCTAqXDlVJ_pETKP6PK84WNJGo1pVOipeKt6GTfzK3J01FmEMaDkK_muY6en7nKps8MKvH
https://lh4.googleusercontent.com/ZhxSYr-U1P8ND0dyEzOktoOxwkXyqb2VZ7shy3mLh1A4l_q6hzUn5xTIGOKPfu6TLmoE5zW6HA11_9q344iPcn0HrX3-3mH-BwqeGEPyNOau3ikXcyfVAMajKEyCQyA3AdgjnExE

Everything works as it should—but only at first glance.

Active Record sends a database query with the parameters presented in the same format in which the controller received them. The JSON format can pass the following parameter types:

a string.a number.an object (JSON object)an array.a boolean.null.

The system expects a string parameter. If you pass the parameter of another type, this will violate the controller logic.

Let’s pass the password parameter presented as boolean.

https://lh3.googleusercontent.com/PzPE9tQ-IGreKHRfodsedgQrfLQvetNv0d2KS_cZlGn2LHHtkS3bgTRANdGFm87JaB5OAVOcNkl4CDpkfh3WLk9ROK7uhB5C81bu8MXBCBDYhOOjHu2Zz6Iejx2_Dl3y-DGf3X96

Without knowing the correct password, we were able to fulfill the condition indicated in the controller. How did this happen?

As mentioned above, Active Record provides the database with the same parameter types it receives. We sent a password value of boolean type and the request turned out to be successful (thanks to MySQL, as well) satisfying the condition indicated in the controller. We got the access.

mysql> select * from user where `name`='admin' and `password` = false;
+-------+--------------------+
| name  | password           |
+-------+--------------------+
| admin | W0o0oO0_very_St0Ng |
+-------+--------------------+
1 row in set (0.00 sec)

Instead of a Conclusion

It is worth noting that the Yii2 Framework 2.0.35 itself is not vulnerable, but incorrectly defined rules can lead to data compromise. 

To avoid such a compromise, we recommend to:

  1. Use strict rules() in the model.
  2. Apply mandatory validation of parameters before sending them to Active Record methods

To make sure that all vulnerabilities are fixed, we encourage you to test your system with the Wallarm WAF scanner. Following this analysis, you’ll be certain that no confidential data could leak from your web applications, microservices, or APIs.

Write us to try Wallarm WAF: sales@wallarm.com