Giter Club home page Giter Club logo

sec-tester-cr's Introduction

sec-tester-cr

example workflow

A library to allow the usage of Bright security scanner inside of the Crystal SPECS unit testing flow

For support and help visit the Bright Discord

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      sec_tester:
        github: NeuraLegion/sec-tester-cr
  2. Run shards install

Usage

Dependencies

Warning

To use the library you will first need to complete all of the below steps.

  1. Register for an account at signup
  2. Generate an API key from your UI
  3. The preferred approach is to setup your API-Key as ENV var BRIGHT_TOKEN for API key.

Use Inside Specs

require "sec_tester"

it "tests my app for XSS" do
  server = HTTP::Server.new do |context|
    name = URI.decode_www_form(context.request.query_params["name"]?.to_s)

    context.response.content_type = "text/html"
    context.response << <<-EOF
      <html>
        <body>
          <h1>Hello, world!</h1>
          <p>#{name}</p>
        </body>
      </html>
      EOF
  end

  addr = server.bind_unused_port
  spawn do
    server.listen
  end

  tester = SecTester::Test.new
  tester.run_check(
    scan_name: "UnitTestingScan - XSS",
    tests: "xss",
    target: SecTester::Target.new("http://#{addr}/?name=jhon")
  )
ensure
  server.try &.close
end

Target Response Configurations

The following example shows how to configure a target manually. this is very useful to control expected response from the target.

Note

Configuring the response information is very important for the scanner to work properly. and can decrease scan times and improve the accuracy of the scan.

target: SecTester::Target.new(
  method: "GET",
  url: "http://#{addr}/?name=jhon",
  response_headers: HTTP::Headers{"Content-Type" => "text/html"},
  response_body: "<html><body><h1>Hello, world!</h1><p>jhon</p></body></html>",
  response_status: 200
  )

Testing Single Function

The following example shows how to test a single function.

tester.run_check(scan_name: "UnitTestingScan - XSS - function only", tests: ["xss"]) do |payload, response|
  spawn do
    while payload_data = payload.receive?
      # This is where we send the payload to the function and send back a response
      # In this example we just want to send back the payload
      # as we are testing reflection
      # my_function is a demo function that returns the payload
      response_data = my_function(payload_data)

      # we end up sending the response back to the channel
      response.send(response_data)
    end
  end
end

Note

You also have an optional "param_overwrite" parameter that allows you to overwrite the parameters in the request. This is useful when your function is expecting a specific data object like JSON or JWT etc..

You can use the param_overwrite to overwrite the value to be attacked like:

jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
tester.run_check(scan_name: "jwt-testing", tests: ["jwt"], param_overwrite: jwt) do |payload, response|
  spawn do
    while payload_data = payload.receive?
      # This is where we send the payload to the function and send back a response
      # In this example we just want to send back the payload
      # as we are testing reflection
      # my_function is a demo function that returns the payload
      response_data = my_JWT_verification(payload_data)

      # we end up sending the response back to the channel
      response.send(response_data)
    end
  end
end

There is also a variant of this interface that accepts target and yields back the whole HTTP::Server::Context. This is useful if you want to do something with the response body or headers.

tester.run_check(scan_name: "UnitTestingScan - XSS - request/response test", target: target, tests: ["xss"]) do |context_channel|
  spawn do
    while context_tuple = context_channel.receive?
      context = context_tuple[:context]
      done_channel = context_tuple[:done_chan]
      input = context.request.query_params["id"]?.to_s
      response_data = my_function(input)

      context.response.headers["Content-Type"] = "text/html"
      context.response.status_code = 200
      context.response.print(response_data)
      done_channel.send(nil) # Important part, make sure to send back nil to the done channel
    end
  end
end

Fail by Severity Threshold

When you want to fail the test by severity threshold, you can use the following example.

tester.run_check(
  scan_name: "UnitTestingScan - cookie_security - Skip low",
  tests: ["cookie_security"],
  target: SecTester::Target.new(
    url: "http://#{addr}/",
  ),
  severity_threshold: :medium
)

