You've already forked greyhack
576 lines
14 KiB
Plaintext
576 lines
14 KiB
Plaintext
if params.len < 1 or params.len > 2 or params[0] == "-h" or params[0] == "--help" then
|
|
current_program = get_shell.host_computer.File(program_path)
|
|
exit "Usage: " + current_program.name + " <gateway ip> [host ip]\n\nWithout host ip, scan the gateway and choose a port.\nWith host ip, try router bounce exploits on gateway:0 against host ip."
|
|
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]
|
|
bounce_host = ""
|
|
if params.len == 2 then bounce_host = params[1]
|
|
bounce_mode = bounce_host != ""
|
|
login_ip = ip
|
|
if bounce_mode then login_ip = bounce_host
|
|
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
|
|
|
|
is_requested_host = function(comp)
|
|
if not bounce_mode then return 1
|
|
if not comp then return 0
|
|
return comp.local_ip == bounce_host
|
|
end function
|
|
|
|
extract_value = function(line)
|
|
start = line.indexOf("<b>")
|
|
finish = line.indexOf("</b>")
|
|
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,
|
|
"passwd_processed": 0,
|
|
"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
|
|
if bounce_mode then
|
|
print ip + " 0 -> " + bounce_host
|
|
else
|
|
print ip + " " + chosen_port
|
|
end if
|
|
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
|
|
if not is_requested_host(sh.host_computer) 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
|
|
if not is_requested_host(comp) 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.path != "/etc/passwd" then return
|
|
if not f.has_permission("r") then
|
|
note("/etc/passwd is not readable")
|
|
return
|
|
end if
|
|
state.passwd_file = f
|
|
note("file " + f.path)
|
|
crack_passwd_on(null)
|
|
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
|
|
|
|
if bounce_mode then
|
|
chosen_port = 0
|
|
note("bouncing through " + ip + " to " + bounce_host)
|
|
else
|
|
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)
|
|
|
|
port_in = user_input("port: ")
|
|
chosen_port = port_in.to_int
|
|
if typeof(chosen_port) != "number" then exit("Invalid port")
|
|
end if
|
|
|
|
if ssh_ports.len == 0 then ssh_ports.push 22
|
|
|
|
try_login = function(user, password)
|
|
if user == "" or password == "" then return null
|
|
if not bounce_mode then
|
|
sh = get_shell(user, password)
|
|
if typeof(sh) == "shell" then return sh
|
|
end if
|
|
for ssh_port in ssh_ports
|
|
sh = local_shell.connect_service(login_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)
|
|
if state.passwd_processed then return 1
|
|
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
|
|
state.passwd_processed = 1
|
|
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
|
|
|
|
if bounce_mode then
|
|
extras = [bounce_host]
|
|
else
|
|
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
|
|
end if
|
|
if not bounce_mode then
|
|
if not has_item(extras, KNOWN_PASS) then extras.push KNOWN_PASS
|
|
mail = user_mail_address
|
|
if mail then extras.push mail
|
|
end if
|
|
|
|
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 "<color=#ff0000>You're so fucked even your momma's breaking out the bandaids.</color>"
|
|
print "This host is toast."
|
|
if state.best_shell then
|
|
print "dropping into " + state.best_user + " anyway"
|
|
state.best_shell.start_terminal
|
|
end if
|