View difference between Paste ID: NkheBeb0 and zG3ph7y5
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/env bash
2
# @author: Danilo Basanta
3
# @author-linkedin: https://www.linkedin.com/in/danilobasanta/
4
# @author-github: https://github.com/dabasanta
5
6
# GLOBAL VARS - DO NOT MODIFY
7
export DOMAIN="None"
8
export EXTENDED_CHECKS=false
9
export zone_transfer="No"
10
export dns_servers_count=0
11
export subdomains_count=0
12
export webservers_count=0
13
# Modify the DNS_BRUTE_THREADS variable to set the number of threads to use in the custom-dictionary attack.
14
# By default, the script will use 15% of the number of records in the dictionary.
15
export DNS_BRUTE_THREADS=0
16
17
# CRTL+C handler
18
function scape() {
19
  clean
20
}
21
trap scape INT
22
23
# Creating the tmp directory
24
tmpdir="/tmp/dnsexplorer"
25
mkdir -p $tmpdir
26
mkdir -p $tmpdir/whatweb
27
mkdir -p $tmpdir/wafw00f
28
tput civis
29
30
end="\e[0m"
31
info="\e[36m[+]"
32
cyan="\e[36m"
33
output_color="\e[0m\e[36m"
34
error="\e[1m\e[91m[!]"
35
question="\e[93m"
36
yellow="\e[1m\e[93m"
37
green="\e[92m"
38
ok="\e[1m\e[92m"
39
resalted_output="\e[1;37m"
40
41
# Clean -exit- function
42
clean(){
43
    echo -e "\n\n"
44
    rm -rf $tmpdir
45
    echo -e "${end}Happy hunting."
46
    tput cnorm
47
    exit 0
48
}
49
50
# Custom dicctionary attack function
51
dictionaryAttackCustom() { #5
52
  dicc_outfile="$tmpdir/dicctionary.results.txt"
53
  : > "$dicc_outfile"
54
  check=0
55
  while [ "$check" -eq 0 ]; do
56
    echo -e "$question"
57
    read -rp "Ingrese la ruta del archivo de diccionario> " dfile
58
    echo -e "$end"
59
60
    if [ ! -f "$dfile" ] || [[ $(file "$dfile" | awk '{print $2}') != @(ASCII|Unicode) ]]; then
61
      echo -e "$error El archivo $dfile no existe o no es un archivo de texto ASCII/Unicode."
62
    else
63
      check=1
64
    fi
65
  done
66
67
  lon_dicc=$(wc -l < "$dfile")
68
  tput civis
69
70
  threads=$(echo "scale=0; ($lon_dicc * 0.15 + 0.5)/1" | bc)
71
  [ "$threads" -lt 1 ] && threads=1
72
  [ "$threads" -gt 40 ] && threads=40
73
  [ "$DNS_BRUTE_THREADS" -ne 0 ] && threads="$DNS_BRUTE_THREADS"
74
  echo -e "    ${question}This file has $lon_dicc records, $threads parallel processes will be used to speed up the attack, press any key to start\n"
75
  read -n 1 -s -r -p ""
76
77
  grep -Eva '[^a-zA-Z0-9\-_]' < "$dfile" | xargs -P $threads -I {} sh -c '
78
    GREEN="\033[32m"
79
    resalted_output="\e[1;37m"
80
    RESET="\033[0m"
81
    lon_dicc="$4"
82
    if host "$1.$2" 2>/dev/null | head -1 | grep -q "has address"; then
83
      echo "$1.$2" >> "$3"
84
      printf "    [+] Found: ${resalted_output}%s.%s${RESET}\n" "$1" "$2"
85
    fi
86
    
87
    echo "." >> /tmp/dnsexplorer/tracker.txt
88
    len=$(wc -l < /tmp/dnsexplorer/tracker.txt)
89
    dicc=$()
90
    percent=$(echo "scale=2; ($len / $lon_dicc) * 100" | bc)
91
    printf "${GREEN}[%.0f%%] Reading file...${RESET}\r" $percent
92
  ' _ {} "$DOMAIN" "$dicc_outfile" "$lon_dicc"
93
94
  total=$(wc -l < $dicc_outfile)
95
  echo ""
96
  echo -e "${info} $total Subdomains found.${end}"
97
  echo ""
98
  crtSH "dicattack"
99
}
100
101
# Dictionary attack function
102
dictionaryAttack(){ #5
103
  tput civis
104
  bitquark="$tmpdir/bit.txt"
105
  dicc_outfile="$tmpdir/dicctionary.results.txt"
106
  echo "" > $dicc_outfile
107
  echo -e "\n${info}Using SECLISTS: bitquark-subdomains-top100000.txt${end}\n"
108
  curl -s https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/bitquark-subdomains-top100000.txt -o $bitquark
109
  l_bitq=$(cat $bitquark | wc -l)
110
  touch $tmpdir/tracker.txt
111
  if [ $l_bitq -gt 999 ];then
112
    grep -v '^ *#' < "$bitquark" | xargs -P 40 -I {} sh -c '
113
    GREEN="\033[32m"
114
    resalted_output="\e[1;37m"
115
    RESET="\033[0m"
116
117
    if host "$1.$2" | head -1 | grep -q "has address"; then
118
      echo "$1.$2" >> "$3"
119
      printf "    [+] Found: ${resalted_output}%s.%s${RESET}\n" "$1" "$2"
120
    fi
121
    
122
    echo "." >> /tmp/dnsexplorer/tracker.txt
123
    len=$(wc -l < /tmp/dnsexplorer/tracker.txt)
124
    percent=$(echo "scale=2; ($len / 100000) * 100" | bc)
125
    printf "${GREEN}[%.0f%%] Reading file...${RESET}\r" $percent
126
  ' _ {} "$DOMAIN" "$dicc_outfile"
127
  total=$(wc -l < $dicc_outfile)
128
  echo ""
129
  echo -e "${info} $total Subdomains found.${end}"
130
  echo ""
131
  crtSH "dicattack"
132
  else
133
    echo -e "$error Could not download dictionary from seclists url.$end"
134
    dictionaryAttackCustom
135
  fi
136
}
137
138
# DNS Brute Force handler function - Trigger the dictorinary attack
139
bruteForceDNS(){
140
  echo -e " 
141
  ██████╗ ██╗ ██████╗ ██████╗████████╗██╗ ██████╗ ███╗   ██╗ █████╗ ██████╗ ██╗   ██╗
142
  ██╔══██╗██║██╔════╝██╔════╝╚══██╔══╝██║██╔═══██╗████╗  ██║██╔══██╗██╔══██╗╚██╗ ██╔╝
143
  ██║  ██║██║██║     ██║        ██║   ██║██║   ██║██╔██╗ ██║███████║██████╔╝ ╚████╔╝ 
144
  ██║  ██║██║██║     ██║        ██║   ██║██║   ██║██║╚██╗██║██╔══██║██╔══██╗  ╚██╔╝  
145
  ██████╔╝██║╚██████╗╚██████╗   ██║   ██║╚██████╔╝██║ ╚████║██║  ██║██║  ██║   ██║   
146
  ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   
147
                                                                                     
148
                   █████╗ ████████╗████████╗ █████╗  ██████╗██╗  ██╗                 
149
                  ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝                 
150
                  ███████║   ██║      ██║   ███████║██║     █████╔╝                  
151
                  ██╔══██║   ██║      ██║   ██╔══██║██║     ██╔═██╗                  
152
                  ██║  ██║   ██║      ██║   ██║  ██║╚██████╗██║  ██╗                 
153
                  ╚═╝  ╚═╝   ╚═╝      ╚═╝   ╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝                 
154
                                                                                     
155
  \n\t\t$output_color Fuzzing subdomains of $1[++]$end\n
156
  ${question}Do yo want to use a custom dictionary? [c=custom/d=default]$end
157
  $info Default: Provides a dictionary with the top 1000 of the most commonly used subdomains.
158
  $info Custom: Use your own custom dictionary.$question\n"
159
160
  while true; do
161
    read -rp "[d/c]> " dc
162
    case $dc in
163
      [Dd]* ) dictionaryAttack; break;;
