(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header: List[str], client_host: Optional[str] = None, kex: Optional[SSH2_Kex] = None, pkm: Optional[SSH1_PublicKeyMessage] = None, print_target: bool = False, dh_rate_test_notes: str = "")
| 564 | |
| 565 | # Returns a exitcodes.* flag to denote if any failures or warnings were encountered. |
| 566 | def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header: List[str], client_host: Optional[str] = None, kex: Optional[SSH2_Kex] = None, pkm: Optional[SSH1_PublicKeyMessage] = None, print_target: bool = False, dh_rate_test_notes: str = "") -> int: |
| 567 | |
| 568 | program_retval = exitcodes.GOOD |
| 569 | client_audit = client_host is not None # If set, this is a client audit. |
| 570 | sshv = 1 if pkm is not None else 2 |
| 571 | algs = Algorithms(pkm, kex) |
| 572 | |
| 573 | # Perform post-processing on the findings to make final adjustments before outputting the results. |
| 574 | algorithm_recommendation_suppress_list, additional_notes = post_process_findings(banner, algs, client_audit, dh_rate_test_notes) |
| 575 | |
| 576 | with out: |
| 577 | if print_target: |
| 578 | host = aconf.host |
| 579 | |
| 580 | # Print the port if it's not the default of 22. |
| 581 | if aconf.port != 22: |
| 582 | |
| 583 | # Check if this is an IPv6 address, as that is printed in a different format. |
| 584 | if Utils.is_ipv6_address(aconf.host): |
| 585 | host = '[%s]:%d' % (aconf.host, aconf.port) |
| 586 | else: |
| 587 | host = '%s:%d' % (aconf.host, aconf.port) |
| 588 | |
| 589 | out.good('(gen) target: {}'. format(host)) |
| 590 | if client_audit: |
| 591 | out.good('(gen) client IP: {}'.format(client_host)) |
| 592 | if len(header) > 0: |
| 593 | out.info('(gen) header: ' + '\n'.join(header)) |
| 594 | if banner is not None: |
| 595 | banner_line = '(gen) banner: {}'.format(banner) |
| 596 | if sshv == 1 or banner.protocol[0] == 1: |
| 597 | out.fail(banner_line) |
| 598 | out.fail('(gen) protocol SSH1 enabled') |
| 599 | else: |
| 600 | out.good(banner_line) |
| 601 | |
| 602 | if not banner.valid_ascii: |
| 603 | # NOTE: RFC 4253, Section 4.2 |
| 604 | out.warn('(gen) banner contains non-printable ASCII') |
| 605 | |
| 606 | software = Software.parse(banner) |
| 607 | if software is not None: |
| 608 | out.good('(gen) software: {}'.format(software)) |
| 609 | else: |
| 610 | software = None |
| 611 | output_compatibility(out, algs, client_audit) |
| 612 | if kex is not None: |
| 613 | compressions = [x for x in kex.server.compression if x != 'none'] |
| 614 | if len(compressions) > 0: |
| 615 | cmptxt = 'enabled ({})'.format(', '.join(compressions)) |
| 616 | else: |
| 617 | cmptxt = 'disabled' |
| 618 | out.good('(gen) compression: {}'.format(cmptxt)) |
| 619 | if not out.is_section_empty() and not aconf.json: # Print output when it exists and JSON output isn't requested. |
| 620 | out.head('# general') |
| 621 | out.flush_section() |
| 622 | out.sep() |
| 623 | maxlen = algs.maxlen + 1 |
no test coverage detected