├── .all-contributorsrc
├── .cargo
└── config.toml
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── actions-rs
│ └── grcov.yml
├── dependabot.yml
├── pull_request_template.md
├── stale.yml
└── workflows
│ ├── build.yml
│ ├── check.yml
│ ├── cicd-to-dockerhub.yml
│ ├── coverage.yml
│ └── winget.yml
├── .gitignore
├── .rustfmt.toml
├── CONTRIBUTING.md
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── Makefile
├── Makefile.toml
├── README.md
├── build.rs
├── choco_package
├── feroxbuster.nuspec
├── legal
│ ├── LICENSE.txt
│ └── VERIFICATION.txt
└── tools
│ ├── chocolateyinstall.ps1
│ └── chocolateyuninstall.ps1
├── docs
├── .nojekyll
└── index.html
├── ferox-config.toml.example
├── img
├── auto-bail-demo.gif
├── auto-tune-demo.gif
├── cancel-menu.png
├── cancel-scan.gif
├── demo.gif
├── dir-scan-bar-explained.png
├── extract-scan-cmp-normal.gif
├── insecure.png
├── limit-demo.gif
├── logo
│ ├── default-cropped.png
│ └── logo.png
├── normal-scan-cmp-extract.gif
├── pause-resume-demo.gif
├── rate-limit-demo.gif
├── replay-proxy-demo.gif
├── response-bar-explained.png
├── resumed-scan.gif
├── save-state.png
├── small-term.png
├── time-limit.gif
└── total-bar-explained.png
├── install-nix.sh
├── shell_completions
├── _feroxbuster
├── _feroxbuster.ps1
├── feroxbuster.bash
├── feroxbuster.elv
└── feroxbuster.fish
├── snapcraft.yaml
├── src
├── banner
│ ├── container.rs
│ ├── entry.rs
│ ├── mod.rs
│ └── tests.rs
├── client.rs
├── config
│ ├── container.rs
│ ├── mod.rs
│ ├── tests.rs
│ └── utils.rs
├── event_handlers
│ ├── command.rs
│ ├── container.rs
│ ├── filters.rs
│ ├── inputs.rs
│ ├── mod.rs
│ ├── outputs.rs
│ ├── scans.rs
│ └── statistics.rs
├── extractor
│ ├── builder.rs
│ ├── container.rs
│ ├── mod.rs
│ └── tests.rs
├── filters
│ ├── container.rs
│ ├── empty.rs
│ ├── init.rs
│ ├── lines.rs
│ ├── mod.rs
│ ├── regex.rs
│ ├── similarity.rs
│ ├── size.rs
│ ├── status_code.rs
│ ├── tests.rs
│ ├── utils.rs
│ ├── wildcard.rs
│ └── words.rs
├── heuristics.rs
├── lib.rs
├── logger.rs
├── macros.rs
├── main.rs
├── message.rs
├── nlp
│ ├── constants.rs
│ ├── document.rs
│ ├── mod.rs
│ ├── model.rs
│ ├── term.rs
│ └── utils.rs
├── parser.rs
├── progress.rs
├── response.rs
├── scan_manager
│ ├── menu.rs
│ ├── mod.rs
│ ├── order.rs
│ ├── response_container.rs
│ ├── scan.rs
│ ├── scan_container.rs
│ ├── state.rs
│ ├── tests.rs
│ └── utils.rs
├── scanner
│ ├── ferox_scanner.rs
│ ├── init.rs
│ ├── limit_heap.rs
│ ├── mod.rs
│ ├── policy_data.rs
│ ├── requester.rs
│ ├── tests.rs
│ └── utils.rs
├── statistics
│ ├── container.rs
│ ├── error.rs
│ ├── field.rs
│ ├── init.rs
│ ├── macros.rs
│ ├── mod.rs
│ └── tests.rs
├── traits.rs
├── url.rs
└── utils.rs
└── tests
├── extra-words
├── mutual-auth
├── Caddyfile
├── README.md
├── certs
│ ├── client
│ │ ├── client.crt
│ │ └── client.key
│ └── server
│ │ ├── ca.crt
│ │ ├── server.crt
│ │ ├── server.crt.1
│ │ ├── server.crt.2
│ │ ├── server.der
│ │ └── server.key
└── gen-certs.sh
├── policy-test-words.shuffled
├── test_banner.rs
├── test_config.rs
├── test_deny_list.rs
├── test_extractor.rs
├── test_filters.rs
├── test_heuristics.rs
├── test_main.rs
├── test_parser.rs
├── test_policies.rs
├── test_scan_manager.rs
├── test_scanner.rs
└── utils
└── mod.rs
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [target.armv7-unknown-linux-gnueabihf]
2 | linker = "arm-linux-gnueabihf-gcc"
3 |
4 | [target.aarch64-unknown-linux-gnu]
5 | linker = "aarch64-linux-gnu-gcc"
6 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [epi052]
4 | ko_fi: epi052
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG] "
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1.
16 | 2.
17 | 3.
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Traceback / Error Output**
23 | If applicable, add error output to help explain your problem.
24 |
25 | **Environment (please complete the following information):**
26 | - feroxbuster version: [e.g. v1.0.1]
27 | - OS [e.g. ubuntu 20.04]
28 |
29 | **Additional context**
30 | Add any other context about the problem here.
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE REQUEST] "
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/actions-rs/grcov.yml:
--------------------------------------------------------------------------------
1 | branch: false
2 | ignore-not-existing: true
3 | llvm: true
4 | output-type: lcov
5 | output-path: ./lcov.info
6 | # excl-br-line: "^\\s*((debug_)?assert(_eq|_ne)?!|#\\[derive\\(|log::)"
7 | ignore:
8 | - "../*"
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: cargo
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Landing a Pull Request (PR)
2 |
3 | Long form explanations of most of the items below can be found in the [CONTRIBUTING](https://github.com/epi052/feroxbuster/blob/master/CONTRIBUTING.md) guide.
4 |
5 | ## Branching checklist
6 | - [ ] There is an issue associated with your PR (bug, feature, etc.. if not, create one)
7 | - [ ] Your PR description references the associated issue (i.e. fixes #123456)
8 | - [ ] Code is in its own branch
9 | - [ ] Branch name is related to the PR contents
10 | - [ ] PR targets main
11 |
12 | ## Static analysis checks
13 | - [ ] All rust files are formatted using `cargo fmt`
14 | - [ ] All `clippy` checks pass when running `cargo clippy --all-targets --all-features -- -D warnings -A clippy::mutex-atomic`
15 | - [ ] All existing tests pass
16 |
17 | ## Documentation
18 | - [ ] New code is documented using [doc comments](https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html)
19 | - [ ] Documentation about your PR is included in the `docs`, as needed. The docs live in a [separate repository](https://epi052.github.io/feroxbuster-docs/docs/). Update the appropriate pages at the links below.
20 | - [ ] update [example config file section](https://epi052.github.io/feroxbuster-docs/docs/configuration/ferox-config-toml/)
21 | - [ ] update [help output section](https://epi052.github.io/feroxbuster-docs/docs/configuration/command-line/)
22 | - [ ] add an [example](https://epi052.github.io/feroxbuster-docs/docs/examples/)
23 |
24 | ## Additional Tests
25 | - [ ] New code is unit tested
26 | - [ ] New code is integration tested, as needed
27 | - [ ] New tests pass
28 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 14
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | - confirmed
10 | # Label to use when marking an issue as stale
11 | staleLabel: stale
12 | # Comment to post when marking an issue as stale. Set to `false` to disable
13 | markComment: >
14 | This issue has been automatically marked as stale because it has not had
15 | recent activity. It will be closed if no further activity occurs. Thank you
16 | for your contributions.
17 | # Comment to post when closing a stale issue. Set to `false` to disable
18 | closeComment: false
19 |
--------------------------------------------------------------------------------
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | name: CI Pipeline
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | check:
7 | name: Check
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 | - name: Cache cargo & target directories
12 | uses: Swatinem/rust-cache@v2
13 | - uses: dtolnay/rust-toolchain@stable
14 | - run: cargo check
15 |
16 | test:
17 | name: Test Suite
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Cache cargo & target directories
22 | uses: Swatinem/rust-cache@v2
23 | - name: Install latest nextest release
24 | uses: taiki-e/install-action@nextest
25 | - uses: dtolnay/rust-toolchain@stable
26 | - name: Test with latest nextest release
27 | run: cargo nextest run --all-features --all-targets --retries 4 --no-fail-fast
28 |
29 | fmt:
30 | name: Rust fmt
31 | runs-on: ubuntu-latest
32 | steps:
33 | - uses: actions/checkout@v4
34 | - name: Cache cargo & target directories
35 | uses: Swatinem/rust-cache@v2
36 | - uses: dtolnay/rust-toolchain@stable
37 | - run: cargo fmt --all -- --check
38 |
39 | clippy:
40 | name: Clippy
41 | runs-on: ubuntu-latest
42 | steps:
43 | - uses: actions/checkout@v4
44 | - name: Cache cargo & target directories
45 | uses: Swatinem/rust-cache@v2
46 | - uses: dtolnay/rust-toolchain@stable
47 | - run: cargo clippy --all-targets --all-features -- -D warnings
48 |
--------------------------------------------------------------------------------
/.github/workflows/cicd-to-dockerhub.yml:
--------------------------------------------------------------------------------
1 | name: ci-to-dockerhub
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v4
13 |
14 | - name: Login to Docker Hub
15 | uses: docker/login-action@v3
16 | with:
17 | username: ${{ secrets.DOCKER_HUB_USERNAME }}
18 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
19 |
20 | - name: Set up Docker Buildx
21 | id: buildx
22 | uses: docker/setup-buildx-action@v3
23 |
24 | - name: Build and push
25 | id: docker_build
26 | uses: docker/build-push-action@v6
27 | with:
28 | context: ./
29 | file: ./Dockerfile
30 | push: true
31 | tags: ${{ secrets.DOCKER_HUB_USERNAME }}/feroxbuster:latest
32 |
33 | - name: Image digest
34 | run: echo ${{ steps.docker_build.outputs.digest }}
35 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | on: [push]
2 |
3 | name: Code Coverage Pipeline
4 |
5 | jobs:
6 | coverage:
7 | name: LLVM Coverage
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: dtolnay/rust-toolchain@stable
12 | with:
13 | components: llvm-tools-preview
14 | - name: Install cargo-llvm-cov and cargo-nextest
15 | uses: taiki-e/install-action@v2
16 | with:
17 | tool: cargo-nextest,cargo-llvm-cov
18 | - name: Generate code coverage
19 | run: cargo llvm-cov nextest --all-features --no-fail-fast --lcov --retries 4 --output-path lcov.info
20 | - name: Upload coverage to Codecov
21 | uses: codecov/codecov-action@v4
22 | with:
23 | files: lcov.info
24 | fail_ci_if_error: true
25 | token: ${{ secrets.CODECOV_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/winget.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Winget
2 | on:
3 | release:
4 | types: [released]
5 | workflow_dispatch:
6 | inputs:
7 | tag_name:
8 | description: 'Tag name of release'
9 | required: true
10 | type: string
11 |
12 | jobs:
13 | publish:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: vedantmgoyal2009/winget-releaser@main
17 | with:
18 | identifier: epi052.feroxbuster
19 | installers-regex: '-windows-feroxbuster\.exe\.zip$'
20 | token: ${{ secrets.WINGET_TOKEN }}
21 | release-tag: ${{ inputs.tag_name || github.event.release.tag_name || github.ref_name }}
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # These are backup files generated by rustfmt
7 | **/*.rs.bk
8 |
9 | # jetbrains metadata folder
10 | .idea/
11 |
12 | # vscode metadata folder
13 | .vscode/
14 |
15 | # personal feroxbuster config for testing
16 | ferox-config.toml
17 |
18 | # images for the README on github
19 | img/**
20 |
21 | # scripts to check code coverage using nightly compiler
22 | check-coverage.sh
23 | lcov_cobertura.py
24 |
25 | # dockerignore file that makes it so i can work on the docker config without copying a 4GB manifest or w/e it is
26 | .dockerignore
27 |
28 | # state file created during tests
29 | ferox-*.state
30 |
31 | # python stuff cuz reasons
32 | Pipfile*
33 |
34 | # ignore choco_package generated nupkg
35 | /choco_package/*.nupkg
36 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | reorder_modules = false
2 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "feroxbuster"
3 | version = "2.11.0"
4 | authors = ["Ben 'epi' Risher (@epi052)"]
5 | license = "MIT"
6 | edition = "2021"
7 | homepage = "https://github.com/epi052/feroxbuster"
8 | repository = "https://github.com/epi052/feroxbuster"
9 | description = "A fast, simple, recursive content discovery tool."
10 | categories = ["command-line-utilities"]
11 | keywords = [
12 | "pentest",
13 | "enumeration",
14 | "url-bruteforce",
15 | "content-discovery",
16 | "web",
17 | ]
18 | exclude = [".github/*", "img/*", "check-coverage.sh"]
19 | build = "build.rs"
20 |
21 | [badges]
22 | maintenance = { status = "actively-developed" }
23 |
24 | [build-dependencies]
25 | clap = { version = "4.5", features = ["wrap_help", "cargo"] }
26 | clap_complete = "4.5"
27 | regex = "1.10"
28 | lazy_static = "1.5"
29 | dirs = "5.0"
30 |
31 | [dependencies]
32 | scraper = "0.19"
33 | futures = "0.3"
34 | tokio = { version = "1.39", features = ["full"] }
35 | tokio-util = { version = "0.7", features = ["codec"] }
36 | log = "0.4"
37 | env_logger = "0.11"
38 | reqwest = { version = "0.12", features = ["socks", "native-tls-alpn"] }
39 | # uses feature unification to add 'serde' to reqwest::Url
40 | url = { version = "2.5", features = ["serde"] }
41 | serde_regex = "1.1"
42 | clap = { version = "4.5", features = ["wrap_help", "cargo"] }
43 | lazy_static = "1.5"
44 | toml = "0.8"
45 | serde = { version = "1.0", features = ["derive", "rc"] }
46 | serde_json = "1.0"
47 | uuid = { version = "1.10", features = ["v4"] }
48 | indicatif = { version = "0.17.8" }
49 | console = "0.15"
50 | openssl = { version = "0.10", features = ["vendored"] }
51 | dirs = "5.0"
52 | regex = "1.10"
53 | crossterm = "0.27"
54 | rlimit = "0.10"
55 | ctrlc = "3.4"
56 | anyhow = "1.0"
57 | leaky-bucket = "1.1"
58 | gaoya = "0.2"
59 | # 0.37+ relies on the broken version of indicatif and forces
60 | # the broken version to be used regardless of the version
61 | # specified above
62 | self_update = { version = "0.40", features = [
63 | "archive-tar",
64 | "compression-flate2",
65 | "archive-zip",
66 | "compression-zip-deflate",
67 | ] }
68 |
69 | [dev-dependencies]
70 | tempfile = "3.12"
71 | httpmock = "0.7"
72 | assert_cmd = "2.0"
73 | predicates = "3.1"
74 |
75 | [profile.release]
76 | lto = true
77 | codegen-units = 1
78 | panic = 'abort'
79 |
80 | [package.metadata.deb]
81 | section = "utility"
82 | license-file = ["LICENSE", "4"]
83 | conf-files = ["/etc/feroxbuster/ferox-config.toml"]
84 | assets = [
85 | [
86 | "target/release/feroxbuster",
87 | "/usr/bin/",
88 | "755",
89 | ],
90 | [
91 | "ferox-config.toml.example",
92 | "/etc/feroxbuster/ferox-config.toml",
93 | "644",
94 | ],
95 | [
96 | "shell_completions/feroxbuster.bash",
97 | "/usr/share/bash-completion/completions/feroxbuster.bash",
98 | "644",
99 | ],
100 | [
101 | "shell_completions/feroxbuster.fish",
102 | "/usr/share/fish/completions/feroxbuster.fish",
103 | "644",
104 | ],
105 | [
106 | "shell_completions/_feroxbuster",
107 | "/usr/share/zsh/vendor-completions/_feroxbuster",
108 | "644",
109 | ],
110 | ]
111 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.17.1 AS build
2 | LABEL maintainer="wfnintr@null.net"
3 |
4 | RUN apk upgrade --update-cache --available && apk add --update openssl
5 |
6 | # Download latest release
7 | RUN wget https://github.com/epi052/feroxbuster/releases/latest/download/x86_64-linux-feroxbuster.zip -qO feroxbuster.zip \
8 | && unzip -d /tmp/ feroxbuster.zip feroxbuster \
9 | && chmod +x /tmp/feroxbuster \
10 | && wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/raft-medium-directories.txt -O /tmp/raft-medium-directories.txt
11 |
12 | FROM alpine:3.17.1 AS release
13 | COPY --from=build /tmp/raft-medium-directories.txt /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
14 | COPY --from=build /tmp/feroxbuster /usr/local/bin/feroxbuster
15 |
16 | RUN adduser \
17 | --gecos "" \
18 | --disabled-password \
19 | feroxbuster
20 |
21 | USER feroxbuster
22 |
23 | ENTRYPOINT ["feroxbuster"]
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-2023 epi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | default_prefix = /usr/local
2 | prefix ?= $(default_prefix)
3 | exec_prefix = $(prefix)
4 | bindir = $(exec_prefix)/bin
5 | datarootdir = $(prefix)/share
6 | datadir = $(datarootdir)
7 | example_config = ferox-config.toml.example
8 | config_file = ferox-config.toml
9 | completion_dir = shell_completions
10 | completion_prefix = $(completion_dir)/$(BIN)
11 |
12 | BIN=feroxbuster
13 | SHR_SOURCES = $(shell find src -type f -wholename '*src/*.rs') Cargo.toml Cargo.lock
14 |
15 | RELEASE = debug
16 | DEBUG ?= 0
17 |
18 | ifeq (0, $(DEBUG))
19 | ARGS = --release
20 | RELEASE = release
21 | endif
22 |
23 | VENDORED ?= 0
24 | ifeq (1,$(VENDORED))
25 | ARGS += --frozen
26 | endif
27 |
28 | TARGET = target/$(RELEASE)
29 |
30 | .PHONY: all clean install uninstall test update
31 |
32 | all: cli
33 | cli: $(TARGET)/$(BIN) $(TARGET)/$(BIN).1.gz $(SHR_SOURCES)
34 | install: all install-cli
35 |
36 | verify:
37 | cargo fmt
38 | cargo clippy --all-targets --all-features -- -D warnings -A clippy::mutex-atomic
39 | cargo test
40 |
41 | clean:
42 | cargo clean
43 |
44 | vendor: vendor.tar
45 |
46 | vendor.tar:
47 | cargo vendor
48 | tar pcf vendor.tar vendor
49 | rm -rf vendor
50 |
51 | install-cli: cli
52 | install -Dm 0644 "$(completion_prefix).bash" "$(DESTDIR)/usr/share/bash-completion/completions/$(BIN).bash"
53 | install -Dm 0644 "$(completion_prefix).fish" "$(DESTDIR)/usr/share/fish/completions/$(BIN).fish"
54 | install -Dm 0644 "$(completion_dir)/_$(BIN)" "$(DESTDIR)/usr/share/zsh/vendor-completions/_$(BIN)"
55 | install -sDm 0755 "$(TARGET)/$(BIN)" "$(DESTDIR)$(bindir)/$(BIN)"
56 | install -Dm 0644 "$(TARGET)/$(BIN).1.gz" "$(DESTDIR)$(datadir)/man/man1/$(BIN).1.gz"
57 | install -Dm 0644 "$(example_config)" "$(DESTDIR)/etc/$(BIN)/$(config_file)"
58 |
59 | uninstall:
60 | rm -f "$(DESTDIR)$(bindir)/$(BIN)"
61 | rm -f "$(DESTDIR)$(datadir)/man/man1/$(BIN).1.gz"
62 | rm -rf "$(DESTDIR)/etc/$(BIN)/"
63 | rm -f "$(DESTDIR)/usr/share/bash-completion/completions/$(BIN).bash"
64 | rm -f "$(DESTDIR)/usr/share/zsh/vendor-completions/_$(BIN)"
65 | rm -f "$(DESTDIR)/usr/share/fish/completions/$(BIN).fish"
66 |
67 | extract:
68 | ifeq (1, $(VENDORED))
69 | tar pxf vendor.tar
70 | endif
71 |
72 | $(TARGET)/$(BIN): extract
73 | mkdir -p .cargo debian
74 | touch debian/cargo.config
75 | cp debian/cargo.config .cargo/config.toml
76 | cargo build $(ARGS)
77 |
78 | $(TARGET)/$(BIN).1.gz: $(TARGET)/$(BIN)
79 | help2man --no-info $< | gzip -c > $@.partial
80 | mv $@.partial $@
81 |
--------------------------------------------------------------------------------
/Makefile.toml:
--------------------------------------------------------------------------------
1 | # composite tasks
2 | [tasks.upgrade]
3 | dependencies = ["upgrade-deps", "update"]
4 |
5 | [tasks.check]
6 | dependencies = ["fmt", "clippy", "test"]
7 |
8 | # cleaning
9 | [tasks.clean-state]
10 | script = """
11 | rm ferox-*.state
12 | """
13 |
14 | # dependency management
15 | [tasks.upgrade-deps]
16 | command = "cargo"
17 | args = ["upgrade", "--exclude", "self_update"]
18 |
19 | [tasks.update]
20 | command = "cargo"
21 | args = ["update"]
22 |
23 | # clippy / lint
24 | [tasks.clippy]
25 | clear = true
26 | script = """
27 | cargo clippy --all-targets --all-features -- -D warnings
28 | """
29 |
30 | [tasks.fmt]
31 | clear = true
32 | script = """
33 | cargo fmt --all
34 | """
35 |
36 | # tests
37 | [tasks.test]
38 | clear = true
39 | script = """
40 | cargo nextest run --all-features --all-targets --retries 4 --no-fail-fast
41 | """
42 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | use std::fs::{copy, create_dir_all, OpenOptions};
2 | use std::io::{Read, Seek, Write};
3 |
4 | use clap_complete::{generate_to, shells};
5 |
6 | include!("src/parser.rs");
7 |
8 | fn main() {
9 | println!("cargo:rerun-if-env-changed=src/parser.rs");
10 |
11 | if std::env::var("DOCS_RS").is_ok() {
12 | return; // only build when we're not generating docs
13 | }
14 |
15 | let outdir = "shell_completions";
16 |
17 | let mut app = initialize();
18 |
19 | generate_to(shells::Bash, &mut app, "feroxbuster", outdir).unwrap();
20 | generate_to(shells::Zsh, &mut app, "feroxbuster", outdir).unwrap();
21 | generate_to(shells::Fish, &mut app, "feroxbuster", outdir).unwrap();
22 | generate_to(shells::PowerShell, &mut app, "feroxbuster", outdir).unwrap();
23 | generate_to(shells::Elvish, &mut app, "feroxbuster", outdir).unwrap();
24 |
25 | // 0xdf pointed out an oddity when tab-completing options that expect file paths, the fix we
26 | // landed on was to add -o plusdirs to the bash completion script. The following code aims to
27 | // automate that fix and have it present in all future builds
28 | let mut contents = String::new();
29 |
30 | let mut bash_file = OpenOptions::new()
31 | .read(true)
32 | .write(true)
33 | .open(format!("{outdir}/feroxbuster.bash"))
34 | .expect("Couldn't open bash completion script");
35 |
36 | bash_file
37 | .read_to_string(&mut contents)
38 | .expect("Couldn't read bash completion script");
39 |
40 | contents = contents.replace("default feroxbuster", "default -o plusdirs feroxbuster");
41 |
42 | bash_file
43 | .rewind()
44 | .expect("Couldn't seek to position 0 in bash completion script");
45 |
46 | bash_file
47 | .write_all(contents.as_bytes())
48 | .expect("Couldn't write updated bash completion script to disk");
49 |
50 | // hunter0x8 let me know that when installing via cargo, it would be nice if we dropped a
51 | // config file during the build process. The following code will place an example config in
52 | // the user's configuration directory
53 | // - linux: $XDG_CONFIG_HOME or $HOME/.config
54 | // - macOS: $HOME/Library/Application Support
55 | // - windows: {FOLDERID_RoamingAppData}
56 | let mut config_dir = dirs::config_dir().expect("Couldn't resolve user's config directory");
57 | config_dir = config_dir.join("feroxbuster"); // $HOME/.config/feroxbuster
58 |
59 | if !config_dir.exists() {
60 | // recursively create the feroxbuster directory and all of its parent components if
61 | // they are missing
62 | if create_dir_all(&config_dir).is_err() {
63 | // only copy the config file when we're not running in the CI/CD pipeline
64 | // which fails with permission denied
65 | eprintln!("Couldn't create one or more directories needed to copy the config file");
66 | return;
67 | }
68 | }
69 |
70 | // hard-coding config name here to not rely on the crate we're building, if DEFAULT_CONFIG_NAME
71 | // ever changes, this will need to be updated
72 | let config_file = config_dir.join("ferox-config.toml");
73 |
74 | if !config_file.exists() {
75 | // config file doesn't exist, add it to the config directory
76 | if copy("ferox-config.toml.example", config_file).is_err() {
77 | eprintln!("Couldn't copy example config into config directory");
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/choco_package/feroxbuster.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | feroxbuster
5 | 2.8.0
6 | https://github.com/epi052/feroxbuster/releases/
7 | epi052
8 | feroxbuster (Install)
9 | epi052
10 | https://github.com/epi052/feroxbuster
11 | https://rawcdn.githack.com/epi052/feroxbuster/2d381e7e057ce60c580b324dd36c9abaf30c2ec7/img/logo/logo.png
12 | 2020-2023
13 | https://github.com/epi052/feroxbuster/blob/main/LICENSE
14 | true
15 | https://github.com/epi052/feroxbuster
16 | https://epi052.github.io/feroxbuster-docs/docs/
17 |
18 | https://github.com/epi052/feroxbuster/issues
19 | content-discovery pentesting-tool url-bruteforcer
20 | A simple, fast, recursive content discovery tool written in Rust
21 |
22 | A simple, fast, recursive content discovery tool written in Rust
23 | [](https://github.com/epi052/feroxbuster)
24 |
25 | ## What the heck is a ferox anyway?
26 |
27 | Ferox is short for Ferric Oxide. Ferric Oxide, simply put, is rust. The name rustbuster was taken, so I decided on a
28 | variation.
29 |
30 | ## What's it do tho?
31 |
32 | `feroxbuster` is a tool designed to perform [Forced Browsing](https://owasp.org/www-community/attacks/Forced_browsing).
33 |
34 | Forced browsing is an attack where the aim is to enumerate and access resources that are not referenced by the web
35 | application, but are still accessible by an attacker.
36 |
37 | `feroxbuster` uses brute force combined with a wordlist to search for unlinked content in target directories. These
38 | resources may store sensitive information about web applications and operational systems, such as source code,
39 | credentials, internal network addressing, etc...
40 |
41 | This attack is also known as Predictable Resource Location, File Enumeration, Directory Enumeration, and Resource
42 | Enumeration.
43 |
44 | ## Quick Start
45 |
46 | This section will cover the minimum amount of information to get up and running with feroxbuster. Please refer the the [documentation](https://epi052.github.io/feroxbuster-docs/docs/), as it's much more comprehensive.
47 |
48 | ### Installation
49 |
50 | There are quite a few other [installation methods](https://epi052.github.io/feroxbuster-docs/docs/installation/), but these snippets should cover the majority of users.
51 |
52 | #### All others Docs
53 |
54 | Please refer the the [documentation](https://epi052.github.io/feroxbuster-docs/docs/).
55 |
56 | ## Example Usage
57 |
58 | Here are a few brief examples to get you started. Please note, feroxbuster can do a **lot more** than what's listed below. As a result, there are **many more** examples, with **demonstration gifs** that highlight specific features, in the [documentation](https://epi052.github.io/feroxbuster-docs/docs/).
59 |
60 | ### Multiple Values
61 |
62 | Options that take multiple values are very flexible. Consider the following ways of specifying extensions:
63 |
64 | ```
65 | ./feroxbuster -u http://127.1 -x pdf -x js,html -x php txt json,docx
66 | ```
67 |
68 | The command above adds .pdf, .js, .html, .php, .txt, .json, and .docx to each url
69 |
70 | All of the methods above (multiple flags, space separated, comma separated, etc...) are valid and interchangeable. The
71 | same goes for urls, headers, status codes, queries, and size filters.
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/choco_package/legal/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | From: https://github.com/epi052/feroxbuster/blob/main/LICENSE
3 |
4 | LICENSE
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020-2023 epi
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 |
--------------------------------------------------------------------------------
/choco_package/legal/VERIFICATION.txt:
--------------------------------------------------------------------------------
1 |
2 | VERIFICATION
3 |
4 | checksum -t sha512 -f .\x86-windows-feroxbuster.exe.zip
5 | checksum -t sha512 -f .\x86_64-windows-feroxbuster.exe.zip
--------------------------------------------------------------------------------
/choco_package/tools/chocolateyinstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
4 | $version = '2.8.0'
5 | $url = "https://github.com/epi052/feroxbuster/releases/download/v$version/x86-windows-feroxbuster.exe.zip"
6 | $url64 = "https://github.com/epi052/feroxbuster/releases/download/v$version/x86_64-windows-feroxbuster.exe.zip"
7 |
8 | $packageArgs = @{
9 | packageName = $env:ChocolateyPackageName
10 | unzipLocation = $toolsDir
11 | fileType = 'exe' #only one of these: exe, msi, msu
12 | url = $url
13 | url64bit = $url64
14 | #file = $fileLocation
15 |
16 | softwareName = 'feroxbuster*'
17 |
18 | # Checksums are now required as of 0.10.0.
19 | # To determine checksums, you can get that from the original site if provided.
20 | # You can also use checksum.exe (choco install checksum) and use it
21 | # e.g. checksum -t sha256 -f path\to\file
22 | checksum = 'e5cac59c737260233903a17706a68bac11fe0d7a15169e1c5a9637cc221e7230fd6ddbfc1a7243833dde6472ad053c033449ca8338164654f7354363da54ba88'
23 | checksumType = 'sha512'
24 | checksum64 = 'cce58d6eacef7e12c31076f5a00fee9742a4e3fdfc69d807d98736200e50469f77359978e137ecafd87b14460845c65c6808d1f8b23ae561f7e7c637e355dee3'
25 | checksumType64= 'sha512'
26 | }
27 | Install-ChocolateyZipPackage @packageArgs # https://docs.chocolatey.org/en-us/create/functions/install-chocolateyzippackage
--------------------------------------------------------------------------------
/choco_package/tools/chocolateyuninstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop' # stop on all errors
2 | $packageArgs = @{
3 | packageName = $env:ChocolateyPackageName
4 | softwareName = 'feroxbuster*' #part or all of the Display Name as you see it in Programs and Features. It should be enough to be unique
5 | fileType = 'exe' #only one of these: MSI or EXE (ignore MSU for now)
6 | }
7 |
8 | # Get-UninstallRegistryKey is new to 0.9.10, if supporting 0.9.9.x and below,
9 | # take a dependency on "chocolatey-core.extension" in your nuspec file.
10 | # This is only a fuzzy search if $softwareName includes '*'. Otherwise it is
11 | # exact. In the case of versions in key names, we recommend removing the version
12 | # and using '*'.
13 | [array]$key = Get-UninstallRegistryKey -SoftwareName $packageArgs['softwareName']
14 |
15 | if ($key.Count -eq 1) {
16 | $key | % {
17 | $packageArgs['file'] = "$($_.UninstallString)" #NOTE: You may need to split this if it contains spaces, see below
18 |
19 | if ($packageArgs['fileType'] -eq 'MSI') {
20 | # The Product Code GUID is all that should be passed for MSI, and very
21 | # FIRST, because it comes directly after /x, which is already set in the
22 | # Uninstall-ChocolateyPackage msiargs (facepalm).
23 | $packageArgs['silentArgs'] = "$($_.PSChildName) $($packageArgs['silentArgs'])"
24 |
25 | # Don't pass anything for file, it is ignored for msi (facepalm number 2)
26 | # Alternatively if you need to pass a path to an msi, determine that and
27 | # use it instead of the above in silentArgs, still very first
28 | $packageArgs['file'] = ''
29 | } else {
30 | # NOTES:
31 | # - You probably will need to sanitize $packageArgs['file'] as it comes from the registry and could be in a variety of fun but unusable formats
32 | # - Split args from exe in $packageArgs['file'] and pass those args through $packageArgs['silentArgs'] or ignore them
33 | # - Ensure you don't pass double quotes in $file (aka $packageArgs['file']) - otherwise you will get "Illegal characters in path when you attempt to run this"
34 | # - Review the code for auto-uninstaller for all of the fun things it does in sanitizing - https://github.com/chocolatey/choco/blob/bfe351b7d10c798014efe4bfbb100b171db25099/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs#L142-L192
35 | }
36 |
37 | Uninstall-ChocolateyPackage @packageArgs
38 | }
39 | } elseif ($key.Count -eq 0) {
40 | Write-Warning "$packageName has already been uninstalled by other means."
41 | } elseif ($key.Count -gt 1) {
42 | Write-Warning "$($key.Count) matches found!"
43 | Write-Warning "To prevent accidental data loss, no programs will be uninstalled."
44 | Write-Warning "Please alert package maintainer the following keys were matched:"
45 | $key | % {Write-Warning "- $($_.DisplayName)"}
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 | The page has moved to:
8 | feroxbuster-docs
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ferox-config.toml.example:
--------------------------------------------------------------------------------
1 | # Example configuration for feroxbuster
2 | #
3 | # If you wish to provide persistent settings to feroxbuster, rename this file to ferox-config.toml and make sure
4 | # it resides in the same directory as the feroxbuster binary.
5 | #
6 | # After that, uncomment any line to override the default value provided by the binary itself.
7 | #
8 | # Any setting used here can be overridden by the corresponding command line option/argument
9 | #
10 | # wordlist = "/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt"
11 | # status_codes = [200, 500]
12 | # filter_status = [301]
13 | # threads = 1
14 | # timeout = 5
15 | # proxy = "http://127.0.0.1:8080"
16 | # replay_proxy = "http://127.0.0.1:8081"
17 | # replay_codes = [200, 302]
18 | # verbosity = 1
19 | # parallel = 8
20 | # scan_limit = 6
21 | # rate_limit = 250
22 | # quiet = true
23 | # silent = true
24 | # auto_tune = true
25 | # auto_bail = true
26 | # json = true
27 | # output = "/targets/ellingson_mineral_company/gibson.txt"
28 | # debug_log = "/var/log/find-the-derp.log"
29 | # user_agent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
30 | # random_agent = false
31 | # redirects = true
32 | # insecure = true
33 | # collect_words = true
34 | # collect_backups = true
35 | # collect_extensions = true
36 | # extensions = ["php", "html"]
37 | # dont_collect = ["png", "gif", "jpg", "jpeg"]
38 | # methods = ["GET", "POST"]
39 | # data = [11, 12, 13, 14, 15]
40 | # url_denylist = ["http://dont-scan.me", "https://also-not.me"]
41 | # regex_denylist = ["/deny.*"]
42 | # no_recursion = true
43 | # add_slash = true
44 | # stdin = true
45 | # dont_filter = true
46 | # extract_links = true
47 | # depth = 1
48 | # limit_bars = 3
49 | # force_recursion = true
50 | # filter_size = [5174]
51 | # filter_regex = ["^ignore me$"]
52 | # filter_similar = ["https://somesite.com/soft404"]
53 | # filter_word_count = [993]
54 | # filter_line_count = [35, 36]
55 | # queries = [["name","value"], ["rick", "astley"]]
56 | # save_state = false
57 | # time_limit = "10m"
58 | # server_certs = ["/some/cert.pem", "/some/other/cert.pem"]
59 | # client_cert = "/some/client/cert.pem"
60 | # client_key = "/some/client/key.pem"
61 | # request_file = "/some/raw/request/file"
62 | # protocol = "http"
63 | # scan_dir_listings = true
64 |
65 | # headers can be specified on multiple lines or as an inline table
66 | #
67 | # inline example
68 | # headers = {"stuff" = "things"}
69 | #
70 | # multi-line example
71 | # note: if multi-line is used, all key/value pairs under it belong to the headers table until the next table
72 | # is found or the end of the file is reached
73 | #
74 | # If you want to use [headers], UNCOMMENT the line below
75 | # [headers]
76 | # stuff = "things"
77 | # more = "headers"
78 |
--------------------------------------------------------------------------------
/img/auto-bail-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/auto-bail-demo.gif
--------------------------------------------------------------------------------
/img/auto-tune-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/auto-tune-demo.gif
--------------------------------------------------------------------------------
/img/cancel-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/cancel-menu.png
--------------------------------------------------------------------------------
/img/cancel-scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/cancel-scan.gif
--------------------------------------------------------------------------------
/img/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/demo.gif
--------------------------------------------------------------------------------
/img/dir-scan-bar-explained.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/dir-scan-bar-explained.png
--------------------------------------------------------------------------------
/img/extract-scan-cmp-normal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/extract-scan-cmp-normal.gif
--------------------------------------------------------------------------------
/img/insecure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/insecure.png
--------------------------------------------------------------------------------
/img/limit-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/limit-demo.gif
--------------------------------------------------------------------------------
/img/logo/default-cropped.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/logo/default-cropped.png
--------------------------------------------------------------------------------
/img/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/logo/logo.png
--------------------------------------------------------------------------------
/img/normal-scan-cmp-extract.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/normal-scan-cmp-extract.gif
--------------------------------------------------------------------------------
/img/pause-resume-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/pause-resume-demo.gif
--------------------------------------------------------------------------------
/img/rate-limit-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/rate-limit-demo.gif
--------------------------------------------------------------------------------
/img/replay-proxy-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/replay-proxy-demo.gif
--------------------------------------------------------------------------------
/img/response-bar-explained.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/response-bar-explained.png
--------------------------------------------------------------------------------
/img/resumed-scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/resumed-scan.gif
--------------------------------------------------------------------------------
/img/save-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/save-state.png
--------------------------------------------------------------------------------
/img/small-term.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/small-term.png
--------------------------------------------------------------------------------
/img/time-limit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/time-limit.gif
--------------------------------------------------------------------------------
/img/total-bar-explained.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epi052/feroxbuster/e321a4e0e6b114787aa5674e16732e1a4482bbd4/img/total-bar-explained.png
--------------------------------------------------------------------------------
/install-nix.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | BASE_URL=https://github.com/epi052/feroxbuster/releases/latest/download
4 |
5 | MAC_ZIP=x86_64-macos-feroxbuster.zip
6 | MAC_URL="$BASE_URL/$MAC_ZIP"
7 |
8 | LIN32_ZIP=x86-linux-feroxbuster.zip
9 | LIN32_URL="$BASE_URL/$LIN32_ZIP"
10 |
11 | LIN64_ZIP=x86_64-linux-feroxbuster.zip
12 | LIN64_URL="$BASE_URL/$LIN64_ZIP"
13 |
14 | EMOJI_URL=https://gist.github.com/epi052/8196b550ea51d0907ad4b93751b1b57d/raw/6112c9f32ae07922983fdc549c54fd3fb9a38e4c/NotoColorEmoji.ttf
15 |
16 | INSTALL_DIR="${1:-$(pwd)}"
17 |
18 | echo "[+] Installing feroxbuster to ${INSTALL_DIR}!"
19 |
20 | which unzip &>/dev/null
21 | if [ "$?" != "0" ]; then
22 | echo "[!] unzip not found, exiting. "
23 | exit -1
24 | fi
25 |
26 | if [[ "$(uname)" == "Darwin" ]]; then
27 | echo "[=] Found MacOS, downloading from $MAC_URL"
28 |
29 | curl -sLO "$MAC_URL"
30 | unzip -o "$MAC_ZIP" -d "${INSTALL_DIR}" >/dev/null
31 | rm "$MAC_ZIP"
32 | elif [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then
33 | if [[ $(getconf LONG_BIT) == 32 ]]; then
34 | echo "[=] Found 32-bit Linux, downloading from $LIN32_URL"
35 |
36 | curl -sLO "$LIN32_URL"
37 | unzip -o "$LIN32_ZIP" -d "${INSTALL_DIR}" >/dev/null
38 | rm "$LIN32_ZIP"
39 | else
40 | echo "[=] Found 64-bit Linux, downloading from $LIN64_URL"
41 |
42 | curl -sLO "$LIN64_URL"
43 | unzip -o "$LIN64_ZIP" -d "${INSTALL_DIR}" >/dev/null
44 | rm "$LIN64_ZIP"
45 | fi
46 |
47 | if [[ "$(fc-list NotoColorEmoji | wc -l)" -gt 0 ]]; then
48 | echo "[=] Found Noto Emoji Font, skipping install"
49 | else
50 | echo "[=] Installing Noto Emoji Font"
51 | mkdir -p ~/.fonts
52 | pushd ~/.fonts 2>&1 >/dev/null
53 |
54 | curl -sLO "$EMOJI_URL"
55 |
56 | fc-cache -f -v >/dev/null
57 |
58 | popd 2>&1 >/dev/null
59 | echo "[+] Noto Emoji Font installed"
60 | fi
61 | fi
62 |
63 | chmod +x "${INSTALL_DIR}/feroxbuster"
64 |
65 | echo "[+] Installed feroxbuster"
66 | echo " [-] path: ${INSTALL_DIR}/feroxbuster"
67 | echo " [-] version: $(${INSTALL_DIR}/feroxbuster -V | awk '{print $2}')"
68 |
--------------------------------------------------------------------------------
/snapcraft.yaml:
--------------------------------------------------------------------------------
1 | name: feroxbuster
2 | version: git
3 | summary: A simple, fast, recursive content discovery tool written in Rust
4 | description: |
5 | feroxbuster is a tool designed to perform Forced Browsing.
6 |
7 | Forced browsing is an attack where the aim is to enumerate and access resources that are not referenced by the web application, but are still accessible by an attacker.
8 |
9 | feroxbuster uses brute force combined with a wordlist to search for unlinked content in target directories. These resources may store sensitive information about web applications and operational systems, such as source code, credentials, internal network addressing, etc...
10 |
11 | This attack is also known as Predictable Resource Location, File Enumeration, Directory Enumeration, and Resource Enumeration.
12 |
13 |
14 | base: core18
15 |
16 | plugs:
17 | etc-feroxbuster:
18 | interface: system-files
19 | read:
20 | - /etc/feroxbuster
21 | dot-config-feroxbuster:
22 | interface: personal-files
23 | read:
24 | - $HOME/.config/feroxbuster
25 |
26 | architectures:
27 | - build-on: amd64
28 | - build-on: i386
29 |
30 | parts:
31 | feroxbuster:
32 | plugin: rust
33 | source: .
34 |
35 | apps:
36 | feroxbuster:
37 | command: bin/feroxbuster
38 | plugs:
39 | - etc-feroxbuster
40 | - dot-config-feroxbuster
41 | - network
42 |
--------------------------------------------------------------------------------
/src/banner/entry.rs:
--------------------------------------------------------------------------------
1 | use console::{measure_text_width, Emoji};
2 | use std::fmt;
3 |
4 | /// Initial visual indentation size used in formatting banner entries
5 | const INDENT: usize = 3;
6 |
7 | /// Column width used in formatting banner entries
8 | const COL_WIDTH: usize = 22;
9 |
10 | /// Represents a single line on the banner
11 | #[derive(Default)]
12 | pub(super) struct BannerEntry {
13 | /// emoji used in the banner entry
14 | emoji: String,
15 |
16 | /// title used in the banner entry
17 | title: String,
18 |
19 | /// value passed in via config/cli/defaults
20 | value: String,
21 | }
22 |
23 | /// implementation of a banner entry
24 | impl BannerEntry {
25 | /// Create a new banner entry from given fields
26 | pub fn new(emoji: &str, title: &str, value: &str) -> Self {
27 | BannerEntry {
28 | emoji: emoji.to_string(),
29 | title: title.to_string(),
30 | value: value.to_string(),
31 | }
32 | }
33 |
34 | /// Simple wrapper for emoji or fallback when terminal doesn't support emoji
35 | fn format_emoji(&self) -> String {
36 | let width = measure_text_width(&self.emoji);
37 | let pad_len = width * width;
38 | let pad = format!("{:) -> fmt::Result {
47 | write!(
48 | f,
49 | "\u{0020}{:\u{0020}(
13 | timeout: u64,
14 | user_agent: &str,
15 | redirects: bool,
16 | insecure: bool,
17 | headers: &HashMap,
18 | proxy: Option<&str>,
19 | server_certs: I,
20 | client_cert: Option<&str>,
21 | client_key: Option<&str>,
22 | ) -> Result
23 | where
24 | I: IntoIterator,
25 | I::Item: AsRef + std::fmt::Debug,
26 | {
27 | let policy = if redirects {
28 | Policy::limited(10)
29 | } else {
30 | Policy::none()
31 | };
32 |
33 | let header_map: HeaderMap = headers.try_into()?;
34 |
35 | let mut client = Client::builder()
36 | .timeout(Duration::new(timeout, 0))
37 | .user_agent(user_agent)
38 | .danger_accept_invalid_certs(insecure)
39 | .default_headers(header_map)
40 | .redirect(policy)
41 | .http1_title_case_headers();
42 |
43 | if let Some(some_proxy) = proxy {
44 | if !some_proxy.is_empty() {
45 | // it's not an empty string; set the proxy
46 | let proxy_obj = Proxy::all(some_proxy)?;
47 | // just add the proxy to the client
48 | // don't build and return it just yet
49 | client = client.proxy(proxy_obj);
50 | }
51 | }
52 |
53 | for cert_path in server_certs {
54 | let buf = std::fs::read(&cert_path)?;
55 |
56 | let cert = match reqwest::Certificate::from_pem(&buf) {
57 | Ok(cert) => cert,
58 | Err(err) => reqwest::Certificate::from_der(&buf).with_context(|| {
59 | format!(
60 | "{:?} does not contain a valid PEM or DER certificate\n{}",
61 | &cert_path, err
62 | )
63 | })?,
64 | };
65 |
66 | client = client.add_root_certificate(cert);
67 | }
68 |
69 | if let (Some(cert_path), Some(key_path)) = (client_cert, client_key) {
70 | if !cert_path.is_empty() && !key_path.is_empty() {
71 | let cert = std::fs::read(cert_path)?;
72 | let key = std::fs::read(key_path)?;
73 |
74 | let identity = reqwest::Identity::from_pkcs8_pem(&cert, &key).with_context(|| {
75 | format!(
76 | "either {} or {} are invalid; expecting PEM encoded certificate and key",
77 | cert_path, key_path
78 | )
79 | })?;
80 |
81 | client = client.identity(identity);
82 | }
83 | }
84 |
85 | Ok(client.build()?)
86 | }
87 |
88 | #[cfg(test)]
89 | mod tests {
90 | use super::*;
91 |
92 | #[test]
93 | #[should_panic]
94 | /// create client with a bad proxy, expect panic
95 | fn client_with_bad_proxy() {
96 | let headers = HashMap::new();
97 | initialize(
98 | 0,
99 | "stuff",
100 | true,
101 | false,
102 | &headers,
103 | Some("not a valid proxy"),
104 | Vec::::new(),
105 | None,
106 | None,
107 | )
108 | .unwrap();
109 | }
110 |
111 | #[test]
112 | /// create client with a proxy, expect no error
113 | fn client_with_good_proxy() {
114 | let headers = HashMap::new();
115 | let proxy = "http://127.0.0.1:8080";
116 | initialize(
117 | 0,
118 | "stuff",
119 | true,
120 | true,
121 | &headers,
122 | Some(proxy),
123 | Vec::::new(),
124 | None,
125 | None,
126 | )
127 | .unwrap();
128 | }
129 |
130 | #[test]
131 | /// create client with a server cert in pem format, expect no error
132 | fn client_with_valid_server_pem() {
133 | let headers = HashMap::new();
134 |
135 | initialize(
136 | 0,
137 | "stuff",
138 | true,
139 | true,
140 | &headers,
141 | None,
142 | vec!["tests/mutual-auth/certs/server/server.crt.1".to_string()],
143 | None,
144 | None,
145 | )
146 | .unwrap();
147 | }
148 |
149 | #[test]
150 | /// create client with a server cert in der format, expect no error
151 | fn client_with_valid_server_der() {
152 | let headers = HashMap::new();
153 |
154 | initialize(
155 | 0,
156 | "stuff",
157 | true,
158 | true,
159 | &headers,
160 | None,
161 | vec!["tests/mutual-auth/certs/server/server.der".to_string()],
162 | None,
163 | None,
164 | )
165 | .unwrap();
166 | }
167 |
168 | #[test]
169 | /// create client with two server certs (pem and der), expect no error
170 | fn client_with_valid_server_pem_and_der() {
171 | let headers = HashMap::new();
172 |
173 | println!("{}", std::env::current_dir().unwrap().display());
174 |
175 | initialize(
176 | 0,
177 | "stuff",
178 | true,
179 | true,
180 | &headers,
181 | None,
182 | vec![
183 | "tests/mutual-auth/certs/server/server.crt.1".to_string(),
184 | "tests/mutual-auth/certs/server/server.der".to_string(),
185 | ],
186 | None,
187 | None,
188 | )
189 | .unwrap();
190 | }
191 |
192 | /// create client with invalid certificate, expect panic
193 | #[test]
194 | #[should_panic]
195 | fn client_with_invalid_server_cert() {
196 | let headers = HashMap::new();
197 |
198 | initialize(
199 | 0,
200 | "stuff",
201 | true,
202 | true,
203 | &headers,
204 | None,
205 | vec!["tests/mutual-auth/certs/client/client.key".to_string()],
206 | None,
207 | None,
208 | )
209 | .unwrap();
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/config/mod.rs:
--------------------------------------------------------------------------------
1 | //! all logic related to instantiating a running configuration
2 |
3 | mod container;
4 | mod utils;
5 | #[cfg(test)]
6 | mod tests;
7 |
8 | pub use self::container::Configuration;
9 | pub use self::utils::{determine_output_level, OutputLevel, RequesterPolicy};
10 |
--------------------------------------------------------------------------------
/src/event_handlers/command.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 | use std::time::Duration;
3 |
4 | use reqwest::StatusCode;
5 | use tokio::sync::oneshot::Sender;
6 |
7 | use crate::response::FeroxResponse;
8 | use crate::{
9 | event_handlers::Handles,
10 | message::FeroxMessage,
11 | statistics::{StatError, StatField},
12 | traits::FeroxFilter,
13 | };
14 |
15 | /// Protocol definition for updating an event handler via mpsc
16 | #[derive(Debug)]
17 | pub enum Command {
18 | /// Add one to the total number of requests
19 | AddRequest,
20 |
21 | /// Add one to the proper field(s) based on the given `StatError`
22 | AddError(StatError),
23 |
24 | /// Add one to the proper field(s) based on the given `StatusCode`
25 | AddStatus(StatusCode),
26 |
27 | /// Create the progress bar (`BarType::Total`) that is updated from the stats thread
28 | ///
29 | /// the u64 value is the offset at which to start the progress bar (can be 0)
30 | CreateBar(u64),
31 |
32 | /// Add to a `Stats` field that corresponds to the given `StatField` by the given `usize` value
33 | AddToUsizeField(StatField, usize),
34 |
35 | /// Subtract from a `Stats` field that corresponds to the given `StatField` by the given `usize` value
36 | SubtractFromUsizeField(StatField, usize),
37 |
38 | /// Update a `Stats` field that corresponds to the given `StatField` by the given `f64` value
39 | AddToF64Field(StatField, f64),
40 |
41 | /// Save a `Stats` object to disk using `reporter::get_cached_file_handle`
42 | Save,
43 |
44 | /// Load a `Stats` object from disk
45 | LoadStats(String),
46 |
47 | /// Add a `FeroxFilter` implementor to `FilterHandler`'s instance of `FeroxFilters`
48 | AddFilter(Box),
49 |
50 | /// Remove a set of `FeroxFilter` implementors from `FeroxFilters` by index
51 | RemoveFilters(Vec),
52 |
53 | /// Send a `FeroxResponse` to the output handler for reporting
54 | Report(Box),
55 |
56 | /// Send a group of urls to be scanned (only used for the urls passed in explicitly by the user)
57 | ScanInitialUrls(Vec),
58 |
59 | /// Send a single url to be scanned (presumably added from the interactive menu)
60 | ScanNewUrl(String),
61 |
62 | /// Determine whether or not recursion is appropriate, given a FeroxResponse, if so start a scan
63 | TryRecursion(Box),
64 |
65 | /// Send a pointer to the wordlist to the recursion handler
66 | UpdateWordlist(Arc>),
67 |
68 | /// Instruct the ScanHandler to join on all known scans, use sender to notify main when done
69 | JoinTasks(Sender),
70 |
71 | /// Command used to test that a spawned task succeeded in initialization
72 | Ping,
73 |
74 | /// Just receive a sender and reply, used for slowing down the main thread
75 | Sync(Sender),
76 |
77 | /// Notify event handler that a new extension has been seen
78 | AddDiscoveredExtension(String),
79 |
80 | /// Write an arbitrary string to disk
81 | WriteToDisk(Box),
82 |
83 | /// Break out of the (infinite) mpsc receive loop
84 | Exit,
85 |
86 | /// Give a handler access to an Arc instance after the handler has
87 | /// already been initialized
88 | AddHandles(Arc),
89 |
90 | /// inform the Stats object about which targets are being scanned
91 | UpdateTargets(Vec),
92 |
93 | /// query the Stats handler about the position of the overall progress bar
94 | QueryOverallBarEta(Sender),
95 | }
96 |
--------------------------------------------------------------------------------
/src/event_handlers/container.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use crate::config::Configuration;
3 | use crate::event_handlers::scans::ScanHandle;
4 | use crate::scan_manager::FeroxScans;
5 | use crate::Joiner;
6 | #[cfg(test)]
7 | use crate::{filters::FeroxFilters, statistics::Stats, Command};
8 | use anyhow::{bail, Result};
9 | use std::collections::HashSet;
10 | use std::sync::{Arc, RwLock};
11 | #[cfg(test)]
12 | use tokio::sync::mpsc::{self, UnboundedReceiver};
13 |
14 | #[derive(Debug)]
15 | /// Simple container for multiple JoinHandles
16 | pub struct Tasks {
17 | /// JoinHandle for terminal handler
18 | pub terminal: Joiner,
19 |
20 | /// JoinHandle for statistics handler
21 | pub stats: Joiner,
22 |
23 | /// JoinHandle for filters handler
24 | pub filters: Joiner,
25 |
26 | /// JoinHandle for scans handler
27 | pub scans: Joiner,
28 | }
29 |
30 | /// Tasks implementation
31 | impl Tasks {
32 | /// Given JoinHandles for terminal, statistics, and filters create a new Tasks object
33 | pub fn new(terminal: Joiner, stats: Joiner, filters: Joiner, scans: Joiner) -> Self {
34 | Self {
35 | terminal,
36 | stats,
37 | filters,
38 | scans,
39 | }
40 | }
41 | }
42 |
43 | #[derive(Debug)]
44 | /// Container for the different *Handles that will be shared across modules
45 | pub struct Handles {
46 | /// Handle for statistics
47 | pub stats: StatsHandle,
48 |
49 | /// Handle for filters
50 | pub filters: FiltersHandle,
51 |
52 | /// Handle for output (terminal/file)
53 | pub output: TermOutHandle,
54 |
55 | /// Handle for Configuration
56 | pub config: Arc,
57 |
58 | /// Handle for recursion
59 | pub scans: RwLock