forked from testssl/testssl.sh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtestssl.sh
executable file
·17780 lines (16485 loc) · 853 KB
/
testssl.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
#
# vim:ts=5:sw=5:expandtab
# we have a spaces softtab, that ensures readability with other editors too
# testssl.sh is a program for spotting weak SSL encryption, ciphers, version and some
# vulnerabilities or features
#
# Devel version is available from https://github.com/drwetter/testssl.sh
# Stable version from https://testssl.sh
# Please file bugs at github! https://github.com/drwetter/testssl.sh/issues
#
# Project lead and initiator: Dirk Wetter, copyleft: 2007-today, contributions so far see CREDITS.md
# Main contriubtions from David Cooper
#
# License: GPLv2, see http://www.fsf.org/licensing/licenses/info/GPLv2.html
# and accompanying license "LICENSE.txt". Redistribution + modification under this
# license permitted.
# If you enclose this script or parts of it in your software, it has to
# be accompanied by the same license (see link) and the place where to get
# the recent version of this program. Do not violate the license and if
# you do not agree to all of these terms, do not use it in the first place.
#
# OpenSSL, which is being used and maybe distributed via one of this projects'
# web sites, is subject to their licensing: https://www.openssl.org/source/license.txt
#
# The client simulation data comes from SSLlabs and is licensed to the 'Qualys SSL Labs
# Terms of Use' (v2.2), see https://www.ssllabs.com/downloads/Qualys_SSL_Labs_Terms_of_Use.pdf,
# stating a CC BY 3.0 US license: https://creativecommons.org/licenses/by/3.0/us/
#
# Please note: USAGE WITHOUT ANY WARRANTY, THE SOFTWARE IS PROVIDED "AS IS".
# USE IT AT your OWN RISK!
# Seriously! The threat is you run this code on your computer and input could be /
# is being supplied via untrusted sources.
#
# HISTORY:
# Back in 2006 it all started with a few openssl commands...
# That's because openssl is a such a good swiss army knife (see e.g.
# wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist
# wrapping some shell commands around it, which I used for my pen tests. This is how
# everything started.
# Now it has grown up, it has bash socket support for most features, which has been basically
# replacing more and more functions of OpenSSL and some sockets functions serve as some kind
# of central functions.
#
# WHY BASH?
# Cross-platform is one of the three main goals of this script. Second: Ease of installation.
# No compiling, install gems, go to CPAN, use pip etc. Third: Easy to use and to interpret
# the results.
# /bin/bash including the builtin sockets fulfill all that. The socket checks in bash may sound
# cool and unique -- they are -- but probably you can achieve e.g. the same result with my favorite
# interactive shell: zsh (zmodload zsh/net/socket -- checkout zsh/net/tcp) too! Oh, and btw.
# ksh93 has socket support too.
# /bin/bash though is way more often used within Linux and it's perfect for cross platform support.
# MacOS X has it and also under Windows the MSYS2 extension or Cygwin as well as Bash on Windows (WSL)
# has /bin/bash.
#
# Q: So what's the difference to www.ssllabs.com/ssltest/ or sslcheck.globalsign.com/ ?
# A: As of now ssllabs only check 1) webservers 2) on standard ports, 3) reachable from the
# internet. And those examples above 4) are 3rd parties. If these restrictions are all fine
# with you and you need a management compatible rating -- go ahead and use those.
#
# But also if your fine with those restrictions: testssl.sh is meant as a tool in your hand
# and it's way more flexible. Oh, and did I mention testssl.sh is open source?
#
#################### Stop talking, action now ####################
########### Definition of error codes
#
declare -r ERR_BASH=255 # Bash version incorrect
declare -r ERR_CMDLINE=254 # Cmd line couldn't be parsed
declare -r ERR_FCREATE=253 # Output file couldn't be created
declare -r ERR_FNAMEPARSE=252 # Input file couldn't be parsed
declare -r ERR_NOSUPPORT=251 # Feature requested is not supported
declare -r ERR_OSSLBIN=250 # Problem with OpenSSL binary
declare -r ERR_DNSBIN=249 # Problem with DNS lookup binaries
declare -r ERR_OTHERCLIENT=248 # Other client problem
declare -r ERR_DNSLOOKUP=247 # Problem with resolving IP addresses or names
declare -r ERR_CONNECT=246 # Connectivity problem
declare -r ERR_CLUELESS=245 # Weird state, either though user options or testssl.sh
declare -r ERR_RESOURCE=244 # Resources testssl.sh needs couldn't be read
declare -r ERR_CHILD=242 # Child received a signal from master
declare -r ALLOK=0 # All is fine
[ -z "${BASH_VERSINFO[0]}" ] && printf "\n\033[1;35m Please make sure you're using \"bash\"! Bye...\033[m\n\n" >&2 && exit $ERR_BASH
[ $(kill -l | grep -c SIG) -eq 0 ] && printf "\n\033[1;35m Please make sure you're calling me without leading \"sh\"! Bye...\033[m\n\n" >&2 && exit $ERR_BASH
[ ${BASH_VERSINFO[0]} -lt 3 ] && printf "\n\033[1;35m Minimum requirement is bash 3.2. You have $BASH_VERSION \033[m\n\n" >&2 && exit $ERR_BASH
[ ${BASH_VERSINFO[0]} -le 3 -a ${BASH_VERSINFO[1]} -le 1 ] && printf "\n\033[1;35m Minimum requirement is bash 3.2. You have $BASH_VERSION \033[m\n\n" >&2 && exit $ERR_BASH
########### Debugging helpers + profiling
#
declare -r PS4='|${LINENO}> \011${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
DEBUGTIME=${DEBUGTIME:-false} # stackoverflow.com/questions/5014823/how-to-profile-a-bash-shell-script-slow-startup#20855353, profiling bash
DEBUG_ALLINONE=${DEBUG_ALLINONE:-false} # true: do debugging in one sceen (old behaviour for testssl.sh and bash3's default
# false: needed for performance analysis or useful for just having an extra file
DEBUG_ALLINONE=${SETX:-false} # SETX as a shortcut for old style debugging, overriding DEBUG_ALLINONE
if [[ "$SHELLOPTS" =~ xtrace ]]; then
if "$DEBUGTIME"; then
# separate debugging, doesn't mess up the screen, $DEBUGTIME determines whether we also do performance analysis
exec 42>&2 2> >(tee /tmp/testssl-$$.log | sed -u 's/^.*$/now/' | date -f - +%s.%N >/tmp/testssl-$$.time)
# BASH_XTRACEFD=42
else
if ! "$DEBUG_ALLINONE"; then
exec 42>| /tmp/testssl-$$.log
BASH_XTRACEFD=42
fi
fi
fi
########### Traps! Make sure that temporary files are cleaned up after use in ANY case
#
trap "cleanup" QUIT EXIT
trap "child_error" USR1
########### Internal definitions
#
declare -r VERSION="3.0rc1"
declare -r SWCONTACT="dirk aet testssl dot sh"
egrep -q "dev|rc|beta" <<< "$VERSION" && \
SWURL="https://testssl.sh/dev/" ||
SWURL="https://testssl.sh/"
declare -r CVS_REL="$(tail -5 "$0" | awk '/dirkw Exp/ { print $4" "$5" "$6}')"
declare -r CVS_REL_SHORT="$(tail -5 "$0" | awk '/dirkw Exp/ { print $4 }')"
if git log &>/dev/null; then
declare -r GIT_REL="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $1" "$2" "$3 }')"
declare -r GIT_REL_SHORT="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $1 }')"
declare -r REL_DATE="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $2 }')"
else
declare -r REL_DATE="$(tail -5 "$0" | awk '/dirkw Exp/ { print $5 }')"
fi
declare -r PROG_NAME="$(basename "$0")"
declare -r RUN_DIR="$(dirname "$0")"
declare -r SYSTEM="$(uname -s)"
SYSTEM2="" # currently only being used for WSL = bash on windows
TESTSSL_INSTALL_DIR="${TESTSSL_INSTALL_DIR:-""}" # If you run testssl.sh and it doesn't find it necessary file automagically set TESTSSL_INSTALL_DIR
CA_BUNDLES_PATH="${CA_BUNDLES_PATH:-""}" # You can have your stores some place else
ADDITIONAL_CA_FILES="${ADDITIONAL_CA_FILES:-""}" # single file with a CA in PEM format or comma separated lists of them
CIPHERS_BY_STRENGTH_FILE=""
TLS_DATA_FILE="" # mandatory file for socket-based handdhakes
OPENSSL_LOCATION=""
HNAME="$(hostname)"
HNAME="${HNAME%%.*}"
declare CMDLINE
declare -r -a CMDLINE_ARRAY=("$@") # When performing mass testing, the child processes need to be sent the
declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and http://mywiki.wooledge.org/BashFAQ/050).
########### Some predefinitions: date, sed (we always use test and not try to determine
# capabilities by querying the OS)
#
HAS_GNUDATE=false
HAS_FREEBSDDATE=false
HAS_OPENBSDDATE=false
if date -d @735275209 >/dev/null 2>&1; then
if date -r @735275209 >/dev/null 2>&1; then
# it can't do any conversion from a plain date output
HAS_OPENBSDDATE=true
else
HAS_GNUDATE=true
fi
fi
# FreeBSD and OS X date(1) accept "-f inputformat"
date -j -f '%s' 1234567 >/dev/null 2>&1 && \
HAS_FREEBSDDATE=true
echo A | sed -E 's/A//' >/dev/null 2>&1 && \
declare -r HAS_SED_E=true || \
declare -r HAS_SED_E=false
########### Terminal defintions
tty -s && \
declare -r INTERACTIVE=true || \
declare -r INTERACTIVE=false
if [[ -z $TERM_WIDTH ]]; then # no batch file and no otherwise predefined TERM_WIDTH
if ! tput cols &>/dev/null || ! "$INTERACTIVE";then # Prevent tput errors if running non interactive
export TERM_WIDTH=${COLUMNS:-80}
else
export TERM_WIDTH=${COLUMNS:-$(tput cols)} # for custom line wrapping and dashes
fi
fi
TERM_CURRPOS=0 # custom line wrapping needs alter the current horizontal cursor pos
########### Defining (and presetting) variables which can be changed
#
# Following variables make use of $ENV and can be used like "OPENSSL=<myprivate_path_to_openssl> ./testssl.sh <URI>"
declare -x OPENSSL OPENSSL_TIMEOUT
PHONE_OUT=${PHONE_OUT:-false} # Whether testssl can retrieve CRLs and OCSP
FAST_SOCKET=${FAST_SOCKET:-false} # EXPERIMENTAL feature to accelerate sockets -- DO NOT USE it for production
COLOR=${COLOR:-2} # 3: Extra color (ciphers, curves), 2: Full color, 1: B/W only 0: No ESC at all
COLORBLIND=${COLORBLIND:-false} # if true, swap blue and green in the output
SHOW_EACH_C=${SHOW_EACH_C:-false} # where individual ciphers are tested show just the positively ones tested
SHOW_SIGALGO=${SHOW_SIGALGO:-false} # "secret" switch whether testssl.sh shows the signature algorithm for -E / -e
SNEAKY=${SNEAKY:-false} # is the referer and useragent we leave behind just usual?
QUIET=${QUIET:-false} # don't output the banner. By doing this you acknowledge usage term appearing in the banner
SSL_NATIVE=${SSL_NATIVE:-false} # we do per default bash sockets where possible "true": switch back to "openssl native"
ASSUME_HTTP=${ASSUME_HTTP:-false} # in seldom cases (WAF, old servers, grumpy SSL) service detection fails. "True" enforces HTTP checks
BUGS=${BUGS:-""} # -bugs option from openssl, needed for some BIG IP F5
WARNINGS=${WARNINGS:-""} # can be either off or batch
DEBUG=${DEBUG:-0} # 1: normal putput the files in /tmp/ are kept for further debugging purposes
# 2: list more what's going on , also lists some errors of connections
# 3: slight hexdumps + other info,
# 4: display bytes sent via sockets
# 5: display bytes received via sockets
# 6: whole 9 yards
FAST=${FAST:-false} # preference: show only first cipher, run_allciphers with openssl instead of sockets
WIDE=${WIDE:-false} # whether to display for some options just ciphers or a table w hexcode/KX,Enc,strength etc.
MASS_TESTING_MODE=${MASS_TESTING_MODE:-serial} # can be serial or parallel. Subject to change
LOGFILE="${LOGFILE:-""}" # logfile if used
JSONFILE="${JSONFILE:-""}" # jsonfile if used
CSVFILE="${CSVFILE:-""}" # csvfile if used
HTMLFILE="${HTMLFILE:-""}" # HTML if used
FNAME=${FNAME:-""} # file name to read commands from
FNAME_PREFIX=${FNAME_PREFIX:-""} # output filename prefix, see --outprefix
APPEND=${APPEND:-false} # append to csv/json file instead of overwriting it
[[ -z "$NODNS" ]] && declare NODNS # If unset it does all DNS lookups per default. "min" only for hosts or "none" at all
HAS_IPv6=${HAS_IPv6:-false} # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes
ALL_CLIENTS=${ALL_CLIENTS:-false} # do you want to run all client simulation form all clients supplied by SSLlabs?
OFFENSIVE=${OFFENSIVE:-true} # do you want to include offensive vulnerability tests which may cause blocking by an IDS?
########### Tuning vars which cannot be set by a cmd line switch. Use instead e.g "HEADER_MAXSLEEP=10 ./testssl.sh <your_args_here>"
#
EXPERIMENTAL=${EXPERIMENTAL:-false}
PROXY_WAIT=${PROXY_WAIT:-20} # waiting at max 20 seconds for socket reply through proxy
HEADER_MAXSLEEP=${HEADER_MAXSLEEP:-5} # we wait this long before killing the process to retrieve a service banner / http header
MAX_SOCKET_FAIL=${MAX_SOCKET_FAIL:-2} # If this many failures for TCP socket connects are reached we terminate
MAX_OSSL_FAIL=${MAX_OSSL_FAIL:-2} # If this many failures for s_client connects are reached we terminate
MAX_HEADER_FAIL=${MAX_HEADER_FAIL:-3} # If this many failures for HTTP GET are encountered we terminate
MAX_WAITSOCK=${MAX_WAITSOCK:-10} # waiting at max 10 seconds for socket reply. There shouldn't be any reason to change this.
CCS_MAX_WAITSOCK=${CCS_MAX_WAITSOCK:-5} # for the two CCS payload (each). There shouldn't be any reason to change this.
HEARTBLEED_MAX_WAITSOCK=${HEARTBLEED_MAX_WAITSOCK:-8} # for the heartbleed payload. There shouldn't be any reason to change this.
STARTTLS_SLEEP=${STARTTLS_SLEEP:-10} # max time wait on a socket for STARTTLS. MySQL has a fixed value of 1 which can't be overwritten (#914)
FAST_STARTTLS=${FAST_STARTTLS:-true} # at the cost of reliabilty decrease the handshakes for STARTTLS
USLEEP_SND=${USLEEP_SND:-0.1} # sleep time for general socket send
USLEEP_REC=${USLEEP_REC:-0.2} # sleep time for general socket receive
HSTS_MIN=${HSTS_MIN:-179} # >179 days is ok for HSTS
HSTS_MIN=$((HSTS_MIN * 86400)) # correct to seconds
HPKP_MIN=${HPKP_MIN:-30} # >=30 days should be ok for HPKP_MIN, practical hints?
HPKP_MIN=$((HPKP_MIN * 86400)) # correct to seconds
DAYS2WARN1=${DAYS2WARN1:-60} # days to warn before cert expires, threshold 1
DAYS2WARN2=${DAYS2WARN2:-30} # days to warn before cert expires, threshold 2
pVULN_THRESHLD=${VULN_THRESHLD:-1} # if vulnerabilities to check >$VULN_THRESHLD we DON'T show a separate header line in the output each vuln. check
DNS_VIA_PROXY=${DNS_VIA_PROXY:-true} # do DNS lookups via proxy. --ip=proxy reverses this
UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses
NO_ENGINE=${NO_ENGINE:-false} # if there are problems finding the (external) openssl engine set this to true
declare -r CLIENT_MIN_PFS=5 # number of ciphers needed to run a test for PFS
CAPATH="${CAPATH:-/etc/ssl/certs/}" # Does nothing yet (FC has only a CA bundle per default, ==> openssl version -d)
GOOD_CA_BUNDLE="" # A bundle of CA certificates that can be used to validate the server's certificate
STAPLED_OCSP_RESPONSE=""
MEASURE_TIME_FILE=${MEASURE_TIME_FILE:-""}
if [[ -n "$MEASURE_TIME_FILE" ]] && [[ -z "$MEASURE_TIME" ]]; then
MEASURE_TIME=true
else
MEASURE_TIME=${MEASURE_TIME:-false}
fi
DISPLAY_CIPHERNAMES="openssl" # display OpenSSL ciphername (but both OpenSSL and RFC ciphernames in wide mode)
declare -r UA_STD="TLS tester from $SWURL"
declare -r UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
########### Initialization part, further global vars just being declared here
#
IKNOW_FNAME=false
FIRST_FINDING=true # is this the first finding we are outputting to file?
JSONHEADER=true # include JSON headers and footers in HTML file, if one is being created
CSVHEADER=true # same for CSV
HTMLHEADER=true # same for HTML
SECTION_FOOTER_NEEDED=false # kludge for tracking whether we need to close the JSON section object
GIVE_HINTS=false # give an additional info to findings
SERVER_SIZE_LIMIT_BUG=false # Some servers have either a ClientHello total size limit or a 128 cipher limit (e.g. old ASAs)
CHILD_MASS_TESTING=${CHILD_MASS_TESTING:-false}
HAD_SLEPT=0
NR_SOCKET_FAIL=0 # Counter for socket failures
NR_OSSL_FAIL=0 # .. for OpenSSL connects
NR_HEADER_FAIL=0 # .. for HTTP_GET
PROTOS_OFFERED="" # This keeps which protocol is being offered. See has_server_protocol().
DETECTED_TLS_VERSION=""
TLS_EXTENSIONS=""
declare -r NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1"
# alpn_protos needs to be space-separated, not comma-seperated, including odd ones observerd @ facebook and others, old ones like h2-17 omitted as they could not be found
declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp"
declare -a SESS_RESUMPTION
TEMPDIR=""
TMPFILE=""
ERRFILE=""
CLIENT_AUTH=false
NO_SSL_SESSIONID=false
HOSTCERT="" # File with host certificate, without intermediate certificate
HEADERFILE=""
HEADERVALUE=""
HTTP_STATUS_CODE=""
KEY_SHARE_EXTN_NR="33" # The extension number for key_share was changed from 40 to 51 in TLSv1.3 draft 23.
# In order to support draft 23 and later in addition to earlier drafts, need to
# know which extension number to use. Note that it appears that a single
# ClientHello cannot advertise both draft 23 and later and earlier drafts.
# Preset may help to deal with STARTTLS + TLS 1.3 draft 23 and later but not earlier.
BAD_SERVER_HELLO_CIPHER=false # reserved for cases where a ServerHello doesn't contain a cipher offered in the ClientHello
GOST_STATUS_PROBLEM=false
PATTERN2SHOW=""
SOCK_REPLY_FILE=""
NW_STR=""
LEN_STR=""
SNI=""
POODLE="" # keep vulnerability status for TLS_FALLBACK_SCSV
OSSL_NAME="" # openssl name, in case of LibreSSL it's LibreSSL
OSSL_VER="" # openssl version, will be auto-determined
OSSL_VER_MAJOR=0
OSSL_VER_MINOR=0
OSSL_VER_APPENDIX="none"
CLIENT_PROB_NO=1
HAS_DH_BITS=${HAS_DH_BITS:-false} # initialize openssl variables
OSSL_SUPPORTED_CURVES=""
HAS_SSL2=false
HAS_SSL3=false
HAS_TLS13=false
HAS_PKUTIL=false
HAS_PKEY=false
HAS_NO_SSL2=false
HAS_NOSERVERNAME=false
HAS_CIPHERSUITES=false
HAS_COMP=false
HAS_NO_COMP=false
HAS_ALPN=false
HAS_NPN=false
HAS_FALLBACK_SCSV=false
HAS_PROXY=false
HAS_XMPP=false
HAS_POSTGRES=false
HAS_MYSQL=false
HAS_CHACHA20=false
HAS_AES128_GCM=false
HAS_AES256_GCM=false
PORT=443 # unless otherwise auto-determined, see below
NODE=""
NODEIP=""
rDNS=""
CORRECT_SPACES="" # Used for IPv6 and proper output formatting
IPADDRs=""
IP46ADDRs=""
LOCAL_A=false # Does the $NODEIP come from /etc/hosts?
LOCAL_AAAA=false # Does the IPv6 IP come from /etc/hosts?
XMPP_HOST=""
PROXY=""
PROXYIP=""
PROXYPORT=""
VULN_COUNT=0
SERVICE="" # Is the server running an HTTP server, SMTP, POP or IMAP?
URI=""
CERT_FINGERPRINT_SHA2=""
RSA_CERT_FINGERPRINT_SHA2=""
STARTTLS_PROTOCOL=""
OPTIMAL_PROTO="" # Need this for IIS6 (sigh) + OpenSSL 1.0.2, otherwise some handshakes will fail see
# https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
STARTTLS_OPTIMAL_PROTO="" # Same for STARTTLS, see https://github.com/drwetter/testssl.sh/issues/188
TLS_TIME="" # To keep the value of TLS server timestamp
TLS_NOW="" # Similar
TLS_DIFFTIME_SET=false # Tells TLS functions to measure the TLS difftime or not
NOW_TIME=""
HTTP_TIME=""
GET_REQ11=""
START_TIME=0 # time in epoch when the action started
END_TIME=0 # .. ended
SCAN_TIME=0 # diff of both: total scan time
LAST_TIME=0 # only used for performance measurements (MEASURE_TIME=true)
SERVER_COUNTER=0 # Counter for multiple servers
TLS_LOW_BYTE="" # For "secret" development stuff, see -q below
HEX_CIPHER="" # "
########### Global variables for parallel mass testing
#
declare -r PARALLEL_SLEEP=1 # Time to sleep after starting each test
MAX_WAIT_TEST=${MAX_WAIT_TEST:-1200} # Maximum time (in seconds) to wait for a test to complete
MAX_PARALLEL=${MAX_PARALLEL:-20} # Maximum number of tests to run in parallel
# This value may be made larger on systems with faster processors
declare -a -i PARALLEL_TESTING_PID=() # process id for each child test (or 0 to indicate test has already completed)
declare -a PARALLEL_TESTING_CMDLINE=() # command line for each child test
declare -i NR_PARALLEL_TESTS=0 # number of parallel tests run
declare -i NEXT_PARALLEL_TEST_TO_FINISH=0 # number of parallel tests that have completed and have been processed
declare FIRST_JSON_OUTPUT=true # true if no output has been added to $JSONFILE yet.
########### Cipher suite information
#
declare -i TLS_NR_CIPHERS=0
declare TLS_CIPHER_HEXCODE=()
declare TLS_CIPHER_OSSL_NAME=()
declare TLS_CIPHER_RFC_NAME=()
declare TLS_CIPHER_SSLVERS=()
declare TLS_CIPHER_KX=()
declare TLS_CIPHER_AUTH=()
declare TLS_CIPHER_ENC=()
declare TLS_CIPHER_EXPORT=()
declare TLS_CIPHER_OSSL_SUPPORTED=()
declare TLS13_OSSL_CIPHERS="TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256"
########### Severity functions and globals
#
INFO=0
OK=0
LOW=1
MEDIUM=2
HIGH=3
CRITICAL=4
SEVERITY_LEVEL=0
set_severity_level() {
local severity=$1
if [[ "$severity" == "LOW" ]]; then
SEVERITY_LEVEL=$LOW
elif [[ "$severity" == "MEDIUM" ]]; then
SEVERITY_LEVEL=$MEDIUM
elif [[ "$severity" == "HIGH" ]]; then
SEVERITY_LEVEL=$HIGH
elif [[ "$severity" == "CRITICAL" ]]; then
SEVERITY_LEVEL=$CRITICAL
else
# WARN will always be logged
echo "Supported severity levels are LOW, MEDIUM, HIGH, CRITICAL!"
help 1
fi
}
show_finding() {
local severity=$1
( [[ "$severity" == "DEBUG" ]] ) ||
( [[ "$severity" == "INFO" ]] && [[ $SEVERITY_LEVEL -le $INFO ]] ) ||
( [[ "$severity" == "OK" ]] && [[ $SEVERITY_LEVEL -le $OK ]] ) ||
( [[ "$severity" == "LOW" ]] && [[ $SEVERITY_LEVEL -le $LOW ]] ) ||
( [[ "$severity" == "MEDIUM" ]] && [[ $SEVERITY_LEVEL -le $MEDIUM ]] ) ||
( [[ "$severity" == "HIGH" ]] && [[ $SEVERITY_LEVEL -le $HIGH ]] ) ||
( [[ "$severity" == "WARN" ]] ) ||
( [[ "$severity" == "CRITICAL" ]] && [[ $SEVERITY_LEVEL -le $CRITICAL ]] )
}
########### Output functions
# For HTML output, replace any HTML reserved characters with the entity name
html_reserved(){
local output
"$do_html" || return 0
#sed -e 's/\&/\&/g' -e 's/</\</g' -e 's/>/\>/g' -e 's/"/\"/g' -e "s/'/\'/g" <<< "$1"
output="${1//\&/\&}"
output="${output//</\<}"
output="${output//>/\>}"
output="${output//\"/\"}"
output="${output//\'/\'}"
tm_out "$output"
return 0
}
html_out() {
"$do_html" || return 0
[[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && printf -- "%b" "${1//%/%%}" >> "$HTMLFILE"
# here and other printf's: a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%'. '--' and %b do the rest.
}
# This is intentionally the same.
safe_echo() { printf -- "%b" "${1//%/%%}"; }
tm_out() { printf -- "%b" "${1//%/%%}"; }
tmln_out() { printf -- "%b" "${1//%/%%}\n"; }
out() { printf -- "%b" "${1//%/%%}"; html_out "$1"; }
outln() { printf -- "%b" "${1//%/%%}\n"; html_out "$1\n"; }
#TODO: Still no shell injection safe but if just run it from the cmd line: that's fine
# Color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
tm_liteblue() { [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && tm_out "\033[0;32m$1" || tm_out "\033[0;34m$1" ) || tm_out "$1"; tm_off; } # not yet used
pr_liteblue() { tm_liteblue "$1"; [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && html_out "<span style=\"color:#00cd00;\">$(html_reserved "$1")</span>" || html_out "<span style=\"color:#0000ee;\">$(html_reserved "$1")</span>" ) || html_out "$(html_reserved "$1")"; }
tmln_liteblue() { tm_liteblue "$1"; tmln_out; }
prln_liteblue() { pr_liteblue "$1"; outln; }
tm_blue() { [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && tm_out "\033[1;32m$1" || tm_out "\033[1;34m$1" ) || tm_out "$1"; tm_off; } # used for head lines of single tests
pr_blue() { tm_blue "$1"; [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && html_out "<span style=\"color:lime;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "<span style=\"color:#5c5cff;font-weight:bold;\">$(html_reserved "$1")</span>" ) || html_out "$(html_reserved "$1")"; }
tmln_blue() { tm_blue "$1"; tmln_out; }
prln_blue() { pr_blue "$1"; outln; }
# we should be able to use aliases here
tm_warning() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[0;35m$1" || tm_underline "$1"; tm_off; } # some local problem: one test cannot be done
tmln_warning() { tm_warning "$1"; tmln_out; } # litemagenta
pr_warning() { tm_warning "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:#cd00cd;\">$(html_reserved "$1")</span>" || ( [[ "$COLOR" -eq 1 ]] && html_out "<u>$(html_reserved "$1")</u>" || html_out "$(html_reserved "$1")" ); }
prln_warning() { pr_warning "$1"; outln; }
tm_magenta() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[1;35m$1" || tm_underline "$1"; tm_off; } # fatal error: quitting because of this!
tmln_magenta() { tm_magenta "$1"; tmln_out; }
# different as warning above?
pr_magenta() { tm_magenta "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:magenta;font-weight:bold;\">$(html_reserved "$1")</span>" || ( [[ "$COLOR" -eq 1 ]] && html_out "<u>$(html_reserved "$1")</u>" || html_out "$(html_reserved "$1")" ); }
prln_magenta() { pr_magenta "$1"; outln; }
tm_litecyan() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[0;36m$1" || tm_out "$1"; tm_off; } # not yet used
tmln_litecyan() { tm_litecyan "$1"; tmln_out; }
pr_litecyan() { tm_litecyan "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:#00cdcd;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
prln_litecyan() { pr_litecyan "$1"; outln; }
tm_cyan() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[1;36m$1" || tm_out "$1"; tm_off; } # additional hint
tmln_cyan() { tm_cyan "$1"; tmln_out; }
pr_cyan() { tm_cyan "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:cyan;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
prln_cyan() { pr_cyan "$1"; outln; }
tm_litegrey() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[0;37m$1" || tm_out "$1"; tm_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876
tmln_litegrey() { tm_litegrey "$1"; tmln_out; } # not really usable on a black background, see ..
prln_litegrey() { pr_litegrey "$1"; outln; }
pr_litegrey() { tm_litegrey "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"color:darkgray;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
tm_grey() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[1;30m$1" || tm_out "$1"; tm_off; }
pr_grey() { tm_grey "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"color:#7f7f7f;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
tmln_grey() { tm_grey "$1"; tmln_out; }
prln_grey() { pr_grey "$1"; outln; }
tm_svrty_good() { [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && tm_out "\033[0;34m$1" || tm_out "\033[0;32m$1" ) || tm_out "$1"; tm_off; } # litegreen (liteblue), This is good
tmln_svrty_good() { tm_svrty_good "$1"; tmln_out; }
pr_svrty_good() { tm_svrty_good "$1"; [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && html_out "<span style=\"color:#0000ee;\">$(html_reserved "$1")</span>" || html_out "<span style=\"color:#00cd00;\">$(html_reserved "$1")</span>" ) || html_out "$(html_reserved "$1")"; }
prln_svrty_good() { pr_svrty_good "$1"; outln; }
tm_svrty_best() { [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && tm_out "\033[1;34m$1" || tm_out "\033[1;32m$1" ) || tm_out "$1"; tm_off; } # green (blue), This is the best
tmln_svrty_best() { tm_svrty_best "$1"; tmln_out; }
pr_svrty_best() { tm_svrty_best "$1"; [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && html_out "<span style=\"color:#5c5cff;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "<span style=\"color:lime;font-weight:bold;\">$(html_reserved "$1")</span>" ) || html_out "$(html_reserved "$1")"; }
prln_svrty_best() { pr_svrty_best "$1"; outln; }
tm_svrty_low() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[1;33m$1" || tm_out "$1"; tm_off; } # yellow brown | academic or minor problem
tmln_svrty_low() { tm_svrty_low "$1"; tmln_out; }
pr_svrty_low() { tm_svrty_low "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:#cdcd00;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
prln_svrty_low() { pr_svrty_low "$1"; outln; }
tm_svrty_medium() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[0;33m$1" || tm_out "$1"; tm_off; } # brown | it is not a bad problem but you shouldn't do this
pr_svrty_medium() { tm_svrty_medium "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:#cd8000;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
tmln_svrty_medium(){ tm_svrty_medium "$1"; tmln_out; }
prln_svrty_medium(){ pr_svrty_medium "$1"; outln; }
tm_svrty_high() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[0;31m$1" || tm_bold "$1"; tm_off; } # litered
pr_svrty_high() { tm_svrty_high "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:#cd0000;\">$(html_reserved "$1")</span>" || ( [[ "$COLOR" -eq 1 ]] && html_out "<span style=\"font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")" ); }
tmln_svrty_high() { tm_svrty_high "$1"; tmln_out; }
prln_svrty_high() { pr_svrty_high "$1"; outln; }
tm_svrty_critical() { [[ "$COLOR" -ge 2 ]] && tm_out "\033[1;31m$1" || tm_bold "$1"; tm_off; } # red
pr_svrty_critical() { tm_svrty_critical "$1"; [[ "$COLOR" -ge 2 ]] && html_out "<span style=\"color:red;font-weight:bold;\">$(html_reserved "$1")</span>" || ( [[ "$COLOR" -eq 1 ]] && html_out "<span style=\"font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")" ); }
tmln_svrty_critical() { tm_svrty_critical "$1"; tmln_out; }
prln_svrty_critical() { pr_svrty_critical "$1"; outln; }
tm_deemphasize() { tm_out "$1"; } # hook for a weakened screen output, see #600
pr_deemphasize() { tm_deemphasize "$1"; html_out "<span style=\"color:darkgray;\">$(html_reserved "$1")</span>"; }
tmln_deemphasize() { tm_deemphasize "$1"; tmln_out; }
prln_deemphasize() { pr_deemphasize "$1"; outln; }
# color=1 functions
tm_off() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[m"; }
tm_bold() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[1m$1" || tm_out "$1"; tm_off; }
tmln_bold() { tm_bold "$1"; tmln_out; }
pr_bold() { tm_bold "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
prln_bold() { pr_bold "$1" ; outln; }
tm_italic() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[3m$1" || tm_out "$1"; tm_off; }
tmln_italic() { tm_italic "$1" ; tmln_out; }
pr_italic() { tm_italic "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<i>$(html_reserved "$1")</i>" || html_out "$(html_reserved "$1")"; }
prln_italic() { pr_italic "$1"; outln; }
tm_strikethru() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[9m$1" || tm_out "$1"; tm_off; } # ugly!
tmln_strikethru() { tm_strikethru "$1"; tmln_out; }
pr_strikethru() { tm_strikethru "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<strike>$(html_reserved "$1")</strike>" || html_out "$(html_reserved "$1")"; }
prln_strikethru() { pr_strikethru "$1" ; outln; }
tm_underline() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[4m$1" || tm_out "$1"; tm_off; }
tmln_underline() { tm_underline "$1"; tmln_out; }
pr_underline() { tm_underline "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<u>$(html_reserved "$1")</u>" || html_out "$(html_reserved "$1")"; }
prln_underline() { pr_underline "$1"; outln; }
tm_reverse() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[7m$1" || tm_out "$1"; tm_off; }
tm_reverse_bold() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[7m\033[1m$1" || tm_out "$1"; tm_off; }
pr_reverse() { tm_reverse "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"color:white;background-color:black;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
pr_reverse_bold() { tm_reverse_bold "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"color:white;background-color:black;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
#pr_headline() { pr_blue "$1"; }
#http://misc.flogisoft.com/bash/tip_colors_and_formatting
#pr_headline() { [[ "$COLOR" -ge 2 ]] && out "\033[1;30m\033[47m$1" || out "$1"; tm_off; }
tm_headline() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[1m\033[4m$1" || tm_out "$1"; tm_off; }
tmln_headline() { tm_headline "$1"; tmln_out; }
pr_headline() { tm_headline "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"text-decoration:underline;font-weight:bold;\">$(html_reserved "$1")</span>" || html_out "$(html_reserved "$1")"; }
pr_headlineln() { pr_headline "$1" ; outln; }
tm_squoted() { tm_out "'$1'"; }
pr_squoted() { out "'$1'"; }
tm_dquoted() { tm_out "\"$1\""; }
pr_dquoted() { out "\"$1\""; }
# either files couldn't be found or openssl isn't good enough (which shouldn't happen anymore)
tm_local_problem() { tm_warning "Local problem: $1"; }
tmln_local_problem() { tmln_warning "Local problem: $1"; }
pr_local_problem() { pr_warning "Local problem: $1"; }
prln_local_problem() { prln_warning "Local problem: $1"; }
# general failure
tm_fixme() { tm_warning "Fixme: $1"; }
tmln_fixme() { tmln_warning "Fixme: $1"; }
pr_fixme() { pr_warning "Fixme: $1"; }
prln_fixme() { prln_warning "Fixme: $1"; }
pr_url() { tm_out "$1"; html_out "<a href="$1" style=\"color:black;text-decoration:none;\">$1</a>"; }
pr_boldurl() { tm_bold "$1"; html_out "<a href="$1" style=\"font-weight:bold;color:black;text-decoration:none;\">$1</a>"; }
### color switcher (see e.g. https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/
### http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
### no output support for HTML!
set_color_functions() {
local ncurses_tput=true
if [[ $(uname) == OpenBSD ]] && [[ "$TERM" =~ xterm-256 ]]; then
export TERM=xterm
# openBSD can't handle 256 colors (yet) in xterm which might lead to ugly errors
# like "tput: not enough arguments (3) for capability `AF'". Not our fault but
# before we get blamed we fix it here.
fi
# empty all vars if we have COLOR=0 equals no escape code:
red=""
green=""
brown=""
blue=""
magenta=""
cyan=""
grey=""
yellow=""
off=""
bold=""
underline=""
italic=""
type -p tput &>/dev/null || return 0 # Hey wait, do we actually have tput / ncurses ?
tput cols &>/dev/null || return 0 # tput under BSDs and GNUs doesn't work either (TERM undefined?)
tput sgr0 &>/dev/null || ncurses_tput=false
tput sgr 0 1 &>/dev/null || ncurses_tput=false # OpenBSD succeed the previous one but fails here
if [[ "$COLOR" -ge 2 ]]; then
if $ncurses_tput; then
red=$(tput setaf 1)
green=$(tput setaf 2)
brown=$(tput setaf 3)
blue=$(tput setaf 4)
magenta=$(tput setaf 5)
cyan=$(tput setaf 6)
grey=$(tput setaf 7)
yellow=$(tput setaf 3; tput bold)
else # this is a try for old BSD, see terminfo(5)
red=$(tput AF 1)
green=$(tput AF 2)
brown=$(tput AF 3)
blue=$(tput AF 4)
magenta=$(tput AF 5)
cyan=$(tput AF 6)
grey=$(tput AF 7)
yellow=$(tput AF 3; tput md)
fi
fi
if [[ "$COLOR" -ge 1 ]]; then
if $ncurses_tput; then
bold=$(tput bold)
underline=$(tput sgr 0 1 2>/dev/null)
italic=$(tput sitm)
italic_end=$(tput ritm)
off=$(tput sgr0)
else # this is a try for old BSD, see terminfo(5)
bold=$(tput md)
underline=$(tput us)
italic=$(tput ZH) # that doesn't work on FreeBSD 9+10.x
italic_end=$(tput ZR) # here too. Probably entry missing in /etc/termcap
reverse=$(tput mr)
off=$(tput me)
fi
# italic doesn't work under Linux, FreeBSD (9). But both work under OpenBSD.
# alternatively we could use escape codes
fi
}
strip_quote() {
# remove color codes (see http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed)
# \', leading and all trailing spaces
sed -e "s,$(echo -e "\033")\[[0-9;]*[a-zA-Z],,g" \
-e "s/\"/\\'/g" \
-e 's/^ *//g' \
-e 's/ *$//g' <<< "$1"
}
#################### JSON FILE FORMATTING ####################
fileout_json_footer() {
if "$do_json"; then
if [[ "$SCAN_TIME" -eq 0 ]]; then
fileout_json_finding "scanTime" "WARN" "Scan interrupted" "" "" ""
elif [[ $SEVERITY_LEVEL -lt $LOW ]] ; then
# no scan time in --severity=low and above, also needed for Travis, hackish...
fileout_json_finding "scanTime" "INFO" $SCAN_TIME "" "" ""
fi
printf "]\n" >> "$JSONFILE"
fi
if "$do_pretty_json"; then
if [[ "$SCAN_TIME" -eq 0 ]]; then
echo -e " ],\n \"scanTime\" : \"Scan interrupted\"\n}" >> "$JSONFILE"
else
echo -e " ],\n \"scanTime\" : ${SCAN_TIME}\n}" >> "$JSONFILE"
fi
fi
}
fileout_json_section() {
case $1 in
1) echo -e " \"singleCipher\" : [" ;;
2) echo -e " \"protocols\" : [" ;;
3) echo -e ",\n \"grease\" : [" ;;
4) echo -e ",\n \"ciphers\" : [" ;;
5) echo -e ",\n \"pfs\" : [" ;;
6) echo -e ",\n \"serverPreferences\" : [" ;;
7) echo -e ",\n \"serverDefaults\" : [" ;;
8) echo -e ",\n \"headerResponse\" : [" ;;
9) echo -e ",\n \"vulnerabilities\" : [" ;;
10) echo -e ",\n \"cipherTests\" : [" ;;
11) echo -e ",\n \"browserSimulations\": [" ;;
*) echo "invalid section" ;;
esac
}
fileout_section_header() {
local str=""
"$2" && str="$(fileout_section_footer false)"
"$do_pretty_json" && FIRST_FINDING=true && (printf "%s%s\n" "$str" "$(fileout_json_section "$1")") >> "$JSONFILE"
SECTION_FOOTER_NEEDED=true
}
# arg1: whether to end object too
fileout_section_footer() {
"$do_pretty_json" && printf "\n ]" >> "$JSONFILE"
"$do_pretty_json" && "$1" && echo -e "\n }" >> "$JSONFILE"
SECTION_FOOTER_NEEDED=false
}
fileout_json_print_parameter() {
local parameter="$1"
local filler="$2"
local value="$3"
local not_last="$4"
local spaces=""
"$do_json" && \
spaces=" " || \
spaces=" "
if [[ ! -z "$value" ]]; then
printf "%s%s%s%s" "$spaces" "\"$parameter\"" "$filler" ": \"$value\"" >> "$JSONFILE"
"$not_last" && printf ",\n" >> "$JSONFILE"
fi
}
fileout_json_finding() {
local target
local finding="$3"
local cve="$4"
local cwe="$5"
local hint="$6"
if "$do_json"; then
"$FIRST_FINDING" || echo -n "," >> "$JSONFILE"
echo -e " {" >> "$JSONFILE"
fileout_json_print_parameter "id" " " "$1" true
fileout_json_print_parameter "ip" " " "$NODE/$NODEIP" true
fileout_json_print_parameter "port" " " "$PORT" true
fileout_json_print_parameter "severity" " " "$2" true
fileout_json_print_parameter "cve" " " "$cve" true
fileout_json_print_parameter "cwe" " " "$cwe" true
"$GIVE_HINTS" && fileout_json_print_parameter "hint" " " "$hint" true
fileout_json_print_parameter "finding" " " "$finding" false
echo -e "\n }" >> "$JSONFILE"
fi
if "$do_pretty_json"; then
if [[ "$1" == "service" ]]; then
if [[ $SERVER_COUNTER -gt 1 ]]; then
echo " ," >> "$JSONFILE"
fi
target="$NODE"
$do_mx_all_ips && target="$URI"
echo -e " {
\"target host\" : \"$target\",
\"ip\" : \"$NODEIP\",
\"port\" : \"$PORT\",
\"service\" : \"$finding\"," >> "$JSONFILE"
$do_mx_all_ips && echo -e " \"hostname\" : \"$NODE\"," >> "$JSONFILE"
else
("$FIRST_FINDING" && echo -n " {" >> "$JSONFILE") || echo -n ",{" >> "$JSONFILE"
echo -e -n "\n" >> "$JSONFILE"
fileout_json_print_parameter "id" " " "$1" true
fileout_json_print_parameter "severity" " " "$2" true
fileout_json_print_parameter "cve" " " "$cve" true
fileout_json_print_parameter "cwe" " " "$cwe" true
"$GIVE_HINTS" && fileout_json_print_parameter "hint" " " "$hint" true
fileout_json_print_parameter "finding" " " "$finding" false
echo -e -n "\n }" >> "$JSONFILE"
fi
fi
}
##################### FILE FORMATTING #########################
fileout_pretty_json_banner() {
local target
if ! "$do_mass_testing"; then
[[ -z "$NODE" ]] && parse_hn_port "${URI}"
# NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place
target="$NODE"
$do_mx_all_ips && target="$URI"
fi
echo -e " \"Invocation\" : \"$PROG_NAME $CMDLINE\",
\"at\" : \"$HNAME:$OPENSSL_LOCATION\",
\"version\" : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\",
\"openssl\" : \"$OSSL_NAME $OSSL_VER from $OSSL_BUILD_DATE\",
\"startTime\" : \"$START_TIME\",
\"scanResult\" : ["
}
fileout_banner() {
#if ! "$APPEND"; then
# if "$CSVHEADER"; then
# :
# fi
if "$JSONHEADER"; then
# "$do_json" && # here we maybe should add a banner, too
"$do_pretty_json" && (printf "%s\n" "$(fileout_pretty_json_banner)") >> "$JSONFILE"
fi
#fi
}
fileout_separator() {
if "$JSONHEADER"; then
"$do_pretty_json" && echo " ," >> "$JSONFILE"
"$do_json" && echo -n "," >> "$JSONFILE"
fi
}
fileout_footer() {
if "$JSONHEADER"; then
fileout_json_footer
fi
# CSV: no footer
return 0
}
fileout_insert_warning() {
# See #815. Make sure we don't mess up the JSON PRETTY format if we complain with a client side warning.
# This should only be called if an *extra* warning will be printed (previously: 'fileout <extra_warning_ID> "WARN" '
# arg1: json identifier, arg2: normally "WARN", arg3: finding
if "$do_pretty_json"; then
echo -e " \"clientProblem${CLIENT_PROB_NO}\" : [" >>"$JSONFILE"
CLIENT_PROB_NO=$((CLIENT_PROB_NO + 1))
FIRST_FINDING=true # make sure we don't have a comma here
fi
fileout "$1" "$2" "$3"
if "$do_pretty_json"; then
echo -e "\n ]," >>"$JSONFILE"
fi
}
# ID, SEVERITY, FINDING, CVE, CWE, HINT
fileout() {
local severity="$2"
local cve="$4"
local cwe="$5"
local hint="$6"
if ( "$do_pretty_json" && [[ "$1" == "service" ]] ) || show_finding "$severity"; then
local finding=$(strip_lf "$(newline_to_spaces "$(strip_quote "$3")")")
[[ -e "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] && (fileout_json_finding "$1" "$severity" "$finding" "$cve" "$cwe" "$hint")
"$do_csv" && [[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] && \
echo -e \""$1\"",\"$NODE/$NODEIP\",\"$PORT"\",\""$severity"\",\""$finding"\",\""$cve"\",\""$cwe"\",\""$hint"\"" >> "$CSVFILE"
"$FIRST_FINDING" && FIRST_FINDING=false
fi
}
json_header() {
local fname_prefix
local filename_provided=false
[[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] && filename_provided=true
# Similar to HTML: Don't create headers and footers in the following scenarios:
# * no JSON/CSV output is being created.
# * mass testing is being performed and each test will have its own file.
# * this is an individual test within a mass test and all output is being placed in a single file.
! "$do_json" && ! "$do_pretty_json" && JSONHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && JSONHEADER=false && return 0
"$CHILD_MASS_TESTING" && "$filename_provided" && JSONHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
elif "$do_mass_testing"; then
:
elif "$do_mx_all_ips"; then
fname_prefix="${FNAME_PREFIX}mx-${URI}"
else
! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}"
# NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place
fname_prefix="${FNAME_PREFIX}${NODE}_p${PORT}"
fi
if [[ -z "$JSONFILE" ]]; then
JSONFILE="$fname_prefix-$(date +"%Y%m%d-%H%M".json)"
elif [[ -d "$JSONFILE" ]]; then
JSONFILE="$JSONFILE/${fname_prefix}-$(date +"%Y%m%d-%H%M".json)"
fi
if "$APPEND"; then
JSONHEADER=false
else
[[ -s "$JSONFILE" ]] && fatal "non-empty \"$JSONFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE
"$do_json" && echo "[" > "$JSONFILE"
"$do_pretty_json" && echo "{" > "$JSONFILE"
fi
#FIRST_FINDING=false
return 0
}
csv_header() {
local fname_prefix
local filename_provided=false
[[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] && filename_provided=true
# CSV similar:
! "$do_csv" && CSVHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && CSVHEADER=false && return 0
"$CHILD_MASS_TESTING" && "$filename_provided" && CSVHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
elif "$do_mass_testing"; then
:
elif "$do_mx_all_ips"; then
fname_prefix="${FNAME_PREFIX}mx-$URI"
else
! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}"
# NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place
fname_prefix="${FNAME_PREFIX}${NODE}_p${PORT}"
fi
if [[ -z "$CSVFILE" ]]; then
CSVFILE="${fname_prefix}-$(date +"%Y%m%d-%H%M".csv)"
elif [[ -d "$CSVFILE" ]]; then
CSVFILE="$CSVFILE/${fname_prefix}-$(date +"%Y%m%d-%H%M".csv)"
fi
if "$APPEND"; then
CSVHEADER=false
else
[[ -s "$CSVFILE" ]] && fatal "non-empty \"$CSVFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE
echo "\"id\",\"fqdn/ip\",\"port\",\"severity\",\"finding\",\"cve\",\"cwe\",\"hint\"" > "$CSVFILE"
fi
return 0
}
################# JSON FILE FORMATTING END. HTML START ####################
html_header() {
local fname_prefix
local filename_provided=false
[[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && filename_provided=true
# Don't create HTML headers and footers in the following scenarios:
# * HTML output is not being created.
# * mass testing is being performed and each test will have its own HTML file.
# * this is an individual test within a mass test and all HTML output is being placed in a single file.
! "$do_html" && HTMLHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && HTMLHEADER=false && return 0
"$CHILD_MASS_TESTING" && "$filename_provided" && HTMLHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
elif "$do_mass_testing"; then
:
elif "$do_mx_all_ips"; then
fname_prefix="${FNAME_PREFIX}mx-$URI"
else
! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}"
# NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place
fname_prefix="${FNAME_PREFIX}${NODE}_p${PORT}"
fi
if [[ -z "$HTMLFILE" ]]; then
HTMLFILE="$fname_prefix-$(date +"%Y%m%d-%H%M".html)"
elif [[ -d "$HTMLFILE" ]]; then
HTMLFILE="$HTMLFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".html)"
fi
if "$APPEND"; then
HTMLHEADER=false