exploit-db-mirror/searchsploit
2020-05-27 10:15:53 +01:00

1073 lines
33 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Name: SearchSploit - Exploit-DB's CLI search tool
# Version: 4.1.1 (2020-05-01)
# Written by: Offensive Security, Unix-Ninja, and g0tmi1k
# Homepage: https://github.com/offensive-security/exploitdb
# Manual: https://www.exploit-db.com/searchsploit
#
## NOTE:
# Exit code '0' means finished normally
# Exit code '1' means something went wrong
# Exit code '2' means help screen
# Exit code '6' means updated packages (APT, brew or Git)
#-----------------------------------------------------------------------------#
## Settings File
rc_file=""
## Default options
CLIPBOARD=0
COLOUR=1
EDBID=0
EXACT=0
EXAMINE=0
FILEPATH=1
FUZZY=1
GETPATH=0
JSON=0
MIRROR=0
OVERFLOW=0
SCASE=0
VERBOSE=0
WEBLINK=0
XML=0
COLOUR_TAG=""
TAGS=""
SEARCH=""
EXCLUDE=""
CASE_TAG_GREP="-i"
CASE_TAG_FGREP="tolower"
AWK_SEARCH=""
FUZZY_SEARCH=""
VERSION=
COLOUR_OFF_GREP=
COLOUR_ON_GREP=
REGEX_GREP=
## Check if our grep supports --color
if grep --help 2>&1 | grep "[-]-color" >/dev/null 2>&1 ; then
COLOUR_OFF_GREP="--color=never"
COLOUR_ON_GREP="--color=always"
fi
## Check if our grep supports ---perl-regexp
if grep --help 2>&1 | grep "[-]-perl-regexp" >/dev/null 2>&1 ; then
REGEX_GREP="-P"
else
REGEX_GREP="-E"
fi
## Set LANG variable to avoid illegal byte sequence errors
LANG=C
## Usage info ~ https://www.tldp.org/LDP/abs/html/standard-options.html
function usage() {
echo " Usage: ${progname} [options] term1 [term2] ... [termN]"
echo ""
echo "=========="
echo " Examples "
echo "=========="
echo " ${progname} afd windows local"
echo " ${progname} -t oracle windows"
echo " ${progname} -p 39446"
echo " ${progname} linux kernel 3.2 --exclude=\"(PoC)|/dos/\""
echo " ${progname} -s Apache Struts 2.0.0"
echo " ${progname} linux reverse password"
echo " ${progname} -j 55555 | json_pp"
echo ""
echo " For more examples, see the manual: https://www.exploit-db.com/searchsploit"
echo ""
echo "========="
echo " Options "
echo "========="
echo "## Search Terms"
echo " -c, --case [Term] Perform a case-sensitive search (Default is inSEnsITiVe)"
echo " -e, --exact [Term] Perform an EXACT & order match on exploit title (Default is an AND match on each term) [Implies \"-t\"]"
echo " e.g. \"WordPress 4.1\" would not be detect \"WordPress Core 4.1\")"
echo " -s, --strict Perform a strict search, so input values must exist, disabling fuzzy search for version range"
echo " e.g. \"1.1\" would not be detected in \"1.0 < 1.3\")"
echo " -t, --title [Term] Search JUST the exploit title (Default is title AND the file's path)"
echo " --exclude=\"term\" Remove values from results. By using \"|\" to separate, you can chain multiple values"
echo " e.g. --exclude=\"term1|term2|term3\""
echo ""
echo "## Output"
echo " -j, --json [Term] Show result in JSON format"
echo " -o, --overflow [Term] Exploit titles are allowed to overflow their columns"
echo " -p, --path [EDB-ID] Show the full path to an exploit (and also copies the path to the clipboard if possible)"
echo " -v, --verbose Display more information in output"
echo " -w, --www [Term] Show URLs to Exploit-DB.com rather than the local path"
echo " --id Display the EDB-ID value rather than local path"
echo " --colour Disable colour highlighting in search results"
echo ""
echo "## Non-Searching"
echo " -m, --mirror [EDB-ID] Mirror (aka copies) an exploit to the current working directory"
echo " -x, --examine [EDB-ID] Examine (aka opens) the exploit using \$PAGER"
echo ""
echo "## Non-Searching"
echo " -h, --help Show this help screen"
echo " -u, --update Check for and install any exploitdb package updates (brew, deb & git)"
echo ""
echo "## Automation"
echo " --nmap [file.xml] Checks all results in Nmap's XML output with service version"
echo " e.g.: nmap [host] -sV -oX file.xml"
echo ""
echo "======="
echo " Notes "
echo "======="
echo " * You can use any number of search terms"
echo " * By default, search terms are not case-sensitive, ordering is irrelevant, and will search between version ranges"
echo " * Use '-c' if you wish to reduce results by case-sensitive searching"
echo " * And/Or '-e' if you wish to filter results by using an exact match"
echo " * And/Or '-s' if you wish to look for an exact version match"
echo " * Use '-t' to exclude the file's path to filter the search results"
echo " * Remove false positives (especially when searching using numbers - i.e. versions)"
echo " * When using '--nmap', adding '-v' (verbose), it will search for even more combinations"
echo " * When updating or displaying help, search terms will be ignored"
echo ""
exit 2
}
## Update database check
function update() {
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
## Check to see if we already have the value
[[ "${tmp_package[*]}" =~ "${package_array[${i}]}" ]] \
&& continue
## Else save all the information
tmp_git+=("${git_array[${i}]}")
tmp_path+=("${path_array[${i}]}")
tmp_package+=("${package_array[${i}]}")
done
## Loop around all the new arrays
arraylength="${#tmp_git[@]}"
for (( i=0; i<${arraylength}; i++ )); do
git="${tmp_git[${i}]}"
path="${tmp_path[${i}]}"
package="${tmp_package[${i}]}"
## Update from the repos (e.g. Kali)
apt-cache search "${package}" 2>/dev/null >/dev/null #dpkg -l "${package}" 2>/dev/null >/dev/null
if [[ "$?" == "0" ]]; then
updatedeb "${package}"
else
## Update from homebrew (e.g. OSX)
brew 2>/dev/null >/dev/null
if [[ "$?" == "0" ]]; then
## This only really only updates "./searchsploit". The rest (can) come via git as its updated more frequently
updatedbrew "${package}"
fi
## Update via Git
updategit "${package}" "${path}" "${git}"
fi
done
## Done
exit 6
}
## Update database (via .deb/apt)
function updatedeb() {
package_in="${1}"
echo -e "[i] Updating via apt package management (Expect weekly-ish updates): ${package_in}\n"
sudo apt update \
|| echo -e "\n[-] Issue with apt update (Please check network connectivity & apt SourcesList values)" 1>&2
sudo apt -y install "${package_in}" \
|| echo -e "\n[-] Issue with apt upgrade" 1>&2
echo -e "\n[*] apt update finished"
}
## Update database (via homebrew)
function updatedbrew() {
package_in="${1}"
echo -e "[i] Updating via brew package management\n"
brew update \
|| echo -e "\n[-] Issue with brew update (Please check network connectivity)" 1>&2
brew upgrade "${package_in}"
echo -e "\n[*] Brew update finished"
}
## Update database (via Git)
function updategit() {
package_in="${1}"
path_in="${2}"
git_in="${3}"
echo -e "[i] Updating via Git (Expect daily updates): ${package_in} ~ ${path_in}\n"
## Make sure we are in the correct folder
mkdir -p "${path_in}/" 2>/dev/null \
|| sudo mkdir -p "${path_in}/"
cd "${path_in}/"
## Are we in a Git repo?
if [[ "$( git rev-parse --is-inside-work-tree 2>/dev/null )" != "true" ]]; then
if [[ "$( ls )" = "" ]]; then
# If directory is empty, just clone
echo -e "\n[-] Nothing here (${path_in}). Starting fresh..."
git clone -v "${git_in}" "${path_in}/" 2>/dev/null \
|| sudo git clone -v "${git_in}" "${path_in}/"
fi
fi
# Is our Git remote added? (aka wouldn't be via homebrew method)
if [[ "$( git remote -v )" != *"upstream"*"${git_in}"* ]]; then
echo -e "\n[-] Missing Git remote upstream (${git_in})"
git init 2>/dev/null \
|| sudo git init
git remote add upstream "${git_in}" 2>/dev/null \
|| sudo git remote add upstream "${git_in}"
fi
# Make sure to prep checkout first
git checkout -- . 2>/dev/null \
|| sudo git checkout -- .
# Update from git
echo -e "\n[i] Git pull'ing"
git pull -v upstream master 2>/dev/null \
|| sudo git pull -v upstream master
# If conflicts, clean and try again
if [[ "$?" -ne 0 ]]; then
echo -e "\n[-] Git conflict"
git clean -d -fx "" \
|| sudo git clean -d -fx ""
git pull -v upstream master \
|| sudo git pull -v upstream master
fi
echo -e "\n[*] Git update finished"
echo "[i] Path: ${path_in}/"
}
## Printing dotted lines in the correct manner
function drawline() {
printf "%0.s-" $( eval echo {1..$(( COL1 + 1 ))} )
echo -n " "
printf "%0.s-" $( eval echo {1..$(( COL2 - 1 ))} )
echo ""
}
## Used in searchsploitout/nmap's XML
function validterm() {
## Check to see if its any phrases which would give a TON of incorrect results
if [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "microsoft" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "microsoft windows" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "windows" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "apache" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "ftp" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "http" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "linux" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "net" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "network" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "oracle" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "ssh" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "unknown" ]; then
echo -e "[-] Skipping term: ${1} (Term is too general. Please re-search manually: $0 ${arg} ${1})\n" 1>&2
## Issues, return with something
return 1
fi
## No issues, return without anything
return 0
}
## Used in searchsploitout/nmap's XML
function searchsploitout() {
## Make sure there is a value
[ "${software}" = "" ] \
&& return
#echo "" 1>&2
arg="-t" ## Title search by default!
[[ "${COLOUR}" != "1" ]] \
&& arg="${arg} --colour"
[[ "${EDBID}" == "1" ]] \
&& arg="${arg} --id"
[[ "${JSON}" == "1" ]] \
&& arg="${arg} --json"
[[ "${OVERFLOW}" == "1" ]] \
&& arg="${arg} --overflow"
[[ "${FUZZY}" != "1" ]] \
&& arg="${arg} --strict"
[[ "${WEBLINK}" == "1" ]] \
&& arg="${arg} --www"
## Try and remove terms that could confuse searches
#software=$( echo "${software}" | sed 's_/_ _g' )
software=$( echo "${software}" | sed -e 's/[^a-zA-Z0-9]/ /g' )
if [[ "${VERBOSE}" -eq 1 ]]; then
## Loop each word?
tmp=""
for word in $( echo ${software} ); do
## Add current search term on
tmp="${tmp}${word}"
## Check to see if its any phrases which would give a TON of incorrect results
validterm "${tmp}" \
|| continue
## Feedback
echo "[i] $0 ${arg} ${tmp}" 1>&2
out=$( bash "$0" ${arg} ${tmp} )
## Are there too many results?
lines=$( echo -e "${out}" | wc -l )
if [[ "${lines}" -gt 100 ]]; then
echo -e "[-] Skipping output: ${tmp} (Too many results, 100+. You'll need to force a search: $0 ${arg} ${tmp})\n" 1>&2
## Are there any results?
elif [[ "${lines}" -gt 5 ]]; then
echo -e "${out}\n\n"
## If there's no results
else
## Exit for loop
break
fi
## Space out for the next word
tmp="${tmp} "
done
## Padding between loops
echo -e "\n\n" 1>&2
else
## Check to see if its any phrases which would give a TON of incorrect results
validterm "${software}" \
|| return
## Feedback
echo "[i] $0 ${arg} ${software}" 1>&2
out=$( bash "$0" ${arg} ${software} )
## Are there too many results?
lines=$( echo -e "${out}" | wc -l )
if [[ "${lines}" -gt 100 ]]; then
echo -e "[-] Skipping output: ${software} (Too many results, 100+. You'll need to force a search: $0 ${arg} ${software})\n" 1>&2
## Are there any results?
elif [[ "${lines}" -gt 5 ]]; then
echo -e "${out}\n\n"
fi
fi
}
## Read XML file
function nmapxml() {
## Feedback to the end user
echo -e "[i] Reading: '${FILE}'\n" 1>&2
## Read in XMP (IP, name, service and version)
xmllint --xpath '//address/@addr|//service/@name|//service/@product|//service/@version' "${FILE}" \
| sed -e $'s/addr=/\\\n[IP] /g; s/name=/\\\n[NAME] /g; s/product=/\\\n[PRODUCT] /g;s/" version="/\\\n[VERSION] /g; s/"//g' \
| grep -v '\[IP\].*\:' \
| while read line; do
type=$( echo "${line}" | cut -d" " -f 1 )
input=$( echo "${line}" | cut -d" " -f 2- )
case "${type}" in
"[IP]")
#[[ "${VERBOSE}" -eq 1 ]] && echo -e "\n\n\e[32m[*] IP: ${input}\e[39m" 1>&2
;;
"[NAME]")
## If we have already looped around and got something, save it before moving onto the current value
if [[ "${software}" ]]; then
#searchsploitout
echo "${software}"
fi
## Something is better than nothing. Will just go on the default service that matches the port. e.g. domain
software="${input}"
## Might not get any more than this, if -sV failed
;;
"[PRODUCT]")
## We have a name, but no version (yet?) e.g. dnsmasq
echo "${software}"
software="${input}"
echo "${software}"
;;
"[VERSION]")
software="${software} ${input}"
## Name & version. There isn't any more information to get, game over. e.g. dnsmasq 2.72
echo "${software}"
software=
;;
esac
done | tr '[:upper:]' '[:lower:]' | awk '!x[$0]++' | while read software; do
searchsploitout
done
}
## Build search terms
function buildterms() {
tag_in="${1}"
## If we are to use colour ("--colour"), add the values to search for between "or"
if [[ "${COLOUR}" -eq 1 ]]; then
[[ "${COLOUR_TAG}" ]] \
&& COLOUR_TAG="${COLOUR_TAG}|"
COLOUR_TAG="${COLOUR_TAG}${tag_in}"
fi
## Search both title AND path
if [[ "${FILEPATH}" -eq 1 ]]; then
## Search command for each term (with case sensitive flag, "-c")
SEARCH="${SEARCH} | grep ${COLOUR_OFF_GREP} -F ${CASE_TAG_GREP} \"${tag_in}\""
## Some regex to try and detect version
## Basic: major.minor[.build][.revision] // major.minor[.maintenance][.build] -- example: 1.2.3.4)
## Plus alphanumeric (e.g. alpha, beta): 1a, 2.2b, 3.3-c, 4.4-rc4, 5.5-r
if ! echo "${tag_in}" | grep ${REGEX_GREP} -q "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$"; then
FUZZY_SEARCH="${FUZZY_SEARCH} | grep ${COLOUR_OFF_GREP} -F ${CASE_TAG_GREP} \"${tag_in}\""
fi
## Search just the title, NOT the path ("-t"/"-e")
else
## If there is already a value, prepend text to get ready
[[ "${AWK_SEARCH}" ]] \
&& AWK_SEARCH="${AWK_SEARCH}/ && ${CASE_TAG_FGREP}(\$3) ~ /"
## Escape any slashes
tag_in="$( echo ${tag_in} | sed 's_/_\\/_g' )"
## Case sensitive ("-c")
if [[ "${SCASE}" -eq 1 ]]; then
AWK_SEARCH="${AWK_SEARCH}${tag_in}"
else
AWK_SEARCH="${AWK_SEARCH}$( echo ${tag_in} | tr '[:upper:]' '[:lower:]' )"
fi
fi
}
## Read in the values from files_*.csv
function findresults() {
file_in="${1}"
path_in="${2}"
name_in="${3}"
if [[ "${name_in}" == "Paper"* ]]; then
url="papers"
elif [[ "${name_in}" == "Shellcode"* ]]; then
url="shellcodes"
else
url="exploits"
fi
## JSON require full options ("--json")
if [[ "${JSON}" -eq 1 ]] || [[ "${FUZZY}" -eq 1 ]]; then
## Read (id, path, title, date, author, type, platform) separated between commas
## Needs to end with a `,` to match the awk search later for FUZZY_SEARCH with "sort -u"
SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3\",\"\$4\",\"\$5\",\"\$6\",\"\$7\",\"}' \"${path_in}/${file_in}\""
## Read (id, path, title) separated between commas & search for less than (and grater than values) too
FUZZY_SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3}' \"${path_in}/${file_in}\" | grep ${COLOUR_OFF_GREP} \"<\|>\""
else
## Read (id, path, title) separated between commas (as these are the only visible fields)
SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3}' \"${path_in}/${file_in}\""
fi
## EXACT search command ("-e")
if [[ "${EXACT}" -eq 1 ]]; then
buildterms "${TAGS}"
## or AND search command?
else
## For each term
for TAG in ${TAGS}; do
buildterms "${TAG}"
done
fi
## If we are NOT to use the path name ("-t"/"-e")
[[ "${FILEPATH}" -eq 0 ]] \
&& SEARCH="${SEARCH} | awk -F '[,]' '${CASE_TAG_FGREP}(\$3) ~ /${AWK_SEARCH}/ {print}'"
## Remove any terms not wanted from the search
[[ "${EXCLUDE}" ]] \
&& SEARCH="${SEARCH} | grep ${REGEX_GREP} -vi '${EXCLUDE}'"
[[ "${EXCLUDE}" ]] && [[ "${FUZZY}" -eq 1 ]] \
&& FUZZY_SEARCH="${FUZZY_SEARCH} | grep ${REGEX_GREP} -vi '${EXCLUDE}'"
## If we are to use colour ("--colour"), add the value here
if [[ "${COLOUR_TAG}" ]] && [[ "${JSON}" -eq 0 ]]; then
COLOUR_TAG="grep ${COLOUR_ON_GREP} -iE \"${COLOUR_TAG}|$\""
fi
## Dynamically set column widths to the current screen size
[[ "${WEBLINK}" -eq 1 ]] \
&& COL2=45 \
|| COL2=$(( 34 )) ## Max length + 2 ~ $ find . ! -path '*/.*' -type f | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- | tail -n 1
#|| COL2=$(( ${#path_in} + 21 ))
COL1=$(( $( tput cols ) - COL2 - 1 ))
## Search, format, and print results (--overflow)
[[ "${OVERFLOW}" -eq 1 ]] \
&& FORMAT_COL1=${COL1} \
|| FORMAT_COL1=${COL1}'.'${COL1}
## Maximum length COL2 can be
FORMAT_COL2=$(( ${COL2} - 2 ))
## Are we doing a fuzzy search & did we manage to detect the version
if [[ "${FUZZY}" -eq 1 ]] && [[ -n "${VERSION}" ]]; then
## SubShells - http://mywiki.wooledge.org/BashFAQ/024
while IFS= read -r TITLE; do
while IFS= read -r RANGE; do
## Read in input and trim
MIN=$( echo "${RANGE}" | awk -F '<' '{print $1}' | xargs )
MAX=$( echo "${RANGE}" | awk -F '<' '{print $2}' | xargs )
## As its optional to put it, set a value if blank
[ -z "${MIN}" ] \
&& MIN=0
RESULT="$( printf '%s\n' "${MIN}" "${VERSION}" "${MAX}" | sort -V )"
## Sub if sort -v isn't working? if (( $( echo "${MIN} <= ${VERSION}" | bc -l ) )) && (( $( echo "${MAX} >= ${VERSION}" | bc -l ) )) ; then
## ...else there is dpkg (if Debian)
if [[ "$( echo "${RESULT}" | head -n 1 )" == "${MIN}" ]] \
&& [[ "$( echo "${RESULT}" | tail -n 1 )" == "${MAX}" ]]; then
[ -n "${ID}" ] \
&& ID="${ID}|"
ID="${ID}$( echo $TITLE | awk -F ',' '{print $1}' )"
## Found one, no point going on
break
fi
done < <(
echo "${TITLE}" \
| grep ${REGEX_GREP} -o "((\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\d|x)*)(\s*))?((<|>)=?)(\s*)(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\d|x)*)" \
| sed 's_=__; s_>_<_'
)
## Do the same search (just without the version) & loop around all the exploit titles (as thats where the versions are)
## Two main "parts"
## (a.b.c.d )(<= e.f.g.h)
## This can be broken down more:
## Group 1 == a & e == major = [0-9]
## Group 2 == b & f == minor = .[0-9] (optional)
## Group 3 == c & g == build/maintenance = .[0-9] (optional)
## Group 4a == d & h == revision/build = . OR - (optional)
## Group 4b == = x OR [0-9] (optional)
## So it really is more like ~ (a)(.b)(.c)(.d)( )(<=)( )(e)(.f)(.g)(.h)
## NOTE: ..."x" is used as a wild card in titles
## Quick regex recap
## Digit == \d
## Space == \s
## Group == ( )
## OR == |
## 1 or more == +
## 0 or more == *
## 0 or 1 == ?
## Should support:
## Exploit < 1 / <= 1.2 / < 1.2.3.4 / < 1.2.3.x
## Exploit 1.0 < 1.2.3.4
## ...This can be better so it doesn't search in brackets: "Linux Kernel (Solaris 10 / < 5.10 138888-01) - Local Privilege Escalation"
done < <(
eval "${FUZZY_SEARCH}"
)
fi
## Magic search Fu + strip double quotes
OUTPUT="$(
( \
eval ${SEARCH}; \
awk "/^(${ID}),/ {print}" "${path_in}/${file_in}" \
) \
| sed 's/\"//g; s_\\_\\\\_g' \
| sort -u
)"
## If there are no results, no point going on
[[ -z "$OUTPUT" ]] \
&& return
## Print JSON format (full options) ("--json")?
if [[ "${JSON}" -eq 1 ]]; then
## Web link format ("--json --www")?
if [[ "${WEBLINK}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"URL\":\"https://www.exploit-db.com/'${url}'/%s\"},", $3, $1 }' )"
## Just the EDB-ID ("--json --id")?
elif [[ "${EDBID}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"EDB-ID\":\"%s\",\"Path\":\"'${path_in}/'%s\"},", $3, $1, $2 }' )"
## Default JSON ("--json")?
else
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"EDB-ID\":\"%s\",\"Date\":\"%s\",\"Author\":\"%s\",\"Type\":\"%s\",\"Platform\":\"%s\",\"Path\":\"'${path_in}/'%s\"},", $3, $1, $4, $5, $6, $7, $2 }' )"
fi
OUTPUT="$( echo -e ${OUTPUT} \
| sort -f \
| sed '$ s/,$//' )"
## Web link format ("--www")?
elif [[ "${WEBLINK}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %s\n", $3, "https://www.exploit-db.com/'${url}'/"$1 }' \
| sort -f )"
## Just the EDB-ID ("--id")?
elif [[ "${EDBID}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %s\n", $3, $1 }' \
| sort -f )"
## Default view
else
OUTPUT="$( echo "${OUTPUT}" \
| sed 's_,exploits/_,_; s_,shellcodes/_,_; s_,papers/_,_' \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %.'${FORMAT_COL2}'s\n", $3, $2 }' \
| sort -f )"
fi
## Display colour highlights ("--colour")?
if [[ "${COLOUR_TAG}" ]] && [[ "${JSON}" -eq 0 ]] && [[ "${OUTPUT}" ]]; then
OUTPUT=$( echo -e "${OUTPUT}" | eval ${COLOUR_TAG} )
fi
}
function printresults() {
title_in="${1}"
path_in="${2}"
json_title="$( echo ${title_in} | tr /a-z/ /A-Z/ )"
## Print header if in JSON ("--json")
if [[ "${JSON}" -eq 1 ]]; then
printf ",\n\t\"DB_PATH_${json_title}\": \"${path_in}\",\n"
printf "\t\"RESULTS_${json_title}\": ["
## ASCII table
else
drawline
printf "%-${COL1}s %s" " ${title_in} Title"
if [[ "${WEBLINK}" -eq 1 ]]; then
echo "| URL"
elif [[ "${EDBID}" -eq 1 ]]; then
echo "| EDB-ID"
else
echo "| Path"
#echo " > Results (0)"
#
#printf "%-${COL1}s "
#echo "| (${path_in}/)"
fi
drawline
fi
## Show content
[[ "${OUTPUT}" ]] \
&& echo "${OUTPUT}"
## Print footer if in JSON ("--json")
if [[ "${JSON}" -eq 1 ]]; then
printf "\t]"
else
drawline
fi
}
#-----------------------------------------------------------------------------#
## Locate setting file
## User home folder config
if [[ -f "${HOME}/.searchsploit_rc" ]]; then
rc_file="${HOME}/.searchsploit_rc"
## Global config
elif [[ -f "/etc/searchsploit_rc" ]]; then
rc_file="/etc/searchsploit_rc"
## Method #1 - File itself
elif [[ -f "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.searchsploit_rc" ]]; then
rc_file="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.searchsploit_rc"
## Method #2 - Symbolic link
elif [[ -f "$( dirname "$( readlink "$0" )" )/.searchsploit_rc" ]]; then
rc_file="$( dirname "$( readlink "$0" )" )/.searchsploit_rc"
## Manually specified?
elif [[ ! -f "${rc_file}" ]]; then
echo "[!] Could not find: rc_file ~ ${rc_file}"
exit 1
fi
## Use config file
source "${rc_file}"
#-----------------------------------------------------------------------------#
## Check for empty arguments
if [[ $# -eq 0 ]]; then
usage >&2
fi
## Parse long arguments
ARGS="-"
for param in "$@"; do
if [[ "${param}" == "--case" ]]; then
SCASE=1
elif [[ "${param}" == "--colour" ]] || [[ "${param}" == "--color" ]]; then
COLOUR=""
elif [[ "${param}" == "--exact" ]]; then
EXACT=1
elif [[ "${param}" == "--examine" ]] || [[ "${param}" == "--open" ]] || [[ "${param}" == "--view" ]]; then
GETPATH=1
EXAMINE=1
elif [[ "${param}" == "--help" ]]; then
usage >&2
elif [[ "${param}" == "--id" ]]; then
EDBID=1
elif [[ "${param}" == "--json" ]]; then
JSON=1
elif [[ "${param}" == "--mirror" ]] || [[ "${param}" == "--copy" ]] || [[ "${param}" == "--dup" ]] || [[ "${param}" == "--duplicate" ]]; then
GETPATH=1
MIRROR=1
elif [[ "${param}" == "--nmap" ]]; then
XML=1
elif [[ "${param}" == "--overflow" ]]; then
OVERFLOW=1
elif [[ "${param}" == "--path" ]]; then
GETPATH=1
CLIPBOARD=1
elif [[ "${param}" == "--strict" ]]; then
FUZZY=0
elif [[ "${param}" == "--title" ]]; then
FILEPATH=0
elif [[ "${param}" == "--update" ]]; then
update
elif [[ "${param}" == "--verbose" ]]; then
VERBOSE=1
elif [[ "${param}" == "--www" ]]; then
WEBLINK=1
elif [[ "${param}" =~ "--exclude=" ]]; then
EXCLUDE="$( echo "${param}" | cut -d '=' -f 2- )"
else
if [[ "${param:0:1}" == "-" ]]; then
ARGS=${ARGS}${param:1}
shift
continue
fi
TAGS="${TAGS} ${param//\`/_}"
fi
done
## Parse short arguments
while getopts "cehjmnopstuvwx" arg "${ARGS}"; do
if [[ "${arg}" = "?" ]]; then
usage >&2;
fi
case ${arg} in
c) SCASE=1;;
e) EXACT=1;;
h) usage >&2;;
j) JSON=1;;
m) GETPATH=1; MIRROR=1;;
n) XML=1;;
o) OVERFLOW=1;;
p) GETPATH=1; CLIPBOARD=1;;
s) FUZZY=0;;
t) FILEPATH=0;;
u) update;;
v) VERBOSE=1;;
w) WEBLINK=1;;
x) GETPATH=1; EXAMINE=1;;
esac
shift $(( OPTIND - 1 ))
done
#-----------------------------------------------------------------------------#
## Check for files_*.csv
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
files="${path_array[${i}]}/${files_array[${i}]}"
if [[ -f "${files}" ]]; then
continue
## Method #1 - File itself
elif [[ -f "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/${files_array[${i}]}" ]]; then
echo "[i] Found (#1): $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/${files_array[${i}]}" 1>&2
echo "[i] To remove this message, please edit \"${rc_file}\" for \"${files_array[${i}]}\" (package_array: ${package_array[${i}]})" 1>&2
echo 1>&2
path_array[${i}]="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
## Method #2 - Symbolic link
elif [[ -f "$( dirname "$( readlink "$0" )" )/${files_array[${i}]}" ]]; then
echo "[i] Found (#2): $( dirname "$( readlink "$0" )" )/${files_array[${i}]}" 1>&2
echo "[i] To remove this message, please edit \"${rc_file}\" for \"${files_array[${i}]}\" (package_array: ${package_array[${i}]})" 1>&2
echo 1>&2
path_array[${i}]="$( dirname "$( readlink "$0" )" )"
else
#echo "[!] Could not find: ${files}" 1>&2
#echo "[i] To remove this message, please remove \"${files_array[${i}]}\" (package_array: ${package_array[${i}]}) from \"${rc_file}\"" 1>&2
#echo 1>&2
unset "files_array[${i}]"
unset "path_array[${i}]"
unset "name_array[${i}]"
unset "git_array[${i}]"
unset "package_array[${i}]"
fi
done
#-----------------------------------------------------------------------------#
## Read in XML
if [[ "${XML}" -eq 1 ]]; then
## Trim white spaces
FILE=$( echo ${TAGS} | xargs )
## Is there a file?
if [[ ! -f "${FILE}" ]]; then
echo -e "\n[!] Could not find file: ${FILE}" 1>&2
exit 1
fi
if ! hash xmllint 2>/dev/null; then
echo -e "\n[!] Please install xmllint" 1>&2
echo -e "[i] Kali Linux: sudo apt -y install libxml2-utils" 1>&2
exit 1
fi
if [[ "${VERBOSE}" -ne 1 ]]; then
echo "[i] SearchSploit's XML mode (without verbose enabled). To enable: ${progname} -v --xml..." 1>&2
fi
## Do the magic
nmapxml
## Done
exit 0
fi
## Print the full path. If pbcopy/xclip is available then copy to the clipboard
if [[ "${GETPATH}" -eq 1 ]]; then
for exploit in ${TAGS}; do
## Get EDB-ID from input
edbdb="$( echo ${exploit} | rev | cut -d '/' -f1 | rev | cut -d'-' -f1 | cut -d'.' -f1 | tr -dc '0-9' )"
## Loop until we find something
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
files="${path_array[${i}]}/${files_array[${i}]}"
## Check to see if the files_*.csv has a value
line=$( grep -m 1 -E "^${edbdb}," "${files}" )
if [[ "${line}" ]]; then
path="$( echo $line | cut -d ',' -f 2 )"
location="${path_array[${i}]}/${path}"
name="${name_array[${i}]}"
if [[ "${name}" == "Paper"* ]]; then
url="papers/${edbdb}"
elif [[ "${name}" == "Shellcode"* ]]; then
url="shellcodes/${edbdb}"
else
url="exploits/${edbdb}"
fi
break
fi
done
## Did we find the exploit?
if [[ -f "${location}" ]]; then
## Get title
title=$( grep -m 1 "${path}" "${files}" | cut -d ',' -f 3 | sed 's/"//g' )
## File type
fileinfo="$( file -b "${location}" )"
## How long is the name?
PADDING=$(( 9 - ${#name} ))
## Display out
printf "%-${PADDING}s%s"
echo "${name}: ${title}"
echo " URL: https://www.exploit-db.com/${url}"
echo " Path: ${location}"
echo "File Type: ${fileinfo}"
echo ""
## Copy to clipboard?
if [[ "${CLIPBOARD}" -eq 1 ]]; then
## Are any copy programs available?
if hash xclip 2>/dev/null || hash pbcopy 2>/dev/null; then
## Linux (Will require $DISPLAY)
if hash xclip 2>/dev/null; then
echo -ne "${location}" | xclip -selection clipboard 2>/dev/null
echo "Copied EDB-ID #${edbdb}'s path to the clipboard"
## OSX
elif hash pbcopy 2>/dev/null; then
echo -ne "${location}" | pbcopy
echo "Copied EDB-ID #${edbdb}'s path to the clipboard"
fi
fi
## Done (early!)
exit 0
fi
## Open the exploit up?
if [[ "${EXAMINE}" -eq 1 ]]; then
if [[ "${PAGER}" ]]; then
/bin/sh -c "${PAGER} ${location}"
elif [[ -f "$( which pager 2>/dev/null )" ]]; then
pager "${location}"
else
less "${location}"
fi
echo -e "\n"
fi
if [[ "${MIRROR}" -eq 1 ]]; then
cp -i "${location}" "$( pwd )/"
echo "Copied to: $( pwd )/$( basename ${location} )"
echo -e "\n"
fi
else
## Feedback
echo "[!] Could not find EDB-ID #${edbdb}"
echo -e "\n"
fi
done
## Done
exit 0
fi
#-----------------------------------------------------------------------------#
## Are we are doing an exact match ("-e")? If so, do NOT check folder path (Implies "-t").
[[ "${EXACT}" -eq 1 ]] \
&& FILEPATH=0
## Case sensitive ("-c"), remove the default flags
[[ "${SCASE}" -eq 1 ]] \
&& CASE_TAG_GREP="" \
&& CASE_TAG_FGREP=""
## Remove leading space
TAGS="$( echo ${TAGS} | sed -e 's/^[[:space:]]//' )"
## Check to see if the version of "sort" is supported
echo | sort -V 2>/dev/null >/dev/null
if [ $? -ne "0" ]; then
echo "[-] 'sort' doesn't support '-V'" 1>&2
echo "[i] Enabling '${progname} --strict'" 1>&2
FUZZY=0
fi
## Some regex to try and detect version
## Basic: major.minor[.build][.revision] // major.minor[.maintenance][.build] -- example: 1.2.3.4)
## Plus alphanumeric (e.g. alpha, beta): 1a, 2.2b, 3.3-c, 4.4-rc4, 5.5-r
for tag_in in ${TAGS}; do
if echo "${tag_in}" | grep ${REGEX_GREP} -q "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$"; then
## 1.2.3-4abc
VERSION=$( echo "${tag_in}" | grep ${REGEX_GREP} -o "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$" )
[[ -n "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]] \
&& echo "[i] Version ID: ${VERSION}"
## 1.2.3-4
CLEANVERSION=$( echo "${tag_in}" | grep ${REGEX_GREP} -o "^(\d*\.?)(\d*\.?)(\d*\.?)((\.|\-)?(\d+))" )
if [[ -n "${CLEANVERSION}" ]] && [[ "${CLEANVERSION}" != "${VERSION}" ]]; then
VERSION="${CLEANVERSION}"
[[ "${VERBOSE}" -eq 1 ]] \
&& echo "[i] Clean ID: ${VERSION}"
fi
fi
done
## Did not get a version? If so, no point doing a fuzzy search
if [[ "${FUZZY}" -eq 1 ]] && [[ -z "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]]; then
echo "[i] Unable to detect version in terms: ${TAGS}" 1>&2
echo "[i] Enabling '${progname} --strict'" 1>&2
FUZZY=0
fi
## Is it just a single tag, disable fuzzy
[[ "${TAGS}" != *" "* ]] \
&& FUZZY=0
#-----------------------------------------------------------------------------#
## Print header if in JSON ("--json")
[[ "${JSON}" -eq 1 ]] \
&& printf "{\n\t\"SEARCH\": \"${TAGS}\""
## Check for files_*.csv
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
## Search
findresults "${files_array[${i}]}" "${path_array[${i}]}" "${name_array[${i}]}"
## Print results if in JSON ("--json") or if there are any results
if ([[ "${JSON}" -eq 1 ]] || [[ "${OUTPUT}" ]]); then
printresults "${name_array[${i}]}" "${path_array[${i}]}"
## Summary if NOT JSON ("--json")
elif [[ "${JSON}" -eq 0 ]]; then
echo "${name_array[${i}]}s: No Results"
fi
## Reset
COLOUR_TAG=""
done
## Print footer if in JSON ("--json")
[[ "${JSON}" -eq 1 ]] \
&& printf "\n}\n"
## Done
exit 0