The severity_threshold is a :low, :medium, :high or :critical value. it allows you to not fail the build if the severity is lower than the threshold.

For example if you want to run the test and fail the build if the severity is :high but continue testing if it's :medium or :low. use the following example.

tester.run_check(
  severity_threshold: :high
)

If the issues are :low or :medium the build will continue.

Scan Options

When running a check you can now pass a few options to the scan. Options are:

  1. Smart scan (true\false) - Specify whether to use automatic smart decisions (such as parameter skipping, detection phases and so on) in order to minimize scan time. When this option is turned off, all tests are run on all the parameters, that increases coverage at the expense of scan time.
  2. Skip Static Params (true\false) - Specify whether to skip static parameters to minimize scan time.
  3. Specify Project ID for the scan - manage-projects
  4. Parameter locations: param_locations - Specify the parameter locations to scan in the Request. this opens supports body, query, fragment, headers and path. defualt is body, query and fragment.
Usage example:

```crystal
tester.run_check(
  options: SecTester::Options.new(
    smart_scan: true,
    skip_static_parameters: true,
    project_id: "ufNQ9Fo7XFVAsuyGpo7YTf",
    param_locations: ["query", "body"]
  )
)

Choosing the right tests

When configuring the target you can choose which tests to run. This is done using the tests: option. This option can be a string or an array of strings.

  # single test
  tests: "xss"

  # multiple tests
  tests: ["xss", "sqli"]

It's also possible to run all tests by using nil option. but it is not recommended. A quick rule of thumb is thinking about the actual technologies used in the target. So for example, if the target is using SQL Database, you should run the SQLi test. Otherwise, if the target is using an HTML rendering engine, you should run the XSS test.

All currently available tests are listed in the tests.cr file

Integrating into the CI

To integrate this library into the CI you will need to add the BRIGHT_TOKEN ENV vars to your CI. Then add the following to your github actions configuration:

steps:
  - name: Install npm and Repeater
    run: |
      apt update
      apt-get install -y libnode-dev node-gyp libssl-dev
      apt-get install -y nodejs npm
      npm install -g @neuralegion/nexploit-cli --unsafe-perm=true
  - name: Run tests
    env:
      BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }}
    run: crystal spec

Example Usage

You can see this shard in action at the Lucky Sec Test repo. Specifically look at the Security Flow Specs

Use as a CLI

For the purpose of testing the library you can use the following command:

shards build
bin/sec_tester_cli -t BRIGHT_TOKEN -u https://brokencrystals.com/ # or another target

This will run the tests on the target and print the results to the console in a nice table format.

You can use -h or --help to see the available options.

Contributing

  1. Fork it (https://github.com/NeuraLegion/sec-tester-cr/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

sec-tester-cr's People

Contributors

bararchy avatar wyhaines avatar aborovsky avatar derevnjuk avatar

Stargazers

Wout avatar GMakkyuu avatar Alexander ADAM avatar  avatar Gabriel Aires Guedes avatar Aleksey avatar Caspian Baska avatar Evan Paterakis avatar Vitalii Elenhaupt avatar asdf avatar Vlad Zarakovsky avatar Jeremy Woertink avatar Johannes Müller avatar

Watchers

James Cloos avatar  avatar Or Rubin avatar Gabriel Aires Guedes avatar Viachaslau avatar  avatar Aleksey avatar

Forkers

wyhaines

sec-tester-cr's Issues

replace nepxloit-cli repeater with native crystal logic

Right now we need to install the nexploit-cli npm package inside of the machine\CI context for this shard to work.
it can be more lightweight dependency wise if the repeater functionality will be integrated into the shard in native crystal.

Getting a closed socket error when running specs

When I run this spec in Lucky, I'm getting a "Closed socket" error

it "tests the sign_in" do
    scanner = LuckySecTester.new
    target = scanner.build_target(SignIns::New)
    scanner.run_check(
      scan_name: "ref: #{ENV["GITHUB_REF"]?} commit: #{ENV["GITHUB_SHA"]?} run id: #{ENV["GITHUB_RUN_ID"]?}",
      tests: [
        "dom_xss", 
        "brute_force_login", 
      ],
      target: target
    )
  end
1) SecTester tests the sign_in

       Closed socket (IO::Error)
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/http/web_socket.cr:77:5 in 'check_open'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/http/web_socket.cr:82:5 in 'send'
         from lib/socket_io/src/engine_io/engine_io.cr:118:7 in 'send_packet'
         from lib/socket_io/src/engine_io/engine_io.cr:35:7 in 'send'
         from lib/socket_io/src/socket_io/socket_io.cr:127:7 in 'send_packet'
         from lib/socket_io/src/socket_io/socket_io.cr:113:7 in 'emit_event'
         from lib/socket_io/src/socket_io/socket_io.cr:62:7 in 'emit'
         from lib/sec_tester/src/sec_tester/repeater.cr:31:7 in 'close'
         from lib/sec_tester/src/sec_tester/scan.cr:222:9 in 'stop'
         from lib/sec_tester/src/sec_tester/scan.cr:195:11 in 'poll:timeout:on_issue:severity_threshold'
         from lib/sec_tester/src/sec_tester/test.cr:34:7 in 'run_check:scan_name:tests:target'
         from lib/lucky_sec_tester/src/lucky_sec_tester.cr:16:3 in 'run_check:scan_name:tests:target'
         from spec/flows/security_spec.cr:10:5 in '->'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/example.cr:45:13 in 'internal_run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/example.cr:32:73 in '->'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/example/procsy.cr:16:15 in 'run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:368:11 in '->'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/example/procsy.cr:16:15 in 'run'
         from spec/setup/configure_lucky_flow.cr:37:1 in '->'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:71:26 in 'run_around_each_hook'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:66:7 in 'internal_run_around_each_hooks'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:59:7 in 'run_around_each_hooks'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:360:13 in 'run_around_each_hooks'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/example.cr:32:15 in 'run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:18:23 in 'internal_run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:342:7 in 'run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:18:23 in 'internal_run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/context.cr:158:7 in 'run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/spec/dsl.cr:212:7 in '->'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/crystal/at_exit_handlers.cr:14:19 in 'run'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/crystal/main.cr:64:14 in 'exit'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/crystal/main.cr:59:5 in 'main'
         from /home/runner/work/_temp/crystal-latest-true-undefined/share/crystal/src/crystal/main.cr:141:3 in 'main'
         from /lib/x86_64-linux-gnu/libc.so.6 in '??'
         from /lib/x86_64-linux-gnu/libc.so.6 in '__libc_start_main'
         from /home/runner/.cache/crystal/crystal-run-spec.tmp in '_start'
         from ???

This is running on the latest release, but maybe I'm missing a setup somewhere since the repeater moved to Crystal?

Unexpected char 'F' when the API token is invalid

Related: #6

I set a fake token

LuckySecTester.configure do |settings|
  settings.nexploit_token = "test123"
end

and got this error:

Error creating repeater: Unexpected char 'F' at line 1, column 1 response: Failed to log in with provided credentials. (SecTester::Error)
         from lib/sec_tester/src/scan.cr:226:7 in 'create_repeater'
         from lib/sec_tester/src/scan.cr:15:19 in 'initialize'
         from lib/sec_tester/src/scan.cr:14:5 in 'new:token'
         from lib/sec_tester/src/test.cr:16:15 in 'initialize'
         from lib/sec_tester/src/test.cr:15:5 in 'new'

I'm assuming the char F is probably from decoding some JSON or something. Thankfully it tells me "Failed to log in", so I knew exactly what the issue was. Not really a huge issue/priority here, but just wanted to report as a potential cleanup in the future or whatever.

Add support for the `amazon_s3_takeover` test

Add support for the amazon_s3_takeover test type to enable the "Amazon AWS S3 bucket takeover" vulnerability scan.

To run this test, it should be possible to use the following code snippet:

require "sec_tester"

it "tests my app for amazon_s3_takeover" do
  server = HTTP::Server.new do |context|
    context.response.content_type = "text/html"
    context.response << <<-EOF
      <html>
        <body>
          <h1>Hello, world!</h1>
          <a href="https://takemeover-bright.s3.amazonaws.com/privacy-policy.pdf">download</p>
        </body>
      </html>
      EOF
  end

  addr = server.bind_unused_port
  spawn do
    server.listen
  end

  tester = SecTester::Test.new
  tester.run_check(
    scan_name: "Amazon AWS S3 bucket takeover",
    tests: "amazon_s3_takeover",
    target: SecTester::Target.new("http://#{addr}/")
  )
ensure
  server.try &.close
end

If the page's body contains a link that points to a 404 resource with the following content:

Code: NoSuchBucket
Message: The specified bucket does not exist
BucketName: cdn.example.com

it indicates that there is a vulnerability.

Please see the following references for more information on this vulnerability:

Unable to cast Int64 to Float64

@mdwagner has been working on some updated specs for Lucky, and started running in to this conversion error. I think it's coming from this line

@scan_duration = response_json["elapsed"].as_f.milliseconds

Maybe response_json["elapsed"] returns Int64 here?

For now we're able to work around it with a little hack 😂

RUN sed -i '131s/as_f/as_i/' lib/sec_tester/src/sec_tester/scan.cr

Question: Should the credential validation be lazily evaluated?

I was writing a simple test that wasn't meant to actually run the nexploit test, but I still got this error:

Error creating repeater: Unexpected char 'F' at line 1, column 1 response: Failed to log in with provided credentials. (SecTester::Error)

scanner = LuckySecTester.new
target = scanner.build_target(IndexAction)
target.headers["Content-Type"].should eq("application/x-www-form-urlencoded")

target = scanner.build_target(IndexAction, headers: HTTP::Headers{"Content-Type" => "application/json"})
target.headers["Content-Type"].should eq("application/json")

I'm assuming that once the target is built, it tries to validate the API token, but I wonder if it needs to? Maybe that step can wait until run_check is called? or are there some technical issues with doing that?

Support new `Prompt Injection` test

Prompt Injection Vulnerabilities in LLMs involve crafty inputs leading to undetected manipulations. The impact ranges from data exposure to unauthorized actions, serving attackers goals.

XSS test types changed, new stored_xss type introduced

XSS test types change is coming.

At the moment there 2 XSS-related test types supported:

  1. xss - produce Stored XSS and Reflective XSS.
  2. dom_xss - Produce Reflective XSS which gets renamed with MEtaDAta..

New supported XSS tests set will be:

  1. xss - corresponds to Cross-Site Scripting test.
  2. stored_xss - corresponds to new Stored Cross-Site Scripting test.
  3. dom_xss - corresponds to Client-Side XSS test. This type is deprecated, will be decommissioned.

Naming issue

Since the repo's name is sectester this

dependencies:
  sec_tester:
    github: NeuraLegion/sec_tester

should be changed to

dependencies:
  sec_tester:
    github: NeuraLegion/sectester  # <= no underscore :)

Also, 1. Fork it (https://github.com/NeuraLegion/sec_tester/fork) has the same underscore issue.

Another solution would be to just change the repo's name to sec_tester.

Support new `CSS Injection` test

CSS injection is a security vulnerability where an attacker injects Cascading Style Sheets (CSS) code into a web application. This vulnerability was found in the (query, input field?) by changing the value of the (query parameter, input field)(query parameter name, input field name, id?). The value (Payload?) was injected, which caused the target to execute CSS changes on the body, which is the replacement of the background image that we use from external resources (image url).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.