--- a/http-git.nse 2012-12-11 15:22:04.103877794 -0600 +++ b/http-git.nse 2012-12-11 12:00:50.989413176 -0600 @@ -1,17 +1,30 @@ --- Checks for a Git repository found in a website's document root (GET /.git/ HTTP/1.1) --- Gets as much information about the repository as possible, including language/framework, Github --- username, last commit message, and repository description. --- +local http = require("http") +local shortport = require("shortport") +local stdnse = require("stdnse") +local strbuf = require("strbuf") +local string = require("string") +local table = require("table") + +description = [[ +Checks for a Git repository found in a website's document root +/.git/) and retrieves as much repo information as +possible, including language/framework, remotes, last commit +message, and repository description. +]] + -- @output --- PORT STATE SERVICE --- 80/tcp open http +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack -- | http-git: --- | Git repository found in web root --- | Last commit message: This is my last commit... --- | Repository description: Unnamed repository; edit this file 'description' to name the... --- | GitHub remote: AlexWebr/nse (accessed over SSH) --- | BitBucket remote: AlexWebr/nse (accessed over HTTP, pull-only) --- |_ Based on the file '.gitignore', this is a Ruby on Rails application +-- | 127.0.0.1:80/.git/ +-- | Git repository found! +-- | .git/config matched patterns 'passw' +-- | Repository description: Unnamed repository; edit this file 'description' to name the... +-- | Remotes: +-- | http://github.com/someuser/somerepo +-- | Project type: Ruby on Rails web application (guessed from .git/info/exclude) +-- | 127.0.0.1:80/damagedrepository/.git/ +-- |_ Potential Git repository found (found 2/6 expected files) -- -- @xmloutput -- [...snip...] @@ -41,21 +54,13 @@ -- -- [...snip...] -local http = require("http") -local shortport = require("shortport") -local stdnse = require("stdnse") -local strbuf = require("strbuf") -local string = require("string") -local table = require("table") -description = [[ Checks for a Git repository found in a website's document root (/.git/) then retrieves as much repo information as possible, including language/framework, Github username, last commit message, and repository description. -]] - -categories = { "safe", "vuln", "default" } +categories = { "default", "safe", "vuln" } author = "Alex Weber" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" portrule = shortport.http -local STATUS_OK = 200 -- We consider 200 to mean "okay, file exists and we received its contents" +-- We consider 200 to mean "okay, file exists and we received its contents". +local STATUS_OK = 200 -- Long strings (like a repository's description) will be truncated to this -- number of characters in normal output. local TRUNC_LENGTH = 60 @@ -75,7 +80,7 @@ end -- Try each root in succession - for i, root in ipairs(roots) do + for _, root in ipairs(roots) do root = tostring(root) root = root or '/' @@ -104,20 +109,20 @@ -- These files are created by creating and using the repository, -- or by popular development frameworks. local repo = { + ".gitignore", + ".git/COMMIT_EDITMSG", ".git/config", ".git/description", ".git/info/exclude", - ".git/COMMIT_EDITMSG", - ".gitignore", } - local prequests = {} -- prequests = pipelined requests (temp) + local pl_requests = {} -- pl_requests = pipelined requests (temp) -- Go through all of the filenames and do an HTTP GET for _, name in ipairs(repo) do -- for every filename - http.pipeline_add(root .. name, nil, prequests) + http.pipeline_add(root .. name, nil, pl_requests) end - -- do the requests - replies = http.pipeline_go(host, port, prequests) + -- Do the requests. + replies = http.pipeline_go(host, port, pl_requests) if replies == nil then stdnse.print_debug("%s: pipeline_go() error. Aborting.", SCRIPT_NAME) return nil @@ -125,7 +130,7 @@ for i, reply in ipairs(replies) do -- We want this to be indexed by filename, not an integer, so we convert it - -- We added to the pipeline in the same order as the filenames, so this is safe + -- We added to the pipeline in the same order as the filenames, so this is safe. replies[repo[i]] = reply -- create index by filename replies[i] = nil -- delete integer-indexed entry end @@ -155,7 +160,7 @@ end end - if ok(".git/COMMIT_EDITMSG") then + if ok(".git/COMMIT_EDITMSG") then loc["last-commit-message"] = replies[".git/COMMIT_EDITMSG"].body end @@ -163,11 +168,7 @@ loc["repository-description"] = replies[".git/description"].body end - -- If we got /.git/config, we might find out things like the user's GitHub name, - -- if they have a Heroku remote, whether this is a bare repository or not (if it - -- is bare, that means it's likely a remote for other people), and in future - -- versions of Git when there are more than one repo format version, we will - -- display that too. + -- .git/config contains a list of remotes, so we try to extract them. if ok(".git/config") then local config = replies[".git/config"].body local remotes = {} @@ -183,16 +184,15 @@ end end - -- These are files that are used by Git to determine - -- what files to ignore. We use this list to make the - -- loop below (used to determine what kind of application - -- is in the repository) more generic + -- These are files that are used by Git to determine what files to ignore. + -- We use this list to make the loop below (used to determine what kind of + -- application is in the repository) more generic. local ignorefiles = { ".gitignore", ".git/info/exclude", } local fingerprints = { - -- Many of these taken from https://github.com/gitignore + -- Many of these taken from https://github.com/github/gitignore { "%.scala_dependencies", "Scala application" }, { "npm%-debug%.log", "node.js application" }, { "joomla%.xml", "Joomla! site" },