contrast-agent 3.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.clang-format +5 -0
- data/.dockerignore +10 -0
- data/.gitignore +58 -0
- data/.gitmodules +6 -0
- data/.rspec +6 -0
- data/.simplecov +4 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +12 -0
- data/Rakefile +15 -0
- data/exe/contrast_service +29 -0
- data/ext/build_funchook.rb +48 -0
- data/ext/cs__assess_active_record_named/cs__active_record_named.c +47 -0
- data/ext/cs__assess_active_record_named/cs__active_record_named.h +10 -0
- data/ext/cs__assess_active_record_named/extconf.rb +2 -0
- data/ext/cs__assess_array/cs__assess_array.c +38 -0
- data/ext/cs__assess_array/cs__assess_array.h +9 -0
- data/ext/cs__assess_array/extconf.rb +2 -0
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +50 -0
- data/ext/cs__assess_basic_object/cs__assess_basic_object.h +17 -0
- data/ext/cs__assess_basic_object/extconf.rb +2 -0
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +86 -0
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.h +34 -0
- data/ext/cs__assess_fiber_track/extconf.rb +2 -0
- data/ext/cs__assess_hash/cs__assess_hash.c +64 -0
- data/ext/cs__assess_hash/cs__assess_hash.h +24 -0
- data/ext/cs__assess_hash/extconf.rb +2 -0
- data/ext/cs__assess_kernel/cs__assess_kernel.c +36 -0
- data/ext/cs__assess_kernel/cs__assess_kernel.h +10 -0
- data/ext/cs__assess_kernel/extconf.rb +2 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +47 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +18 -0
- data/ext/cs__assess_marshal_module/extconf.rb +2 -0
- data/ext/cs__assess_module/cs__assess_module.c +78 -0
- data/ext/cs__assess_module/cs__assess_module.h +25 -0
- data/ext/cs__assess_module/extconf.rb +2 -0
- data/ext/cs__assess_regexp/cs__assess_regexp.c +48 -0
- data/ext/cs__assess_regexp/cs__assess_regexp.h +22 -0
- data/ext/cs__assess_regexp/extconf.rb +2 -0
- data/ext/cs__assess_regexp_track/cs__assess_regexp_track.c +63 -0
- data/ext/cs__assess_regexp_track/cs__assess_regexp_track.h +29 -0
- data/ext/cs__assess_regexp_track/extconf.rb +2 -0
- data/ext/cs__assess_string/cs__assess_string.c +38 -0
- data/ext/cs__assess_string/cs__assess_string.h +19 -0
- data/ext/cs__assess_string/extconf.rb +2 -0
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +31 -0
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.h +13 -0
- data/ext/cs__assess_string_interpolation26/extconf.rb +2 -0
- data/ext/cs__common/cs__common.c +60 -0
- data/ext/cs__common/cs__common.h +28 -0
- data/ext/cs__common/extconf.rb +20 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +445 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.h +196 -0
- data/ext/cs__contrast_patch/extconf.rb +2 -0
- data/ext/cs__protect_kernel/cs__protect_kernel.c +37 -0
- data/ext/cs__protect_kernel/cs__protect_kernel.h +11 -0
- data/ext/cs__protect_kernel/extconf.rb +2 -0
- data/ext/cs__scope/cs__scope.c +96 -0
- data/ext/cs__scope/cs__scope.h +33 -0
- data/ext/cs__scope/extconf.rb +2 -0
- data/ext/extconf_common.rb +49 -0
- data/funchook/LICENSE +360 -0
- data/funchook/Makefile +29 -0
- data/funchook/Makefile.in +29 -0
- data/funchook/README.md +121 -0
- data/funchook/appveyor.yml +42 -0
- data/funchook/autogen.sh +3 -0
- data/funchook/autom4te.cache/output.0 +4976 -0
- data/funchook/autom4te.cache/requests +78 -0
- data/funchook/autom4te.cache/traces.0 +364 -0
- data/funchook/config.guess +1530 -0
- data/funchook/config.log +490 -0
- data/funchook/config.status +1016 -0
- data/funchook/config.sub +1773 -0
- data/funchook/configure +4976 -0
- data/funchook/configure.ac +59 -0
- data/funchook/distorm/COPYING +26 -0
- data/funchook/distorm/MANIFEST +25 -0
- data/funchook/distorm/MANIFEST.in +4 -0
- data/funchook/distorm/README.md +12 -0
- data/funchook/distorm/disOps/disOps.py +795 -0
- data/funchook/distorm/disOps/x86db.py +404 -0
- data/funchook/distorm/disOps/x86header.py +247 -0
- data/funchook/distorm/disOps/x86sets.py +1664 -0
- data/funchook/distorm/examples/cs/TestdiStorm/Program.cs +79 -0
- data/funchook/distorm/examples/cs/TestdiStorm/Properties/AssemblyInfo.cs +36 -0
- data/funchook/distorm/examples/cs/TestdiStorm/TestdiStorm.csproj +69 -0
- data/funchook/distorm/examples/cs/distorm-net.sln +26 -0
- data/funchook/distorm/examples/cs/distorm-net/CodeInfo.cs +23 -0
- data/funchook/distorm/examples/cs/distorm-net/DecodedInst.cs +15 -0
- data/funchook/distorm/examples/cs/distorm-net/DecodedResult.cs +14 -0
- data/funchook/distorm/examples/cs/distorm-net/DecomposedInst.cs +36 -0
- data/funchook/distorm/examples/cs/distorm-net/DecomposedResult.cs +14 -0
- data/funchook/distorm/examples/cs/distorm-net/Opcodes.cs +1268 -0
- data/funchook/distorm/examples/cs/distorm-net/Opcodes.tt +37 -0
- data/funchook/distorm/examples/cs/distorm-net/Operand.cs +25 -0
- data/funchook/distorm/examples/cs/distorm-net/Properties/AssemblyInfo.cs +36 -0
- data/funchook/distorm/examples/cs/distorm-net/diStorm3.cs +411 -0
- data/funchook/distorm/examples/cs/distorm-net/distorm-net.csproj +80 -0
- data/funchook/distorm/examples/cs/readme +3 -0
- data/funchook/distorm/examples/ddk/README +48 -0
- data/funchook/distorm/examples/ddk/distorm.ini +11 -0
- data/funchook/distorm/examples/ddk/dummy.c +15 -0
- data/funchook/distorm/examples/ddk/main.c +91 -0
- data/funchook/distorm/examples/ddk/makefile +1 -0
- data/funchook/distorm/examples/ddk/sources +10 -0
- data/funchook/distorm/examples/java/Makefile +23 -0
- data/funchook/distorm/examples/java/distorm/src/Main.java +43 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/CodeInfo.java +27 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedInst.java +32 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedResult.java +11 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedInst.java +89 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedResult.java +11 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/OpcodeEnum.java +131 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/Opcodes.java +1123 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/Operand.java +24 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/distorm3.java +41 -0
- data/funchook/distorm/examples/java/jdistorm.c +405 -0
- data/funchook/distorm/examples/java/jdistorm.h +40 -0
- data/funchook/distorm/examples/java/jdistorm.sln +20 -0
- data/funchook/distorm/examples/java/jdistorm.vcproj +208 -0
- data/funchook/distorm/examples/linux/Makefile +15 -0
- data/funchook/distorm/examples/linux/main.c +181 -0
- data/funchook/distorm/examples/tests/Makefile +15 -0
- data/funchook/distorm/examples/tests/main.cpp +42 -0
- data/funchook/distorm/examples/tests/main.py +66 -0
- data/funchook/distorm/examples/tests/test_distorm3.py +1672 -0
- data/funchook/distorm/examples/tests/tests.sln +20 -0
- data/funchook/distorm/examples/tests/tests.vcxproj +82 -0
- data/funchook/distorm/examples/tests/tests.vcxproj.filters +22 -0
- data/funchook/distorm/examples/win32/disasm.sln +25 -0
- data/funchook/distorm/examples/win32/disasm.vcxproj +201 -0
- data/funchook/distorm/examples/win32/disasm.vcxproj.filters +14 -0
- data/funchook/distorm/examples/win32/main.cpp +163 -0
- data/funchook/distorm/include/distorm.h +482 -0
- data/funchook/distorm/include/mnemonics.h +301 -0
- data/funchook/distorm/make/linux/Makefile +28 -0
- data/funchook/distorm/make/mac/Makefile +24 -0
- data/funchook/distorm/make/win32/cdistorm.vcxproj +239 -0
- data/funchook/distorm/make/win32/cdistorm.vcxproj.filters +80 -0
- data/funchook/distorm/make/win32/distorm.sln +25 -0
- data/funchook/distorm/make/win32/resource.h +14 -0
- data/funchook/distorm/make/win32/resource.rc +99 -0
- data/funchook/distorm/python/distorm3/__init__.py +957 -0
- data/funchook/distorm/python/distorm3/sample.py +51 -0
- data/funchook/distorm/setup.cfg +10 -0
- data/funchook/distorm/setup.py +266 -0
- data/funchook/distorm/src/config.h +169 -0
- data/funchook/distorm/src/decoder.c +641 -0
- data/funchook/distorm/src/decoder.h +33 -0
- data/funchook/distorm/src/distorm.c +413 -0
- data/funchook/distorm/src/instructions.c +597 -0
- data/funchook/distorm/src/instructions.h +463 -0
- data/funchook/distorm/src/insts.c +7939 -0
- data/funchook/distorm/src/insts.h +64 -0
- data/funchook/distorm/src/mnemonics.c +284 -0
- data/funchook/distorm/src/operands.c +1290 -0
- data/funchook/distorm/src/operands.h +28 -0
- data/funchook/distorm/src/prefix.c +368 -0
- data/funchook/distorm/src/prefix.h +64 -0
- data/funchook/distorm/src/textdefs.c +172 -0
- data/funchook/distorm/src/textdefs.h +57 -0
- data/funchook/distorm/src/wstring.c +47 -0
- data/funchook/distorm/src/wstring.h +35 -0
- data/funchook/distorm/src/x86defs.h +82 -0
- data/funchook/include/funchook.h +123 -0
- data/funchook/install-sh +527 -0
- data/funchook/src/Makefile +70 -0
- data/funchook/src/Makefile.in +70 -0
- data/funchook/src/__strerror.h +109 -0
- data/funchook/src/config.h +101 -0
- data/funchook/src/config.h.in +100 -0
- data/funchook/src/decoder.o +0 -0
- data/funchook/src/distorm.o +0 -0
- data/funchook/src/funchook.c +440 -0
- data/funchook/src/funchook.o +0 -0
- data/funchook/src/funchook_internal.h +155 -0
- data/funchook/src/funchook_io.c +182 -0
- data/funchook/src/funchook_io.h +64 -0
- data/funchook/src/funchook_io.o +0 -0
- data/funchook/src/funchook_syscall.S +134 -0
- data/funchook/src/funchook_syscall.o +0 -0
- data/funchook/src/funchook_unix.c +480 -0
- data/funchook/src/funchook_unix.o +0 -0
- data/funchook/src/funchook_windows.c +397 -0
- data/funchook/src/funchook_x86.c +622 -0
- data/funchook/src/funchook_x86.o +0 -0
- data/funchook/src/instructions.o +0 -0
- data/funchook/src/insts.o +0 -0
- data/funchook/src/libfunchook.so +0 -0
- data/funchook/src/mnemonics.o +0 -0
- data/funchook/src/operands.o +0 -0
- data/funchook/src/os_func.c +115 -0
- data/funchook/src/os_func.h +75 -0
- data/funchook/src/os_func.o +0 -0
- data/funchook/src/os_func_unix.c +94 -0
- data/funchook/src/os_func_unix.o +0 -0
- data/funchook/src/os_func_windows.c +32 -0
- data/funchook/src/prefix.o +0 -0
- data/funchook/src/printf_base.c +1688 -0
- data/funchook/src/printf_base.h +46 -0
- data/funchook/src/printf_base.o +0 -0
- data/funchook/src/textdefs.o +0 -0
- data/funchook/src/wstring.o +0 -0
- data/funchook/test/Makefile +43 -0
- data/funchook/test/Makefile.in +43 -0
- data/funchook/test/funchook_test +0 -0
- data/funchook/test/libfunchook_test.c +25 -0
- data/funchook/test/libfunchook_test.so +0 -0
- data/funchook/test/libfunchook_test2.c +18 -0
- data/funchook/test/suffix.list +600 -0
- data/funchook/test/test_main.c +430 -0
- data/funchook/test/test_main.o +0 -0
- data/funchook/test/x86_64_test.S +10 -0
- data/funchook/test/x86_64_test.o +0 -0
- data/funchook/test/x86_test.S +339 -0
- data/funchook/win32/config.h +1 -0
- data/funchook/win32/funchook.sln +52 -0
- data/funchook/win32/funchook.vcxproj +188 -0
- data/funchook/win32/funchook.vcxproj.filters +84 -0
- data/funchook/win32/funchook_test.vcxproj +170 -0
- data/funchook/win32/funchook_test.vcxproj.filters +22 -0
- data/funchook/win32/funchook_test_dll.vcxproj +184 -0
- data/funchook/win32/funchook_test_dll.vcxproj.filters +30 -0
- data/funchook/win32/funchook_test_exe.def +3 -0
- data/lib/contrast-agent.rb +8 -0
- data/lib/contrast.rb +57 -0
- data/lib/contrast/agent.rb +80 -0
- data/lib/contrast/agent/assess.rb +45 -0
- data/lib/contrast/agent/assess/adjusted_span.rb +25 -0
- data/lib/contrast/agent/assess/class_reverter.rb +82 -0
- data/lib/contrast/agent/assess/contrast_event.rb +398 -0
- data/lib/contrast/agent/assess/frozen_properties.rb +41 -0
- data/lib/contrast/agent/assess/insulator.rb +53 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +78 -0
- data/lib/contrast/agent/assess/policy/patcher.rb +85 -0
- data/lib/contrast/agent/assess/policy/policy.rb +116 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +289 -0
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +44 -0
- data/lib/contrast/agent/assess/policy/preshift.rb +94 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +260 -0
- data/lib/contrast/agent/assess/policy/propagation_node.rb +127 -0
- data/lib/contrast/agent/assess/policy/propagator.rb +35 -0
- data/lib/contrast/agent/assess/policy/propagator/append.rb +54 -0
- data/lib/contrast/agent/assess/policy/propagator/base.rb +37 -0
- data/lib/contrast/agent/assess/policy/propagator/center.rb +73 -0
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +36 -0
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +62 -0
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +55 -0
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +26 -0
- data/lib/contrast/agent/assess/policy/propagator/next.rb +42 -0
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +50 -0
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +76 -0
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +27 -0
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +38 -0
- data/lib/contrast/agent/assess/policy/propagator/select.rb +86 -0
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +60 -0
- data/lib/contrast/agent/assess/policy/propagator/split.rb +49 -0
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +169 -0
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +81 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +79 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +209 -0
- data/lib/contrast/agent/assess/policy/source_node.rb +62 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +209 -0
- data/lib/contrast/agent/assess/policy/trigger_node.rb +198 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +77 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +31 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +40 -0
- data/lib/contrast/agent/assess/properties.rb +392 -0
- data/lib/contrast/agent/assess/rule.rb +18 -0
- data/lib/contrast/agent/assess/rule/base.rb +72 -0
- data/lib/contrast/agent/assess/rule/csrf.rb +66 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_action.rb +28 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +69 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_watcher.rb +132 -0
- data/lib/contrast/agent/assess/rule/provider.rb +21 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +62 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +73 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +121 -0
- data/lib/contrast/agent/assess/rule/redos.rb +68 -0
- data/lib/contrast/agent/assess/rule/response_scanning_rule.rb +47 -0
- data/lib/contrast/agent/assess/rule/response_watcher.rb +36 -0
- data/lib/contrast/agent/assess/rule/watcher.rb +36 -0
- data/lib/contrast/agent/assess/tag.rb +151 -0
- data/lib/contrast/agent/at_exit_hook.rb +33 -0
- data/lib/contrast/agent/class_reopener.rb +195 -0
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +26 -0
- data/lib/contrast/agent/deadzone/policy/policy.rb +57 -0
- data/lib/contrast/agent/disable_reaction.rb +24 -0
- data/lib/contrast/agent/exclusion_matcher.rb +190 -0
- data/lib/contrast/agent/feature_state.rb +379 -0
- data/lib/contrast/agent/inventory/policy/policy.rb +32 -0
- data/lib/contrast/agent/inventory/policy/trigger_node.rb +22 -0
- data/lib/contrast/agent/logger_manager.rb +116 -0
- data/lib/contrast/agent/middleware.rb +352 -0
- data/lib/contrast/agent/module_data.rb +16 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +37 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +58 -0
- data/lib/contrast/agent/patching/policy/method_policy.rb +94 -0
- data/lib/contrast/agent/patching/policy/module_policy.rb +116 -0
- data/lib/contrast/agent/patching/policy/patch.rb +312 -0
- data/lib/contrast/agent/patching/policy/patch_status.rb +192 -0
- data/lib/contrast/agent/patching/policy/patcher.rb +310 -0
- data/lib/contrast/agent/patching/policy/policy.rb +138 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +80 -0
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +28 -0
- data/lib/contrast/agent/patching/policy/trigger_node.rb +81 -0
- data/lib/contrast/agent/protect/policy/policy.rb +37 -0
- data/lib/contrast/agent/protect/policy/trigger_node.rb +23 -0
- data/lib/contrast/agent/protect/rule.rb +58 -0
- data/lib/contrast/agent/protect/rule/base.rb +300 -0
- data/lib/contrast/agent/protect/rule/base_service.rb +88 -0
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +156 -0
- data/lib/contrast/agent/protect/rule/csrf.rb +118 -0
- data/lib/contrast/agent/protect/rule/csrf/csrf_evaluator.rb +103 -0
- data/lib/contrast/agent/protect/rule/csrf/csrf_token_injector.rb +85 -0
- data/lib/contrast/agent/protect/rule/default_scanner.rb +300 -0
- data/lib/contrast/agent/protect/rule/deserialization.rb +193 -0
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +80 -0
- data/lib/contrast/agent/protect/rule/no_sqli.rb +101 -0
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +40 -0
- data/lib/contrast/agent/protect/rule/path_traversal.rb +143 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +101 -0
- data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +16 -0
- data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +38 -0
- data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +22 -0
- data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +19 -0
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
- data/lib/contrast/agent/protect/rule/xss.rb +24 -0
- data/lib/contrast/agent/protect/rule/xxe.rb +120 -0
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +82 -0
- data/lib/contrast/agent/railtie.rb +30 -0
- data/lib/contrast/agent/reaction_processor.rb +47 -0
- data/lib/contrast/agent/request.rb +493 -0
- data/lib/contrast/agent/request_context.rb +225 -0
- data/lib/contrast/agent/require_state.rb +61 -0
- data/lib/contrast/agent/response.rb +215 -0
- data/lib/contrast/agent/rewriter.rb +244 -0
- data/lib/contrast/agent/scope.rb +28 -0
- data/lib/contrast/agent/service_heartbeat.rb +37 -0
- data/lib/contrast/agent/settings_state.rb +148 -0
- data/lib/contrast/agent/socket_client.rb +125 -0
- data/lib/contrast/agent/thread.rb +26 -0
- data/lib/contrast/agent/tracepoint_hook.rb +51 -0
- data/lib/contrast/agent/version.rb +8 -0
- data/lib/contrast/api.rb +17 -0
- data/lib/contrast/api/.gitkeep +0 -0
- data/lib/contrast/api/connection_status.rb +49 -0
- data/lib/contrast/api/socket.rb +43 -0
- data/lib/contrast/api/speedracer.rb +206 -0
- data/lib/contrast/api/tcp_socket.rb +31 -0
- data/lib/contrast/api/unix_socket.rb +25 -0
- data/lib/contrast/common_agent_configuration.rb +86 -0
- data/lib/contrast/components/agent.rb +85 -0
- data/lib/contrast/components/app_context.rb +188 -0
- data/lib/contrast/components/assess.rb +67 -0
- data/lib/contrast/components/config.rb +135 -0
- data/lib/contrast/components/contrast_service.rb +113 -0
- data/lib/contrast/components/heap_dump.rb +34 -0
- data/lib/contrast/components/interface.rb +178 -0
- data/lib/contrast/components/inventory.rb +23 -0
- data/lib/contrast/components/logger.rb +92 -0
- data/lib/contrast/components/protect.rb +38 -0
- data/lib/contrast/components/sampling.rb +41 -0
- data/lib/contrast/components/scope.rb +106 -0
- data/lib/contrast/components/settings.rb +140 -0
- data/lib/contrast/config.rb +33 -0
- data/lib/contrast/config/agent_configuration.rb +24 -0
- data/lib/contrast/config/application_configuration.rb +27 -0
- data/lib/contrast/config/assess_configuration.rb +22 -0
- data/lib/contrast/config/assess_rules_configuration.rb +18 -0
- data/lib/contrast/config/base_configuration.rb +105 -0
- data/lib/contrast/config/default_value.rb +16 -0
- data/lib/contrast/config/exception_configuration.rb +21 -0
- data/lib/contrast/config/heap_dump_configuration.rb +23 -0
- data/lib/contrast/config/inventory_configuration.rb +20 -0
- data/lib/contrast/config/logger_configuration.rb +20 -0
- data/lib/contrast/config/protect_configuration.rb +20 -0
- data/lib/contrast/config/protect_rule_configuration.rb +37 -0
- data/lib/contrast/config/protect_rules_configuration.rb +30 -0
- data/lib/contrast/config/root_configuration.rb +26 -0
- data/lib/contrast/config/ruby_configuration.rb +39 -0
- data/lib/contrast/config/sampling_configuration.rb +22 -0
- data/lib/contrast/config/server_configuration.rb +23 -0
- data/lib/contrast/config/service_configuration.rb +22 -0
- data/lib/contrast/configuration.rb +214 -0
- data/lib/contrast/core_extensions/assess.rb +51 -0
- data/lib/contrast/core_extensions/assess/array.rb +58 -0
- data/lib/contrast/core_extensions/assess/assess_extension.rb +145 -0
- data/lib/contrast/core_extensions/assess/basic_object.rb +15 -0
- data/lib/contrast/core_extensions/assess/erb.rb +42 -0
- data/lib/contrast/core_extensions/assess/exec_trigger.rb +48 -0
- data/lib/contrast/core_extensions/assess/fiber.rb +125 -0
- data/lib/contrast/core_extensions/assess/hash.rb +22 -0
- data/lib/contrast/core_extensions/assess/kernel.rb +95 -0
- data/lib/contrast/core_extensions/assess/module.rb +14 -0
- data/lib/contrast/core_extensions/assess/regexp.rb +206 -0
- data/lib/contrast/core_extensions/assess/string.rb +75 -0
- data/lib/contrast/core_extensions/assess/tilt_template_trigger.rb +73 -0
- data/lib/contrast/core_extensions/delegator.rb +14 -0
- data/lib/contrast/core_extensions/eval_trigger.rb +52 -0
- data/lib/contrast/core_extensions/inventory.rb +22 -0
- data/lib/contrast/core_extensions/inventory/datastores.rb +37 -0
- data/lib/contrast/core_extensions/module.rb +42 -0
- data/lib/contrast/core_extensions/object.rb +27 -0
- data/lib/contrast/core_extensions/protect.rb +20 -0
- data/lib/contrast/core_extensions/protect/applies_command_injection_rule.rb +70 -0
- data/lib/contrast/core_extensions/protect/applies_deserialization_rule.rb +58 -0
- data/lib/contrast/core_extensions/protect/applies_no_sqli_rule.rb +81 -0
- data/lib/contrast/core_extensions/protect/applies_path_traversal_rule.rb +119 -0
- data/lib/contrast/core_extensions/protect/applies_sqli_rule.rb +63 -0
- data/lib/contrast/core_extensions/protect/applies_xxe_rule.rb +141 -0
- data/lib/contrast/core_extensions/protect/kernel.rb +30 -0
- data/lib/contrast/core_extensions/protect/psych.rb +7 -0
- data/lib/contrast/core_extensions/thread.rb +31 -0
- data/lib/contrast/internal_exception.rb +8 -0
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +48 -0
- data/lib/contrast/rails_extensions/assess/active_record.rb +32 -0
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +61 -0
- data/lib/contrast/rails_extensions/assess/configuration.rb +26 -0
- data/lib/contrast/rails_extensions/buffer.rb +30 -0
- data/lib/contrast/rails_extensions/rack.rb +45 -0
- data/lib/contrast/security_exception.rb +14 -0
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +26 -0
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +59 -0
- data/lib/contrast/tasks/service.rb +95 -0
- data/lib/contrast/utils/assess/sampling_util.rb +96 -0
- data/lib/contrast/utils/assess/tracking_util.rb +39 -0
- data/lib/contrast/utils/boolean_util.rb +33 -0
- data/lib/contrast/utils/cache.rb +69 -0
- data/lib/contrast/utils/class_util.rb +58 -0
- data/lib/contrast/utils/comment_range.rb +19 -0
- data/lib/contrast/utils/data_store_util.rb +23 -0
- data/lib/contrast/utils/duck_utils.rb +58 -0
- data/lib/contrast/utils/env_configuration_item.rb +52 -0
- data/lib/contrast/utils/environment_util.rb +152 -0
- data/lib/contrast/utils/freeze_util.rb +36 -0
- data/lib/contrast/utils/gemfile_reader.rb +191 -0
- data/lib/contrast/utils/hash_digest.rb +148 -0
- data/lib/contrast/utils/heap_dump_util.rb +113 -0
- data/lib/contrast/utils/invalid_configuration_util.rb +88 -0
- data/lib/contrast/utils/inventory_util.rb +126 -0
- data/lib/contrast/utils/io_util.rb +61 -0
- data/lib/contrast/utils/object_share.rb +117 -0
- data/lib/contrast/utils/operating_environment.rb +38 -0
- data/lib/contrast/utils/os.rb +49 -0
- data/lib/contrast/utils/path_util.rb +151 -0
- data/lib/contrast/utils/performs_logging.rb +152 -0
- data/lib/contrast/utils/preflight_util.rb +13 -0
- data/lib/contrast/utils/prevent_serialization.rb +52 -0
- data/lib/contrast/utils/rack_assess_session_cookie.rb +104 -0
- data/lib/contrast/utils/rails_assess_configuration.rb +95 -0
- data/lib/contrast/utils/random_util.rb +22 -0
- data/lib/contrast/utils/resource_loader.rb +23 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +74 -0
- data/lib/contrast/utils/scope_util.rb +99 -0
- data/lib/contrast/utils/service_response_util.rb +116 -0
- data/lib/contrast/utils/service_sender_util.rb +98 -0
- data/lib/contrast/utils/sha256_builder.rb +69 -0
- data/lib/contrast/utils/sinatra_helper.rb +49 -0
- data/lib/contrast/utils/stack_trace_utils.rb +209 -0
- data/lib/contrast/utils/string_utils.rb +72 -0
- data/lib/contrast/utils/tag_util.rb +139 -0
- data/lib/contrast/utils/thread_tracker.rb +54 -0
- data/lib/contrast/utils/timer.rb +78 -0
- data/resources/assess/policy.json +1673 -0
- data/resources/csrf/inject.js +44 -0
- data/resources/deadzone/policy.json +55 -0
- data/resources/factory-bot-spec/spec_helper.rb +30 -0
- data/resources/inventory/policy.json +110 -0
- data/resources/protect/policy.json +417 -0
- data/resources/rubocops/kernel/catch_cop.rb +37 -0
- data/resources/rubocops/kernel/require_cop.rb +37 -0
- data/resources/rubocops/kernel/require_relative_cop.rb +33 -0
- data/resources/rubocops/module/autoload_cop.rb +37 -0
- data/resources/rubocops/module/const_defined_cop.rb +37 -0
- data/resources/rubocops/module/const_get_cop.rb +37 -0
- data/resources/rubocops/module/const_set_cop.rb +37 -0
- data/resources/rubocops/module/constants_cop.rb +37 -0
- data/resources/rubocops/module/name_cop.rb +37 -0
- data/resources/rubocops/object/class_cop.rb +37 -0
- data/resources/rubocops/object/freeze_cop.rb +37 -0
- data/resources/rubocops/object/frozen_cop.rb +37 -0
- data/resources/rubocops/object/is_a_cop.rb +37 -0
- data/resources/rubocops/object/method_cop.rb +37 -0
- data/resources/rubocops/object/respond_to_cop.rb +37 -0
- data/resources/rubocops/object/singleton_class_cop.rb +37 -0
- data/resources/rubocops/regexp/spelling_cop.rb +44 -0
- data/resources/rubocops/thread/new_cop.rb +39 -0
- data/resources/ruby-spec/ancestors_spec.rb +70 -0
- data/resources/ruby-spec/modulo_spec.rb +831 -0
- data/resources/ruby-spec/parameters_spec.rb +261 -0
- data/resources/ruby-spec/ruby_spec_spec_helper.rb +35 -0
- data/resources/test_marker.txt +1 -0
- data/ruby-agent.gemspec +129 -0
- data/service_executables/.gitkeep +0 -0
- data/service_executables/VERSION +1 -0
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +945 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Protect
|
|
7
|
+
module Rule
|
|
8
|
+
# This class handles our implementation of the Untrusted
|
|
9
|
+
# Deserialization Protect rule.
|
|
10
|
+
class Deserialization < Contrast::Agent::Protect::Rule::Base
|
|
11
|
+
# The TeamServer recognized name of this rule
|
|
12
|
+
NAME = 'untrusted-deserialization'
|
|
13
|
+
|
|
14
|
+
# The rule specific reason for raising a security exception.
|
|
15
|
+
BLOCK_MESSAGE = 'Untrusted Deserialization rule triggered. Deserialization blocked.'
|
|
16
|
+
|
|
17
|
+
# Gadgets that map to ERB modules
|
|
18
|
+
ERB_GADGETS = %w[
|
|
19
|
+
object:ERB
|
|
20
|
+
].cs__freeze
|
|
21
|
+
|
|
22
|
+
# Gadgets that map to ActionDispatch modules
|
|
23
|
+
ACTION_DISPATCH_GADGETS = %w[
|
|
24
|
+
object:ActionDispatch::Routing::RouteSet::NamedRouteCollection
|
|
25
|
+
].cs__freeze
|
|
26
|
+
|
|
27
|
+
# Gadgets that map to Arel Modules
|
|
28
|
+
AREL_GADGETS = %w[
|
|
29
|
+
string:Arel::Nodes::SqlLiteral
|
|
30
|
+
object:Arel::Nodes
|
|
31
|
+
].cs__freeze
|
|
32
|
+
|
|
33
|
+
# Used to indicate to TeamServer the gadget is an ERB module
|
|
34
|
+
ERB = 'ERB'
|
|
35
|
+
# Used to indicate to TeamServer the gadget is an ActionDispatch
|
|
36
|
+
# module
|
|
37
|
+
DISPATCH = 'ActionDispatch'
|
|
38
|
+
# Used to indicate to TeamServer the gadget is an Arel module
|
|
39
|
+
AREL = 'Arel'
|
|
40
|
+
|
|
41
|
+
# Return the TeamServer understood id / name of this rule.
|
|
42
|
+
# @return [String] the TeamServer understood id / name of this rule.
|
|
43
|
+
def name
|
|
44
|
+
NAME
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return the specific blocking message for this rule.
|
|
48
|
+
# @return [String] the reason for the raised security exception.
|
|
49
|
+
def block_message
|
|
50
|
+
BLOCK_MESSAGE
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Per the spec, this rule applies regardless of input. Only the mode
|
|
54
|
+
# of the rule and code exclusions apply at this point.
|
|
55
|
+
# @return [Boolean] should the rule apply to this call.
|
|
56
|
+
def infilter? _context
|
|
57
|
+
return false unless enabled?
|
|
58
|
+
return false if protect_excluded_by_code?
|
|
59
|
+
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Determine if the input being deserialized is an attack and take
|
|
64
|
+
# appropriate actions.
|
|
65
|
+
# @param context [Contrast::Agent::RequestContext] the request
|
|
66
|
+
# context in which this attack is occurring.
|
|
67
|
+
# @param serialized_input [String] the string being deserialized.
|
|
68
|
+
# @raise [Contrast::SecurityException] if attack detected while in
|
|
69
|
+
# block mode.
|
|
70
|
+
def infilter context, serialized_input
|
|
71
|
+
return unless infilter?(context)
|
|
72
|
+
|
|
73
|
+
gadget = find_gadget(serialized_input)
|
|
74
|
+
# If there isn't a gadget, this isn't an attack, so we have nothing
|
|
75
|
+
# to do here. Let the application carry on business as usual.
|
|
76
|
+
return unless gadget
|
|
77
|
+
|
|
78
|
+
ia_result = build_evaluation(serialized_input)
|
|
79
|
+
kwargs = { GADGET_TYPE: gadget }
|
|
80
|
+
result = build_attack_with_match(context, ia_result, nil, serialized_input, **kwargs)
|
|
81
|
+
append_to_activity(context, result)
|
|
82
|
+
|
|
83
|
+
raise Contrast::SecurityException.new(self, block_message) if blocked?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Determine if the issued command was called while we're
|
|
87
|
+
# deserializing. If we are, treat this as an attack.
|
|
88
|
+
# @param gadget_command [String] the command being executed during
|
|
89
|
+
# the deserialization call.
|
|
90
|
+
# @raise [Contrast::SecurityException] if attack detected while in
|
|
91
|
+
# block mode.
|
|
92
|
+
def check_command_scope gadget_command
|
|
93
|
+
return unless deserializing?
|
|
94
|
+
|
|
95
|
+
context = Contrast::Agent::REQUEST_TRACKER.current
|
|
96
|
+
kwargs = { COMMAND_SCOPE: true }
|
|
97
|
+
ia_result = build_evaluation(gadget_command)
|
|
98
|
+
result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
|
|
99
|
+
append_to_activity(context, result)
|
|
100
|
+
raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# I don't know a better way to do this without introducing another
|
|
104
|
+
# scope.
|
|
105
|
+
# The policy files are designed around using the Module names, not
|
|
106
|
+
# the file names, but stack is built using filenames. These are the
|
|
107
|
+
# files and methods we patch for this rule.
|
|
108
|
+
#
|
|
109
|
+
# There's not real test for this since I can't figure out how to get
|
|
110
|
+
# a command to execute from within the .load methods
|
|
111
|
+
# Assuming someone else does, this should just work (tested with
|
|
112
|
+
# Screener and the combination "%w[erb.rb `result']")
|
|
113
|
+
DESERIALIZER_STACK = [
|
|
114
|
+
%w[/psych.rb: `load'],
|
|
115
|
+
%w[/marshal.rb: `load']
|
|
116
|
+
].cs__freeze
|
|
117
|
+
# We're considered deserializing if the call stack includes a
|
|
118
|
+
# reference to the file in which a serializer is defined and
|
|
119
|
+
# the method of that serializer responsible for deserializing.
|
|
120
|
+
# @return [Boolean] if the caller indicates deserialization.
|
|
121
|
+
def deserializing?
|
|
122
|
+
Kernel.caller.product(DESERIALIZER_STACK).find do |(frame, (class_signature_form, method_signature_form))|
|
|
123
|
+
frame.end_with?(method_signature_form) && frame.include?(class_signature_form)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
protected
|
|
128
|
+
|
|
129
|
+
# Build the RaspRuleSample for the detected Deserialization attack.
|
|
130
|
+
# @param context [Contrast::Agent::RequestContext] the request
|
|
131
|
+
# context in which this attack is occurring.
|
|
132
|
+
# @param input_analysis_result [Contrast::Api::Settings::InputAnalysisResult]
|
|
133
|
+
# the result of the analysis done by this rule.
|
|
134
|
+
# @param _candidate_string [nil] unused.
|
|
135
|
+
# @param kwargs [Hash, nil] Hash of inputs used by this rule to flesh
|
|
136
|
+
# out the report to TeamServer in order to provide specific
|
|
137
|
+
# information for this rule.
|
|
138
|
+
# @return [Contrast::Api::Dtm::RaspRuleSample] the information needed
|
|
139
|
+
# to render this attack event in TeamServer.
|
|
140
|
+
def build_sample context, input_analysis_result, _candidate_string, **kwargs
|
|
141
|
+
sample = build_base_sample(context, input_analysis_result)
|
|
142
|
+
sample.untrusted_deserialization = Contrast::Api::Dtm::UntrustedDeserializationDetails.new
|
|
143
|
+
|
|
144
|
+
deserializer = kwargs[:GADGET_TYPE]
|
|
145
|
+
sample.untrusted_deserialization.deserializer = Contrast::Utils::StringUtils.protobuf_safe_string(deserializer)
|
|
146
|
+
|
|
147
|
+
command = !!kwargs[:COMMAND_SCOPE]
|
|
148
|
+
sample.untrusted_deserialization.command = command
|
|
149
|
+
|
|
150
|
+
sample
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
# Used to name this input since input analysis isn't done for this
|
|
156
|
+
# rule
|
|
157
|
+
INPUT_NAME = 'Serialized Gadget'
|
|
158
|
+
# We know that this attack happened, so the result is always matched
|
|
159
|
+
# and the level is always critical. Only variable is the Gadget
|
|
160
|
+
# supplied by the attacker.
|
|
161
|
+
# @param gadget_string [String] the input to be deserialized in which
|
|
162
|
+
# the gadget exists or the command that resulted from deserializing
|
|
163
|
+
# an input not detected in the initial infilter.
|
|
164
|
+
# @return [Contrast::Api::Settings::InputAnalysisResult] the result
|
|
165
|
+
# of the analysis done by this rule.
|
|
166
|
+
def build_evaluation gadget_string
|
|
167
|
+
ia_result = Contrast::Api::Settings::InputAnalysisResult.new
|
|
168
|
+
ia_result.rule_id = name
|
|
169
|
+
ia_result.input_type = :UNKNOWN
|
|
170
|
+
ia_result.key = INPUT_NAME
|
|
171
|
+
ia_result.value = Contrast::Utils::StringUtils.protobuf_safe_string(gadget_string)
|
|
172
|
+
ia_result
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Find the gadget within the serialized input, if such a gadget
|
|
176
|
+
# exists.
|
|
177
|
+
# @param serialized_input [String] the string being deserialized in
|
|
178
|
+
# which the gadget may exist.
|
|
179
|
+
# @return [String, nil] the gadget, if found.
|
|
180
|
+
def find_gadget serialized_input
|
|
181
|
+
if ERB_GADGETS.any? { |gadget| serialized_input.index(gadget) }
|
|
182
|
+
ERB
|
|
183
|
+
elsif ACTION_DISPATCH_GADGETS.any? { |gadget| serialized_input.index(gadget) }
|
|
184
|
+
DISPATCH
|
|
185
|
+
elsif AREL_GADGETS.any? { |gadget| serialized_input.index(gadget) }
|
|
186
|
+
AREL
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Protect
|
|
7
|
+
module Rule
|
|
8
|
+
# The Ruby implementation of the Protect HTTP Method Tampering rule.
|
|
9
|
+
class HttpMethodTampering < Contrast::Agent::Protect::Rule::BaseService
|
|
10
|
+
NAME = 'method-tampering'
|
|
11
|
+
STANDARD_METHODS = %w[GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH].cs__freeze
|
|
12
|
+
|
|
13
|
+
def name
|
|
14
|
+
NAME
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def postfilter context
|
|
18
|
+
return unless enabled? && POSTFILTER_MODES.include?(mode)
|
|
19
|
+
|
|
20
|
+
logger.debug("#{ name } postfilter...")
|
|
21
|
+
return if normal_request?(context)
|
|
22
|
+
|
|
23
|
+
# The only way to be here in postfilter with a result is if the rule mode was MONITOR
|
|
24
|
+
ia_results = gather_ia_results(context)
|
|
25
|
+
return if ia_results.empty?
|
|
26
|
+
|
|
27
|
+
# does the status code start with 4 or 5? Rails responds with 404 (but java is checking 501)
|
|
28
|
+
response_code = context&.response&.response_code
|
|
29
|
+
return unless response_code
|
|
30
|
+
|
|
31
|
+
method = ia_results.first.value
|
|
32
|
+
result = if response_code.to_s.start_with?('4', '5')
|
|
33
|
+
build_attack_without_match(
|
|
34
|
+
context,
|
|
35
|
+
nil,
|
|
36
|
+
nil,
|
|
37
|
+
method: method,
|
|
38
|
+
response_code: response_code)
|
|
39
|
+
else
|
|
40
|
+
build_attack_with_match(
|
|
41
|
+
context,
|
|
42
|
+
nil,
|
|
43
|
+
nil,
|
|
44
|
+
nil,
|
|
45
|
+
method: method,
|
|
46
|
+
response_code: response_code)
|
|
47
|
+
end
|
|
48
|
+
append_to_activity(context, result) if result
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
def build_sample context, evaluation, _candidate_string, **kwargs
|
|
54
|
+
sample = build_base_sample(context, evaluation)
|
|
55
|
+
sample.user_input.value = kwargs[:method]
|
|
56
|
+
|
|
57
|
+
sample.method_tampering = Contrast::Api::Dtm::HttpMethodTamperingDetails.new
|
|
58
|
+
sample.method_tampering.method = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:method])
|
|
59
|
+
code = kwargs[:response_code] || -1
|
|
60
|
+
sample.method_tampering.response_code = code.to_i
|
|
61
|
+
sample
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_user_input _evaluation
|
|
65
|
+
input = Contrast::Api::Dtm::UserInput.new
|
|
66
|
+
input.input_type = :METHOD
|
|
67
|
+
input
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def normal_request? context
|
|
73
|
+
method = context.request.request_method
|
|
74
|
+
context.request.static_request? || method.nil? || STANDARD_METHODS.include?(method.upcase)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# This could be useful for making patterns maybe?
|
|
5
|
+
# https://github.com/cr0hn/nosqlinjection_wordlists
|
|
6
|
+
# https://www.owasp.org/index.php/Testing_for_NoSQL_injection
|
|
7
|
+
module Contrast
|
|
8
|
+
module Agent
|
|
9
|
+
module Protect
|
|
10
|
+
module Rule
|
|
11
|
+
# The Ruby implementation of the Protect NoSQL Injection rule.
|
|
12
|
+
class NoSqli < Contrast::Agent::Protect::Rule::BaseService
|
|
13
|
+
NAME = 'nosql-injection'
|
|
14
|
+
BLOCK_MESSAGE = 'NoSQLi rule triggered. Response blocked.'
|
|
15
|
+
|
|
16
|
+
def name
|
|
17
|
+
NAME
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def block_message
|
|
21
|
+
BLOCK_MESSAGE
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def infilter context, database, query_string
|
|
25
|
+
return nil unless infilter?(context)
|
|
26
|
+
|
|
27
|
+
result = find_attacker(context, query_string, database: database)
|
|
28
|
+
return nil unless result
|
|
29
|
+
|
|
30
|
+
append_to_activity(context, result)
|
|
31
|
+
|
|
32
|
+
raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_attack_with_match context, input_analysis_result, result, query_string, **kwargs
|
|
36
|
+
return result if mode == :NO_ACTION || mode == :PERMIT
|
|
37
|
+
|
|
38
|
+
attack_string = input_analysis_result.value
|
|
39
|
+
regexp = Regexp.new(Regexp.escape(attack_string), Regexp::IGNORECASE)
|
|
40
|
+
return nil unless query_string.match?(regexp)
|
|
41
|
+
|
|
42
|
+
scanner = select_scanner
|
|
43
|
+
ss = StringScanner.new(query_string)
|
|
44
|
+
length = attack_string.length
|
|
45
|
+
while ss.scan_until(regexp)
|
|
46
|
+
# the pos of StringScanner is at the end of the regexp (input string),
|
|
47
|
+
# we need the beginning
|
|
48
|
+
idx = ss.pos - attack_string.length
|
|
49
|
+
last_boundary, boundary = scanner.crosses_boundary(query_string, idx, input_analysis_result.value)
|
|
50
|
+
next unless last_boundary && boundary
|
|
51
|
+
|
|
52
|
+
kwargs[:start_idx] = idx
|
|
53
|
+
kwargs[:end_idx] = idx + length
|
|
54
|
+
kwargs[:boundary_overrun_idx] = boundary
|
|
55
|
+
kwargs[:input_boundary_idx] = last_boundary
|
|
56
|
+
|
|
57
|
+
result ||= build_attack_result(context)
|
|
58
|
+
update_successful_attack_response(context, input_analysis_result, result, query_string)
|
|
59
|
+
append_sample(context, input_analysis_result, result, query_string, **kwargs)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
protected
|
|
66
|
+
|
|
67
|
+
def find_attacker context, potential_attack_string, **kwargs
|
|
68
|
+
if potential_attack_string
|
|
69
|
+
# We need the query hash to be a JSON string to match on JSON input attacks
|
|
70
|
+
begin
|
|
71
|
+
potential_attack_string = JSON.generate(potential_attack_string).to_s
|
|
72
|
+
rescue JSON::GeneratorError
|
|
73
|
+
logger.debug("Error in JSON::generate from input #{ potential_attack_string }")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
super(context, potential_attack_string, **kwargs)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def build_sample context, input_analysis_result, candidate_string, **kwargs
|
|
80
|
+
input = input_analysis_result.value
|
|
81
|
+
|
|
82
|
+
sample = build_base_sample(context, input_analysis_result)
|
|
83
|
+
sample.no_sqli = Contrast::Api::Dtm::NoSqlInjectionDetails.new
|
|
84
|
+
sample.no_sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
|
85
|
+
sample.no_sqli.start_idx = sample.no_sqli.query.index(input).to_i
|
|
86
|
+
sample.no_sqli.boundary_overrun_idx = sample.no_sqli.start_idx + input.length
|
|
87
|
+
sample.no_sqli.input_boundary_idx = kwargs[:boundary].to_i
|
|
88
|
+
sample.no_sqli.input_boundary_idx = kwargs[:last_boundary].to_i
|
|
89
|
+
sample
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def select_scanner
|
|
95
|
+
@_select_scanner ||= Contrast::Agent::Protect::Rule::NoSqli::MongoNoSqlScanner.new
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Protect
|
|
7
|
+
module Rule
|
|
8
|
+
class NoSqli
|
|
9
|
+
# The Mongo specific NoSQL scanner, used by the NoSQLI rule to
|
|
10
|
+
# determine if a NoSQL attack was performed against a Mongo database.
|
|
11
|
+
#
|
|
12
|
+
# @deprecated RUBY-356
|
|
13
|
+
class MongoNoSqlScanner < Contrast::Agent::Protect::Rule::DefaultScanner
|
|
14
|
+
# Is the current & next character '//' or are the current and
|
|
15
|
+
# subsequent characters '<--' ?
|
|
16
|
+
def start_line_comment? char, index, query
|
|
17
|
+
if char == Contrast::Utils::ObjectShare::SLASH &&
|
|
18
|
+
query[index + 1] == Contrast::Utils::ObjectShare::SLASH
|
|
19
|
+
return true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
char == Contrast::Utils::ObjectShare::LEFT_ANGLE &&
|
|
23
|
+
query[index + 1] == Contrast::Utils::ObjectShare::DASH &&
|
|
24
|
+
query[index + 2] == Contrast::Utils::ObjectShare::DASH
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def start_block_comment? _char, _index, _query
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Indicates if '""' inside of double quotes is the equivalent of '\"'
|
|
32
|
+
def double_quote_escape_in_double_quote?
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
cs__scoped_require 'contrast/utils/stack_trace_utils'
|
|
5
|
+
cs__scoped_require 'contrast/components/interface'
|
|
6
|
+
|
|
7
|
+
module Contrast
|
|
8
|
+
module Agent
|
|
9
|
+
module Protect
|
|
10
|
+
module Rule
|
|
11
|
+
# This class handles our implementation of the Path Traversal
|
|
12
|
+
# Protect rule.
|
|
13
|
+
class PathTraversal < Contrast::Agent::Protect::Rule::BaseService
|
|
14
|
+
include Contrast::Components::Interface
|
|
15
|
+
access_component :logging, :agent
|
|
16
|
+
|
|
17
|
+
NAME = 'path-traversal'
|
|
18
|
+
SYSTEM_PATHS = %w[
|
|
19
|
+
/proc/self
|
|
20
|
+
etc/passwd
|
|
21
|
+
etc/shadow
|
|
22
|
+
etc/hosts
|
|
23
|
+
etc/groups
|
|
24
|
+
etc/gshadow
|
|
25
|
+
ntuser.dat
|
|
26
|
+
/Windows/win.ini
|
|
27
|
+
/windows/system32/
|
|
28
|
+
/windows/repair/
|
|
29
|
+
].cs__freeze
|
|
30
|
+
|
|
31
|
+
KNOWN_SECURITY_BYPASS_MARKERS = ['::$DATA', '::$Index', '', '\x00'].cs__freeze
|
|
32
|
+
|
|
33
|
+
def name
|
|
34
|
+
NAME
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def infilter context, method, path
|
|
38
|
+
return unless infilter?(context)
|
|
39
|
+
|
|
40
|
+
result = find_attacker(context, path)
|
|
41
|
+
return unless result
|
|
42
|
+
|
|
43
|
+
append_to_activity(context, result)
|
|
44
|
+
return unless blocked?
|
|
45
|
+
|
|
46
|
+
raise Contrast::SecurityException.new(
|
|
47
|
+
self,
|
|
48
|
+
"Path Traversal rule triggered. Call to File.#{ method } blocked.")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
def find_attacker context, path
|
|
54
|
+
attack_result = nil
|
|
55
|
+
attack_result = super(context, path) if infilter?(context)
|
|
56
|
+
attack_result = check_rep_features(context, path, attack_result)
|
|
57
|
+
attack_result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Build a subclass of the RaspRuleSample using the query string and the
|
|
61
|
+
# evaluation
|
|
62
|
+
def build_sample context, input_analysis_result, path, **_kwargs
|
|
63
|
+
sample = build_base_sample(context, input_analysis_result)
|
|
64
|
+
sample.path_traversal = Contrast::Api::Dtm::PathTraversalDetails.new
|
|
65
|
+
path ||= input_analysis_result.value
|
|
66
|
+
sample.path_traversal.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
|
67
|
+
sample
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Build a subclass of the RaspRuleSample if the sample matches
|
|
73
|
+
# TODO: SPEED-? delete me when implemented in Speedracer.
|
|
74
|
+
# this implementation duplicates logic that is moving to Speedracer
|
|
75
|
+
# the reason it is still here is Speedracer doesn't yet have a method to correlate
|
|
76
|
+
# and (update) attack results that are generated because the evaluation results
|
|
77
|
+
# from REP, it can only forward the attack results are generated by the agent.
|
|
78
|
+
def build_rep_sample context, path
|
|
79
|
+
sample = build_base_sample(context, nil)
|
|
80
|
+
sample.path_traversal_semantic = Contrast::Api::Dtm::PathTraversalSemanticAnalysisDetails.new
|
|
81
|
+
path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
|
82
|
+
sample.path_traversal_semantic.path = path
|
|
83
|
+
|
|
84
|
+
if custom_code_access_sysfile_enabled? && custom_code_accessing_system_file?(path)
|
|
85
|
+
sample.path_traversal_semantic.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
|
|
86
|
+
return sample
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if common_file_exploits_enabled? && contains_known_attack_signatures?(path)
|
|
90
|
+
sample.path_traversal_semantic.findings << :COMMON_FILE_EXPLOITS
|
|
91
|
+
return sample
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def check_rep_features context, path, attack_result
|
|
98
|
+
rep_sample = build_rep_sample(context, path)
|
|
99
|
+
if rep_sample
|
|
100
|
+
attack_result = build_attack_result(context) if attack_result.nil?
|
|
101
|
+
build_attack_with_match(context, nil, attack_result, path)
|
|
102
|
+
end
|
|
103
|
+
attack_result
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def custom_code_access_sysfile_enabled?
|
|
107
|
+
AGENT.report_custom_code_sysfile_access?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def custom_code_accessing_system_file? input
|
|
111
|
+
system_file?(input) && Contrast::Utils::StackTraceUtils.custom_code_context?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def system_file? path
|
|
115
|
+
return false unless path
|
|
116
|
+
|
|
117
|
+
SYSTEM_PATHS.any? { |sys_path| sys_path.include?(path) }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def common_file_exploits_enabled?
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def contains_known_attack_signatures? input
|
|
125
|
+
utf8 = Contrast::Utils::StringUtils.force_utf8(input)
|
|
126
|
+
_ = CGI.unescape(utf8)
|
|
127
|
+
# TODO: RUBY-318 implement REP for known attack signatures
|
|
128
|
+
# try:
|
|
129
|
+
# realpath = os.path.realpath(unescaped).lower().rstrip('/')
|
|
130
|
+
# except ValueError as e:
|
|
131
|
+
# return 'embedded null byte' == str(e)
|
|
132
|
+
# except TypeError as e:
|
|
133
|
+
# return 'NUL' in str(e) or 'null byte' in str(e) or (PY34 and 'embedded NUL character' == str(e))
|
|
134
|
+
# except Exception as e:
|
|
135
|
+
# return 'null byte' in str(e).lower()
|
|
136
|
+
# return return any([bypass_markers.lower().rstrip('/') in realpath for bypass_markers in PathTraversalREPMixin.KNOWN_SECURITY_BYPASS_MARKERS])
|
|
137
|
+
false
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|