diff --git a/source/rootshell.src b/source/rootshell.src new file mode 100644 index 0000000..e2ff139 --- /dev/null +++ b/source/rootshell.src @@ -0,0 +1,538 @@ +if params.len < 1 or params[0] == "-h" or params[0] == "--help" then + current_program = get_shell.host_computer.File(program_path) + exit "Usage: " + current_program.name + " " +end if + +mx = include_lib("/lib/metaxploit.so") +if not mx then mx = include_lib(current_path + "/metaxploit.so") +if not mx then exit("Unable to load metaxploit.so") + +crypto = include_lib("/lib/crypto.so") +if not crypto then crypto = include_lib(current_path + "/crypto.so") + +local_shell = get_shell +local_cp = local_shell.host_computer +ip = params[0] +KNOWN_PASS = "Root1234" + +rank_of = function(user) + if user == "root" then return 3 + if user == "guest" then return 1 + if user == "" then return 0 + return 2 +end function + +extract_value = function(line) + start = line.indexOf("") + finish = line.indexOf("") + if start != -1 and finish != -1 and finish > start then + return line[start + 3 : finish] + end if + + before_dot = line.split("\.")[0] + words = before_dot.split(" ") + value = "" + for word in words + if word != "" then value = word + end for + return value +end function + +object_user = function(comp) + if not comp then return "guest" + + root_dir = comp.File("/root") + if root_dir and root_dir.has_permission("w") then return "root" + + passwd = comp.File("/etc/passwd") + if passwd and passwd.has_permission("w") then return "root" + + home = comp.File("/home") + if not home then return "guest" + folders = home.get_folders + if not folders then return "guest" + for folder in folders + if folder.name == "guest" then continue + if folder.has_permission("w") then return folder.name + end for + return "guest" +end function + +has_item = function(list, value) + for item in list + if item == value then return 1 + end for + return 0 +end function + +lib_name_of = function(name) + n = name.lower + if n.indexOf("ssh") != -1 then return "ssh" + if n.indexOf("ftp") != -1 then return "ftp" + return "" +end function + +state = { + "best_shell": null, + "best_user": "guest", + "best_computer": null, + "passwd_file": null, + "password_changed": 0, +} + +logs = [] +last_clear = [0] +chosen_port = 0 + +redraw = function() + gap = 0.11 + elapsed = time - last_clear[0] + if elapsed < gap then + delay = gap - elapsed + if delay < 0.01 then delay = 0.01 + wait delay + end if + clear_screen + last_clear[0] = time + print ip + " " + chosen_port + for line in logs + print line + end for +end function + +note = function(line) + logs.push(line) + redraw +end function + +drop_root = function(sh) + if not sh then return + if object_user(sh.host_computer) != "root" then return + note("root shell") + sh.start_terminal + exit +end function + +take_shell = function(sh) + if typeof(sh) != "shell" then return + user = object_user(sh.host_computer) + if state.best_shell == null or rank_of(user) > rank_of(state.best_user) then + state.best_shell = sh + state.best_user = user + note("shell user:" + user) + end if + drop_root(sh) +end function + +take_computer = function(comp) + if typeof(comp) != "computer" then return + user = object_user(comp) + if state.best_computer == null or rank_of(user) > rank_of(object_user(state.best_computer)) then + state.best_computer = comp + note("computer user:" + user) + end if +end function + +take_file = function(f) + if typeof(f) != "file" then return + if f.is_folder then + files = f.get_files + if not files then return + for inner in files + if inner.name == "passwd" then take_file(inner) + end for + return + end if + if f.name != "passwd" then return + if not f.has_permission("r") then return + state.passwd_file = f + note("file " + f.path) +end function + +consider = function(of) + t = typeof(of) + if t == "shell" then + take_shell(of) + else if t == "computer" then + take_computer(of) + else if t == "file" then + take_file(of) + else if t == "number" then + if of == 1 then state.password_changed = 1 + end if +end function + +access_computer = function() + if state.best_shell then + comp = state.best_shell.host_computer + if comp then return comp + end if + return state.best_computer +end function + +read_passwd_text = function(comp) + if state.passwd_file then + content = state.passwd_file.get_content + if content then return content + end if + if not comp then return "" + passwd = comp.File("/etc/passwd") + if not passwd then return "" + if not passwd.has_permission("r") then return "" + content = passwd.get_content + if not content then return "" + return content +end function + +parse_passwd = function(content) + accounts = [] + if not content then return accounts + for line in content.split(char(10)) + if line == "" then continue + parts = line.split(":") + if parts.len < 2 then continue + user = parts[0] + hash = parts[1] + if user == "" or hash == "" then continue + accounts.push({"user": user, "hash": hash}) + end for + ordered = [] + for acc in accounts + if acc.user == "root" then ordered.push acc + end for + for acc in accounts + if acc.user == "root" then continue + if acc.user == "guest" then continue + ordered.push acc + end for + for acc in accounts + if acc.user == "guest" then ordered.push acc + end for + return ordered +end function + +scan_rows = [] +lan_ips = [] +ssh_ports = [] + +push_lan = function(lan) + if lan == "" then return + if has_item(lan_ips, lan) then return + lan_ips.push lan +end function + +push_ssh = function(port_num, libname) + kind = lib_name_of(libname) + if kind != "ssh" and port_num != 22 then return + if has_item(ssh_ports, port_num) then return + ssh_ports.push port_num +end function + +add_scan_row = function(port_num, state_name, libname, version, lan) + scan_rows.push({ + "port": port_num, + "state": state_name, + "lib": libname, + "version": version, + "lan": lan, + }) + push_lan lan + push_ssh port_num, libname +end function + +print "scanning " + ip + +router_sess = mx.net_use(ip) +if router_sess then + router_lib = router_sess.dump_lib + if router_lib then + add_scan_row 0, "open", router_lib.lib_name, router_lib.version, "" + end if +end if + +router = get_router(ip) +if router then + used = router.used_ports + if used then + for p in used + if p.is_closed == 0 then st = "open" else st = "closed" + lan = p.get_lan_ip + sess = mx.net_use(ip, p.port_number) + if not sess then + add_scan_row p.port_number, st, "N/A", "N/A", lan + continue + end if + lib = sess.dump_lib + if not lib then + add_scan_row p.port_number, st, "N/A", "N/A", lan + continue + end if + add_scan_row p.port_number, st, lib.lib_name, lib.version, lan + end for + end if +end if + +if scan_rows.len == 0 then exit("Unable to scan target") + +table = "PORT STATE LIBRARY VERSION LAN" +for row in scan_rows + table = table + "\n" + row.port + " " + row.state + " " + row.lib + " " + row.version + " " + row.lan +end for +print format_columns(table) + +if ssh_ports.len == 0 then ssh_ports.push 22 + +port_in = user_input("port: ") +chosen_port = port_in.to_int +if typeof(chosen_port) != "number" then exit("Invalid port") + +try_login = function(user, password) + if user == "" or password == "" then return null + sh = get_shell(user, password) + if typeof(sh) == "shell" then return sh + for ssh_port in ssh_ports + sh = local_shell.connect_service(ip, ssh_port, user, password) + if typeof(sh) == "shell" then return sh + end for + return null +end function + +login_accounts = function(accounts) + if not crypto then return + for acc in accounts + plain = crypto.decipher(acc.hash) + redraw + if not plain then continue + note(acc.user + ":" + plain) + sh = try_login(acc.user, plain) + take_shell sh + end for +end function + +crack_passwd_on = function(comp) + content = read_passwd_text(comp) + if not content then return 0 + note("reading /etc/passwd") + accounts = parse_passwd(content) + if accounts.len == 0 then return 0 + login_accounts accounts + return 1 +end function + +steal_home_creds = function(comp) + if not comp then return + if not crypto then return + home = comp.File("/home") + if not home then return + if not home.has_permission("r") then return + folders = home.get_folders + if not folders then return + for folder in folders + names = ["Mail.txt", "Bank.txt"] + for fname in names + cfg = comp.File(folder.path + "/Config/" + fname) + if not cfg then continue + if not cfg.has_permission("r") then continue + text = cfg.get_content + if not text then continue + line = text.split(char(10))[0] + parts = line.split(":") + if parts.len < 2 then continue + hash = parts[1] + if hash == "" then continue + plain = crypto.decipher(hash) + redraw + if not plain then continue + note(folder.name + " " + fname + ":" + plain) + take_shell try_login(folder.name, plain) + take_shell try_login("root", plain) + end for + end for +end function + +root_computer_accounts = function(comp) + if not comp then return + if object_user(comp) != "root" then return + changed = comp.change_password("root", KNOWN_PASS) + if changed == 1 then + state.password_changed = 1 + note("changed root password") + take_shell try_login("root", KNOWN_PASS) + end if + created = comp.create_user("rsu", KNOWN_PASS) + if created == 1 then + note("created user rsu") + take_shell try_login("rsu", KNOWN_PASS) + end if +end function + +writable_dir = function(comp, user) + paths = [] + if user == "root" then paths.push "/root" + if user != "guest" and user != "root" then paths.push "/home/" + user + paths.push "/home/guest" + paths.push "/tmp" + paths.push "/var/tmp" + for p in paths + f = comp.File(p) + if not f then continue + if not f.is_folder then continue + if not f.has_permission("w") then continue + return p + end for + return null +end function + +find_local = function(name, places) + for place in places + f = local_cp.File(place + "/" + name) + if f then return f + end for + return null +end function + +remote_privesc = function(sh) + if typeof(sh) != "shell" then return 0 + comp = sh.host_computer + if not comp then return 0 + libdir = comp.File("/lib") + if not libdir then return 0 + if not libdir.has_permission("r") then return 0 + + user = object_user(comp) + dest = writable_dir(comp, user) + if not dest then return 0 + + mx_file = find_local("metaxploit.so", ["/lib", current_path, home_dir, home_dir + "/guest"]) + privesc_file = find_local("privesc", ["/bin", current_path, home_dir + "/guest"]) + if not mx_file then return 0 + if not privesc_file then return 0 + + note("uploading privesc to " + dest) + if libdir.has_permission("w") then + local_shell.scp(mx_file.path, "/lib", sh) + end if + copied_mx = local_shell.scp(mx_file.path, dest, sh) + copied_pr = local_shell.scp(privesc_file.path, dest, sh) + if typeof(copied_mx) == "string" then return 0 + if typeof(copied_pr) == "string" then return 0 + + note("launching remote privesc") + sh.launch dest + "/privesc" + return 1 +end function + +overflow_target = function(port_num, extras) + if port_num == 0 then + sess = mx.net_use(ip) + else + sess = mx.net_use(ip, port_num) + end if + if not sess then + note("Unable to establish session") + return + end if + lib = sess.dump_lib + if not lib then + note("Unable to dump library") + return + end if + + adds = mx.scan(lib) + redraw + if not adds then return + + candidates = [] + for add in adds + info = mx.scan_address(lib, add) + if not info then continue + lines = info.split(char(10)) + for line in lines + pos = line.indexOf("Unsafe check:") + if pos == null then continue + if pos == -1 then continue + value = extract_value(line) + if value == "" then continue + candidates.push add + " " + value + end for + end for + + for candidate in candidates + parts = candidate.split(" ") + add = parts[0] + value = parts[1] + for extra in extras + if port_num == 0 then + sess = mx.net_use(ip) + else + sess = mx.net_use(ip, port_num) + end if + if not sess then + redraw + continue + end if + lib = sess.dump_lib + if not lib then + redraw + continue + end if + if extra == "" then + of = lib.overflow(add, value) + else + of = lib.overflow(add, value, extra) + end if + consider of + redraw + end for + end for +end function + +extras = [""] +if chosen_port == 0 then + for lan in lan_ips + if has_item(extras, lan) then continue + extras.push lan + end for +end if +if not has_item(extras, KNOWN_PASS) then extras.push KNOWN_PASS +mail = user_mail_address +if mail then extras.push mail + +note("exploiting port " + chosen_port) +overflow_target chosen_port, extras + +if state.password_changed then + note("trying " + KNOWN_PASS) + take_shell try_login("root", KNOWN_PASS) +end if + +comp = access_computer +if crack_passwd_on(comp) == 0 then + note("no readable /etc/passwd") +end if + +steal_home_creds comp +root_computer_accounts comp + +if state.best_user == "root" then + drop_root state.best_shell +end if + +if state.best_shell then + if state.best_user != "root" then + note("escalating from " + state.best_user) + remote_privesc state.best_shell + drop_root state.best_shell + end if +end if + +if state.best_user == "root" and state.best_shell then + drop_root state.best_shell +end if + +print "You're so fucked even your momma's breaking out the bandaids." +print "This host is toast." +if state.best_shell then + print "dropping into " + state.best_user + " anyway" + state.best_shell.start_terminal +end if