#!/bin/bash # Name: SearchSploit - Exploit-DB's CLI search tool # Version: 4.1.0 (2020-04-27) # 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="" COLOUR_OFF_GREP= COLOUR_ON_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 ## 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 -s --exclude=\"(PoC)|/dos/\"" echo " ${progname} linux reverse password" 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 -Eq "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$"; then ## 1.2.3-4abc VERSION=$( echo "${tag_in}" | grep -Eo "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$" ) [[ -n "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]] \ && echo "[i] Version ID: ${VERSION}" ## 1.2.3-4 CLEANVERSION=$( echo "${tag_in}" | grep -Eo "^(\d*\.?)(\d*\.?)(\d*\.?)((\.|\-)?(\d+))" ) if [[ -n "${CLEANVERSION}" ]] && [[ "${CLEANVERSION}" != "${VERSION}" ]]; then VERSION="${CLEANVERSION}" [[ "${VERBOSE}" -eq 1 ]] \ && echo "[i] Clean ID: ${VERSION}" fi else 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 SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3\",\"\$4\",\"\$5\",\"\$6\",\"\$7}' \"${path_in}/${file_in}\"" ## Read (id, title) separated between commas & search for less than (and grater than values) too FUZZY_SEARCH="awk -F '[,]' '{print \$1\",\"\$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}'" ## 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=$(( ${#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 )) ## Remove any terms not wanted from the search [[ "${EXCLUDE}" ]] \ && SEARCH="${SEARCH} | grep -vEi '${EXCLUDE}'" ## Did we manage to detect the version? if [[ "${FUZZY}" -eq 1 ]] && [[ -z "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]]; then echo "[i] Unable to detect version in terms: ${TAGS}" 1>&2 echo "[i] Disabling '${progname} -f'" 1>&2 elif [[ "${FUZZY}" -eq 1 ]]; then ## Check to see if sort is supported echo | sort -V 2>/dev/null >/dev/null if [ $? -ne "0" ]; then echo "[-] 'sort' doesn't support '-V'" 1>&2 echo "[-] Disabling '${progname} -f'" 1>&2 else ## 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 -Eo "((\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 done < <( eval "${FUZZY_SEARCH}" ) fi fi ## Magic search Fu + strip double quotes OUTPUT="$( ( \ eval ${SEARCH}; \ awk "/^(${ID}),/ {print}" "${path_in}/${file_in}" \ ) \ | sed 's/\"//g' )" ## 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}" \ | awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %.'${FORMAT_COL2}'s\n", $3, $2 }' \ | sort -f )" #| sed 's_,exploits/_,_; s_,shellcodes/_,_; s_,papers/_,_' \ 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 $( echo ${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"). if [[ "${EXACT}" -eq 1 ]]; then FILEPATH=0 fi ## Case sensitive? if [[ "${SCASE}" -eq 1 ]]; then ## Remove the default flags CASE_TAG_GREP="" CASE_TAG_FGREP="" fi ## Remove leading space TAGS="$( echo ${TAGS} | sed -e 's/^[[:space:]]//' )" #-----------------------------------------------------------------------------# ## 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