matrix-fuzz

git clone git://archive.git.mtrnord.blog/MTRNord/matrix-fuzz.git
Log | Files | Refs | README | LICENSE

commit 01fb94e51100d041731a34ea87ea5cb03189066f
parent 521cd3d1f908c8beffa08ddece4679641493df1f
Author: MTRNord <mtrnord1@gmail.com>
Date:   Tue, 30 Aug 2022 20:11:14 +0200

Run tests in CI and pass creds via env vars instead

Diffstat:
R.github/workflows/docker-build-publish-fuzz_login.yml -> .github/workflows/docker_fuzz_login.yml | 0
A.github/workflows/docker_synapse_test_env.yml | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.github/workflows/test_known.yml | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME.md | 44++++++++++++++------------------------------
Adockerfiles/synapse | 9+++++++++
Adockerfiles/synapse_config.yaml | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adockerfiles/synapse_generate_login.py | 28++++++++++++++++++++++++++++
Adockerfiles/synapse_log.yaml | 20++++++++++++++++++++
Adockerfiles/synapse_signing.key | 2++
Msrc/lib.rs | 25+++++++++++++++++++------
10 files changed, 383 insertions(+), 36 deletions(-)

diff --git a/.github/workflows/docker-build-publish-fuzz_login.yml b/.github/workflows/docker_fuzz_login.yml diff --git a/.github/workflows/docker_synapse_test_env.yml b/.github/workflows/docker_synapse_test_env.yml @@ -0,0 +1,83 @@ +name: Docker Synapse +on: + push: + branches: ["main"] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as <account>/<repo> + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@7e0881f8fe90b25e305bbf0309761e9314607e25 + with: + cosign-release: "v1.9.0" + + # Workaround: https://github.com/docker/build-push-action/issues/461 + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: type=raw,value=latest + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a + with: + build-args: version=v1.66.0rc2 + context: ./dockerfiles + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + file: ./dockerfiles/synapse + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + COSIGN_EXPERIMENTAL: "true" + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign {}@${{ steps.build-and-push.outputs.digest }} diff --git a/.github/workflows/test_known.yml b/.github/workflows/test_known.yml @@ -0,0 +1,65 @@ +name: Test known issues + +on: + workflow_run: + workflows: ["Docker Synapse"] + types: + - completed + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + runs-on: ubuntu-latest + container: rustlang/rust:nightly-slim + + services: + postgres: + # Docker Hub image + image: postgres + # Provide the password for postgres + env: + POSTGRES_PASSWORD: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + redis: + image: redis + # Set health checks to wait until redis has started + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + synapse: + image: ghcr.io/MTRNord/synapse:latest + # Set health checks to wait until redis has started + options: >- + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v3 + - name: Login get_nonce + id: get_nonce + run: echo "::set-output nonce=$(curl http://localhost:8008/_synapse/admin/v1/register | jq -r .nonce)" + - name: Generate mac + id: generate_mac + run: python3 ./dockerfiles/synapse_generate_login.py + env: + NONCE: ${{ steps.get_nonce.outputs.nonce }} + - name: Login + run: 'curl -X POST http://localhost:8008/_synapse/admin/v1/login -d ''{"mac": "${MAC}", "nonce": "${NONCE}", "username": "fuzzer", "password": "Chu8chool0dooqueiwo0lohviegho6ieveuNg3Ohcio2aekaiw0ioF6waifo8eep"}''' + env: + MAC: ${{ steps.generate_mac.outputs.hmac }} + NONCE: ${{ steps.get_nonce.outputs.nonce }} + - name: Run tests + run: cargo test + env: + MATRIX_USERNAME: "fuzzer" + MATRIX_PASSWORD: "Chu8chool0dooqueiwo0lohviegho6ieveuNg3Ohcio2aekaiw0ioF6waifo8eep" diff --git a/README.md b/README.md @@ -13,41 +13,25 @@ Matrix fuzzing is a dumb fuzzer fuzzing a few matrix endpoints. It requires a HS # Usage of fuzzcheck-rs 1. Create a HS -1. Setup a user -1. Add the secrets to src/secrets.rs. - - Example code: - - ```rust - pub const USERNAME: &str = "@a:localhost"; - pub const PASSWORD: &str = "abc123"; - ``` - -1. Install fuzzcheck -> https://github.com/loiclec/fuzzcheck-rs#setup -1. Run `cargo fuzzcheck <target>` -1. Wait until it crashes -1. Verify the error by trying the output json yourself -1. Please make sure to follow https://matrix.org/security-disclosure-policy/ for found errors instead of posting them in public unless you are 100% sure they are not a security issue. If you are in doubt prefer the security disclosure policy. +2. Setup a user +3. Set `MATRIX_USERNAME` and `MATRIX_PASSWORD` to the username and password of the user you want to fuzz as. +4. Install fuzzcheck -> https://github.com/loiclec/fuzzcheck-rs#setup +5. Run `cargo fuzzcheck <target>` +6. Wait until it crashes +7. Verify the error by trying the output json yourself +8. Please make sure to follow https://matrix.org/security-disclosure-policy/ for found errors instead of posting them in public unless you are 100% sure they are not a security issue. If you are in doubt prefer the security disclosure policy. # Usage of afl.rs 1. Create a HS 1. Setup a user -1. Add the secrets to src/secrets.rs. - - Example code: - - ```rust - pub const USERNAME: &str = "@a:localhost"; - pub const PASSWORD: &str = "abc123"; - ``` - -1. Install afl.rs -> `cargo install afl` -1. Run `cargo afl build` -2. Run `cargo afl fuzz -i ./afl/<target>/in -o ./afl/<target>/out ./target/debug/<target>` -3. Wait until it crashes -4. Verification is a little harder. See https://github.com/rust-fuzz/afl.rs/issues/215 on how to reproduce things -5. Please make sure to follow https://matrix.org/security-disclosure-policy/ for found errors instead of posting them in public unless you are 100% sure they are not a security issue. If you are in doubt prefer the security disclosure policy. +2. Install afl.rs -> `cargo install afl` +3. Run `cargo afl build` +4. Set `MATRIX_USERNAME` and `MATRIX_PASSWORD` to the username and password of the user you want to fuzz as. +5. Run `cargo afl fuzz -i ./afl/<target>/in -o ./afl/<target>/out ./target/debug/<target>` +6. Wait until it crashes +7. Verification is a little harder. See https://github.com/rust-fuzz/afl.rs/issues/215 on how to reproduce things +8. Please make sure to follow https://matrix.org/security-disclosure-policy/ for found errors instead of posting them in public unless you are 100% sure they are not a security issue. If you are in doubt prefer the security disclosure policy. # Hall of Explosions (Bugs found) diff --git a/dockerfiles/synapse b/dockerfiles/synapse @@ -0,0 +1,8 @@ +ARG version +FROM matrix/synapse:${version} + +COPY ./synapse_config.yaml /synapse/config/homeserver.yaml +COPY ./synapse_log.yaml /synapse/config/log.yaml +COPY ./synapse_signing.key /synapse/keys/signing.key + +CMD "exec python -B -m synapse.app.homeserver -c /synapse/config/homeserver.yaml" +\ No newline at end of file diff --git a/dockerfiles/synapse_config.yaml b/dockerfiles/synapse_config.yaml @@ -0,0 +1,143 @@ +## Server ## + +server_name: "localhost" +public_baseurl: "http://localhost:8008" +pid_file: /homeserver.pid +web_client: False +soft_file_limit: 0 +log_config: "/synapse/config/log.yaml" +report_stats: false + +## Ports ## + +listeners: + - port: 8008 + tls: false + bind_addresses: ["::"] + type: http + x_forwarded: true + + resources: + - names: [client, federation] + compress: false + + - port: 9090 + tls: false + bind_addresses: ["::"] + type: http + + resources: + - names: [metrics] + compress: false + + - port: 9093 + tls: false + bind_addresses: ["::"] + type: http + + resources: + - names: [replication] + compress: false + +## Files ## + +media_store_path: "/synapse/data/media" +uploads_path: "/synapse/data/uploads" + +## Registration ## +registration_shared_secret: "sahZae3yahjaequ8boh2cae5uo5eiciede2hoa9eew8mai1oy4iiChietheequ9U" +enable_registration: false + +## API Configuration ## + +## Database configuration ## +database: + name: "psycopg2" + args: + user: "postgres" + password: "postgres" + sslmode: "prefer" + database: "synapse" + port: 5432 + cp_min: 5 + cp_max: 10 + host: "postgres" + +## Redis configuration ## +redis: + enabled: true + host: "redis" + port: 6379 + +## Metrics ### + +enable_metrics: true + +## Signing Keys ## + +signing_key_path: "/synapse/keys/signing.key" + +# The trusted servers to download signing keys from. +trusted_key_servers: + - server_name: matrix.org + +## Extra config ## + +enable_media_repo: false +federation_domain_whitelist: [] +federation_rr_transactions_per_room_per_second: 1000000000000 +federation_verify_certificates: false +rc_3pid_validation: + burst_count: 150000000000 + per_second: 1000000000000 +rc_admin_redaction: + burst_count: 150000000000 + per_second: 1000000000000 +rc_federation: + concurrent: 3 + reject_limit: 1000000000000 + sleep_delay: 500 + sleep_limit: 10 + window_size: 1000 +rc_invites: + per_issuer: + burst_count: 150000000000 + per_second: 1000000000000 + per_room: + burst_count: 150000000000 + per_second: 1000000000000 + per_user: + burst_count: 150000000000 + per_second: 1000000000000 +rc_joins: + local: + burst_count: 150000000000 + per_second: 1000000000000 + remote: + burst_count: 150000000000 + per_second: 1000000000000 +rc_joins_per_room: + burst_count: 150000000000 + per_second: 1000000000000 +rc_login: + account: + burst_count: 150000000000 + per_second: 1000000000000 + address: + burst_count: 150000000000 + per_second: 1000000000000 + failed_attempts: + burst_count: 150000000000 + per_second: 1000000000000 +rc_message: + burst_count: 150000000000 + per_second: 1000000000000 +rc_registration: + burst_count: 150000000000 + per_second: 1000000000000 +rc_registration_token_validity: + burst_count: 150000000000 + per_second: 1000000000000 +rc_third_party_invite: + burst_count: 150000000000 + per_second: 1000000000000 diff --git a/dockerfiles/synapse_generate_login.py b/dockerfiles/synapse_generate_login.py @@ -0,0 +1,28 @@ +import hashlib +import hmac +import os + + +def generate_mac(nonce, user, password, admin=False, user_type=None): + mac = hmac.new( + key=shared_secret, + digestmod=hashlib.sha1, + ) + + mac.update(nonce.encode('utf8')) + mac.update(b"\x00") + mac.update(user.encode('utf8')) + mac.update(b"\x00") + mac.update(password.encode('utf8')) + mac.update(b"\x00") + mac.update(b"admin" if admin else b"notadmin") + if user_type: + mac.update(b"\x00") + mac.update(user_type.encode('utf8')) + + return mac.hexdigest() + + +hmac = generate_mac(os.environ['NONCE'], "fuzzer", + "Chu8chool0dooqueiwo0lohviegho6ieveuNg3Ohcio2aekaiw0ioF6waifo8eep") +print(f"::set-output hmac={hmac}") diff --git a/dockerfiles/synapse_log.yaml b/dockerfiles/synapse_log.yaml @@ -0,0 +1,20 @@ +version: 1 +formatters: + precise: + format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s- %(message)s" +filters: + context: + (): synapse.util.logcontext.LoggingContextFilter + request: "" +handlers: + console: + class: logging.StreamHandler + formatter: precise + filters: [context] + level: INFO +loggers: + synapse: + level: INFO +root: + level: INFO + handlers: [console] diff --git a/dockerfiles/synapse_signing.key b/dockerfiles/synapse_signing.key @@ -0,0 +1 @@ +ed25519 a_oLGZ qZ1gPAYosg/jvgVQN316wNjJzIiadkKJ4Wglek5VDIY +\ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs @@ -5,9 +5,8 @@ use crate::types::{Flow, LoginGet, LoginPost}; use once_cell::sync::OnceCell; -use std::collections::HashMap; +use std::{collections::HashMap, env}; -pub mod secrets; pub mod types; #[no_coverage] @@ -31,6 +30,14 @@ pub fn client() -> &'static reqwest::blocking::Client { #[no_coverage] fn login() -> String { + let username = match env::var("MATRIX_USERNAME") { + Ok(v) => v, + Err(e) => panic!("$MATRIX_USERNAME is not set ({})", e), + }; + let password = match env::var("MATRIX_PASSWORD") { + Ok(v) => v, + Err(e) => panic!("$MATRIX_PASSWORD is not set ({})", e), + }; let client = crate::client(); let res: LoginGet = client .get("http://localhost:8008/_matrix/client/v3/login") @@ -44,8 +51,8 @@ fn login() -> String { let mut map = HashMap::new(); map.insert("type", "m.login.password"); - map.insert("user", secrets::USERNAME); - map.insert("password", secrets::PASSWORD); + map.insert("user", &username); + map.insert("password", &password); let res: LoginPost = client .post("http://localhost:8008/_matrix/client/v3/login") .json(&map) @@ -228,11 +235,17 @@ mod tests { json_data._type = "m.login.password".to_string(); } } + + let username = match env::var("MATRIX_USERNAME") { + Ok(v) => v, + Err(e) => panic!("$MATRIX_USERNAME is not set ({})", e), + }; + if json_data.user.is_some() { - json_data.user = Some(crate::secrets::USERNAME.to_string()); + json_data.user = Some(username); } if let Some(identifier) = &mut json_data.identifier { - identifier.user = crate::secrets::USERNAME.to_string(); + identifier.user = username; identifier._type = "m.id.user".to_string(); }