164
      [Cc]* ) dictionaryAttackCustom; break;;
165
      * ) echo -e "$error Please answer$green D$end \e[1m\e[91mor$end$green\e[1m C$end\e[1m\e[91m.$end\n";;
166
    esac
167
  done
168
}
169
170
# Check if the subdomain has webserver
171
check_web_server() { #8
172
  local end="\e[0m"
173
  local cyan="\e[1m\e[36m"
174
  local ok="\e[1m\e[92m"
175
  local resalted_output="\e[1;37m"
176
  local -r webservers_outfile=$(mktemp /tmp/dnsexplorer/XXXX.webservers.txt)
177
178
  local response=$(curl -m 3 --head --silent --output /dev/null --write-out '%{http_code}' "http://${1}")
179
  if ((response >= 100 && response <= 599)); then
180
    [ "$protocol" == "https" ] && secure=" secure"
181
    echo -e "${end}The domain ${resalted_output}$1${end} has a web server. [${cyan}HTTP${end}:$ok$response$end]"
182
    echo "http://$1" >> "$webservers_outfile"
183
  fi
184
185
  local response=$(curl -m 3 --head --silent --output /dev/null --write-out '%{http_code}' "https://${1}")
186
  if ((response >= 100 && response <= 599)); then
187
    [ "$protocol" == "https" ] && secure=" secure"
188
    echo -e "${end}The domain $resalted_output$1$end has a secure web server. [${cyan}HTTPS${end}:$ok$response$end]"
189
    echo "https://$1" >> "$webservers_outfile"
190
  fi
191
}
192
export -f check_web_server
193
194
# Abuse of crt.sh website - Internet connection required.
195
crtSH(){ #7
196
  declare -r call_source="$1"
197
198
  # Make sure the function is called from another function
199
  if [ -z "$call_source" ]; then
200
    echo -e "$error The function 'crtSH' must be called from another function.$end"
201
    return 1
202
  fi
203
204
  crtshoutput="$tmpdir/crtsh-output.txt" # crt.sh RAW output
205
  crtsh_parsed_output="$tmpdir/crt.sh.reg" # crt.sh parsed output
206
  subdomain_file="$output/$DOMAIN.subdomains.txt" # Subdomains results without wildcard
207
  subdomain_wildcard_file="$output/$DOMAIN.wildcard.txt" # Subdomains results with wildcard
208
  final_outputfile="$output/$DOMAIN-all.txt" # Final output subdomain file
209
  webservers_outfile="$output/$DOMAIN.webservers" # Final output subdomain file
210
  SANs_tmp_file=$(mktemp $tmpdir/XXX.SAN.tmp) # Final output SAN certificates file
211
  new_SANs="$tmpdir/found_new_SAN.tmp"
212
213
  if [ "$call_source" == "dicattack" ];then
214
    previus_result="$tmpdir/dicctionary.results.txt" # Dictionary attack results
215
  fi
216
  if [ "$call_source" == "zonetransfer" ];then
217
    previus_result="$output/$DOMAIN.zoneTransfer.txt" # Dictionary attack results
218
    #zonetransfer_tmp_data=$(cat "$previus_result"|  grep -E "([a-zA-Z0-9.-]+)\.$DOMAIN.*" | awk '{print $1}' | sort -u | grep -v '_' | sed 's/\.$//')
219
    cat "$previus_result" | grep -E "([a-zA-Z0-9.-]+)\.$DOMAIN.*" | awk '{print $1}' | sort -u | grep -v '_' | sed 's/\.$//' > $tmpdir/zonetransfer.results.txt
220
    zonetransfer_tmp_data="$tmpdir/zonetransfer.results.txt"
221
  fi
222
  if [ "$call_source" == "None" ];then
223
    previus_result="None" # Dictionary attack results
224
  fi
225
226
  echo "" > $subdomain_file && echo "" > $subdomain_wildcard_file && echo "" > $SANs_tmp_file && echo "" > $new_SANs
227
  echo -e "\n$info Finding subdomains - abusing Certificate Transparency Logs using https://crt.sh/\n$end"
228
229
  max_retries=3
230
  retry_count=0
231
  crtsh_susscess=1
232
233
  while [ $retry_count -lt $max_retries ]; do
234
    # Make sure we have a valid response
235
    crtsh_response=$(curl -s -w "%{size_download} %{http_code}" "https://crt.sh/?q=${DOMAIN}&output=json" -o $crtshoutput)
236
    crtsh_response_size=$(echo $crtsh_response | awk '{print $1}')
237
    crtsh_response_code=$(echo $crtsh_response | awk '{print $2}')
238
239
    if [ "$crtsh_response_size" -gt 2 ] && [ "$crtsh_response_code" -eq 200 ]; then
240
      real_response_size=$(wc -c < $crtshoutput)
241
242
      if [ "$real_response_size" -gt 2 ]; then
243
244
      ### INTEGRAR VALIDACION DE SI EL ATAQUE DE DICCIONARIO SE LLEVO A CABO
245
246
247
        grep -o '"common_name":"[^"]*' $crtshoutput | awk -F ':"' '{ print $2 }' | sort -u > $crtsh_parsed_output
248
        size_crtsh_output=$(wc -l < $crtsh_parsed_output)
249
250
        # Filter and sort subdomains only once
251
        sorted_subdomains=$(sort -u $crtsh_parsed_output)
252
253
        # Check if there are wildcard subdomains
254
        if echo "$sorted_subdomains" | grep -q '^\*\.'; then
255
          # Calculate the size of the output list - with wildcard
256
          crtsh_parsed_output_wildcard_size=$(echo "$sorted_subdomains" | grep '^\*\.' | wc -l)
257
258
          # Calculate the size of the output list - without wildcard
259
          crtsh_parsed_output_no_wildcard_size=$(echo "$sorted_subdomains" | grep -v '^\*\.' | wc -l)
260
261
          # Save subdomains without wildcard
262
          echo "$sorted_subdomains" | sed 's/^\*\.//g' > $subdomain_file
263
264
          # Save subdomains with wildcard
265
          echo "$sorted_subdomains" | grep '^\*\.' > $subdomain_wildcard_file
266
267
          # Print results
268
          echo -e "$ok[$resalted_output$crtsh_parsed_output_no_wildcard_size$ok] subdomains found"
269
          echo -e "$ok[$resalted_output$crtsh_parsed_output_wildcard_size$ok] wildcard subdomains found"
270
          echo -e "$ok[$resalted_output$size_crtsh_output$ok] total subdomains"
271
          crtsh_susscess=0
272
        else
273
          # Save subdomains without wildcard
274
          echo "$sorted_subdomains" > $subdomain_file
275
276
          # Print results
277
          echo -e "$info CRTsh results\n[$size_crtsh_output] total subdomains found"
278
          crtsh_susscess=0
279
        fi
280
281
        if [ "$call_source" == "zonetransfer" ]; then
282
          cat "$zonetransfer_tmp_data" "$subdomain_wildcard_file" "$subdomain_file" | sed '/^$/d' | sed 's/*\.//g' | sort -u > "$final_outputfile"
283
        elif [ "$call_source" == "dicattack" ]; then
284
          cat "$previus_result" "$subdomain_wildcard_file" "$subdomain_file" | sed '/^$/d' | sed 's/*\.//g' | sort -u > "$final_outputfile"
285
        else
286
          cat "$subdomain_wildcard_file" "$subdomain_file" | sed '/^$/d' | sed 's/*\.//g' | sort -u > "$final_outputfile"
287
        fi
288
289
        # Calculate the number of threads to use
290
        count_domains=$(wc -l < "$final_outputfile")
291
        threads=$(echo "scale=0; ($count_domains * 0.15 + 0.5)/1" | bc)
292
        [ "$threads" -lt 1 ] && threads=1
293
        [ "$threads" -gt 25 ] && threads=25
294
295
        # Print info
296
        echo -e "$info Loaded $count_domains targets...$end\n\n"
297
298
        # Execute the check_web_server function in parallel
299
        sort -u "$final_outputfile" | parallel -j "$threads" check_web_server
300
301
        break
302
      else
303
        echo -e "$error Unable to connect to CTR.sh$end"
304
        retry_count=$((retry_count + 1))
305
      fi
306
    else
307
      echo -e "$error Unable to connect to CTR.sh$end"
308
      retry_count=$((retry_count + 1))
309
    fi
310
  done
311
312
  # if we have reached the max number of retries
313
  if [ "$max_retries" -eq "$retry_count" ]; then
314
315
    # if the previus_result variable is empty and the crtsh was not success
316
    if [[ "$call_source" == "None" && "$crtsh_susscess" -eq 1 ]]; then
317
318
      # No previus result and crtsh was not success, try to enumerate the main domain
319
      echo -e "$error It was not possible to find subdomains using any conventional enumeration method. Running the extra mile......$end"
320
      check_web_server "$DOMAIN"
321
      cat /tmp/dnsexplorer/*.webservers.txt > "$webservers_outfile"
322
      webservers_count=$(wc -l < $webservers_outfile)
323
324
      # Check if there are webservers
325
      if [ "$webservers_count" -gt 0 ]; then
326
        echo -e "$ok[$resalted_output$webservers_count$ok] webservers found"
327
        protocol=$(grep "https://" "$webservers_outfile" | head -1 | awk -F'://' '{print $1}')
328
329
        # Check if there are https webservers
330
        if [ "$protocol" == "https" ]; then
331
          echo -e "$info Checking for SANs in the certificate$end"
332
          echo "" > "$SANs_tmp_file"
333
          grep "https://" "$webservers_outfile" | xargs -I {} -P 1 -n 1 bash -c 'checkCertificateSubjectsAlternativeNames "$1" "$2" "$3"' _ {} 443 "$SANs_tmp_file"
334
335
        # Check if there are http webservers
336
        elif [ "$protocol" == "http" ]; then
337
          if $EXTENDED_CHECKS;then
338
            local -r webEnumOutputCSV="$output/$DOMAIN.webenum.csv"
339
            echo "URL,HTTPServer,IP,PoweredBy,X-Powered-By,Country,WAF" > "$webEnumOutputCSV"
340
341
            count_webservers=$(wc -l < "$webservers_outfile")
342
            threads_webenum=$(echo "scale=0; ($count_webservers * 0.15 + 0.5)/1" | bc)
343
            [ "$threads_webenum" -lt 1 ] && threads=1
344
            [ "$threads_webenum" -gt 25 ] && threads=25
345
346
            sort -u "$webservers_outfile" | parallel -j "$threads_webenum" webEnum
347
            cat $tmpdir/whatweb/*.csv >> "$webEnumOutputCSV"
348
349
            printResults 0.05
350
            # FIN
351
          fi
352
        fi
353
      else
354
        echo -e "$error No webservers found$end"
355
        printResults 0.05
356
        clean
357
        # FIN
358
      fi
359
    else
360
361
      # Check if the previus result is not empty
362
      if [ "$call_source" == "zonetransfer" ];then
363
        echo "$zonetransfer_tmp_data" > "$final_outputfile"
364
        count_domains=$(wc -l < "$final_outputfile")
365
        threads=$(echo "scale=0; ($count_domains * 0.15 + 0.5)/1" | bc)
366
        [ "$threads" -lt 1 ] && threads=1
367
        [ "$threads" -gt 25 ] && threads=25
368
        echo -e "$info Loaded $count_domains targets...$end\n\n"
369
        sort -u "$final_outputfile" | parallel -j "$threads" check_web_server
370
371
      # Check if the previus result is not empty
372
      elif [ "$call_source" == "dicattack" ];then
373
        cp "$previus_result" "$final_outputfile"
374
        count_domains=$(wc -l < "$final_outputfile")
375
        threads=$(echo "scale=0; ($count_domains * 0.15 + 0.5)/1" | bc)
376
        [ "$threads" -lt 1 ] && threads=1
377
        [ "$threads" -gt 25 ] && threads=25
378
        echo -e "$info Loaded $count_domains targets...$end\n\n"
379
        sort -u "$final_outputfile" | parallel -j "$threads" check_web_server
380
      fi
381
    fi
382
  fi
383
384
  # Even if crt.sh has no results, the script takes the data from the source and performs the web server check, even if dicct attacks was not successful.
385
386
  # Merge all the webservers files
387
  cat /tmp/dnsexplorer/*.webservers.txt | sort -u > "$webservers_outfile"
388
  https_count=$(grep "https://" "$webservers_outfile" | wc -l)
389
  threads_enumSANs=$(echo "scale=0; ($https_count * 0.15 + 0.5)/1" | bc)
390
  [ "$threads_enumSANs" -lt 1 ] && threads=1
391
  [ "$threads_enumSANs" -gt 25 ] && threads=25
392
393
  # Check if there are SANs
394
  grep "https://" "$webservers_outfile" | xargs -I {} -P "$threads_enumSANs" -n 1 bash -c 'checkCertificateSubjectsAlternativeNames "$1" "$2" "$3"' _ {} 443 "$SANs_tmp_file"
395
396
  # Discover new SANs
397
  sort -u $SANs_tmp_file | sed 's/^\*\.//g' | grep -E '.*\.'$domain'\\b' > $new_SANs
398
  new_subdomains=$(mktemp $tmpdir/XXX.new_subdomains.tmp)
399
  curated_previus_webservers=$(mktemp $tmpdir/XXX.curated_previus_webservers.tmp)
400
  cat "$webservers_outfile" | sed 's/http\:\/\///g' | sed 's/https\:\/\///g' | sort -u > $curated_previus_webservers
401
  grep -Fxv -f $curated_previus_webservers $new_SANs > $new_subdomains
402
  count_newsubdomains=$(wc -l < $new_subdomains)
403
404
  # Print results
405
  if [ "$count_newsubdomains" -gt 0 ]; then
406
    echo -e "\n${info}${count_newsubdomains} New subdomains was found${end}"
407
    cat $new_subdomains
408
    cat $new_subdomains >> $final_outputfile
409
  else
410
    echo -e "\n${cyan}No new subdomains found"
411
  fi
412
413
  webservers_count=$(wc -l < $webservers_outfile)
414
  subdomains_count=$(wc -l < $final_outputfile)
415
416
  # ExtendedChecks
417
  if $EXTENDED_CHECKS;then
418
    local -r webEnumOutputCSV="$output/$DOMAIN.webenum.csv"
419
    echo "URL,HTTPServer,IP,PoweredBy,X-Powered-By,Country,WAF" > "$webEnumOutputCSV"
420
421
    # Calculate the number of threads to use and launch the webEnum function in parallel
422
    count_webservers=$(wc -l < "$webservers_outfile")
423
    threads_webenum=$(echo "scale=0; ($count_webservers * 0.15 + 0.5)/1" | bc)
424
    [ "$threads_webenum" -lt 1 ] && threads=1
425
    [ "$threads_webenum" -gt 25 ] && threads=25
426
    echo "" # Just a blank line :)
427
    sort -u "$webservers_outfile" | parallel -j "$threads_webenum" webEnum
428
429
    # Merge all the CSV files
430
    cat $tmpdir/whatweb/*.csv >> "$webEnumOutputCSV"
431
  fi
432
433
  printResults 0.05
434
}
435
436
# Print results function
437
printResults() {
438
  delay=$1
439
440
  DNSExplorerResults="Domain:${DOMAIN}
441
      DNS Servers: ${dns_servers_count}
442
      Zone Transfer: ${zone_transfer}
443
      Subdomains: ${subdomains_count}
444
      Webservers: ${webservers_count}"
445
  echo -e "\e[92;1m"
446
  for i in $(seq 0 $((${#DNSExplorerResults} - 1))); do
447
    echo -ne "${DNSExplorerResults:$i:1}"
448
    sleep "$delay"
449
  done
450
  echo -e "\e[0m"  # New line
451
}
452
453
# Extended checks capabilities
454
webEnum() { #9
455
  info="\e[36m[+]"
456
  end="\e[0m"
457
  if [ -z "$1" ]; then
458
    return 1
459
  fi
460
461
  if [[ $1 != http://* && $1 != https://* ]]; then
462
    return 1
463
  fi
464
465
  local -r wafwoof_txt_tmp_output=$(mktemp /tmp/dnsexplorer/wafw00f/XXX.wafw00f)
466
  local -r output_txt_webenum_file=$(mktemp /tmp/dnsexplorer/whatweb/XXX.csv)
467
468
  # Check if the site has a WAF
469
  wafw00f "$1" -f json -o "$wafwoof_txt_tmp_output" > /dev/null 2>&1
470
  firewall_detected=$(cat "$wafwoof_txt_tmp_output" | grep -oE '"firewall": "[^"]+"' | awk -F': ' '{gsub(/[",]/, "", $2); print $2}')
471
  [ -z "$firewall_detected" ] && firewall_detected="None"
472
473
  # Check webserver technologies
474
  local -r ww_result=$(whatweb --no-errors --open-timeout=7 --read-timeout=15 --colour=never "$1" 2>/dev/null)
475
476
  echo -e "${info} Testing webserver ${end}$1"
477
478
  # Get the webserver technologies
479
  httpserver=$(echo "$ww_result" | grep -o 'HTTPServer\[[^]]*\]' | cut -d'[' -f2 | cut -d']' -f1)
480
  [ -z "$httpserver" ] && httpserver="None"
481
  ip=$(echo "$ww_result" | grep -o 'IP\[[^]]*\]' | cut -d'[' -f2 | cut -d']' -f1 | head -1)
482
  [ -z "$ip" ] && ip="None"
483
  poweredby=$(echo "$ww_result" | grep -o 'PoweredBy\[[^]]*\]' | cut -d'[' -f2 | cut -d']' -f1)
484
  [ -z "$country" ] && country="None"
485
  xpoweredby=$(echo "$ww_result" | grep -o 'X-Powered-By\[[^]]*\]' | cut -d'[' -f2 | cut -d']' -f1)
486
  [ -z "$poweredby" ] && poweredby="None"
487
  country=$(echo "$ww_result" | grep -o 'Country\[[^]]*\]' | cut -d'[' -f2 | cut -d']' -f1)
488
  [ -z "$xpoweredby" ] && xpoweredby="None"
489
490
  # Save results
491
  echo "\"$1\",\"$httpserver\",\"$ip\",\"$poweredby\",\"$xpoweredby\",\"$country\",\"$firewall_detected\"" > "$output_txt_webenum_file"
492
}
493
export -f webEnum
494
495
# Funcion for checking if the site has Subject Alternative Names
496
checkCertificateSubjectsAlternativeNames() { #6
497
  port=$2
498
  declare output_file="$3"
499
500
  if [ -z "$1" ]; then
501
    return 1
502
  fi
503
504
  if [ -z "$port" ]; then
505
    port=443
506
  fi
507
508
  if [ -z "$output_file" ]; then
509
    return 1
510
  fi
511
512
  server=$(echo "$1" | sed 's/http:\/\///' | sed 's/https:\/\///' | sed 's/\/$//')
513
514
  # Make sure opnessl can connect to the site
515
  connected=$(echo -n | openssl s_client -connect  "$server:$port" 2>/dev/null | head -1 | awk -F "(" '{print $1}')
516
517
  # Check if the site has a webserver
518
  if [[ "$connected" == "CONNECTED" ]];then
519
520
    # Check if the site has DNS names
521
    DNS=$(echo -n | openssl s_client -connect "$server:$port" 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text 2> /dev/null | sed 's/\                //'|grep -i "DNS:" | awk -F ":" '{print $1}')
522
523
    # Make sure the site has DNS names
524
    if [[ "$DNS" == "DNS" ]];then
525
526
      # Get the number of Subject Alternative Names
527
      len_subjects=$(echo -n | openssl s_client -connect "$server:$port" 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text 2>/dev/null | grep "DNS:" 2>/dev/null | tr ',' '\n' | sed 's/\               //' | wc -l)
528
529
      # Check if the site has almost one Subject Alternative Names
530
      if [ $len_subjects -ge 1 ];then
531
532
        # Get the Subject Alternative Names
533
        SANs=$(echo -n | openssl s_client -connect "$server:$port" 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text | grep "DNS:"| tr ',' '\n' | sed 's/\               //' | sed 's/\s//g' | sed 's/DNS://g')
534
535
        # return the Subject Alternative Names
536
        echo "$SANs" >> "$output_file"
537
      else
538
        return 1
539
      fi
540
    else
541
      return 1
542
    fi
543
  else
544
    return 1
545
  fi
546
}
547
export -f checkCertificateSubjectsAlternativeNames
548
549
# Initial recon
550
initHostRecon(){ #3
551
  echo -e "\n${info} A records for ${resalted_output}${DOMAIN}${end}${cyan} domain${end}"
552
  # A Records
553
  host "$DOMAIN" | grep 'has address' | awk '{print $4}'
554
555
  echo -e "\n${info} AAA records for ${resalted_output}${DOMAIN}${end}${cyan} domain${end}"
556
  # AAA Records
557
  if host "$DOMAIN" | grep 'IPv6' >/dev/null 2>&1;then
558
    host "$DOMAIN" | grep 'IPv6'| awk '{print $5}'
559
  else
560
    echo -e "$question Hosts $DOMAIN has not IPv6 address"
561
  fi
562
563
  echo -e "\n${info} MX records for ${resalted_output}${DOMAIN}${end}${cyan} domain${end}"
564
  # MAIL Records
565
  if host -t MX "$DOMAIN" | grep 'mail' >/dev/null 2>&1;then
566
    host "$DOMAIN" | grep 'mail' | awk '{print $6,$7}'
567
  else
568
    echo -e "$question Hosts $DOMAIN has not mail server records\n"
569
  fi
570
571
  echo -e "\n${info} CNAME records for ${resalted_output}${DOMAIN}${end}${cyan} domain${end}"
572
  # CNAME Records
573
  if host -t CNAME "$DOMAIN" | grep 'alias' >/dev/null 2>&1;then
574
    host -t CNAME "$DOMAIN" | awk '{print $1,$4,$6}'
575
  else
576
    echo -e "$question Hosts $DOMAIN has not alias records"
577
  fi
578
579
  echo -e "\n${info} TXT records for ${resalted_output}${DOMAIN}${end}${cyan} domain${end}"
580
` # TXT Records`
581
  if host -t txt "$DOMAIN" | grep 'descriptive' >/dev/null 2>&1;then
582
    host -t txt "$DOMAIN" | grep 'descriptive'
583
  else
584
    echo -e "$question Hosts $DOMAIN has not description records\n"
585
  fi
586
}
587
588
# Zone transfer attack function
589
doZoneTransfer(){ #4
590
  success=1
591
  if host -t NS "$DOMAIN" | grep 'name server' >/dev/null 2>&1;then
592
    echo -e "\n${info} Enumerating DNS Servers..."
593
    host -t NS "$DOMAIN" | cut -d " " -f 4 > $tmpdir/NameServers.txt
594
595
    ns=$(wc -l $tmpdir/NameServers.txt | awk '{print $1}')
596
597
    if [ $ns -ge 1 ];then
598
      echo -e "    ${green}[${ns}] DNS Servers was found, trying ZoneTransfer on these servers${end}"
599
      dns_servers_count=$ns
600
601
      # Verify if the DNS servers accept zone transfer
602
      while IFS= read -r nameserver;do
603
        host -t axfr "$DOMAIN" "$nameserver" | grep -E 'Received ([0-9]+) bytes from [0-9\.]+#[0-9]+ in ([0-9]+) ms' >/dev/null 2>&1
604
605
        if [ $? -eq 0 ];then
606
          axfr_tmp_file="$tmpdir/axfr.tmp" && echo "" > "$axfr_tmp_file"
607
          axfr_parsed_file="$tmpdir/parsed_axfr.tmp" && echo "" > "$axfr_tmp_file"
608
          host -t axfr "$DOMAIN" "$nameserver" > "$axfr_tmp_file"
609
          declare -a record_types=("A" "AAA" "AXFR" "CNAME" "MX" "NS" "SOA" "SRV" "TXT")
610
          total_records=0
611
          success=0
612
613
          # SHow blinking message
614
          for i in {1..3}; do
615
            echo -ne "${ok}\e[5;7mNameServer ${nameserver} accept ZoneTransfer\e[0m"
616
            sleep 0.5
617
            echo -ne "\r\e[K"
618
            sleep 0.5
619
          done
620
621
          for record_type in "${record_types[@]}"; do
622
            # Extract the current records of the current type
623
            current_records=$(cat "$axfr_tmp_file" | grep "IN[[:space:]]\+$record_type" | sort -u | awk '{print $1 "\t" $NF}' | column -t -s $'\t')
624
625
            # Verify if there are records of the current type
626
            if [ -n "$current_records" ]; then
627
              count_current_records=$(cat "$axfr_tmp_file" | grep "IN[[:space:]]\+$record_type" | sort -u | wc -l)
628
              echo -e "${info} ${count_current_records} '$record_type' records found:${end}\n$current_records\n"
629
              echo -e "$current_records" >> "$axfr_parsed_file"
630
              total_records=$((total_records + count_current_records))
631
            fi
632
          done
633
          echo -e "${ok}[${total_records}] Records found in $nameserver$end\nPlease take note of the other DNS servers, they may do zone transfers as well.${end}"
634
          break
635
        else
636
          echo -e "    $error NameServer $nameserver does not accept zone transfer$end"
637
        fi
638
      done < <(grep -v '^ *#' < $tmpdir/NameServers.txt)
639
    else
640
      echo -e "$error No DNS servers found for $DOMAIN$end"
641
    fi
642
643
    # Check if the zone transfer was successful
644
    if [ $success -eq 0 ];then
645
      echo -e "\n$ok DNS zone transfer was possible, no bruteforce attacks on the subdomains are required. $end\n"
646
647
      cp "$axfr_parsed_file" $output/$DOMAIN.zoneTransfer.txt
648
      zone_transfer="Yes"
649
      crtSH "zonetransfer"
650
      clean
651
652
    # If the zonetransfer was not successful, then call to bruteforce
653
    else
654
      echo -e "\n$error DNS zone transfer was not possible, DNS servers are not accept it"
655
656
      while true; do
657
        echo ""
658
        tput cnorm
659
        echo -e "$question"
660
        read -rp "Do you want to brute force subdomains? [Y/n]> " yn
661
        echo -e "$end"
662
663
        case $yn in
664
          [Yy]* ) bruteForceDNS; clean; break;;
665
          [Nn]* ) crtSH "None"; clean;;
666
          * ) echo -e "$error Please answer yes or no.$end\n";;
667
        esac
668
      done
669
    fi
670
  fi
671
}
672
673
# Init recon with 'host' command
674
basicRecon(){  #2
675
  initHostRecon
676
  doZoneTransfer
677
}
678
679
# Check dependencies: curl, host, parallel.
680
checkDependencies() {
681
  declare -A dependencies=(
682
    ["host"]="bind-utils/dnsutils"
683
    ["curl"]="curl"
684
    ["parallel"]="Parallel"
685
    ["bc"]="BC"
686
  )
687
  for cmd in "${!dependencies[@]}"; do
688
    if ! command -v "$cmd" &> /dev/null; then
689
      echo -e "$error '$cmd' command is not available, please install the ${dependencies[$1]} package. $end"
690
        clean
691
    fi
692
  done
693
}
694
695
# Check opt dependencies: wafw00f, whatweb.
696
optDependencies() { 
697
  declare -A dependencies=(
698
    ["whatweb"]="whatweb"
699
    ["wafw00f"]="wafw00f"
700
  )
701
  for cmd in "${!dependencies[@]}"; do
702
    if ! command -v "$cmd" &> /dev/null; then
703
      echo -e "$error '$cmd' command is not available, please install the ${dependencies[$1]} package for extended checks. $end"
704
        clean
705
    fi
706
  done
707
}
708
709
banner(){
710
    echo -e "\e[91m
711
        ▓█████▄  ███▄    █   ██████ ▓█████ ▒██   ██▒ ██▓███   ██▓     ▒█████   ██▀███  ▓█████  ██▀███  
712
        ▒██▀ ██▌ ██ ▀█   █ ▒██    ▒ ▓█   ▀ ▒▒ █ █ ▒░▓██░  ██▒▓██▒    ▒██▒  ██▒▓██ ▒ ██▒▓█   ▀ ▓██ ▒ ██▒
713
        ░██   █▌▓██  ▀█ ██▒░ ▓██▄   ▒███   ░░  █   ░▓██░ ██▓▒▒██░    ▒██░  ██▒▓██ ░▄█ ▒▒███   ▓██ ░▄█ ▒
714
        ░▓█▄   ▌▓██▒  ▐▌██▒  ▒   ██▒▒▓█  ▄  ░ █ █ ▒ ▒██▄█▓▒ ▒▒██░    ▒██   ██░▒██▀▀█▄  ▒▓█  ▄ ▒██▀▀█▄  
715
        ░▒████▓ ▒██░   ▓██░▒██████▒▒░▒████▒▒██▒ ▒██▒▒██▒ ░  ░░██████▒░ ████▓▒░░██▓ ▒██▒░▒████▒░██▓ ▒██▒
716
        ▒▒▓  ▒ ░ ▒░   ▒ ▒ ▒ ▒▓▒ ▒ ░░░ ▒░ ░▒▒ ░ ░▓ ░▒▓▒░ ░  ░░ ▒░▓  ░░ ▒░▒░▒░ ░ ▒▓ ░▒▓░░░ ▒░ ░░ ▒▓ ░▒▓░
717
        ░ ▒  ▒ ░ ░░   ░ ▒░░ ░▒  ░ ░ ░ ░  ░░░   ░▒ ░░▒ ░     ░ ░ ▒  ░  ░ ▒ ▒░   ░▒ ░ ▒░ ░ ░  ░  ░▒ ░ ▒░
718
        ░ ░  ░    ░   ░ ░ ░  ░  ░     ░    ░    ░  ░░         ░ ░   ░ ░ ░ ▒    ░░   ░    ░     ░░   ░ 
719
        ░             ░       ░     ░  ░ ░    ░               ░  ░    ░ ░     ░        ░  ░   ░     
720
      v:2.0 ░ $end By: Danilo Basanta (https://github.com/dabasanta/) ░ (https://www.linkedin.com/in/danilobasanta/)\n\n
721
722
723
\033[3mThe author does not promote malicious actions or the use of the script for illegal operations. Remember to always obtain prior permission from the target company's system administrators before performing any malicious actions.\033[0m\n\n"
724
}
725
726
help(){ # Simply help function
727
    echo -e "\e[91m                               
728
        @@@  @@@  @@@@@@@@  @@@       @@@@@@@   
729
        @@@  @@@  @@@@@@@@  @@@       @@@@@@@@  
730
        @@!  @@@  @@!       @@!       @@!  @@@  
731
        !@!  @!@  !@!       !@!       !@!  @!@  
732
        @!@!@!@!  @!!!:!    @!!       @!@@!@!   
733
        !!!@!!!!  !!!!!:    !!!       !!@!!!    
734
        !!:  !!!  !!:       !!:       !!:       
735
        :!:  !:!  :!:        :!:      :!:       
736
        ::   :::   :: ::::   :: ::::   ::       
737
        :   : :  : :: ::   : :: : :   :        
738
v:2.0   ░ By: Danilo Basanta (https://github.com/dabasanta/) ░ (https://www.linkedin.com/in/danilobasanta/)\n\n${end}
739
"
740
741
    options=$(cat <<- EOM
742
${resalted_output}Usage:${end}        ${green}\e[3m./DNSExplorer.sh <domain>${end}
743
744
${resalted_output}Extended:${end}     ${green}\e[3m./DNSExplorer.sh <domain> --extended${end}
745
746
${resalted_output}Help:${end}         ${green}\e[3m-h, --help Display this help and exit${end}
747
748
EOM
749
    )
750
echo -e "$options"
751
tput cnorm
752
}
753
754
main(){ #1
755
  export output="$DOMAIN.out"
756
  mkdir -p $output
757
758
  banner
759
  checkDependencies
760
  if ping -c 1 "$DOMAIN" > /dev/null 2>&1;then
761
      if host "$DOMAIN" > /dev/null 2>&1;then
762
          basicRecon "$DOMAIN"
763
      else
764
          echo -e "$error No route to host, please verify your DNS server or internet connection$end"
765
          clean
766
      fi
767
  else
768
      echo -e "${question}PING was not success, does server ignoring ICMP packets?$end"
769
      if host "$DOMAIN" > /dev/null 2>&1;then
770
          echo -e "${info}Running checks anyway$end\n"
771
          basicRecon "$DOMAIN"
772
      else
773
          echo -e "$error No route to host, please verify your DNS server or internet connection$end"
774
          clean
775
      fi
776
  fi
777
}
778
779
# Init  flow
780
if [ "$1" = "-h" ] || [ "$1" = "help" ] || [ "$1" = "--help" ] || [ "$2" = "-h" ] || [ "$2" = "--help" ] || [ "$2" = "help" ]; then
781
  help
782
elif [ $# -eq 2 ]; then
783
784
  # Check if the second parameter is --extended
785
  if [ "$2" = "--extended" ]; then
786
    DOMAIN=$1
787
    EXTENDED_CHECKS=true
788
    optDependencies
789
    main "$DOMAIN"
790
  else
791
    echo -e "$error Parameter '$2' is not recognized $end"
792
    help
793
    tput cnorm
794
    exit 1
795
  fi
796
elif [ $# -eq 0 ]; then
797
  help
798
  tput cnorm
799
  exit 1
800
else
801
  DOMAIN=$1
802
  main "$DOMAIN"
803
fi