├── lib ├── detect_util.c ├── bash │ ├── bash_test.h │ ├── CMakeLists.txt │ ├── bash.h │ ├── bash.c │ └── bash_parser.y ├── detect_int.h ├── test │ ├── CMakeLists.txt │ └── detect_unit.c ├── pt │ ├── pt.h │ ├── CMakeLists.txt │ ├── pt_parser.y │ ├── pt.c │ └── pt_lexer.re2c ├── sqli │ ├── CMakeLists.txt │ ├── sqli.h │ ├── sqli.c │ ├── sqli_lexer.re2c │ └── sqli_parser.y ├── CMakeLists.txt ├── detect_parser.c ├── detect_re2c.c └── detect.c ├── .gitignore ├── config ├── perf ├── vg.in ├── CMakeLists.txt └── perf.c ├── CTestConfig.cmake ├── Dockerfile ├── cmake └── modules │ └── FindLibWallarmMisc.cmake ├── CMakeLists.txt ├── LICENSE ├── include └── detect │ ├── detect_buf.h │ ├── detect_re2c.h │ ├── detect_parser.h │ └── detect.h ├── README.md └── .clang-format /lib/detect_util.c: -------------------------------------------------------------------------------- 1 | #include "detect_int.h" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /TAGS 3 | /build 4 | .*.sw? 5 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf build 4 | mkdir build 5 | cd build 6 | cmake .. "$@" 7 | -------------------------------------------------------------------------------- /lib/bash/bash_test.h: -------------------------------------------------------------------------------- 1 | #ifndef BASH_TEST_H 2 | #define BASH_TEST_H 3 | 4 | #include 5 | 6 | int 7 | bash_lexer_test(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /perf/vg.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | valgrind --tool=memcheck --trace-children=yes \ 4 | --show-reachable=yes \ 5 | --track-fds=yes \ 6 | --num-callers=32 \ 7 | --memcheck:leak-check=yes --memcheck:leak-resolution=high \ 8 | "$@" 9 | -------------------------------------------------------------------------------- /CTestConfig.cmake: -------------------------------------------------------------------------------- 1 | set(CTEST_PROJECT_NAME "PoC-Detect") 2 | set(MEMORYCHECK_COMMAND_OPTIONS "--tool=memcheck --show-reachable=yes --track-fds=yes --num-callers=32 --memcheck:leak-check=yes --memcheck:leak-resolution=high --error-exitcode=1") 3 | -------------------------------------------------------------------------------- /lib/detect_int.h: -------------------------------------------------------------------------------- 1 | #ifndef DETECT_INT_H 2 | #define DETECT_INT_H 3 | 4 | #include 5 | 6 | DETECT_HIDDEN int 7 | detect_parser_init(void); 8 | DETECT_HIDDEN int 9 | detect_parser_deinit(void); 10 | 11 | #endif /* DETECT_INT_H */ 12 | -------------------------------------------------------------------------------- /lib/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (UNIT_SOURCES 2 | detect_unit.c 3 | ) 4 | 5 | set (UNIT_TARGET "unit") 6 | 7 | add_executable(${UNIT_TARGET} ${UNIT_SOURCES}) 8 | target_link_libraries(${UNIT_TARGET} ${TARGET} cunit) 9 | add_test(NAME ${UNIT_TARGET} COMMAND ${UNIT_TARGET}) 10 | -------------------------------------------------------------------------------- /perf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(LibWallarmMisc REQUIRED) 2 | 3 | set (TARGET "libdetection_perf") 4 | 5 | include_directories( 6 | ${CMAKE_CURRENT_SOURCE_DIR}/../include 7 | ${LIBWALLARMMISC_INCLUDE_DIRS} 8 | ) 9 | 10 | set (SOURCES 11 | perf.c 12 | ) 13 | 14 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/vg.in" 15 | "${CMAKE_CURRENT_BINARY_DIR}/vg" @ONLY) 16 | 17 | add_executable(${TARGET} ${SOURCES}) 18 | target_link_libraries(${TARGET} detection) 19 | install(TARGETS ${TARGET} 20 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && \ 4 | apt-get -y install git cmake libcunit1-dev bison re2c && \ 5 | mkdir -p /opt/libwallarmmisc 6 | 7 | RUN git clone https://github.com/wallarm/libwallarmmisc.git /opt/libwallarmmisc 8 | 9 | RUN cd /opt/libwallarmmisc && \ 10 | ./config -DCMAKE_INSTALL_PREFIX=/usr/local && \ 11 | make -C build && \ 12 | make -C build install 13 | 14 | RUN mkdir -p /opt/libdetection 15 | WORKDIR /opt/libdetection 16 | ADD ./ . 17 | RUN ./config && \ 18 | cd build && \ 19 | make && \ 20 | export LD_LIBRARY_PATH=/usr/local/lib && \ 21 | echo '123e2union select-id FRoM users--a-' |./perf/libdetection_perf -evvv && \ 22 | echo '123; DROP DATABASE users; ' |./perf/libdetection_perf -evvv 23 | -------------------------------------------------------------------------------- /cmake/modules/FindLibWallarmMisc.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libwallarmmisc library 2 | # Once done this will define 3 | # LIBWALLARMMISC_FOUND - System has libwallarmmisc 4 | # LIBWALLARMMISC_INCLUDE_DIRS - The libwallarmmisc include directories 5 | # LIBWALLARMMISC_LIBRARIES - The libraries needed to use libwallarmmisc 6 | 7 | find_path(LIBWALLARMMISC_INCLUDE_DIR wallarm/tree.h) 8 | find_path(LIBWALLARMMISC_CONFIG_INCLUDE_DIR wallarm/config.h) 9 | 10 | include(FindPackageHandleStandardArgs) 11 | find_package_handle_standard_args( 12 | LibWallarmMisc 13 | REQUIRED_VARS LIBWALLARMMISC_INCLUDE_DIR LIBWALLARMMISC_CONFIG_INCLUDE_DIR) 14 | mark_as_advanced(LIBWALLARMMISC_INCLUDE_DIR) 15 | set (LIBWALLARMMISC_LIBRARIES) 16 | set(LIBWALLARMMISC_INCLUDE_DIRS 17 | "${LIBWALLARMMISC_INCLUDE_DIR}" 18 | "${LIBWALLARMMISC_CONFIG_INCLUDE_DIR}" 19 | ) 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.8 FATAL_ERROR) 2 | cmake_policy (VERSION 2.8) 3 | 4 | project (PoC-Detect C) 5 | 6 | if(NOT CMAKE_BUILD_TYPE) 7 | message(STATUS "Setting build type to 'Release' as none was specified.") 8 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) 9 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 10 | endif() 11 | 12 | set (CMAKE_C_FLAGS "-Wall -std=gnu99 -fno-omit-frame-pointer ${CMAKE_C_FLAGS}") 13 | 14 | set(CMAKE_C_FLAGS_DEBUG "-O0 -g") 15 | set(CMAKE_C_FLAGS_MINSIZEREL "-O2") 16 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") 17 | set(CMAKE_C_FLAGS_RELEASE "-O2 -g") 18 | 19 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") 20 | 21 | include(CTest) 22 | include(GNUInstallDirs) 23 | 24 | if (NOT ENABLE_STATIC AND NOT ENABLE_SHARED) 25 | set (ENABLE_SHARED ON) 26 | endif() 27 | 28 | add_subdirectory(lib) 29 | add_subdirectory(perf) 30 | -------------------------------------------------------------------------------- /lib/pt/pt.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHTRAVERSAL_H 2 | #define PATHTRAVERSAL_H 3 | 4 | #include 5 | #include 6 | 7 | struct pt_detect_ctx; 8 | 9 | enum PT_CTX { 10 | PT_CTX_INJECTION = 0, 11 | }; 12 | 13 | struct pt_token_arg_data { 14 | struct detect_str value; 15 | #define PT_KEY_INSTR (1 << 0) 16 | #define PT_VALUE_NEEDFREE (1 << 1) 17 | uint32_t flags; 18 | int tok; 19 | }; 20 | 21 | struct pt_detect_lexer_ctx { 22 | struct detect_re2c re2c; 23 | int state; 24 | int condition; 25 | struct detect_buf buf; 26 | }; 27 | 28 | #include "pt_parser.h" 29 | 30 | struct pt_detect_ctx { 31 | struct detect_ctx base; 32 | enum PT_CTX type; 33 | unsigned ctxnum; 34 | #define PT_PENDING_SEP 1 35 | #define PT_PENDING_END 2 36 | unsigned pending; 37 | struct detect *detect; 38 | pt_parser_pstate *pstate; 39 | struct pt_detect_lexer_ctx lexer; 40 | struct detect_ctx_result res; 41 | }; 42 | 43 | DETECT_HIDDEN int 44 | pt_get_token(struct pt_detect_ctx *ctx, union PT_PARSER_STYPE *arg); 45 | DETECT_HIDDEN void 46 | pt_token_data_destructor(void *token); 47 | DETECT_HIDDEN int 48 | pt_store_data(struct pt_detect_ctx *ctx, struct pt_token_arg_data *info); 49 | 50 | #endif /* PATHTRAVERSAL_H */ 51 | -------------------------------------------------------------------------------- /lib/pt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FindBISON) 2 | 3 | FIND_PROGRAM(RE2C_EXECUTABLE NAMES re2c) 4 | 5 | if(RE2C_EXECUTABLE) 6 | macro(RE2C_FILE FILENAME CFILE HFILE) 7 | set (OUT_CFILE "${CMAKE_CURRENT_BINARY_DIR}/${CFILE}") 8 | set (OUT_HFILE "${CMAKE_CURRENT_BINARY_DIR}/${HFILE}") 9 | add_custom_command( 10 | OUTPUT "${OUT_CFILE}" "${OUT_HFILE}" 11 | COMMAND "${RE2C_EXECUTABLE}" 12 | ARGS "-c" "-f" 13 | "-o" "${OUT_CFILE}" 14 | "-t" "${OUT_HFILE}" 15 | "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" 16 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") 17 | SET_SOURCE_FILES_PROPERTIES("${OUT_CFILE}" "${OUT_HFILE}" PROPERTIES GENERATED TRUE) 18 | SET_SOURCE_FILES_PROPERTIES("${OUT_HFILE}" PROPERTIES HEADER_FILE_ONLY TRUE) 19 | endmacro() 20 | endif() 21 | 22 | bison_target(pt pt_parser.y 23 | ${CMAKE_CURRENT_BINARY_DIR}/pt_parser.c 24 | ) 25 | 26 | re2c_file(pt_lexer.re2c pt_lexer.c pt_lexer.h) 27 | 28 | include_directories( 29 | ${CMAKE_CURRENT_SOURCE_DIR} 30 | ${CMAKE_CURRENT_BINARY_DIR} 31 | ) 32 | 33 | set (SOURCES 34 | pt.c 35 | ${BISON_pt_OUTPUT_SOURCE} 36 | ${CMAKE_CURRENT_BINARY_DIR}/pt_lexer.c 37 | ) 38 | 39 | add_library(${PARSER_TARGET} OBJECT ${SOURCES}) 40 | set_target_properties(${PARSER_TARGET} PROPERTIES 41 | POSITION_INDEPENDENT_CODE TRUE) 42 | -------------------------------------------------------------------------------- /lib/bash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FindBISON) 2 | 3 | FIND_PROGRAM(RE2C_EXECUTABLE NAMES re2c) 4 | 5 | if(RE2C_EXECUTABLE) 6 | macro(RE2C_FILE FILENAME CFILE HFILE) 7 | set (OUT_CFILE "${CMAKE_CURRENT_BINARY_DIR}/${CFILE}") 8 | set (OUT_HFILE "${CMAKE_CURRENT_BINARY_DIR}/${HFILE}") 9 | add_custom_command( 10 | OUTPUT "${OUT_CFILE}" "${OUT_HFILE}" 11 | COMMAND "${RE2C_EXECUTABLE}" 12 | ARGS "-c" "-f" 13 | "-o" "${OUT_CFILE}" 14 | "-t" "${OUT_HFILE}" 15 | "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" 16 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") 17 | SET_SOURCE_FILES_PROPERTIES("${OUT_CFILE}" "${OUT_HFILE}" PROPERTIES GENERATED TRUE) 18 | SET_SOURCE_FILES_PROPERTIES("${OUT_HFILE}" PROPERTIES HEADER_FILE_ONLY TRUE) 19 | endmacro() 20 | endif() 21 | 22 | bison_target(bash bash_parser.y 23 | ${CMAKE_CURRENT_BINARY_DIR}/bash_parser.c 24 | ) 25 | 26 | re2c_file(bash_lexer.re2c bash_lexer.c bash_lexer.h) 27 | 28 | include_directories( 29 | ${CMAKE_CURRENT_SOURCE_DIR} 30 | ${CMAKE_CURRENT_BINARY_DIR} 31 | ) 32 | 33 | set (SOURCES 34 | bash.c 35 | ${BISON_bash_OUTPUT_SOURCE} 36 | ${CMAKE_CURRENT_BINARY_DIR}/bash_lexer.c 37 | ) 38 | 39 | add_library(${PARSER_TARGET} OBJECT ${SOURCES}) 40 | set_target_properties(${PARSER_TARGET} PROPERTIES 41 | POSITION_INDEPENDENT_CODE TRUE) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Wallarm 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /lib/sqli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FindBISON) 2 | 3 | FIND_PROGRAM(RE2C_EXECUTABLE NAMES re2c) 4 | 5 | if(RE2C_EXECUTABLE) 6 | macro(RE2C_FILE FILENAME CFILE HFILE) 7 | set (OUT_CFILE "${CMAKE_CURRENT_BINARY_DIR}/${CFILE}") 8 | set (OUT_HFILE "${CMAKE_CURRENT_BINARY_DIR}/${HFILE}") 9 | add_custom_command( 10 | OUTPUT "${OUT_CFILE}" "${OUT_HFILE}" 11 | COMMAND "${RE2C_EXECUTABLE}" 12 | ARGS "-c" "-f" 13 | "-o" "${OUT_CFILE}" 14 | "-t" "${OUT_HFILE}" 15 | "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" 16 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") 17 | SET_SOURCE_FILES_PROPERTIES("${OUT_CFILE}" "${OUT_HFILE}" PROPERTIES GENERATED TRUE) 18 | SET_SOURCE_FILES_PROPERTIES("${OUT_HFILE}" PROPERTIES HEADER_FILE_ONLY TRUE) 19 | endmacro() 20 | endif() 21 | 22 | bison_target(sqli sqli_parser.y 23 | ${CMAKE_CURRENT_BINARY_DIR}/sqli_parser.c 24 | # VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/sqli_parser 25 | ) 26 | 27 | re2c_file(sqli_lexer.re2c sqli_lexer.c sqli_lexer.h) 28 | 29 | include_directories( 30 | ${CMAKE_CURRENT_SOURCE_DIR} 31 | ${CMAKE_CURRENT_BINARY_DIR} 32 | ) 33 | 34 | set (SOURCES 35 | sqli.c 36 | ${BISON_sqli_OUTPUT_SOURCE} 37 | ${CMAKE_CURRENT_BINARY_DIR}/sqli_lexer.c 38 | ) 39 | 40 | add_library(${PARSER_TARGET} OBJECT ${SOURCES}) 41 | set_target_properties(${PARSER_TARGET} PROPERTIES 42 | POSITION_INDEPENDENT_CODE TRUE) 43 | -------------------------------------------------------------------------------- /lib/pt/pt_parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "pt.h" 3 | 4 | static void 5 | pt_parser_error(struct pt_detect_ctx *ctx, const char *s) 6 | { 7 | ctx->res.parse_error = true; 8 | } 9 | %} 10 | 11 | %define api.pure full 12 | %define api.push-pull push 13 | %define api.prefix {pt_parser_} 14 | %parse-param {struct pt_detect_ctx *ctx} 15 | %union { 16 | struct pt_token_arg_data data; 17 | } 18 | 19 | %token TOK_START_PT_INJ 20 | %token TOK_SEP TOK_TRAV 21 | %token TOK_ROOT TOK_NAME 22 | 23 | %left TOK_TRAV TOK_NAME TOK_ROOT 24 | %left PTRAV 25 | 26 | %destructor { 27 | pt_token_data_destructor(&$$); 28 | } 29 | 30 | %% 31 | 32 | context: start_pt 33 | ; 34 | start_pt: TOK_START_PT_INJ inj 35 | | TOK_START_PT_INJ seps inj 36 | ; 37 | 38 | ssep: TOK_SEP[k] { 39 | pt_store_data(ctx, &$k); 40 | } 41 | ; 42 | strav: TOK_TRAV[k] { 43 | pt_store_data(ctx, &$k); 44 | } 45 | ; 46 | sroot: TOK_ROOT[r] { 47 | pt_store_data(ctx, &$r); 48 | } 49 | ; 50 | sname: TOK_NAME[n] { 51 | pt_store_data(ctx, &$n); 52 | } 53 | ; 54 | 55 | seps: ssep 56 | | seps ssep 57 | ; 58 | inj_pref: 59 | | inj_pref sroot seps 60 | | inj_pref sname seps 61 | ; 62 | inj_pref2: inj_pref strav seps 63 | | inj_pref3 strav seps 64 | | inj_pref2 strav seps 65 | ; 66 | inj_pref3: inj_pref2 sname seps 67 | | inj_pref3 sname seps 68 | ; 69 | inj: inj_pref2 sroot { 70 | YYACCEPT; 71 | } 72 | | inj_pref3 sroot { 73 | YYACCEPT; 74 | } 75 | ; 76 | %% 77 | -------------------------------------------------------------------------------- /lib/bash/bash.h: -------------------------------------------------------------------------------- 1 | #ifndef BASH_H 2 | #define BASH_H 3 | 4 | #include 5 | #include 6 | 7 | struct bash_detect_ctx; 8 | 9 | enum BASH_CTX { 10 | BASH_CTX_RCE = 0, 11 | BASH_CTX_IN_WORD = 1, 12 | BASH_CTX_LAST, 13 | }; 14 | 15 | struct bash_token_arg_data { 16 | struct detect_str value; 17 | #define BASH_KEY_INSTR (1 << 0) 18 | #define BASH_VALUE_NEEDFREE (1 << 1) 19 | #define BASH_DATA_NOSTART (1 << 2) 20 | #define BASH_DATA_NOEND (1 << 3) 21 | uint32_t flags; 22 | int tok; 23 | }; 24 | 25 | struct var { 26 | struct detect_str name; 27 | struct detect_str val; 28 | 29 | RB_ENTRY(var) link; 30 | }; 31 | 32 | RB_HEAD(vars_tree, var); 33 | WRB_PROTOTYPE(vars_tree, var, struct detect_str *); 34 | 35 | struct bash_detect_lexer_ctx { 36 | unsigned inword : 1; 37 | 38 | struct detect_re2c re2c; 39 | int state; 40 | int condition; 41 | struct detect_buf buf; 42 | struct detect_buf var_name; 43 | struct vars_tree vars; 44 | }; 45 | 46 | #include "bash_parser.h" 47 | 48 | struct bash_detect_ctx { 49 | struct detect_ctx base; 50 | enum BASH_CTX type; 51 | unsigned ctxnum; 52 | struct detect *detect; 53 | bash_parser_pstate *pstate; 54 | struct bash_detect_lexer_ctx lexer; 55 | struct detect_ctx_result res; 56 | int last_read_token; 57 | int token_before_that; 58 | }; 59 | 60 | DETECT_HIDDEN int 61 | bash_get_token(struct bash_detect_ctx *ctx, union BASH_PARSER_STYPE *arg); 62 | DETECT_HIDDEN void 63 | bash_token_data_destructor(void *token); 64 | DETECT_HIDDEN int 65 | bash_store_data(struct bash_detect_ctx *ctx, struct bash_token_arg_data *info); 66 | 67 | #endif /* BASH_H */ 68 | -------------------------------------------------------------------------------- /include/detect/detect_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef DETECT_BUF_H 2 | #define DETECT_BUF_H 3 | 4 | #include "detect.h" 5 | #include 6 | #include 7 | #include 8 | 9 | struct detect_buf { 10 | struct detect_str data; 11 | size_t allocated; 12 | size_t minsiz; 13 | ssize_t maxsiz; 14 | }; 15 | 16 | static inline int 17 | detect_buf_reinit(struct detect_buf *buf) 18 | { 19 | buf->data.str = NULL; 20 | buf->data.len = 0; 21 | buf->allocated = 0; 22 | return (0); 23 | } 24 | 25 | static inline int 26 | detect_buf_init(struct detect_buf *buf, size_t minsiz, ssize_t maxsiz) 27 | { 28 | if (!minsiz) 29 | minsiz = 32; 30 | buf->minsiz = minsiz; 31 | if (maxsiz > 0 && minsiz > (size_t)maxsiz) 32 | maxsiz = minsiz; 33 | buf->maxsiz = maxsiz; 34 | return (detect_buf_reinit(buf)); 35 | } 36 | 37 | static inline void 38 | detect_buf_deinit(struct detect_buf *buf) 39 | { 40 | if (buf->data.str != NULL) { 41 | free(buf->data.str); 42 | buf->data.str = NULL; 43 | } 44 | } 45 | 46 | static inline int 47 | detect_buf_add_char(struct detect_buf *buf, unsigned char ch) 48 | { 49 | void *str; 50 | size_t new_allocated; 51 | 52 | if (buf->data.len != buf->allocated) { 53 | buf->data.str[buf->data.len++] = ch; 54 | return (0); 55 | } 56 | new_allocated = buf->allocated ? buf->allocated * 2 : buf->minsiz; 57 | if (buf->maxsiz >= 0 && new_allocated > buf->maxsiz) 58 | new_allocated = buf->maxsiz; 59 | if (buf->data.len == new_allocated) 60 | return (EOVERFLOW); 61 | if ((str = realloc(buf->data.str, new_allocated)) == NULL) 62 | return (ENOMEM); 63 | buf->data.str = str; 64 | buf->allocated = new_allocated; 65 | buf->data.str[buf->data.len++] = ch; 66 | return (0); 67 | } 68 | 69 | #endif /* DETECT_BUF_H */ 70 | -------------------------------------------------------------------------------- /include/detect/detect_re2c.h: -------------------------------------------------------------------------------- 1 | #ifndef DETECT_RE2C_H 2 | #define DETECT_RE2C_H 3 | 4 | #include 5 | #include 6 | 7 | #define DETECT_RE2C_YYCTYPE unsigned char 8 | #define DETECT_RE2C_YYCURSOR(ctx) (ctx)->pos 9 | #define DETECT_RE2C_YYMARKER(ctx) (ctx)->marker 10 | #define DETECT_RE2C_YYCTXMARKER(ctx) (ctx)->ctxmarker 11 | #define DETECT_RE2C_YYFILL(ctx, pend, need, maxfill) \ 12 | do { \ 13 | int rv; \ 14 | if (!!(rv = detect_re2c_yyfill(ctx, pend, need, maxfill))) \ 15 | return (-rv); \ 16 | } while (0) 17 | #define DETECT_RE2C_UNUSED_BEFORE(ctx) \ 18 | do { \ 19 | (ctx)->start = DETECT_RE2C_YYCURSOR(ctx); \ 20 | } while (0) 21 | 22 | struct detect_re2c { 23 | unsigned char *tmp_data; 24 | const unsigned char *data; 25 | const unsigned char *start; 26 | const unsigned char *pos; 27 | const unsigned char *marker; 28 | const unsigned char *ctxmarker; 29 | size_t siz; 30 | unsigned yyfill_need; 31 | unsigned data_copied; 32 | unsigned tmp_data_siz; 33 | unsigned tmp_data_alloc; 34 | 35 | unsigned fin : 1; 36 | unsigned tmp_data_in_use : 1; 37 | }; 38 | 39 | int 40 | detect_re2c_init(struct detect_re2c *ctx); 41 | int 42 | detect_re2c_deinit(struct detect_re2c *ctx); 43 | 44 | int 45 | detect_re2c_add_data(struct detect_re2c *ctx, const void *data, size_t siz, bool fin); 46 | 47 | int 48 | detect_re2c_yyfill( 49 | struct detect_re2c *ctx, const unsigned char **end, unsigned need, unsigned maxfill); 50 | 51 | int 52 | detect_re2c_prepare_input(struct detect_re2c *ctx, const unsigned char **end, unsigned maxfill); 53 | 54 | #endif /* DETECT_RE2C_H */ 55 | -------------------------------------------------------------------------------- /lib/sqli/sqli.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLI_H 2 | #define SQLI_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct sqli_detect_ctx; 9 | 10 | enum SQLI_CTX { 11 | SQLI_CTX_DATA = 0, 12 | SQLI_CTX_IN_STRING, 13 | SQLI_CTX_RCE, 14 | SQLI_CTX_DATA_VAR_START_WITH_NUM, 15 | SQLI_CTX_IN_STRING_VAR_START_WITH_NUM, 16 | SQLI_CTX_RCE_VAR_START_WITH_NUM, 17 | SQLI_CTX_LAST, 18 | }; 19 | 20 | struct sqli_token_arg_data { 21 | struct detect_str value; 22 | #define SQLI_KEY_READ (1 << 0) 23 | #define SQLI_KEY_WRITE (1 << 1) 24 | #define SQLI_KEY_INSTR (1 << 2) 25 | #define SQLI_DATA_NOSTART (1 << 3) 26 | #define SQLI_DATA_NOEND (1 << 4) 27 | #define SQLI_VALUE_NEEDFREE (1 << 5) 28 | uint32_t flags; 29 | int tok; 30 | }; 31 | 32 | #include "sqli_parser.h" 33 | 34 | #define MAX_PENDING_TOKENS 1 35 | #define SQLI_PENDING_NEXT(idx) (((idx) == (MAX_PENDING_TOKENS - 1)) ? 0 : (idx) + 1) 36 | #define SQLI_PENDING_SHIFT(idx) ({ idx = SQLI_PENDING_NEXT(idx); }) 37 | 38 | struct sqli_pending_token { 39 | int tok; 40 | union SQLI_PARSER_STYPE arg; 41 | void (*destructor)(void *token); 42 | }; 43 | 44 | DETECT_HIDDEN void 45 | sqli_token_data_destructor(void *token); 46 | 47 | struct sqli_detect_lexer_ctx { 48 | unsigned instring : 1; 49 | 50 | struct detect_re2c re2c; 51 | 52 | struct sqli_pending_token pending[MAX_PENDING_TOKENS]; 53 | unsigned pending_first; 54 | unsigned pending_last; 55 | int state; 56 | int condition; 57 | struct detect_buf buf; 58 | }; 59 | 60 | struct sqli_detect_ctx { 61 | struct detect_ctx base; 62 | enum SQLI_CTX type; 63 | unsigned ctxnum; 64 | bool has_any_tokens; 65 | struct detect *detect; 66 | sqli_parser_pstate *pstate; 67 | struct sqli_detect_lexer_ctx lexer; 68 | struct detect_ctx_result res; 69 | bool var_start_with_num; 70 | }; 71 | 72 | DETECT_HIDDEN int 73 | sqli_get_token(struct sqli_detect_ctx *ctx, union SQLI_PARSER_STYPE *arg); 74 | 75 | DETECT_HIDDEN int 76 | sqli_store_data(struct sqli_detect_ctx *ctx, struct sqli_token_arg_data *info); 77 | 78 | #endif /* SQLI_H */ 79 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(LibWallarmMisc REQUIRED) 2 | 3 | set (TARGET "detection") 4 | set (TARGET_OBJ "${TARGET}_obj") 5 | set (TARGET_VERSION_MAJOR 1) 6 | set (TARGET_VERSION_MINOR 2) 7 | set (TARGET_VERSION_PATCH 1) 8 | set (TARGET_VERSION_FULL 9 | ${TARGET_VERSION_MAJOR}.${TARGET_VERSION_MINOR}.${TARGET_VERSION_PATCH}) 10 | set (TARGET_VERSION ${TARGET_VERSION_MAJOR}) 11 | 12 | include_directories( 13 | ${CMAKE_CURRENT_SOURCE_DIR}/../include 14 | ${LIBWALLARMMISC_INCLUDE_DIRS} 15 | ) 16 | 17 | set (LIBDEPS 18 | ${LIBWALLARMMISC_LIBRARIES} 19 | ) 20 | 21 | set (SOURCES 22 | detect.c 23 | detect_util.c 24 | detect_re2c.c 25 | detect_parser.c 26 | ) 27 | 28 | set (PARSERS 29 | sqli 30 | pt 31 | bash 32 | ) 33 | 34 | add_library(${TARGET_OBJ} OBJECT ${SOURCES}) 35 | set_target_properties(${TARGET_OBJ} PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 36 | 37 | set (OBJ_TARGETS $) 38 | 39 | foreach (PARSER ${PARSERS}) 40 | set (PARSER_TARGET "${TARGET}_${PARSER}") 41 | set (OBJ_TARGETS ${OBJ_TARGETS} $) 42 | add_subdirectory(${PARSER}) 43 | endforeach() 44 | 45 | if (ENABLE_SHARED) 46 | set (SHARED_TARGET ${TARGET}) 47 | if (ENABLE_STATIC) 48 | set (STATIC_TARGET ${TARGET}_static) 49 | else() 50 | set (STATIC_TARGET) 51 | endif() 52 | else() 53 | set (SHARED_TARGET) 54 | set (STATIC_TARGET ${TARGET}) 55 | endif() 56 | 57 | set (LIB_TARGETS ${STATIC_TARGET} ${SHARED_TARGET}) 58 | 59 | if (ENABLE_SHARED) 60 | add_library(${SHARED_TARGET} SHARED ${OBJ_TARGETS}) 61 | target_link_libraries(${SHARED_TARGET} ${LIBDEPS}) 62 | set_target_properties(${SHARED_TARGET} PROPERTIES 63 | VERSION ${TARGET_VERSION_FULL} 64 | SOVERSION ${TARGET_VERSION} 65 | ) 66 | endif() 67 | if (ENABLE_STATIC) 68 | add_library(${STATIC_TARGET} STATIC ${OBJ_TARGETS}) 69 | target_link_libraries(${STATIC_TARGET} ${LIBDEPS}) 70 | set_target_properties(${STATIC_TARGET} PROPERTIES OUTPUT_NAME ${TARGET}) 71 | endif() 72 | install(TARGETS ${LIB_TARGETS} 73 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 74 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 75 | install(DIRECTORY ../include/detect DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 76 | 77 | add_subdirectory(test) 78 | -------------------------------------------------------------------------------- /include/detect/detect_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef DETECT_PARSER_H 2 | #define DETECT_PARSER_H 3 | 4 | #include "detect.h" 5 | #include "detect_buf.h" 6 | #include 7 | 8 | #define DETECT_HIDDEN __attribute__((visibility("hidden"))) 9 | 10 | struct detect_parser; 11 | 12 | typedef int (*detect_parser_init_func)(void); 13 | typedef int (*detect_parser_deinit_func)(void); 14 | typedef struct detect *(*detect_parser_open_func)(struct detect_parser *parser); 15 | typedef int (*detect_parser_close_func)(struct detect *detect); 16 | typedef int (*detect_parser_set_options_func)(struct detect *detect, const char *options); 17 | typedef int (*detect_parser_start_func)(struct detect *detect); 18 | typedef int (*detect_parser_stop_func)(struct detect *detect); 19 | typedef int (*detect_parser_add_data_func)( 20 | struct detect *detect, const void *data, size_t siz, bool fin); 21 | 22 | struct detect_parser { 23 | struct detect_str name; 24 | 25 | detect_parser_init_func init; 26 | detect_parser_deinit_func deinit; 27 | 28 | detect_parser_open_func open; 29 | detect_parser_close_func close; 30 | detect_parser_set_options_func set_options; 31 | 32 | detect_parser_start_func start; 33 | detect_parser_stop_func stop; 34 | detect_parser_add_data_func add_data; 35 | }; 36 | 37 | struct detect_ctx { 38 | struct detect_ctx_desc *desc; 39 | struct detect_ctx_result *res; 40 | }; 41 | 42 | struct detect { 43 | struct detect_parser *parser; 44 | 45 | detect_finish_cb finish_cb; 46 | void *finish_cb_arg; 47 | 48 | struct detect_ctx **ctxs; 49 | unsigned nctx; 50 | unsigned nctx_finished; 51 | 52 | unsigned started : 1; 53 | }; 54 | 55 | /* Alphanumeric comparison */ 56 | static inline int 57 | detect_str_cmp(const struct detect_str *s1, const struct detect_str *s2) 58 | { 59 | int rc; 60 | 61 | if (s1->len < s2->len) { 62 | if (!(rc = memcmp(s1->str, s2->str, s1->len))) 63 | rc = -1; 64 | } else if (!(rc = memcmp(s1->str, s2->str, s2->len))) 65 | rc = (s1->len != s2->len); 66 | return (rc); 67 | } 68 | 69 | /* Fast but non-alphanumeric comparison */ 70 | static inline int 71 | intdetect_str_cmp_fast(const struct detect_str *s1, const struct detect_str *s2) 72 | { 73 | if (s1->len < s2->len) 74 | return (-1); 75 | if (s1->len > s2->len) 76 | return (1); 77 | return (memcmp(s1->str, s2->str, s1->len)); 78 | } 79 | 80 | struct detect_parser * 81 | detect_parser_find(struct detect_str *name); 82 | 83 | int 84 | detect_instance_init(struct detect *detect, struct detect_parser *parser); 85 | 86 | int 87 | detect_ctx_result_init(struct detect_ctx_result *res); 88 | int 89 | detect_ctx_result_deinit(struct detect_ctx_result *res); 90 | 91 | int 92 | detect_ctx_result_store_token( 93 | struct detect_ctx_result *res, const struct detect_str *flag, const struct detect_str *name); 94 | 95 | int 96 | detect_ctx_result_store_data( 97 | struct detect_ctx_result *res, const struct detect_str *kind, struct detect_str *value); 98 | 99 | #endif /* DETECT_PARSER_H */ 100 | -------------------------------------------------------------------------------- /lib/detect_parser.c: -------------------------------------------------------------------------------- 1 | #include "detect_int.h" 2 | #include 3 | #include 4 | 5 | struct detect_parser_info { 6 | struct detect_parser *parser; 7 | struct detect_str name; 8 | RB_ENTRY(detect_parser_info) link; 9 | }; 10 | 11 | RB_HEAD(detect_parser_tree, detect_parser_info); 12 | #define DETECT_PARSER_INFO2KEY(info) (&(info)->name) 13 | WRB_GENERATE_STATIC( 14 | detect_parser_tree, detect_parser_info, struct detect_str *, link, detect_str_cmp, 15 | DETECT_PARSER_INFO2KEY); 16 | 17 | static struct detect_parser_tree detect_parsers; 18 | 19 | #define TRYLOAD(rc, parser) \ 20 | do { \ 21 | extern struct detect_parser parser; \ 22 | \ 23 | if (!!(rc = s_detect_parser_load(&parser))) \ 24 | goto done; \ 25 | } while (0) 26 | 27 | static int 28 | s_detect_parser_load(struct detect_parser *parser) 29 | { 30 | struct detect_parser_info *pi; 31 | 32 | if (parser->name.str == NULL) 33 | return (EINVAL); 34 | if (WRB_FIND(detect_parser_tree, &detect_parsers, &parser->name) != NULL) 35 | return (EINVAL); 36 | if (parser->init != NULL) { 37 | int rc; 38 | 39 | if (!!(rc = parser->init())) 40 | return (rc); 41 | } 42 | pi = calloc(1, sizeof(*pi)); 43 | pi->parser = parser; 44 | pi->name = parser->name; 45 | RB_INSERT(detect_parser_tree, &detect_parsers, pi); 46 | return (0); 47 | } 48 | 49 | int 50 | detect_parser_init(void) 51 | { 52 | int rc = 0; 53 | 54 | RB_INIT(&detect_parsers); 55 | 56 | TRYLOAD(rc, detect_parser_sqli); 57 | TRYLOAD(rc, detect_parser_pt); 58 | TRYLOAD(rc, detect_parser_bash); 59 | done: 60 | if (rc) { 61 | detect_parser_deinit(); 62 | } 63 | return (rc); 64 | } 65 | 66 | int 67 | detect_parser_deinit(void) 68 | { 69 | struct detect_parser_info *pi, *pi_tmp; 70 | 71 | WRB_FOREACH_PDFS (pi, detect_parser_tree, &detect_parsers, pi_tmp) { 72 | if (pi->parser->deinit != NULL) 73 | pi->parser->deinit(); 74 | free(pi); 75 | } 76 | return (0); 77 | } 78 | 79 | struct detect_parser * 80 | detect_parser_find(struct detect_str *name) 81 | { 82 | struct detect_parser_info *pi; 83 | 84 | if ((pi = WRB_FIND(detect_parser_tree, &detect_parsers, name)) == NULL) 85 | return (NULL); 86 | return (pi->parser); 87 | } 88 | 89 | void * 90 | detect_parser_list(const struct detect_str **name) 91 | { 92 | struct detect_parser_info *pi; 93 | 94 | pi = RB_MIN(detect_parser_tree, &detect_parsers); 95 | if (pi == NULL) 96 | return (NULL); 97 | *name = &pi->name; 98 | return (pi); 99 | } 100 | 101 | void * 102 | detect_parser_list_next(void *ctx, const struct detect_str **name) 103 | { 104 | struct detect_parser_info *pi = ctx; 105 | 106 | pi = RB_NEXT(detect_parser_tree, &detect_parsers, pi); 107 | if (pi == NULL) 108 | return (NULL); 109 | *name = &pi->name; 110 | return (pi); 111 | } 112 | -------------------------------------------------------------------------------- /include/detect/detect.h: -------------------------------------------------------------------------------- 1 | #ifndef DETECT_H 2 | #define DETECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | int 12 | detect_init(void); 13 | int 14 | detect_deinit(void); 15 | 16 | struct detect; 17 | 18 | #define CSTR_LEN(cstr) .str = cstr, .len = sizeof(cstr) - 1 19 | #define DETECT_CSTR(cstr) \ 20 | { \ 21 | CSTR_LEN(cstr) \ 22 | } 23 | struct detect_str { 24 | char *str; 25 | size_t len; 26 | }; 27 | 28 | struct detect_token_stat { 29 | struct detect_str token_name; 30 | size_t count; 31 | 32 | RB_ENTRY(detect_token_stat) link; 33 | }; 34 | 35 | RB_HEAD(detect_token_stat_tree, detect_token_stat); 36 | WRB_PROTOTYPE(detect_token_stat_tree, detect_token_stat, struct detect_str *); 37 | 38 | struct detect_flag_stat { 39 | struct detect_str flag_name; 40 | size_t count; 41 | struct detect_token_stat_tree stat_by_tokens; 42 | 43 | RB_ENTRY(detect_flag_stat) link; 44 | }; 45 | 46 | struct detect_data { 47 | struct detect_str kind; 48 | struct detect_str value; 49 | 50 | STAILQ_ENTRY(detect_data) link; 51 | }; 52 | 53 | struct detect_ctx_desc { 54 | struct detect_str name; 55 | unsigned rce : 1; 56 | }; 57 | 58 | RB_HEAD(detect_flag_stat_tree, detect_flag_stat); 59 | WRB_PROTOTYPE(detect_flag_stat_tree, detect_flag_stat, struct detect_str *); 60 | 61 | struct detect_ctx_result { 62 | struct detect_flag_stat_tree stat_by_flags; 63 | STAILQ_HEAD(, detect_data) datas; 64 | unsigned finished : 1; 65 | unsigned disabled : 1; 66 | unsigned parse_error : 1; 67 | }; 68 | 69 | typedef int (*detect_finish_cb)( 70 | struct detect *detect, unsigned ctxnum, unsigned n_unfinished, void *arg); 71 | 72 | void * 73 | detect_parser_list(const struct detect_str **name); 74 | void * 75 | detect_parser_list_next(void *ctx, const struct detect_str **name); 76 | 77 | struct detect * 78 | detect_open(const char *parser); 79 | const struct detect_str * 80 | detect_name(struct detect *detect); 81 | int 82 | detect_close(struct detect *detect); 83 | 84 | int 85 | detect_set_options(struct detect *detect, const char *options); 86 | int 87 | detect_set_finish_cb(struct detect *detect, detect_finish_cb cb, void *arg); 88 | 89 | int 90 | detect_start(struct detect *detect); 91 | int 92 | detect_stop(struct detect *detect); 93 | unsigned 94 | detect_get_nctx(struct detect *detect); 95 | 96 | #define DETECT_ATTACK_RCE (1 << 0) 97 | #define DETECT_ATTACK_INJ (1 << 1) 98 | 99 | bool 100 | detect_has_attack(struct detect *detect, uint32_t *attack_types); 101 | int 102 | detect_ctx_disable(struct detect *detect, unsigned ctxnum); 103 | bool 104 | detect_ctx_has_attack(struct detect *detect, unsigned ctxnum); 105 | const struct detect_ctx_desc * 106 | detect_ctx_get_desc(struct detect *detect, unsigned ctxnum); 107 | const struct detect_ctx_result * 108 | detect_ctx_get_result(struct detect *detect, unsigned ctxnum); 109 | 110 | int 111 | detect_add_data(struct detect *detect, const void *data, size_t siz, bool fin); 112 | 113 | #endif /* DETECT_H */ 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libdetection 2 | 3 | Extendable library for detection syntaxes by formal notations. 4 | Can be used to detect injections and commanding attacks such as SQLi and others. 5 | Does not require attacks samples to learn. 6 | 7 | 8 | ## Requirements 9 | 10 | - cmake 11 | - cunit 12 | - bison 13 | - re2c 14 | - [libwallarmmisc](https://github.com/wallarm/libwallarmmisc) 15 | 16 | ## Installation 17 | 18 | To build, run 19 | 20 | ```sh 21 | $ ./config 22 | ``` 23 | or, to build with optimizations 24 | 25 | ```sh 26 | $ CFLAGS="-Ofast -mtune=native -march=native" ./configure 27 | ``` 28 | 29 | then run make 30 | 31 | ```sh 32 | $ make -C build 33 | ``` 34 | 35 | After that, you will get 36 | 37 | 1. The library `build/lib/libdetection.so` 38 | 2. Example executable: `build/perf/libdetection_perf` 39 | 40 | 41 | ## Extend syntaxes 42 | 43 | To add your own module to the library, you have to 44 | 45 | 1. Add the module code into a new directory under `lib/`. See 46 | `lib/sqli/sqli.c` code which implements libdetection interface 47 | for sqli module (look at `detect_parser_sqli` global variable). 48 | 2. Make libdetection to load the module statically, see 49 | `TRYLOAD` in `detect_parser_init()`. Dynamic loading is not 50 | implemented yet. 51 | 3. Include your module name in `lib/CMakeLists.txt` for automatic building. 52 | 53 | 54 | ## Formal model 55 | 56 | For attacks detection, a formal model is used which allows to make a decision based on the type of attack. This approach allows us to implement the library without having to specify precedents of attacks (without a signature of each specific attack). Here you will not find the files with fingerprints or static rules with regular expressions. 57 | 58 | Determination of the attack - the user input (string coming to the input of the library) can be processed as a sequence of data, among which there will be at least one syntax instruction. 59 | 60 | For example, in the string `123 union select` there are one data token and two instructions (commands). And the string is considered to be an attack. But it’s not complete as there no names of column and table used. 61 | 62 | Within the library, each parser state (where line processing begins) is called a context. Contexts are formulated in such a way to reduce the number of false positives of similar type. 63 | 64 | 65 | ### Types of attacks in a single syntax 66 | 67 | The library supports a variety of contexts (the parser states) with two main groups - injection attacks and commanding attacks. This approach allows us to share all the attacks on the two groups suitable for subsequent analysis and attribution to vulnerabilities in the code. 68 | 69 | For example, for phpMyAdmin or similar tools, a set of parameters for query parameter will take attacks such commanding as legitimate. Such attacks will consist entirely of SQL syntax for initial parsing state: `? Query = SELECT id FROM users ...`. At the same time the defective syntax of SQL (injection attacks) in these parameters can be easily blocked, for example: `? Query = 123 UNION SELECT id FROM users - a-`. 70 | 71 | For ease of understanding, you can distinguish injections and commanding attacks in the following way. In the case of injection attacks, there is, at least, one user input to be handled entirely as data. If the case of commanding attacks any user input will contain at least one instruction. 72 | 73 | 74 | ### Syntax 75 | 76 | Currently, we publish PoC for only SQL syntax. This allows you to test the idea against different SQL-injection attacks. To add new syntax (eg, path traversal, bash / sh, PHP, HTML5, JavaScript) you only need to describe the lexer and the BNF. So you can avoid programming in C. Just copy directory `./lib/sqli`, and then edit the files `sqli_lexer.re2c` (lexer description) and `sqli_parser.y` (syntax description in BNF). That's all you need to do. No need to write regular expressions or collecting attacks samples. 77 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Wallarm C formatting rules 2 | # date: Mon Dec 6 15:55:30 MSK 2021 3 | # 4 | # Clang-Format Style Options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html 5 | 6 | BasedOnStyle: Google 7 | 8 | AccessModifierOffset: -4 9 | AlignAfterOpenBracket: AlwaysBreak 10 | AlignArrayOfStructures: Left 11 | AlignConsecutiveBitFields: AcrossEmptyLinesAndComments 12 | AlignConsecutiveMacros: AcrossEmptyLinesAndComments 13 | AlignConsecutiveAssignments: None 14 | AlignConsecutiveDeclarations: None # Also formats struct fields. 15 | AlignEscapedNewlines: Left 16 | AlignOperands: Align 17 | AlignTrailingComments: true 18 | AllowAllArgumentsOnNextLine: true 19 | AllowAllConstructorInitializersOnNextLine: false 20 | AllowAllParametersOfDeclarationOnNextLine: true 21 | AllowShortBlocksOnASingleLine: Never 22 | AllowShortCaseLabelsOnASingleLine: false 23 | AllowShortEnumsOnASingleLine: false 24 | AllowShortFunctionsOnASingleLine: Inline 25 | AllowShortIfStatementsOnASingleLine: Never 26 | AllowShortLambdasOnASingleLine: Inline 27 | AllowShortLoopsOnASingleLine: true 28 | AlwaysBreakAfterReturnType: All 29 | AlwaysBreakBeforeMultilineStrings: false 30 | AlwaysBreakTemplateDeclarations: 'Yes' 31 | AttributeMacros: 32 | - WALLARM_UNUSED 33 | - DETECT_HIDDEN 34 | BinPackArguments: true 35 | BinPackParameters: true 36 | BitFieldColonSpacing: Both 37 | BreakBeforeBinaryOperators: NonAssignment 38 | BreakBeforeBraces: Custom 39 | BraceWrapping: 40 | AfterCaseLabel: false 41 | AfterClass: true 42 | AfterControlStatement: Never 43 | AfterEnum: false 44 | AfterFunction: true 45 | AfterNamespace: false 46 | AfterStruct: false 47 | AfterUnion: false 48 | AfterExternBlock: false 49 | BeforeCatch: true 50 | BeforeElse: false 51 | BeforeWhile: false 52 | IndentBraces: false 53 | SplitEmptyFunction: false 54 | SplitEmptyRecord: false 55 | BreakBeforeTernaryOperators: true 56 | BreakConstructorInitializers: BeforeComma 57 | BreakInheritanceList: BeforeComma 58 | ColumnLimit: 100 59 | CompactNamespaces: false 60 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 61 | ConstructorInitializerIndentWidth: 4 62 | ContinuationIndentWidth: 4 63 | Cpp11BracedListStyle: true 64 | DerivePointerAlignment: false 65 | FixNamespaceComments: false 66 | ForEachMacros: 67 | - PROTON_ATTBLOCK_FOREACH 68 | - PROTON_ATTCLASS_FOREACH 69 | - PROTON_ATTSRC_FOREACH 70 | - PROTON_BUFCHAIN_FOREACH 71 | - PROTON_BUFCHAIN_FOREACH_SAFE 72 | - PROTON_VEC_FOREACH 73 | - PROTON_VEC_FOREACH_SAFE 74 | - PROTON_VEC_FOREACH_SAFE_I 75 | - RB_FOREACH 76 | - RB_FOREACH_FROM 77 | - RB_FOREACH_REVERSE 78 | - RB_FOREACH_REVERSE_FROM 79 | - RB_FOREACH_REVERSE_SAFE 80 | - RB_FOREACH_SAFE 81 | - SPLAY_FOREACH 82 | - STAILQ_FOREACH 83 | - W_INT_FOREACH 84 | - W_UINT32_FOREACH 85 | - W_UINT64_FOREACH 86 | - WPTRCACHE_FOREACH 87 | - WRB_FOREACH_PDFS 88 | - WSTAILQ_FOREACH_SAFE 89 | IncludeBlocks: Regroup 90 | IndentCaseLabels: false 91 | IndentPPDirectives: AfterHash 92 | IndentWidth: 4 93 | IndentWrappedFunctionNames: false 94 | KeepEmptyLinesAtTheStartOfBlocks: false 95 | Language: Cpp 96 | # Interpret WALLARM_LIST_FOREACH_BEGIN* macros as Begin blocks 97 | # instead of Foreach block because they must be ended with 98 | # WALLARM_LIST_FOREACH_END macro. 99 | MacroBlockBegin: "^WALLARM_LIST_FOREACH_BEGIN|WALLARM_LIST_FOREACH_BEGIN_LAST$" 100 | MacroBlockEnd: "^WALLARM_LIST_FOREACH_END$" 101 | MaxEmptyLinesToKeep: 1 102 | NamespaceIndentation: None 103 | PointerAlignment: Right 104 | ReflowComments: true 105 | SortIncludes: false 106 | SpaceAfterCStyleCast: false 107 | SpaceAfterLogicalNot: false 108 | SpaceAfterTemplateKeyword: false 109 | SpaceBeforeAssignmentOperators: true 110 | SpaceBeforeCpp11BracedList: false 111 | SpaceBeforeCtorInitializerColon: true 112 | SpaceBeforeInheritanceColon: true 113 | SpaceBeforeParens: ControlStatements 114 | SpaceBeforeRangeBasedForLoopColon: true 115 | SpaceInEmptyBlock: false 116 | SpaceInEmptyParentheses: false 117 | SpacesBeforeTrailingComments: 1 118 | SpacesInAngles: false 119 | SpacesInCStyleCastParentheses: false 120 | SpacesInParentheses: false 121 | SpacesInSquareBrackets: false 122 | Standard: Cpp11 123 | TabWidth: 4 124 | TypenameMacros: 125 | - RB_ENTRY 126 | - STAILQ_ENTRY 127 | UseTab: Never 128 | -------------------------------------------------------------------------------- /lib/detect_re2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | detect_re2c_add_data(struct detect_re2c *ctx, const void *data, size_t siz, bool fin) 9 | { 10 | assert(ctx->data == NULL || ctx->pos == ctx->data + ctx->siz); 11 | 12 | if (!ctx->tmp_data_in_use) { 13 | ctx->pos = data; 14 | ctx->start = data; 15 | ctx->marker = NULL; 16 | ctx->ctxmarker = NULL; 17 | } 18 | ctx->data = data; 19 | ctx->siz = siz; 20 | ctx->data_copied = 0; 21 | ctx->fin = fin; 22 | 23 | return (0); 24 | } 25 | 26 | static bool 27 | detect_re2c_chk_switch_to_data(struct detect_re2c *ctx, const unsigned char **end) 28 | { 29 | size_t data_since_start; 30 | const unsigned char *tmp_data_end = *end; 31 | const unsigned char *data_copied_ptr; 32 | 33 | data_since_start = tmp_data_end - ctx->start; 34 | if (!ctx->data || (ctx->data_copied < data_since_start)) 35 | return (false); 36 | 37 | data_copied_ptr = ctx->data + ctx->data_copied; 38 | 39 | ctx->pos = data_copied_ptr - (tmp_data_end - ctx->pos); 40 | ctx->start = data_copied_ptr - data_since_start; 41 | ctx->tmp_data_in_use = false; 42 | ctx->data_copied = 0; 43 | ctx->tmp_data_siz = 0; 44 | *end = ctx->data + ctx->siz; 45 | 46 | return (true); 47 | } 48 | 49 | int 50 | detect_re2c_prepare_input(struct detect_re2c *ctx, const unsigned char **end, unsigned maxfill) 51 | { 52 | if (!ctx->tmp_data_in_use) { 53 | *end = ctx->data + ctx->siz; 54 | goto fill; 55 | } 56 | 57 | *end = ctx->tmp_data + ctx->tmp_data_siz; 58 | detect_re2c_chk_switch_to_data(ctx, end); 59 | 60 | fill: 61 | if (!ctx->yyfill_need && *end == ctx->pos && ctx->fin) { 62 | /* 63 | * A special case: we have to finalize the data 64 | * but have no more characters in the buffer. 65 | * Require at least one character. 66 | */ 67 | ctx->yyfill_need = 1; 68 | } 69 | if (ctx->yyfill_need && *end - ctx->pos < ctx->yyfill_need) 70 | return (detect_re2c_yyfill(ctx, end, ctx->yyfill_need, maxfill)); 71 | return (0); 72 | } 73 | 74 | static int 75 | detect_re2c_switch_to_tmp_data( 76 | struct detect_re2c *ctx, const unsigned char **end, unsigned need, unsigned maxfill) 77 | { 78 | unsigned start_pos_offset = ctx->pos - ctx->start; 79 | unsigned start_marker_offset = ctx->marker - ctx->start; 80 | unsigned start_ctxmarker_offset = ctx->ctxmarker - ctx->start; 81 | unsigned siz = start_pos_offset + maxfill; 82 | 83 | if (ctx->tmp_data == NULL) { 84 | if ((ctx->tmp_data = malloc(siz)) == NULL) 85 | return (errno); 86 | ctx->tmp_data_alloc = siz; 87 | } else { 88 | if (siz > ctx->tmp_data_alloc) { 89 | unsigned new_alloc; 90 | unsigned char *new_tmp_data; 91 | 92 | for (new_alloc = ctx->tmp_data_alloc * 2; siz > new_alloc; new_alloc *= 2) 93 | ; 94 | if ((new_tmp_data = realloc(ctx->tmp_data, new_alloc)) == NULL) 95 | return (errno); 96 | ctx->tmp_data = new_tmp_data; 97 | ctx->tmp_data_alloc = new_alloc; 98 | } 99 | } 100 | ctx->tmp_data_siz = *end - ctx->start; 101 | memcpy(ctx->tmp_data, ctx->start, ctx->tmp_data_siz); 102 | ctx->start = ctx->tmp_data; 103 | ctx->pos = ctx->tmp_data + start_pos_offset; 104 | ctx->marker = ctx->tmp_data + start_marker_offset; 105 | ctx->ctxmarker = ctx->tmp_data + start_ctxmarker_offset; 106 | ctx->tmp_data_in_use = true; 107 | ctx->data = NULL; 108 | ctx->siz = 0; 109 | ctx->data_copied = 0; 110 | *end = ctx->tmp_data + ctx->tmp_data_siz; 111 | 112 | return (EAGAIN); 113 | } 114 | 115 | static int 116 | detect_re2c_update_tmp_data( 117 | struct detect_re2c *ctx, const unsigned char **end, unsigned need, unsigned maxfill) 118 | { 119 | unsigned tmp_data_size, siz; 120 | unsigned shift; 121 | 122 | /* first of all, shift the data to the beginning of the tmp buffer */ 123 | tmp_data_size = *end - ctx->start; 124 | shift = ctx->start - ctx->tmp_data; 125 | if (shift) { 126 | memmove(ctx->tmp_data, ctx->start, tmp_data_size); 127 | ctx->start = ctx->tmp_data; 128 | ctx->tmp_data_siz -= shift; 129 | ctx->pos -= shift; 130 | ctx->marker -= shift; 131 | ctx->ctxmarker -= shift; 132 | (*end) -= shift; 133 | } 134 | 135 | /* 136 | * Make sure that the temporary buffer is of enough size 137 | * to fill maxfill bytes. 138 | */ 139 | siz = (ctx->pos - ctx->tmp_data) + maxfill; 140 | if (siz > ctx->tmp_data_alloc) { 141 | unsigned new_alloc; 142 | unsigned char *new_tmp_data; 143 | 144 | for (new_alloc = ctx->tmp_data_alloc * 2; siz > new_alloc; new_alloc *= 2) 145 | ; 146 | if ((new_tmp_data = realloc(ctx->tmp_data, new_alloc)) == NULL) 147 | return (errno); 148 | ctx->start = new_tmp_data; 149 | ctx->pos = new_tmp_data + (ctx->pos - ctx->tmp_data); 150 | ctx->marker = new_tmp_data + (ctx->marker - ctx->tmp_data); 151 | ctx->ctxmarker = new_tmp_data + (ctx->ctxmarker - ctx->tmp_data); 152 | *end = new_tmp_data + (*end - ctx->tmp_data); 153 | ctx->tmp_data = new_tmp_data; 154 | ctx->tmp_data_alloc = new_alloc; 155 | } 156 | 157 | /* 158 | * Now, try to copy available bytes from the original buffer into 159 | * the tmp one. 160 | */ 161 | if (ctx->data != NULL) { 162 | unsigned avail = ctx->siz - ctx->data_copied; 163 | 164 | /* 165 | * It is possible to (avail == 0) here, 166 | * when ctx->siz == 0 && ctx->data_copied == 0. 167 | */ 168 | 169 | need -= *end - ctx->pos; 170 | 171 | if (avail > need) 172 | avail = need; 173 | memcpy((void *)*end, ctx->data + ctx->data_copied, avail); 174 | (*end) += avail; 175 | ctx->data_copied += avail; 176 | ctx->tmp_data_siz += avail; 177 | if (ctx->data_copied == ctx->siz) { 178 | ctx->data = NULL; 179 | ctx->siz = 0; 180 | ctx->data_copied = 0; 181 | } else 182 | detect_re2c_chk_switch_to_data(ctx, end); 183 | } 184 | if (*end - ctx->pos < need) 185 | return (EAGAIN); 186 | return (0); 187 | } 188 | 189 | int 190 | detect_re2c_yyfill( 191 | struct detect_re2c *ctx, const unsigned char **end, unsigned need, unsigned maxfill) 192 | { 193 | int rv; 194 | 195 | assert(need <= maxfill); 196 | 197 | if (!ctx->tmp_data_in_use) 198 | rv = detect_re2c_switch_to_tmp_data(ctx, end, need, maxfill); 199 | else 200 | rv = detect_re2c_update_tmp_data(ctx, end, need, maxfill); 201 | 202 | if (rv != EAGAIN) { 203 | ctx->yyfill_need = 0; 204 | return (rv); 205 | } 206 | 207 | if (!ctx->fin) { 208 | ctx->yyfill_need = need; 209 | return (EAGAIN); 210 | } else { 211 | /* zero-fill the requested part of tmp_data buffer */ 212 | unsigned zero_fill_size = need - (*end - ctx->pos); 213 | memset((void *)*end, 0, zero_fill_size); 214 | (*end) += zero_fill_size; 215 | return (0); 216 | } 217 | } 218 | 219 | int 220 | detect_re2c_init(struct detect_re2c *ctx) 221 | { 222 | memset(ctx, 0, sizeof(*ctx)); 223 | return (0); 224 | } 225 | 226 | int 227 | detect_re2c_deinit(struct detect_re2c *ctx) 228 | { 229 | free(ctx->tmp_data); 230 | return (0); 231 | } 232 | -------------------------------------------------------------------------------- /lib/bash/bash.c: -------------------------------------------------------------------------------- 1 | #include "bash.h" 2 | #include 3 | #include 4 | 5 | #define VAR2KEY(var) (&(var)->name) 6 | WRB_GENERATE(vars_tree, var, struct detect_str *, link, detect_str_cmp, VAR2KEY); 7 | 8 | static const struct { 9 | struct detect_ctx_desc desc; 10 | enum bash_parser_tokentype start_tok; 11 | } bash_ctxs[] = { 12 | // clang-format off 13 | [BASH_CTX_RCE] = { 14 | .desc = {.name = {CSTR_LEN("rce")}}, 15 | .start_tok = TOK_START_RCE, 16 | }, 17 | [BASH_CTX_IN_WORD] = { 18 | .desc = {.name = {CSTR_LEN("word")}}, 19 | .start_tok = TOK_START_WORD, 20 | }, 21 | }; 22 | // clang-format on 23 | 24 | static struct detect * 25 | detect_bash_open(struct detect_parser *parser) 26 | { 27 | struct detect *detect; 28 | unsigned i; 29 | 30 | detect = malloc(sizeof(*detect)); 31 | detect_instance_init(detect, parser); 32 | detect->nctx = BASH_CTX_LAST; 33 | detect->ctxs = malloc(detect->nctx * sizeof(*detect->ctxs)); 34 | 35 | for (i = 0; i < detect->nctx; i++) { 36 | struct bash_detect_ctx *ctx; 37 | 38 | ctx = calloc(1, sizeof(*ctx)); 39 | ctx->base.desc = (struct detect_ctx_desc *)&bash_ctxs[i].desc; 40 | ctx->base.res = &ctx->res; 41 | detect_ctx_result_init(ctx->base.res); 42 | ctx->type = i; 43 | ctx->ctxnum = i; 44 | ctx->detect = detect; 45 | detect->ctxs[i] = (void *)ctx; 46 | } 47 | return (detect); 48 | } 49 | 50 | static int 51 | detect_bash_close(struct detect *detect) 52 | { 53 | unsigned i; 54 | 55 | for (i = 0; i < detect->nctx; i++) { 56 | struct bash_detect_ctx *ctx = (void *)detect->ctxs[i]; 57 | 58 | detect_ctx_result_deinit(ctx->base.res); 59 | if (ctx->pstate != NULL) 60 | bash_parser_pstate_delete(ctx->pstate); 61 | free(ctx); 62 | } 63 | if (detect->ctxs != NULL) 64 | free(detect->ctxs); 65 | free(detect); 66 | 67 | return (0); 68 | } 69 | 70 | static void 71 | bash_lexer_init(struct bash_detect_lexer_ctx *lexer) 72 | { 73 | memset(lexer, 0, sizeof(*lexer)); 74 | detect_re2c_init(&lexer->re2c); 75 | lexer->state = -1; 76 | RB_INIT(&lexer->vars); 77 | 78 | struct var *var = calloc(1, sizeof(*var)); 79 | var->name.str = "IFS"; 80 | var->name.len = 3; 81 | var->val.str = " "; 82 | var->val.len = 1; 83 | RB_INSERT(vars_tree, &lexer->vars, var); 84 | } 85 | 86 | static void 87 | bash_lexer_deinit(struct bash_detect_lexer_ctx *lexer) 88 | { 89 | struct detect_parser_info *v, *v_tmp; 90 | 91 | WRB_FOREACH_PDFS (v, vars_tree, &lexer->vars, v_tmp) { 92 | free(v); 93 | } 94 | 95 | detect_buf_deinit(&lexer->buf); 96 | detect_buf_deinit(&lexer->var_name); 97 | detect_re2c_deinit(&lexer->re2c); 98 | } 99 | 100 | void 101 | bash_token_data_destructor(void *token) 102 | { 103 | struct bash_token_arg_data *data = token; 104 | 105 | if (!!(data->flags & BASH_VALUE_NEEDFREE) && data->value.str != NULL) 106 | free(data->value.str); 107 | } 108 | 109 | static int 110 | detect_bash_push_token(struct bash_detect_ctx *ctx, int tok, void *tok_val) 111 | { 112 | int rv; 113 | 114 | if (ctx->res.finished) 115 | return (0); 116 | 117 | rv = bash_parser_push_parse(ctx->pstate, tok, tok_val, ctx); 118 | if (rv == YYPUSH_MORE) 119 | rv = 0; 120 | else { 121 | ctx->res.finished = true; 122 | ctx->detect->nctx_finished++; 123 | /* 124 | * On parse error, ctx->res.parse_error is already set by 125 | * bash_parser_error(). 126 | */ 127 | if (ctx->detect->finish_cb != NULL) 128 | rv = ctx->detect->finish_cb( 129 | ctx->detect, ctx->ctxnum, ctx->detect->nctx - ctx->detect->nctx_finished, 130 | ctx->detect->finish_cb_arg); 131 | else 132 | rv = 0; 133 | } 134 | return (rv); 135 | } 136 | 137 | static int 138 | detect_bash_start(struct detect *detect) 139 | { 140 | unsigned i; 141 | 142 | for (i = 0; i < detect->nctx; i++) { 143 | struct bash_detect_ctx *ctx = (void *)detect->ctxs[i]; 144 | 145 | if (ctx->res.finished) 146 | continue; 147 | ctx->pstate = bash_parser_pstate_new(); 148 | ctx->last_read_token = '\n'; 149 | ctx->token_before_that = 0; 150 | bash_lexer_init(&ctx->lexer); 151 | if (detect_bash_push_token(ctx, bash_ctxs[ctx->type].start_tok, NULL) != 0) 152 | break; 153 | } 154 | return (0); 155 | } 156 | 157 | static int 158 | detect_bash_stop(struct detect *detect) 159 | { 160 | unsigned i; 161 | 162 | for (i = 0; i < detect->nctx; i++) { 163 | struct bash_detect_ctx *ctx = (void *)detect->ctxs[i]; 164 | 165 | if (ctx->pstate == NULL) 166 | continue; 167 | 168 | /* We have to finish bison state machine or memleaks will occur*/ 169 | if (!ctx->res.finished) 170 | detect_bash_push_token(ctx, 0, NULL); 171 | 172 | bash_parser_pstate_delete(ctx->pstate); 173 | ctx->pstate = NULL; 174 | bash_lexer_deinit(&ctx->lexer); 175 | detect_ctx_result_deinit(&ctx->res); 176 | } 177 | return (0); 178 | } 179 | 180 | static int 181 | bash_lexer_add_data(struct bash_detect_ctx *ctx, const void *data, size_t siz, bool fin) 182 | { 183 | return (detect_re2c_add_data(&ctx->lexer.re2c, data, siz, fin)); 184 | } 185 | 186 | static int 187 | detect_bash_add_data(struct detect *detect, const void *data, size_t siz, bool fin) 188 | { 189 | unsigned i; 190 | union BASH_PARSER_STYPE token_arg; 191 | int rv = 0; 192 | 193 | for (i = 0; i < detect->nctx; i++) { 194 | struct bash_detect_ctx *ctx = (void *)detect->ctxs[i]; 195 | int token; 196 | 197 | if (ctx->res.finished) 198 | continue; 199 | bash_lexer_add_data(ctx, data, siz, fin); 200 | do { 201 | memset(&token_arg, 0, sizeof(token_arg)); 202 | token = bash_get_token(ctx, &token_arg); 203 | 204 | if (token >= 0) { 205 | if ((rv = detect_bash_push_token(ctx, token, &token_arg)) != 0) 206 | goto done; 207 | } else if (token != -EAGAIN) { 208 | /* fatal error */ 209 | rv = -token; 210 | goto done; 211 | } 212 | } while (!ctx->res.finished && token != -EAGAIN); 213 | } 214 | 215 | done: 216 | return (rv); 217 | } 218 | 219 | static int 220 | bash_store_key(struct bash_detect_ctx *ctx, struct bash_token_arg_data *info) 221 | { 222 | const static struct { 223 | struct detect_str name; 224 | uint32_t flag; 225 | } flagnames[] = { 226 | {.flag = BASH_KEY_INSTR, .name = {CSTR_LEN("INSTR")}}, 227 | }; 228 | unsigned i; 229 | 230 | for (i = 0; i < sizeof(flagnames) / sizeof(flagnames[0]); i++) { 231 | if (!(info->flags & flagnames[i].flag)) 232 | continue; 233 | detect_ctx_result_store_token(&ctx->res, &flagnames[i].name, &info->value); 234 | } 235 | bash_token_data_destructor(info); 236 | return (0); 237 | } 238 | 239 | int 240 | bash_store_data(struct bash_detect_ctx *ctx, struct bash_token_arg_data *info) 241 | { 242 | switch (info->tok) { 243 | default: 244 | return (bash_store_key(ctx, info)); 245 | } 246 | } 247 | 248 | struct detect_parser detect_parser_bash = { 249 | .name = {CSTR_LEN("bash")}, 250 | .open = detect_bash_open, 251 | .close = detect_bash_close, 252 | .start = detect_bash_start, 253 | .stop = detect_bash_stop, 254 | .add_data = detect_bash_add_data, 255 | }; 256 | -------------------------------------------------------------------------------- /lib/pt/pt.c: -------------------------------------------------------------------------------- 1 | #include "pt.h" 2 | #include 3 | #include 4 | 5 | static const struct { 6 | struct detect_ctx_desc desc; 7 | enum pt_parser_tokentype start_tok; 8 | } pt_ctxs[] = { 9 | // clang-format off 10 | [PT_CTX_INJECTION] = { 11 | .desc = {.name = {CSTR_LEN("inj")}}, 12 | .start_tok = TOK_START_PT_INJ, 13 | }, 14 | }; 15 | // clang-format on 16 | 17 | static struct detect * 18 | detect_pt_open(struct detect_parser *parser) 19 | { 20 | struct detect *detect; 21 | unsigned i; 22 | 23 | detect = malloc(sizeof(*detect)); 24 | detect_instance_init(detect, parser); 25 | detect->nctx = 1; 26 | detect->ctxs = malloc(detect->nctx * sizeof(*detect->ctxs)); 27 | 28 | for (i = 0; i < detect->nctx; i++) { 29 | struct pt_detect_ctx *ctx; 30 | 31 | ctx = calloc(1, sizeof(*ctx)); 32 | ctx->base.desc = (struct detect_ctx_desc *)&pt_ctxs[i].desc; 33 | ctx->base.res = &ctx->res; 34 | detect_ctx_result_init(ctx->base.res); 35 | ctx->type = i; 36 | ctx->ctxnum = i; 37 | ctx->detect = detect; 38 | detect->ctxs[i] = (void *)ctx; 39 | } 40 | return (detect); 41 | } 42 | 43 | static int 44 | detect_pt_close(struct detect *detect) 45 | { 46 | unsigned i; 47 | 48 | for (i = 0; i < detect->nctx; i++) { 49 | struct pt_detect_ctx *ctx = (void *)detect->ctxs[i]; 50 | 51 | detect_ctx_result_deinit(ctx->base.res); 52 | if (ctx->pstate != NULL) 53 | pt_parser_pstate_delete(ctx->pstate); 54 | free(ctx); 55 | } 56 | if (detect->ctxs != NULL) 57 | free(detect->ctxs); 58 | free(detect); 59 | 60 | return (0); 61 | } 62 | 63 | static void 64 | pt_lexer_init(struct pt_detect_lexer_ctx *lexer) 65 | { 66 | memset(lexer, 0, sizeof(*lexer)); 67 | detect_re2c_init(&lexer->re2c); 68 | lexer->state = -1; 69 | } 70 | 71 | static void 72 | pt_lexer_deinit(struct pt_detect_lexer_ctx *lexer) 73 | { 74 | detect_buf_deinit(&lexer->buf); 75 | detect_re2c_deinit(&lexer->re2c); 76 | } 77 | 78 | void 79 | pt_token_data_destructor(void *token) 80 | { 81 | struct pt_token_arg_data *data = token; 82 | 83 | if (!!(data->flags & PT_VALUE_NEEDFREE) && data->value.str != NULL) 84 | free(data->value.str); 85 | } 86 | 87 | static int 88 | detect_pt_push_token(struct pt_detect_ctx *ctx, int tok, void *tok_val) 89 | { 90 | int rv; 91 | 92 | if (ctx->res.finished) 93 | return (0); 94 | 95 | rv = pt_parser_push_parse(ctx->pstate, tok, tok_val, ctx); 96 | if (rv == YYPUSH_MORE) 97 | rv = 0; 98 | else { 99 | ctx->res.finished = true; 100 | ctx->detect->nctx_finished++; 101 | /* 102 | * On parse error, ctx->res.parse_error is already set by 103 | * pt_parser_error(). 104 | */ 105 | if (ctx->detect->finish_cb != NULL) 106 | rv = ctx->detect->finish_cb( 107 | ctx->detect, ctx->ctxnum, ctx->detect->nctx - ctx->detect->nctx_finished, 108 | ctx->detect->finish_cb_arg); 109 | else 110 | rv = 0; 111 | } 112 | return (rv); 113 | } 114 | 115 | static int 116 | detect_pt_start(struct detect *detect) 117 | { 118 | unsigned i; 119 | 120 | for (i = 0; i < detect->nctx; i++) { 121 | struct pt_detect_ctx *ctx = (void *)detect->ctxs[i]; 122 | 123 | if (ctx->res.finished) 124 | continue; 125 | ctx->pstate = pt_parser_pstate_new(); 126 | pt_lexer_init(&ctx->lexer); 127 | if (detect_pt_push_token(ctx, pt_ctxs[ctx->type].start_tok, NULL) != 0) 128 | break; 129 | } 130 | return (0); 131 | } 132 | 133 | static int 134 | detect_pt_stop(struct detect *detect) 135 | { 136 | unsigned i; 137 | 138 | for (i = 0; i < detect->nctx; i++) { 139 | struct pt_detect_ctx *ctx = (void *)detect->ctxs[i]; 140 | 141 | if (ctx->pstate == NULL) 142 | continue; 143 | 144 | /* We have to finish bison state machine or memleaks will occur*/ 145 | if (!ctx->res.finished) 146 | detect_pt_push_token(ctx, 0, NULL); 147 | 148 | pt_parser_pstate_delete(ctx->pstate); 149 | ctx->pstate = NULL; 150 | pt_lexer_deinit(&ctx->lexer); 151 | detect_ctx_result_deinit(&ctx->res); 152 | } 153 | return (0); 154 | } 155 | 156 | static int 157 | pt_lexer_add_data(struct pt_detect_ctx *ctx, const void *data, size_t siz, bool fin) 158 | { 159 | return (detect_re2c_add_data(&ctx->lexer.re2c, data, siz, fin)); 160 | } 161 | 162 | static int 163 | detect_pt_add_data(struct detect *detect, const void *data, size_t siz, bool fin) 164 | { 165 | unsigned i; 166 | union PT_PARSER_STYPE token_arg; 167 | int rv = 0; 168 | 169 | for (i = 0; i < detect->nctx; i++) { 170 | struct pt_detect_ctx *ctx = (void *)detect->ctxs[i]; 171 | int token; 172 | 173 | if (ctx->res.finished) 174 | continue; 175 | pt_lexer_add_data(ctx, data, siz, fin); 176 | do { 177 | memset(&token_arg, 0, sizeof(token_arg)); 178 | token = pt_get_token(ctx, &token_arg); 179 | 180 | if (token >= 0) { 181 | if ((rv = detect_pt_push_token(ctx, token, &token_arg)) != 0) 182 | goto done; 183 | } else if (token != -EAGAIN) { 184 | /* fatal error */ 185 | rv = -token; 186 | goto done; 187 | } 188 | } while (!ctx->res.finished && token != -EAGAIN); 189 | } 190 | 191 | done: 192 | return (rv); 193 | } 194 | 195 | static int 196 | pt_store_key(struct pt_detect_ctx *ctx, struct pt_token_arg_data *info) 197 | { 198 | const static struct { 199 | struct detect_str name; 200 | uint32_t flag; 201 | } flagnames[] = { 202 | {.flag = PT_KEY_INSTR, .name = {CSTR_LEN("INSTR")}}, 203 | }; 204 | unsigned i; 205 | 206 | for (i = 0; i < sizeof(flagnames) / sizeof(flagnames[0]); i++) { 207 | if (!(info->flags & flagnames[i].flag)) 208 | continue; 209 | switch (info->tok) { 210 | case TOK_SEP: 211 | detect_ctx_result_store_token( 212 | &ctx->res, &flagnames[i].name, &(struct detect_str){CSTR_LEN("sep")}); 213 | break; 214 | case TOK_TRAV: 215 | detect_ctx_result_store_token( 216 | &ctx->res, &flagnames[i].name, &(struct detect_str){CSTR_LEN("trav")}); 217 | break; 218 | default: 219 | break; 220 | } 221 | } 222 | pt_token_data_destructor(info); 223 | return (0); 224 | } 225 | 226 | int 227 | pt_store_data(struct pt_detect_ctx *ctx, struct pt_token_arg_data *info) 228 | { 229 | switch (info->tok) { 230 | case TOK_NAME: 231 | assert(!!(info->flags & PT_VALUE_NEEDFREE)); 232 | detect_ctx_result_store_data( 233 | &ctx->res, &(struct detect_str){CSTR_LEN("NAME")}, &info->value); 234 | return (0); 235 | case TOK_ROOT: { 236 | char *copy; 237 | 238 | assert(!(info->flags & PT_VALUE_NEEDFREE)); 239 | 240 | copy = malloc(info->value.len); 241 | memcpy(copy, info->value.str, info->value.len); 242 | info->value.str = copy; 243 | 244 | detect_ctx_result_store_data( 245 | &ctx->res, &(struct detect_str){CSTR_LEN("NAME")}, &info->value); 246 | return (0); 247 | } 248 | default: 249 | return (pt_store_key(ctx, info)); 250 | } 251 | } 252 | 253 | struct detect_parser detect_parser_pt = { 254 | .name = {CSTR_LEN("pt")}, 255 | .open = detect_pt_open, 256 | .close = detect_pt_close, 257 | .start = detect_pt_start, 258 | .stop = detect_pt_stop, 259 | .add_data = detect_pt_add_data, 260 | }; 261 | -------------------------------------------------------------------------------- /lib/pt/pt_lexer.re2c: -------------------------------------------------------------------------------- 1 | #include "pt.h" 2 | #include "pt_lexer.h" 3 | 4 | /*!max:re2c */ 5 | 6 | static int 7 | pt_name_end(struct pt_detect_ctx *ctx, union PT_PARSER_STYPE *arg) 8 | { 9 | arg->data.value = ctx->lexer.buf.data; 10 | arg->data.tok = TOK_NAME; 11 | arg->data.flags = PT_VALUE_NEEDFREE; 12 | detect_buf_reinit(&ctx->lexer.buf); 13 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 14 | return (TOK_NAME); 15 | } 16 | 17 | static int 18 | pt_root(struct pt_detect_ctx *ctx, union PT_PARSER_STYPE *arg, 19 | const char *name, size_t len) 20 | { 21 | arg->data.value.str = (void *)name; 22 | arg->data.value.len = len; 23 | arg->data.tok = TOK_ROOT; 24 | arg->data.flags = 0; 25 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 26 | return (TOK_ROOT); 27 | } 28 | 29 | int 30 | pt_get_token( 31 | struct pt_detect_ctx *ctx, union PT_PARSER_STYPE *arg) 32 | { 33 | const unsigned char *end; 34 | unsigned char yych/*, yyaccept*/; 35 | int rv; 36 | 37 | switch (ctx->pending) { 38 | case PT_PENDING_SEP: 39 | ctx->pending = 0; 40 | arg->data.tok = TOK_SEP; 41 | arg->data.flags = PT_KEY_INSTR; 42 | return (TOK_SEP); 43 | case PT_PENDING_END: 44 | ctx->pending = 0; 45 | return (0); 46 | default: 47 | break; 48 | } 49 | 50 | if ((rv = detect_re2c_prepare_input(&ctx->lexer.re2c, &end, YYMAXFILL)) != 0) 51 | return (-rv); 52 | 53 | #define YYGETSTATE() ctx->lexer.state 54 | #define YYSETSTATE(st) \ 55 | ({ \ 56 | ctx->lexer.state = (st); \ 57 | }) 58 | #define YYGETCONDITION() ctx->lexer.condition 59 | #define YYSETCONDITION(cond) ({ctx->lexer.condition = (cond);}) 60 | #define CSTR(cstr) cstr, sizeof(cstr) - 1 61 | 62 | /*!re2c 63 | re2c:define:YYCTYPE = "DETECT_RE2C_YYCTYPE"; 64 | re2c:define:YYCURSOR = DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c); 65 | re2c:define:YYMARKER = DETECT_RE2C_YYMARKER(&ctx->lexer.re2c); 66 | re2c:define:YYCTXMARKER = DETECT_RE2C_YYCTXMARKER(&ctx->lexer.re2c); 67 | re2c:define:YYLIMIT = end; 68 | re2c:define:YYCONDTYPE = PT_LEXER_CONDTYPE; 69 | re2c:define:YYFILL = "DETECT_RE2C_YYFILL(&ctx->lexer.re2c, &end, @@, YYMAXFILL);"; 70 | re2c:define:YYFILL@len = @@ ; 71 | re2c:define:YYFILL:naked = 1; 72 | re2c:yyfill:enable = 1; 73 | re2c:yyfill:check = 1; 74 | re2c:yych:conversion = 0; 75 | re2c:indent:top = 1; 76 | re2c:variable:yych = yych; 77 | re2c:variable:yyaccept = yyaccept; 78 | re2c:condprefix = pt_; 79 | re2c:condenumprefix = pt_; 80 | 81 | dot = "."|"\xc0\xae"; 82 | dir_sep = [/\\]|"\xc0\xaf"|"\xc1\x9c"; 83 | dir_sep_end = [\x00]|dir_sep; 84 | 85 | <> { 86 | YYSETCONDITION(pt_INITIAL); 87 | goto pt_INITIAL; 88 | } 89 | dir_sep => INITIAL { 90 | YYSETSTATE(-1); 91 | ctx->pending = PT_PENDING_SEP; 92 | return (pt_name_end(ctx, arg)); 93 | } 94 | [\x00] => INITIAL { 95 | YYSETSTATE(-1); 96 | ctx->pending = PT_PENDING_END; 97 | return (pt_name_end(ctx, arg)); 98 | } 99 | [^] { 100 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 101 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 102 | goto pt_NAME; 103 | } 104 | dir_sep { 105 | arg->data.tok = TOK_SEP; 106 | arg->data.flags = PT_KEY_INSTR; 107 | return (TOK_SEP); 108 | } 109 | (dot){2,3}/dir_sep_end { 110 | arg->data.tok = TOK_TRAV; 111 | arg->data.flags = PT_KEY_INSTR; 112 | return (TOK_TRAV); 113 | } 114 | "bin"/dir_sep_end { 115 | return (pt_root(ctx, arg, CSTR("bin"))); 116 | } 117 | "boot"/dir_sep_end { 118 | return (pt_root(ctx, arg, CSTR("boot"))); 119 | } 120 | "cdrom"/dir_sep_end { 121 | return (pt_root(ctx, arg, CSTR("cdrom"))); 122 | } 123 | "dev"/dir_sep_end { 124 | return (pt_root(ctx, arg, CSTR("dev"))); 125 | } 126 | "devices"/dir_sep_end { 127 | return (pt_root(ctx, arg, CSTR("devices"))); 128 | } 129 | "etc"/dir_sep_end { 130 | return (pt_root(ctx, arg, CSTR("etc"))); 131 | } 132 | "export"/dir_sep_end { 133 | return (pt_root(ctx, arg, CSTR("export"))); 134 | } 135 | "home"/dir_sep_end { 136 | return (pt_root(ctx, arg, CSTR("home"))); 137 | } 138 | "kern"/dir_sep_end { 139 | return (pt_root(ctx, arg, CSTR("kern"))); 140 | } 141 | "kernel"/dir_sep_end { 142 | return (pt_root(ctx, arg, CSTR("kernel"))); 143 | } 144 | "lib"/dir_sep_end { 145 | return (pt_root(ctx, arg, CSTR("lib"))); 146 | } 147 | "lib64"/dir_sep_end { 148 | return (pt_root(ctx, arg, CSTR("lib64"))); 149 | } 150 | "libdata"/dir_sep_end { 151 | return (pt_root(ctx, arg, CSTR("libdata"))); 152 | } 153 | "libexec"/dir_sep_end { 154 | return (pt_root(ctx, arg, CSTR("libexec"))); 155 | } 156 | "lost+found"/dir_sep_end { 157 | return (pt_root(ctx, arg, CSTR("lost+found"))); 158 | } 159 | "media"/dir_sep_end { 160 | return (pt_root(ctx, arg, CSTR("media"))); 161 | } 162 | "mnt"/dir_sep_end { 163 | return (pt_root(ctx, arg, CSTR("mnt"))); 164 | } 165 | "net"/dir_sep_end { 166 | return (pt_root(ctx, arg, CSTR("net"))); 167 | } 168 | "opt"/dir_sep_end { 169 | return (pt_root(ctx, arg, CSTR("opt"))); 170 | } 171 | "platform"/dir_sep_end { 172 | return (pt_root(ctx, arg, CSTR("platform"))); 173 | } 174 | "proc"/dir_sep_end { 175 | return (pt_root(ctx, arg, CSTR("proc"))); 176 | } 177 | "rescue"/dir_sep_end { 178 | return (pt_root(ctx, arg, CSTR("rescue"))); 179 | } 180 | "root"/dir_sep_end { 181 | return (pt_root(ctx, arg, CSTR("root"))); 182 | } 183 | "run"/dir_sep_end { 184 | return (pt_root(ctx, arg, CSTR("run"))); 185 | } 186 | "sbin"/dir_sep_end { 187 | return (pt_root(ctx, arg, CSTR("sbin"))); 188 | } 189 | "selinux"/dir_sep_end { 190 | return (pt_root(ctx, arg, CSTR("selinux"))); 191 | } 192 | ".snap"/dir_sep_end { 193 | return (pt_root(ctx, arg, CSTR(".snap"))); 194 | } 195 | ".snapshots"/dir_sep_end { 196 | return (pt_root(ctx, arg, CSTR(".snapshots"))); 197 | } 198 | "srv"/dir_sep_end { 199 | return (pt_root(ctx, arg, CSTR("srv"))); 200 | } 201 | "stand"/dir_sep_end { 202 | return (pt_root(ctx, arg, CSTR("stand"))); 203 | } 204 | "sys"/dir_sep_end { 205 | return (pt_root(ctx, arg, CSTR("sys"))); 206 | } 207 | "system"/dir_sep_end { 208 | return (pt_root(ctx, arg, CSTR("system"))); 209 | } 210 | "tmp"/dir_sep_end { 211 | return (pt_root(ctx, arg, CSTR("tmp"))); 212 | } 213 | "usr"/dir_sep_end { 214 | return (pt_root(ctx, arg, CSTR("usr"))); 215 | } 216 | "var"/dir_sep_end { 217 | return (pt_root(ctx, arg, CSTR("var"))); 218 | } 219 | "boot.ini"/dir_sep_end { 220 | return (pt_root(ctx, arg, CSTR("boot.ini"))); 221 | } 222 | [\x00] { return (0); } 223 | [^] => NAME { 224 | detect_buf_init(&ctx->lexer.buf, 32, 1024); 225 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 226 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 227 | goto pt_NAME; 228 | } 229 | */ 230 | } 231 | -------------------------------------------------------------------------------- /lib/detect.c: -------------------------------------------------------------------------------- 1 | #include "detect_int.h" 2 | #include 3 | #include 4 | #include 5 | 6 | static bool detect_initialized = false; 7 | 8 | #define DETECT_TOKEN_STAT2KEY(stat) (&(stat)->token_name) 9 | WRB_GENERATE( 10 | detect_token_stat_tree, detect_token_stat, struct detect_str *, link, detect_str_cmp, 11 | DETECT_TOKEN_STAT2KEY); 12 | 13 | #define DETECT_FLAG_STAT2KEY(stat) (&(stat)->flag_name) 14 | WRB_GENERATE( 15 | detect_flag_stat_tree, detect_flag_stat, struct detect_str *, link, detect_str_cmp, 16 | DETECT_FLAG_STAT2KEY); 17 | 18 | static int 19 | s_detect_deinit(void) 20 | { 21 | detect_parser_deinit(); 22 | return (0); 23 | } 24 | 25 | int 26 | detect_init(void) 27 | { 28 | int rc; 29 | 30 | if (detect_initialized) 31 | return (0); 32 | 33 | if (!!(rc = detect_parser_init())) { 34 | s_detect_deinit(); 35 | return (rc); 36 | } 37 | detect_initialized = true; 38 | return (0); 39 | } 40 | 41 | int 42 | detect_deinit(void) 43 | { 44 | if (!detect_initialized) 45 | return (0); 46 | s_detect_deinit(); 47 | detect_initialized = false; 48 | return (0); 49 | } 50 | 51 | struct detect * 52 | detect_open(const char *parser_name) 53 | { 54 | struct detect_str name = {.str = (char *)parser_name, .len = strlen(parser_name)}; 55 | struct detect_parser *parser; 56 | 57 | if ((parser = detect_parser_find(&name)) == NULL) 58 | return (NULL); 59 | 60 | return (parser->open(parser)); 61 | } 62 | 63 | const struct detect_str * 64 | detect_name(struct detect *detect) 65 | { 66 | return (&detect->parser->name); 67 | } 68 | 69 | int 70 | detect_close(struct detect *detect) 71 | { 72 | if (detect->started) 73 | detect_stop(detect); 74 | return (detect->parser->close(detect)); 75 | } 76 | 77 | int 78 | detect_set_options(struct detect *detect, const char *options) 79 | { 80 | if (detect->started) 81 | return (EINVAL); 82 | 83 | if (detect->parser->set_options != NULL) 84 | return (detect->parser->set_options(detect, options)); 85 | else 86 | return (0); 87 | } 88 | 89 | int 90 | detect_set_finish_cb(struct detect *detect, detect_finish_cb cb, void *arg) 91 | { 92 | detect->finish_cb = cb; 93 | detect->finish_cb_arg = arg; 94 | 95 | return (0); 96 | } 97 | 98 | int 99 | detect_start(struct detect *detect) 100 | { 101 | unsigned i; 102 | int rc; 103 | 104 | if (detect->started) 105 | return (EINVAL); 106 | 107 | for (i = 0; i < detect->nctx; i++) 108 | detect->ctxs[i]->res->finished = detect->ctxs[i]->res->disabled; 109 | if (!!(rc = detect->parser->start(detect))) 110 | return (rc); 111 | 112 | detect->started = true; 113 | return (0); 114 | } 115 | 116 | int 117 | detect_stop(struct detect *detect) 118 | { 119 | int rc; 120 | 121 | if (!detect->started) 122 | return (EINVAL); 123 | 124 | if (!!(rc = detect->parser->stop(detect))) 125 | return (rc); 126 | 127 | detect->started = false; 128 | return (0); 129 | } 130 | 131 | unsigned 132 | detect_get_nctx(struct detect *detect) 133 | { 134 | return (detect->nctx); 135 | } 136 | 137 | const struct detect_ctx_desc * 138 | detect_ctx_get_desc(struct detect *detect, unsigned ctxnum) 139 | { 140 | if (ctxnum >= detect->nctx) { 141 | errno = EINVAL; 142 | return (NULL); 143 | } 144 | return (detect->ctxs[ctxnum]->desc); 145 | } 146 | 147 | int 148 | detect_ctx_disable(struct detect *detect, unsigned ctxnum) 149 | { 150 | if (detect->started && ctxnum >= detect->nctx) 151 | return (EINVAL); 152 | detect->ctxs[ctxnum]->res->disabled = true; 153 | return (0); 154 | } 155 | 156 | const struct detect_ctx_result * 157 | detect_ctx_get_result(struct detect *detect, unsigned ctxnum) 158 | { 159 | if (ctxnum >= detect->nctx) { 160 | errno = EINVAL; 161 | return (NULL); 162 | } 163 | return (detect->ctxs[ctxnum]->res); 164 | } 165 | 166 | int 167 | detect_add_data(struct detect *detect, const void *data, size_t siz, bool fin) 168 | { 169 | if (!detect->started) 170 | return (EINVAL); 171 | if (!siz && !fin) 172 | return (0); 173 | return (detect->parser->add_data(detect, data, siz, fin)); 174 | } 175 | 176 | int 177 | detect_instance_init(struct detect *detect, struct detect_parser *parser) 178 | { 179 | memset(detect, 0, sizeof(*detect)); 180 | detect->parser = parser; 181 | 182 | return (0); 183 | } 184 | 185 | int 186 | detect_ctx_result_init(struct detect_ctx_result *res) 187 | { 188 | memset(res, 0, sizeof(*res)); 189 | RB_INIT(&res->stat_by_flags); 190 | STAILQ_INIT(&res->datas); 191 | return (0); 192 | } 193 | 194 | int 195 | detect_ctx_result_deinit(struct detect_ctx_result *res) 196 | { 197 | struct detect_data *data; 198 | struct detect_flag_stat *fs, *fs_tmp; 199 | 200 | WRB_FOREACH_PDFS (fs, detect_flag_stat_tree, &res->stat_by_flags, fs_tmp) { 201 | struct detect_token_stat *ts, *ts_tmp; 202 | 203 | WRB_FOREACH_PDFS (ts, detect_token_stat_tree, &fs->stat_by_tokens, ts_tmp) 204 | free(ts); 205 | free(fs); 206 | } 207 | RB_INIT(&res->stat_by_flags); 208 | while ((data = STAILQ_FIRST(&res->datas)) != NULL) { 209 | STAILQ_REMOVE_HEAD(&res->datas, link); 210 | free(data->value.str); 211 | free(data); 212 | } 213 | res->finished = 0; 214 | res->parse_error = 0; 215 | return (0); 216 | } 217 | 218 | int 219 | detect_ctx_result_store_token( 220 | struct detect_ctx_result *res, const struct detect_str *flag, const struct detect_str *name) 221 | { 222 | struct detect_flag_stat *fs; 223 | struct detect_token_stat *ts; 224 | 225 | if ((fs = WRB_FIND(detect_flag_stat_tree, &res->stat_by_flags, flag)) != NULL) { 226 | fs->count++; 227 | } else { 228 | fs = calloc(1, sizeof(*fs) + flag->len + 1); 229 | 230 | fs->flag_name.str = (void *)(fs + 1); 231 | memcpy(fs->flag_name.str, flag->str, flag->len); 232 | fs->flag_name.len = flag->len; 233 | fs->flag_name.str[fs->flag_name.len] = 0; 234 | fs->count = 1; 235 | RB_INIT(&fs->stat_by_tokens); 236 | RB_INSERT(detect_flag_stat_tree, &res->stat_by_flags, fs); 237 | } 238 | if ((ts = WRB_FIND(detect_token_stat_tree, &fs->stat_by_tokens, name)) != NULL) { 239 | ts->count++; 240 | } else { 241 | ts = calloc(1, sizeof(*ts) + name->len + 1); 242 | 243 | ts->token_name.str = (void *)(ts + 1); 244 | memcpy(ts->token_name.str, name->str, name->len); 245 | ts->token_name.len = name->len; 246 | ts->token_name.str[ts->token_name.len] = 0; 247 | ts->count++; 248 | RB_INSERT(detect_token_stat_tree, &fs->stat_by_tokens, ts); 249 | } 250 | return (0); 251 | } 252 | 253 | int 254 | detect_ctx_result_store_data( 255 | struct detect_ctx_result *res, const struct detect_str *kind, struct detect_str *value) 256 | { 257 | struct detect_data *dd; 258 | 259 | dd = malloc(sizeof(*dd) + kind->len + 1); 260 | dd->kind.str = (void *)(dd + 1); 261 | memcpy(dd->kind.str, kind->str, kind->len); 262 | dd->kind.len = kind->len; 263 | dd->kind.str[dd->kind.len] = 0; 264 | dd->value = *value; 265 | 266 | STAILQ_INSERT_TAIL(&res->datas, dd, link); 267 | 268 | return (0); 269 | } 270 | 271 | bool 272 | detect_ctx_has_attack(struct detect *detect, unsigned ctxnum) 273 | { 274 | struct detect_ctx *ctx; 275 | 276 | if (ctxnum >= detect->nctx) 277 | return (false); 278 | ctx = detect->ctxs[ctxnum]; 279 | if (ctx->res->disabled || !ctx->res->finished) 280 | return (false); 281 | if (ctx->res->parse_error) 282 | return (false); 283 | if (ctx->desc->rce) 284 | return (true); 285 | /* 286 | * Injection. At least one instruction should present. 287 | * Just test if instruction tree is not empty. 288 | */ 289 | if (!RB_EMPTY(&ctx->res->stat_by_flags)) 290 | return (true); 291 | return (false); 292 | } 293 | 294 | bool 295 | detect_has_attack(struct detect *detect, uint32_t *attack_types) 296 | { 297 | unsigned i; 298 | 299 | *attack_types = 0; 300 | for (i = 0; i < detect->nctx; i++) { 301 | struct detect_ctx *ctx; 302 | 303 | if (!detect_ctx_has_attack(detect, i)) 304 | continue; 305 | 306 | ctx = detect->ctxs[i]; 307 | if (ctx->desc->rce) 308 | (*attack_types) |= DETECT_ATTACK_RCE; 309 | else 310 | (*attack_types) |= DETECT_ATTACK_INJ; 311 | } 312 | 313 | return (!!(*attack_types)); 314 | } 315 | -------------------------------------------------------------------------------- /perf/perf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define ERR(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) 10 | 11 | #define MAXLINE 4096 12 | #define MAX_DISABLED_CTXS 8 13 | 14 | enum perf_action { 15 | PERF_ACTION_PARSE = 0, 16 | PERF_ACTION_LIST_PARSERS, 17 | }; 18 | 19 | static void 20 | s_perf_dump_result(struct detect *detect) 21 | { 22 | unsigned nctx, i; 23 | 24 | nctx = detect_get_nctx(detect); 25 | for (i = 0; i < nctx; i++) { 26 | const struct detect_ctx_desc *desc; 27 | struct detect_ctx_result *res; 28 | 29 | desc = detect_ctx_get_desc(detect, i); 30 | res = (typeof(res))detect_ctx_get_result(detect, i); 31 | printf( 32 | "[%s]%.*s:%s\n", desc->rce ? "rce" : "inj", (int)desc->name.len, desc->name.str, 33 | res->parse_error ? "error:" : ""); 34 | if (!RB_EMPTY(&res->stat_by_flags)) { 35 | struct detect_flag_stat *fs; 36 | 37 | printf(" flags:\n"); 38 | RB_FOREACH (fs, detect_flag_stat_tree, &res->stat_by_flags) { 39 | struct detect_token_stat *ts; 40 | 41 | printf(" %.*s:%zu:\n ", (int)fs->flag_name.len, fs->flag_name.str, fs->count); 42 | 43 | RB_FOREACH (ts, detect_token_stat_tree, &fs->stat_by_tokens) { 44 | printf(" %.*s:%zu", (int)ts->token_name.len, ts->token_name.str, ts->count); 45 | } 46 | printf("\n"); 47 | } 48 | } 49 | if (!STAILQ_EMPTY(&res->datas)) { 50 | struct detect_data *dd; 51 | 52 | printf(" datas:\n"); 53 | STAILQ_FOREACH (dd, &res->datas, link) { 54 | printf( 55 | " kind:%.*s value:%.*s\n", (int)dd->kind.len, dd->kind.str, (int)dd->value.len, 56 | dd->value.str); 57 | } 58 | } 59 | } 60 | } 61 | 62 | static void 63 | s_report_attack(uint64_t cstr, bool has_attack, uint32_t attack_types) 64 | { 65 | if (!has_attack) { 66 | printf("Test %" PRIu64 ": clear\n", cstr + 1); 67 | return; 68 | } 69 | printf("Test %" PRIu64 ":", cstr + 1); 70 | if (!!(attack_types & DETECT_ATTACK_INJ)) 71 | printf(" inj"); 72 | if (!!(attack_types & DETECT_ATTACK_RCE)) 73 | printf(" rce"); 74 | printf("\n"); 75 | } 76 | 77 | static void 78 | perf_usage(const char *progname) 79 | { 80 | fprintf( 81 | stderr, 82 | "Usage:\n" 83 | "\t%s [-Pehnrv] [-p parser] [-d disabled_context]\n" 84 | "\n" 85 | "\t-h Show this help message\n" 86 | "\t-v Increase verbose level\n" 87 | "\t-P List available parsers and exit\n" 88 | "\t-p Parser to use (default is sqli)\n" 89 | "\t-d Context number to disable\n" 90 | "\t-e Echo input strings\n" 91 | "\t (verbose=0: attacks only, verbose>0: all)\n" 92 | "\t-r Show small report for each input string\n" 93 | "\t-n Show total number of strings and attacks\n", 94 | progname); 95 | } 96 | 97 | static int 98 | perf_list_parsers(void) 99 | { 100 | void *list; 101 | const struct detect_str *name; 102 | 103 | printf("Parsers available:\n"); 104 | for (list = detect_parser_list(&name); list; list = detect_parser_list_next(list, &name)) { 105 | struct detect *detect; 106 | 107 | printf("%.*s\n", (int)name->len, name->str); 108 | if ((detect = detect_open(name->str)) != NULL) { 109 | unsigned i, nctx; 110 | 111 | nctx = detect_get_nctx(detect); 112 | for (i = 0; i != nctx; i++) { 113 | const struct detect_ctx_desc *ctx; 114 | 115 | ctx = detect_ctx_get_desc(detect, i); 116 | printf( 117 | "\tContext %u: %.*s (%s)\n", i, (int)ctx->name.len, ctx->name.str, 118 | ctx->rce ? "rce" : "inj"); 119 | } 120 | } 121 | } 122 | return (EXIT_SUCCESS); 123 | } 124 | 125 | int 126 | main(int argc, char **argv) 127 | { 128 | struct detect *detect; 129 | char *progname; 130 | char buf[MAXLINE]; 131 | unsigned disabled_ctxs[MAX_DISABLED_CTXS]; 132 | unsigned n_disabled_ctxs = 0, ictx; 133 | char parser[32] = "sqli"; 134 | size_t len; 135 | int argval; 136 | unsigned verbose = 0; 137 | enum perf_action action = PERF_ACTION_PARSE; 138 | bool print_nstr = false; 139 | bool report_attacks = false; 140 | bool echo = false; 141 | bool detect_initialized = false; 142 | uint64_t nstr, nattacks; 143 | int rc = EXIT_SUCCESS; 144 | 145 | if ((progname = strrchr(argv[0], '/')) != NULL) 146 | progname++; 147 | else 148 | progname = argv[0]; 149 | progname = strdup(progname); 150 | 151 | while ((argval = getopt(argc, argv, "Pp:ehnrvd:")) != EOF) { 152 | switch (argval) { 153 | case 'P': 154 | action = PERF_ACTION_LIST_PARSERS; 155 | break; 156 | case 'p': 157 | snprintf(parser, sizeof(parser), "%s", optarg); 158 | break; 159 | case 'd': { 160 | unsigned ctx = atoi(optarg); 161 | 162 | for (ictx = 0; ictx != n_disabled_ctxs; ictx++) 163 | if (disabled_ctxs[ictx] == ctx) 164 | break; 165 | if (ictx == n_disabled_ctxs) { 166 | if (n_disabled_ctxs == MAX_DISABLED_CTXS) { 167 | ERR("Too many disabled contexts specified"); 168 | rc = EXIT_FAILURE; 169 | goto done; 170 | } 171 | disabled_ctxs[n_disabled_ctxs++] = ctx; 172 | } 173 | break; 174 | } 175 | case 'e': 176 | echo = true; 177 | break; 178 | case 'n': 179 | print_nstr = true; 180 | break; 181 | case 'v': 182 | verbose++; 183 | break; 184 | case 'r': 185 | report_attacks = true; 186 | break; 187 | default: 188 | rc = EXIT_FAILURE; 189 | /* FALLTHROUGH */ 190 | case 'h': 191 | perf_usage(progname); 192 | goto done; 193 | } 194 | } 195 | if (detect_init()) { 196 | ERR("Cannot initialize detect"); 197 | rc = EXIT_FAILURE; 198 | goto done; 199 | } 200 | detect_initialized = true; 201 | 202 | switch (action) { 203 | case PERF_ACTION_LIST_PARSERS: 204 | rc = perf_list_parsers(); 205 | goto done; 206 | default: 207 | break; 208 | } 209 | 210 | if ((detect = detect_open(parser)) == NULL) { 211 | ERR("Cannot open %s parser", parser); 212 | rc = EXIT_FAILURE; 213 | goto done; 214 | } 215 | for (ictx = 0; ictx != n_disabled_ctxs; ictx++) { 216 | const struct detect_ctx_desc *ctx; 217 | int rv; 218 | 219 | ctx = detect_ctx_get_desc(detect, disabled_ctxs[ictx]); 220 | if (ctx == NULL) { 221 | ERR("Cannot get context[%u] description: %s", disabled_ctxs[ictx], strerror(errno)); 222 | rc = EXIT_FAILURE; 223 | goto done; 224 | } 225 | if (!!(rv = detect_ctx_disable(detect, disabled_ctxs[ictx]))) { 226 | fprintf( 227 | stderr, "Cannot disable context %u[%.*s]: %s\n", disabled_ctxs[ictx], 228 | (int)ctx->name.len, ctx->name.str, strerror(rv)); 229 | rc = EXIT_FAILURE; 230 | goto done; 231 | } 232 | } 233 | nstr = 0; 234 | nattacks = 0; 235 | while (fgets(buf, sizeof(buf), stdin) != NULL) { 236 | bool has_attack; 237 | bool multiline; 238 | uint32_t attack_types; 239 | 240 | len = strlen(buf); 241 | if (len > 0 && buf[len - 1] == '\n') { 242 | --len; 243 | if (len > 0 && buf[len - 1] == '\r') 244 | --len; 245 | } 246 | /* skip comment lines */ 247 | if (len != 0 && buf[0] == '#') 248 | continue; 249 | multiline = (len != 0 && buf[len - 1] == '\\'); 250 | if (multiline) 251 | --len; 252 | detect_start(detect); 253 | detect_add_data(detect, buf, len, !multiline); 254 | if (multiline) 255 | continue; 256 | has_attack = detect_has_attack(detect, &attack_types); 257 | if (report_attacks) 258 | s_report_attack(nstr, has_attack, attack_types); 259 | if (echo && (has_attack || verbose > 0)) 260 | printf("%s", buf); 261 | if (has_attack ? verbose > 0 : verbose > 1) 262 | s_perf_dump_result(detect); 263 | detect_stop(detect); 264 | nstr++; 265 | if (has_attack) 266 | nattacks++; 267 | } 268 | detect_close(detect); 269 | detect_deinit(); 270 | if (print_nstr) 271 | fprintf(stderr, "Got %" PRIu64 " strings, %" PRIu64 " attacks\n", nstr, nattacks); 272 | done: 273 | if (detect_initialized) 274 | detect_deinit(); 275 | free(progname); 276 | return (rc); 277 | } 278 | -------------------------------------------------------------------------------- /lib/sqli/sqli.c: -------------------------------------------------------------------------------- 1 | #include "sqli.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static const struct { 9 | struct detect_ctx_desc desc; 10 | enum sqli_parser_tokentype start_tok; 11 | bool var_start_with_num; 12 | } sqli_ctxs[] = { 13 | // clang-format off 14 | [SQLI_CTX_DATA] = { 15 | .desc = {.name = {CSTR_LEN("data")}}, 16 | .start_tok = TOK_START_DATA, 17 | .var_start_with_num = false, 18 | }, 19 | [SQLI_CTX_IN_STRING] = { 20 | .desc = {.name = {CSTR_LEN("str")}}, 21 | .start_tok = TOK_START_STRING, 22 | .var_start_with_num = false, 23 | }, 24 | [SQLI_CTX_RCE] = { 25 | .desc = {.name = {CSTR_LEN("rce")}, .rce = true}, 26 | .start_tok = TOK_START_RCE, 27 | .var_start_with_num = false, 28 | }, 29 | [SQLI_CTX_DATA_VAR_START_WITH_NUM] = { 30 | .desc = {.name = {CSTR_LEN("data_num")}}, 31 | .start_tok = TOK_START_DATA, 32 | .var_start_with_num = true, 33 | }, 34 | [SQLI_CTX_IN_STRING_VAR_START_WITH_NUM] = { 35 | .desc = {.name = {CSTR_LEN("str_num")}}, 36 | .start_tok = TOK_START_STRING, 37 | .var_start_with_num = true, 38 | }, 39 | [SQLI_CTX_RCE_VAR_START_WITH_NUM] = { 40 | .desc = {.name = {CSTR_LEN("rce_num")}, .rce = true}, 41 | .start_tok = TOK_START_RCE, 42 | .var_start_with_num = true, 43 | }, 44 | }; 45 | // clang-format on 46 | 47 | static struct detect * 48 | detect_sqli_open(struct detect_parser *parser) 49 | { 50 | struct detect *detect; 51 | unsigned i; 52 | 53 | detect = malloc(sizeof(*detect)); 54 | detect_instance_init(detect, parser); 55 | 56 | detect->nctx = SQLI_CTX_LAST; 57 | detect->ctxs = malloc(detect->nctx * sizeof(*detect->ctxs)); 58 | for (i = 0; i < detect->nctx; i++) { 59 | struct sqli_detect_ctx *ctx; 60 | 61 | ctx = calloc(1, sizeof(*ctx)); 62 | ctx->base.desc = (struct detect_ctx_desc *)&sqli_ctxs[i].desc; 63 | ctx->base.res = &ctx->res; 64 | detect_ctx_result_init(ctx->base.res); 65 | ctx->type = i; 66 | ctx->ctxnum = i; 67 | ctx->detect = detect; 68 | ctx->var_start_with_num = sqli_ctxs[i].var_start_with_num; 69 | detect->ctxs[i] = (void *)ctx; 70 | } 71 | 72 | return (detect); 73 | } 74 | 75 | static int 76 | detect_sqli_close(struct detect *detect) 77 | { 78 | unsigned i; 79 | 80 | for (i = 0; i < detect->nctx; i++) { 81 | struct sqli_detect_ctx *ctx = (void *)detect->ctxs[i]; 82 | 83 | detect_ctx_result_deinit(ctx->base.res); 84 | if (ctx->pstate != NULL) 85 | sqli_parser_pstate_delete(ctx->pstate); 86 | free(ctx); 87 | } 88 | if (detect->ctxs != NULL) 89 | free(detect->ctxs); 90 | free(detect); 91 | 92 | return (0); 93 | } 94 | 95 | static int 96 | detect_sqli_push_token(struct sqli_detect_ctx *ctx, int tok, void *tok_val) 97 | { 98 | int rv; 99 | 100 | if (ctx->res.finished) 101 | return (0); 102 | 103 | rv = sqli_parser_push_parse(ctx->pstate, tok, tok_val, ctx); 104 | if (rv == YYPUSH_MORE) 105 | rv = 0; 106 | else { 107 | ctx->res.finished = true; 108 | ctx->detect->nctx_finished++; 109 | /* 110 | * On parse error, ctx->res.parse_error is already set by 111 | * sqli_parser_error(). 112 | */ 113 | if (ctx->detect->finish_cb != NULL) 114 | rv = ctx->detect->finish_cb( 115 | ctx->detect, ctx->ctxnum, ctx->detect->nctx - ctx->detect->nctx_finished, 116 | ctx->detect->finish_cb_arg); 117 | else 118 | rv = 0; 119 | } 120 | return (rv); 121 | } 122 | 123 | static void 124 | sqli_lexer_init(struct sqli_detect_lexer_ctx *lexer) 125 | { 126 | memset(lexer, 0, sizeof(*lexer)); 127 | detect_re2c_init(&lexer->re2c); 128 | lexer->state = -1; 129 | } 130 | 131 | static void 132 | sqli_lexer_deinit(struct sqli_detect_lexer_ctx *lexer) 133 | { 134 | detect_buf_deinit(&lexer->buf); 135 | while (1) { 136 | struct sqli_pending_token *token = lexer->pending + lexer->pending_first; 137 | 138 | if (token->tok <= 0) 139 | break; 140 | if (token->destructor != 0) 141 | token->destructor(token); 142 | token->tok = 0; 143 | SQLI_PENDING_SHIFT(lexer->pending_first); 144 | } 145 | lexer->pending_last = lexer->pending_first; 146 | detect_re2c_deinit(&lexer->re2c); 147 | } 148 | 149 | void 150 | sqli_token_data_destructor(void *token) 151 | { 152 | struct sqli_token_arg_data *data = token; 153 | 154 | if (!!(data->flags & SQLI_VALUE_NEEDFREE) && data->value.str != NULL) 155 | free(data->value.str); 156 | data->value.str = NULL; 157 | data->value.len = 0; 158 | } 159 | 160 | static int 161 | detect_sqli_start(struct detect *detect) 162 | { 163 | unsigned i; 164 | 165 | for (i = 0; i < detect->nctx; i++) { 166 | struct sqli_detect_ctx *ctx = (void *)detect->ctxs[i]; 167 | 168 | if (ctx->res.finished) 169 | continue; 170 | 171 | ctx->pstate = sqli_parser_pstate_new(); 172 | sqli_lexer_init(&ctx->lexer); 173 | if (detect_sqli_push_token(ctx, sqli_ctxs[ctx->type].start_tok, NULL) != 0) 174 | break; 175 | } 176 | return (0); 177 | } 178 | 179 | static int 180 | detect_sqli_stop(struct detect *detect) 181 | { 182 | unsigned i; 183 | 184 | for (i = 0; i < detect->nctx; i++) { 185 | struct sqli_detect_ctx *ctx = (void *)detect->ctxs[i]; 186 | 187 | if (ctx->pstate == NULL) 188 | continue; 189 | 190 | if (!ctx->res.finished) 191 | detect_sqli_push_token(ctx, 0, NULL); 192 | sqli_parser_pstate_delete(ctx->pstate); 193 | ctx->pstate = NULL; 194 | ctx->has_any_tokens = false; 195 | sqli_lexer_deinit(&ctx->lexer); 196 | detect_ctx_result_deinit(&ctx->res); 197 | } 198 | return (0); 199 | } 200 | 201 | static int 202 | sqli_lexer_add_data(struct sqli_detect_ctx *ctx, const void *data, size_t siz, bool fin) 203 | { 204 | return (detect_re2c_add_data(&ctx->lexer.re2c, data, siz, fin)); 205 | } 206 | 207 | static int 208 | detect_sqli_add_data(struct detect *detect, const void *data, size_t siz, bool fin) 209 | { 210 | unsigned i; 211 | union SQLI_PARSER_STYPE token_arg; 212 | int rv = 0; 213 | 214 | for (i = 0; i < detect->nctx; i++) { 215 | struct sqli_detect_ctx *ctx = (void *)detect->ctxs[i]; 216 | int token; 217 | 218 | if (ctx->res.finished) 219 | continue; 220 | sqli_lexer_add_data(ctx, data, siz, fin); 221 | do { 222 | memset(&token_arg, 0, sizeof(token_arg)); 223 | token = sqli_get_token(ctx, &token_arg); 224 | 225 | if (token > 0) { 226 | if (token != TOK_ERROR) { 227 | ctx->has_any_tokens = true; 228 | if ((rv = detect_sqli_push_token(ctx, token, &token_arg)) != 0) 229 | goto done; 230 | } 231 | } else if (token < 0 && token != -EAGAIN) { 232 | /* fatal error */ 233 | rv = -token; 234 | goto done; 235 | } 236 | 237 | /* We stop parsing with success on end of data */ 238 | if (!ctx->res.finished) { 239 | if (token == 0 || token == TOK_ERROR || (token == -EAGAIN && fin)) { 240 | /* We push $end to the parser */ 241 | detect_sqli_push_token(ctx, 0, NULL); 242 | ctx->res.finished = true; 243 | /* 244 | * We may clear error here to: 245 | * - ignore unfinished syntax 246 | * - ignore results of reduce/reduce conflicts 247 | */ 248 | ctx->res.parse_error = 249 | (token == TOK_ERROR || RB_EMPTY(&ctx->res.stat_by_flags) 250 | /* || !ctx->has_any_tokens */); 251 | } 252 | } 253 | } while (!ctx->res.finished && token != -EAGAIN); 254 | } 255 | 256 | done: 257 | return (rv); 258 | } 259 | 260 | static int 261 | sqli_store_key(struct sqli_detect_ctx *ctx, struct sqli_token_arg_data *info) 262 | { 263 | const static struct { 264 | struct detect_str name; 265 | uint32_t flag; 266 | } flagnames[] = { 267 | {.flag = SQLI_KEY_READ, .name = {CSTR_LEN("READ")}}, 268 | {.flag = SQLI_KEY_WRITE, .name = {CSTR_LEN("WRITE")}}, 269 | {.flag = SQLI_KEY_INSTR, .name = {CSTR_LEN("INSTR")}}, 270 | }; 271 | unsigned i; 272 | 273 | for (i = 0; i < sizeof(flagnames) / sizeof(flagnames[0]); i++) { 274 | if (!(info->flags & flagnames[i].flag)) 275 | continue; 276 | detect_ctx_result_store_token(&ctx->res, &flagnames[i].name, &info->value); 277 | } 278 | sqli_token_data_destructor(info); 279 | return (0); 280 | } 281 | 282 | int 283 | sqli_store_data(struct sqli_detect_ctx *ctx, struct sqli_token_arg_data *info) 284 | { 285 | switch (info->tok) { 286 | case TOK_NUM: 287 | case TOK_DATA: 288 | detect_ctx_result_store_data( 289 | &ctx->res, &(struct detect_str){CSTR_LEN("DATA")}, &info->value); 290 | return (0); 291 | case TOK_NAME: 292 | detect_ctx_result_store_data( 293 | &ctx->res, &(struct detect_str){CSTR_LEN("NAME")}, &info->value); 294 | return (0); 295 | default: 296 | return (sqli_store_key(ctx, info)); 297 | } 298 | } 299 | 300 | struct detect_parser detect_parser_sqli = { 301 | .name = {CSTR_LEN("sqli")}, 302 | .open = detect_sqli_open, 303 | .close = detect_sqli_close, 304 | .start = detect_sqli_start, 305 | .stop = detect_sqli_stop, 306 | .add_data = detect_sqli_add_data, 307 | }; 308 | -------------------------------------------------------------------------------- /lib/bash/bash_parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "bash.h" 3 | 4 | #if !defined(YYUSE) 5 | #define YYUSE(arg) YY_USE(arg) 6 | #endif 7 | 8 | static void 9 | bash_parser_error(struct bash_detect_ctx *ctx, const char *s) 10 | { 11 | ctx->res.parse_error = true; 12 | } 13 | %} 14 | 15 | %define api.pure full 16 | %define api.push-pull push 17 | %define api.prefix {bash_parser_} 18 | %parse-param {struct bash_detect_ctx *ctx} 19 | %union { 20 | struct bash_token_arg_data data; 21 | } 22 | 23 | %token TOK_START_RCE 24 | %token TOK_START_WORD 25 | %token '<' '>' '-' ';' '(' ')' '|' '&' '\n' '\r' '`' 26 | %token TOK_WORD TOK_NUMBER TOK_IF TOK_THEN TOK_ELSE TOK_ELIF TOK_FI 27 | TOK_CASE TOK_ESAC TOK_FOR TOK_SELECT TOK_WHILE TOK_UNTIL TOK_DO 28 | TOK_DONE TOK_IN TOK_FUNCTION TOK_TIME TOK_TIMEOPT TOK_TIMEIGN 29 | TOK_BEGIN TOK_END TOK_BANG TOK_COND_START TOK_COND_END TOK_COPROC 30 | TOK_ASSIGNMENT_WORD TOK_REDIR_WORD TOK_ARITH_CMD 31 | TOK_ARITH_FOR_EXPRS TOK_COND_CMD TOK_SUBSH_START 32 | %token TOK_LESS_LESS_LESS TOK_LESS_LESS_MINUS TOK_AND_GREATER_GREATER 33 | TOK_SEMI_SEMI_AND TOK_LESS_LESS TOK_LESS_GREATER TOK_LESS_AND 34 | TOK_GREATER_GREATER TOK_GREATER_AND TOK_GREATER_BAR 35 | TOK_AND_GREATER TOK_AND_AND TOK_OR_OR TOK_SEMI_SEMI TOK_SEMI_AND 36 | TOK_BAR_AND 37 | %token TOK_ERROR 38 | 39 | %destructor { 40 | bash_token_data_destructor(&$$); 41 | } 42 | 43 | %% 44 | 45 | context: start_rce 46 | | start_word 47 | ; 48 | 49 | start_rce: TOK_START_RCE inj 50 | ; 51 | 52 | start_word: TOK_START_WORD { 53 | ctx->lexer.inword = true; 54 | } inj 55 | ; 56 | 57 | inj: simple_list 58 | | inj simple_list_terminator 59 | | simple_list_terminator inj 60 | | ';'[u1] inj { 61 | YYUSE($u1); 62 | } 63 | | '|'[u1] inj { 64 | YYUSE($u1); 65 | } 66 | | TOK_OR_OR[u1] inj { 67 | YYUSE($u1); 68 | } 69 | | '&'[u1] inj { 70 | YYUSE($u1); 71 | } 72 | | TOK_AND_AND[u1] inj { 73 | YYUSE($u1); 74 | } 75 | ; 76 | 77 | word_list: TOK_WORD[u1] { 78 | YYUSE($u1); 79 | } 80 | | word_list TOK_WORD[u1] { 81 | YYUSE($u1); 82 | } 83 | ; 84 | 85 | redirection: '>'[u1] TOK_WORD[u2] { 86 | YYUSE($u1); 87 | YYUSE($u2); 88 | } 89 | | '<'[u1] TOK_WORD[u2] { 90 | YYUSE($u1); 91 | YYUSE($u2); 92 | } 93 | | TOK_NUMBER[u1] '>'[u2] TOK_WORD[u3] { 94 | YYUSE($u1); 95 | YYUSE($u2); 96 | YYUSE($u3); 97 | } 98 | | TOK_NUMBER[u1] '<'[u2] TOK_WORD[u3] { 99 | YYUSE($u1); 100 | YYUSE($u2); 101 | YYUSE($u3); 102 | } 103 | | TOK_REDIR_WORD[u1] '>'[u2] TOK_WORD[u3] { 104 | YYUSE($u1); 105 | YYUSE($u2); 106 | YYUSE($u3); 107 | } 108 | | TOK_REDIR_WORD[u1] '<'[u2] TOK_WORD[u3] { 109 | YYUSE($u1); 110 | YYUSE($u2); 111 | YYUSE($u3); 112 | } 113 | | TOK_GREATER_GREATER[u1] TOK_WORD[u2] { 114 | YYUSE($u1); 115 | YYUSE($u2); 116 | } 117 | | TOK_NUMBER[u1] TOK_GREATER_GREATER[u2] TOK_WORD[u3] { 118 | YYUSE($u1); 119 | YYUSE($u2); 120 | YYUSE($u3); 121 | } 122 | | TOK_REDIR_WORD[u1] TOK_GREATER_GREATER[u2] TOK_WORD[u3] { 123 | YYUSE($u1); 124 | YYUSE($u2); 125 | YYUSE($u3); 126 | } 127 | | TOK_GREATER_BAR[u1] TOK_WORD[u2] { 128 | YYUSE($u1); 129 | YYUSE($u2); 130 | } 131 | | TOK_NUMBER[u1] TOK_GREATER_BAR[u2] TOK_WORD[u3] { 132 | YYUSE($u1); 133 | YYUSE($u2); 134 | YYUSE($u3); 135 | } 136 | | TOK_REDIR_WORD[u1] TOK_GREATER_BAR[u2] TOK_WORD[u3] { 137 | YYUSE($u1); 138 | YYUSE($u2); 139 | YYUSE($u3); 140 | } 141 | | TOK_LESS_GREATER[u1] TOK_WORD[u2] { 142 | YYUSE($u1); 143 | YYUSE($u2); 144 | } 145 | | TOK_NUMBER[u1] TOK_LESS_GREATER[u2] TOK_WORD[u3] { 146 | YYUSE($u1); 147 | YYUSE($u2); 148 | YYUSE($u3); 149 | } 150 | | TOK_REDIR_WORD[u1] TOK_LESS_GREATER[u2] TOK_WORD[u3] { 151 | YYUSE($u1); 152 | YYUSE($u2); 153 | YYUSE($u3); 154 | } 155 | | TOK_LESS_LESS[u1] TOK_WORD[u2] { 156 | YYUSE($u1); 157 | YYUSE($u2); 158 | } 159 | | TOK_NUMBER[u1] TOK_LESS_LESS[u2] TOK_WORD[u3] { 160 | YYUSE($u1); 161 | YYUSE($u2); 162 | YYUSE($u3); 163 | } 164 | | TOK_REDIR_WORD[u1] TOK_LESS_LESS[u2] TOK_WORD[u3] { 165 | YYUSE($u1); 166 | YYUSE($u2); 167 | YYUSE($u3); 168 | } 169 | | TOK_LESS_LESS_MINUS[u1] TOK_WORD[u2] { 170 | YYUSE($u1); 171 | YYUSE($u2); 172 | } 173 | | TOK_NUMBER[u1] TOK_LESS_LESS_MINUS[u2] TOK_WORD[u3] { 174 | YYUSE($u1); 175 | YYUSE($u2); 176 | YYUSE($u3); 177 | } 178 | | TOK_REDIR_WORD[u1] TOK_LESS_LESS_MINUS[u2] TOK_WORD[u3] { 179 | YYUSE($u1); 180 | YYUSE($u2); 181 | YYUSE($u3); 182 | } 183 | | TOK_LESS_LESS_LESS[u1] TOK_WORD[u2] { 184 | YYUSE($u1); 185 | YYUSE($u2); 186 | } 187 | | TOK_NUMBER[u1] TOK_LESS_LESS_LESS[u2] TOK_WORD[u3] { 188 | YYUSE($u1); 189 | YYUSE($u2); 190 | YYUSE($u3); 191 | } 192 | | TOK_REDIR_WORD[u1] TOK_LESS_LESS_LESS[u2] TOK_WORD[u3] { 193 | YYUSE($u1); 194 | YYUSE($u2); 195 | YYUSE($u3); 196 | } 197 | | TOK_LESS_AND[u1] TOK_NUMBER[u2] { 198 | YYUSE($u1); 199 | YYUSE($u2); 200 | } 201 | | TOK_NUMBER[u1] TOK_LESS_AND[u2] TOK_NUMBER[u3] { 202 | YYUSE($u1); 203 | YYUSE($u2); 204 | YYUSE($u3); 205 | } 206 | | TOK_REDIR_WORD[u1] TOK_LESS_AND[u2] TOK_NUMBER[u3] { 207 | YYUSE($u1); 208 | YYUSE($u2); 209 | YYUSE($u3); 210 | } 211 | | TOK_GREATER_AND[u1] TOK_NUMBER[u2] { 212 | YYUSE($u1); 213 | YYUSE($u2); 214 | } 215 | | TOK_NUMBER[u1] TOK_GREATER_AND[u2] TOK_NUMBER[u3] { 216 | YYUSE($u1); 217 | YYUSE($u2); 218 | YYUSE($u3); 219 | } 220 | | TOK_REDIR_WORD[u1] TOK_GREATER_AND[u2] TOK_NUMBER[u3] { 221 | YYUSE($u1); 222 | YYUSE($u2); 223 | YYUSE($u3); 224 | } 225 | | TOK_LESS_AND[u1] TOK_WORD[u2] { 226 | YYUSE($u1); 227 | YYUSE($u2); 228 | } 229 | | TOK_NUMBER[u1] TOK_LESS_AND[u2] TOK_WORD[u3] { 230 | YYUSE($u1); 231 | YYUSE($u2); 232 | YYUSE($u3); 233 | } 234 | | TOK_REDIR_WORD[u1] TOK_LESS_AND[u2] TOK_WORD[u3] { 235 | YYUSE($u1); 236 | YYUSE($u2); 237 | YYUSE($u3); 238 | } 239 | | TOK_GREATER_AND[u1] TOK_WORD[u2] { 240 | YYUSE($u1); 241 | YYUSE($u2); 242 | } 243 | | TOK_NUMBER[u1] TOK_GREATER_AND[u2] TOK_WORD[u3] { 244 | YYUSE($u1); 245 | YYUSE($u2); 246 | YYUSE($u3); 247 | } 248 | | TOK_REDIR_WORD[u1] TOK_GREATER_AND[u2] TOK_WORD[u3] { 249 | YYUSE($u1); 250 | YYUSE($u2); 251 | YYUSE($u3); 252 | } 253 | | TOK_GREATER_AND[u1] '-'[u2] { 254 | YYUSE($u1); 255 | YYUSE($u2); 256 | } 257 | | TOK_NUMBER[u1] TOK_GREATER_AND[u2] '-'[u3] { 258 | YYUSE($u1); 259 | YYUSE($u2); 260 | YYUSE($u3); 261 | } 262 | | TOK_REDIR_WORD[u1] TOK_GREATER_AND[u2] '-'[u3] { 263 | YYUSE($u1); 264 | YYUSE($u2); 265 | YYUSE($u3); 266 | } 267 | | TOK_LESS_AND[u1] '-'[u2] { 268 | YYUSE($u1); 269 | YYUSE($u2); 270 | } 271 | | TOK_NUMBER[u1] TOK_LESS_AND[u2] '-'[u3] { 272 | YYUSE($u1); 273 | YYUSE($u2); 274 | YYUSE($u3); 275 | } 276 | | TOK_REDIR_WORD[u1] TOK_LESS_AND[u2] '-'[u3] { 277 | YYUSE($u1); 278 | YYUSE($u2); 279 | YYUSE($u3); 280 | } 281 | | TOK_AND_GREATER[u1] TOK_WORD[u2] { 282 | YYUSE($u1); 283 | YYUSE($u2); 284 | } 285 | | TOK_AND_GREATER_GREATER[u1] TOK_WORD[u2] { 286 | YYUSE($u1); 287 | YYUSE($u2); 288 | } 289 | ; 290 | 291 | simple_command_element: TOK_WORD[tk1] { 292 | bash_store_data(ctx, &$tk1); 293 | } 294 | | TOK_ASSIGNMENT_WORD[u1] { 295 | YYUSE($u1); 296 | } 297 | | redirection 298 | ; 299 | 300 | redirection_list: redirection 301 | | redirection_list redirection 302 | ; 303 | 304 | simple_command1: simple_command_element 305 | | simple_command1 simple_command_element 306 | ; 307 | 308 | simple_command2: simple_command1 309 | | '`'[u1] simple_command1 '`'[u2] { 310 | YYUSE($u1); 311 | YYUSE($u2); 312 | } 313 | | TOK_SUBSH_START[u1] simple_command1 ')'[u2] { 314 | YYUSE($u1); 315 | YYUSE($u2); 316 | } 317 | | '`'[u1] simple_list1 '`'[u2] { 318 | YYUSE($u1); 319 | YYUSE($u2); 320 | } 321 | | TOK_SUBSH_START[u1] simple_list1 ')'[u2] { 322 | YYUSE($u1); 323 | YYUSE($u2); 324 | } 325 | ; 326 | 327 | simple_command: simple_command2 328 | | simple_command simple_command2 329 | ; 330 | 331 | command: simple_command 332 | | shell_command 333 | | shell_command redirection_list 334 | | function_def 335 | | coproc 336 | ; 337 | 338 | shell_command: for_command 339 | | case_command 340 | | TOK_WHILE[tk1] compound_list TOK_DO[tk2] compound_list TOK_DONE[tk3] { 341 | bash_store_data(ctx, &$tk1); 342 | bash_store_data(ctx, &$tk2); 343 | bash_store_data(ctx, &$tk3); 344 | } 345 | | TOK_UNTIL[tk1] compound_list TOK_DO[tk2] compound_list TOK_DONE[tk3] { 346 | bash_store_data(ctx, &$tk1); 347 | bash_store_data(ctx, &$tk2); 348 | bash_store_data(ctx, &$tk3); 349 | } 350 | | select_command 351 | | if_command 352 | | subshell 353 | | group_command 354 | | arith_command 355 | | cond_command 356 | | arith_for_command 357 | ; 358 | 359 | for_command: TOK_FOR[tk1] TOK_WORD[tk2] newline_list 360 | TOK_DO[tk3] compound_list TOK_DONE[tk4] { 361 | bash_store_data(ctx, &$tk1); 362 | bash_store_data(ctx, &$tk2); 363 | bash_store_data(ctx, &$tk3); 364 | bash_store_data(ctx, &$tk4); 365 | } 366 | | TOK_FOR[tk1] TOK_WORD[tk2] newline_list 367 | TOK_BEGIN[tk3] compound_list TOK_END[tk4] { 368 | bash_store_data(ctx, &$tk1); 369 | bash_store_data(ctx, &$tk2); 370 | bash_store_data(ctx, &$tk3); 371 | bash_store_data(ctx, &$tk4); 372 | } 373 | | TOK_FOR[tk1] TOK_WORD[tk2] ';'[u1] newline_list 374 | TOK_DO[tk3] compound_list TOK_DONE[tk4] { 375 | bash_store_data(ctx, &$tk1); 376 | bash_store_data(ctx, &$tk2); 377 | YYUSE($u1); 378 | bash_store_data(ctx, &$tk3); 379 | bash_store_data(ctx, &$tk4); 380 | } 381 | | TOK_FOR[tk1] TOK_WORD[tk2] ';'[u1] newline_list 382 | TOK_BEGIN[tk3] compound_list TOK_END[tk4] { 383 | bash_store_data(ctx, &$tk1); 384 | bash_store_data(ctx, &$tk2); 385 | YYUSE($u1); 386 | bash_store_data(ctx, &$tk3); 387 | bash_store_data(ctx, &$tk4); 388 | } 389 | | TOK_FOR[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] word_list 390 | list_terminator newline_list 391 | TOK_DO[tk4] compound_list TOK_DONE[tk5] { 392 | bash_store_data(ctx, &$tk1); 393 | bash_store_data(ctx, &$tk2); 394 | bash_store_data(ctx, &$tk3); 395 | bash_store_data(ctx, &$tk4); 396 | bash_store_data(ctx, &$tk5); 397 | } 398 | | TOK_FOR[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] word_list 399 | list_terminator newline_list 400 | TOK_BEGIN[tk4] compound_list TOK_END[tk5] 401 | { 402 | bash_store_data(ctx, &$tk1); 403 | bash_store_data(ctx, &$tk2); 404 | bash_store_data(ctx, &$tk3); 405 | bash_store_data(ctx, &$tk4); 406 | bash_store_data(ctx, &$tk5); 407 | } 408 | | TOK_FOR[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] 409 | list_terminator newline_list 410 | TOK_DO[tk4] compound_list TOK_DONE[tk5] { 411 | bash_store_data(ctx, &$tk1); 412 | bash_store_data(ctx, &$tk2); 413 | bash_store_data(ctx, &$tk3); 414 | bash_store_data(ctx, &$tk4); 415 | bash_store_data(ctx, &$tk5); 416 | } 417 | | TOK_FOR[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] 418 | list_terminator newline_list 419 | TOK_BEGIN[tk4] compound_list TOK_END[tk5] { 420 | bash_store_data(ctx, &$tk1); 421 | bash_store_data(ctx, &$tk2); 422 | bash_store_data(ctx, &$tk3); 423 | bash_store_data(ctx, &$tk4); 424 | bash_store_data(ctx, &$tk5); 425 | } 426 | ; 427 | 428 | arith_for_command: TOK_FOR[tk1] TOK_ARITH_FOR_EXPRS[tk2] 429 | list_terminator newline_list 430 | TOK_DO[tk3] compound_list TOK_DONE[tk4] { 431 | bash_store_data(ctx, &$tk1); 432 | bash_store_data(ctx, &$tk2); 433 | bash_store_data(ctx, &$tk3); 434 | bash_store_data(ctx, &$tk4); 435 | } 436 | | TOK_FOR[tk1] TOK_ARITH_FOR_EXPRS[tk2] list_terminator newline_list 437 | TOK_BEGIN[tk3] compound_list TOK_END[tk4] { 438 | bash_store_data(ctx, &$tk1); 439 | bash_store_data(ctx, &$tk2); 440 | bash_store_data(ctx, &$tk3); 441 | bash_store_data(ctx, &$tk4); 442 | } 443 | | TOK_FOR[tk1] TOK_ARITH_FOR_EXPRS[tk2] 444 | TOK_DO[tk3] compound_list TOK_DONE[tk4] { 445 | bash_store_data(ctx, &$tk1); 446 | bash_store_data(ctx, &$tk2); 447 | bash_store_data(ctx, &$tk3); 448 | bash_store_data(ctx, &$tk4); 449 | } 450 | | TOK_FOR[tk1] TOK_ARITH_FOR_EXPRS[tk2] 451 | TOK_BEGIN[tk3] compound_list TOK_END[tk4] { 452 | bash_store_data(ctx, &$tk1); 453 | bash_store_data(ctx, &$tk2); 454 | bash_store_data(ctx, &$tk3); 455 | bash_store_data(ctx, &$tk4); 456 | } 457 | ; 458 | 459 | select_command: TOK_SELECT[tk1] TOK_WORD[tk2] newline_list 460 | TOK_DO[tk3] list TOK_DONE[tk4] { 461 | bash_store_data(ctx, &$tk1); 462 | bash_store_data(ctx, &$tk2); 463 | bash_store_data(ctx, &$tk3); 464 | bash_store_data(ctx, &$tk4); 465 | } 466 | | TOK_SELECT[tk1] TOK_WORD[tk2] newline_list 467 | TOK_BEGIN[tk3] list TOK_END[tk4] { 468 | bash_store_data(ctx, &$tk1); 469 | bash_store_data(ctx, &$tk2); 470 | bash_store_data(ctx, &$tk3); 471 | bash_store_data(ctx, &$tk4); 472 | } 473 | | TOK_SELECT[tk1] TOK_WORD[tk2] ';'[u1] newline_list 474 | TOK_DO[tk3] list TOK_DONE[tk4] { 475 | bash_store_data(ctx, &$tk1); 476 | bash_store_data(ctx, &$tk2); 477 | YYUSE($u1); 478 | bash_store_data(ctx, &$tk3); 479 | bash_store_data(ctx, &$tk4); 480 | } 481 | | TOK_SELECT[tk1] TOK_WORD[tk2] ';'[u1] newline_list 482 | TOK_BEGIN[tk3] list TOK_END[tk4] { 483 | bash_store_data(ctx, &$tk1); 484 | bash_store_data(ctx, &$tk2); 485 | YYUSE($u1); 486 | bash_store_data(ctx, &$tk3); 487 | bash_store_data(ctx, &$tk4); 488 | } 489 | | TOK_SELECT[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] word_list 490 | list_terminator newline_list TOK_DO[tk4] list TOK_DONE[tk5] { 491 | bash_store_data(ctx, &$tk1); 492 | bash_store_data(ctx, &$tk2); 493 | bash_store_data(ctx, &$tk3); 494 | bash_store_data(ctx, &$tk4); 495 | bash_store_data(ctx, &$tk5); 496 | } 497 | | TOK_SELECT[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] word_list 498 | list_terminator newline_list TOK_BEGIN[tk4] list TOK_END[tk5] { 499 | bash_store_data(ctx, &$tk1); 500 | bash_store_data(ctx, &$tk2); 501 | bash_store_data(ctx, &$tk3); 502 | bash_store_data(ctx, &$tk4); 503 | bash_store_data(ctx, &$tk5); 504 | } 505 | | TOK_SELECT[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] list_terminator 506 | newline_list TOK_DO[tk4] compound_list TOK_DONE[tk5] { 507 | bash_store_data(ctx, &$tk1); 508 | bash_store_data(ctx, &$tk2); 509 | bash_store_data(ctx, &$tk3); 510 | bash_store_data(ctx, &$tk4); 511 | bash_store_data(ctx, &$tk5); 512 | } 513 | | TOK_SELECT[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] list_terminator 514 | newline_list TOK_BEGIN[tk4] compound_list TOK_END[tk5] { 515 | bash_store_data(ctx, &$tk1); 516 | bash_store_data(ctx, &$tk2); 517 | bash_store_data(ctx, &$tk3); 518 | bash_store_data(ctx, &$tk4); 519 | bash_store_data(ctx, &$tk5); 520 | } 521 | ; 522 | 523 | case_command: TOK_CASE[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] newline_list 524 | TOK_ESAC[tk4] { 525 | bash_store_data(ctx, &$tk1); 526 | bash_store_data(ctx, &$tk2); 527 | bash_store_data(ctx, &$tk3); 528 | bash_store_data(ctx, &$tk4); 529 | } 530 | | TOK_CASE[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] 531 | case_clause_sequence newline_list TOK_ESAC[tk4] { 532 | bash_store_data(ctx, &$tk1); 533 | bash_store_data(ctx, &$tk2); 534 | bash_store_data(ctx, &$tk3); 535 | bash_store_data(ctx, &$tk4); 536 | } 537 | | TOK_CASE[tk1] TOK_WORD[tk2] newline_list TOK_IN[tk3] case_clause 538 | TOK_ESAC[tk4] { 539 | bash_store_data(ctx, &$tk1); 540 | bash_store_data(ctx, &$tk2); 541 | bash_store_data(ctx, &$tk3); 542 | bash_store_data(ctx, &$tk4); 543 | } 544 | ; 545 | 546 | function_def: TOK_WORD[tk1] '('[u1] ')'[u2] newline_list function_body { 547 | bash_store_data(ctx, &$tk1); 548 | YYUSE($u1); 549 | YYUSE($u2); 550 | } 551 | | TOK_FUNCTION[tk1] TOK_WORD[tk2] '('[u1] ')'[u2] newline_list 552 | function_body { 553 | bash_store_data(ctx, &$tk1); 554 | bash_store_data(ctx, &$tk2); 555 | YYUSE($u1); 556 | YYUSE($u2); 557 | } 558 | | TOK_FUNCTION[tk1] TOK_WORD[tk2] newline_list function_body { 559 | bash_store_data(ctx, &$tk1); 560 | bash_store_data(ctx, &$tk2); 561 | } 562 | ; 563 | 564 | function_body: shell_command 565 | | shell_command redirection_list 566 | ; 567 | 568 | subshell: '('[u1] compound_list ')'[u2] { 569 | YYUSE($u1); 570 | YYUSE($u2); 571 | } 572 | ; 573 | 574 | coproc: TOK_COPROC[tk1] shell_command { 575 | bash_store_data(ctx, &$tk1); 576 | } 577 | | TOK_COPROC[tk1] shell_command redirection_list { 578 | bash_store_data(ctx, &$tk1); 579 | } 580 | | TOK_COPROC[tk1] TOK_WORD[tk2] shell_command { 581 | bash_store_data(ctx, &$tk1); 582 | bash_store_data(ctx, &$tk2); 583 | } 584 | | TOK_COPROC[tk1] TOK_WORD[tk2] shell_command redirection_list { 585 | bash_store_data(ctx, &$tk1); 586 | bash_store_data(ctx, &$tk2); 587 | } 588 | | TOK_COPROC[tk1] simple_command { 589 | bash_store_data(ctx, &$tk1); 590 | } 591 | ; 592 | 593 | if_command: TOK_IF[tk1] compound_list TOK_THEN[tk2] compound_list TOK_FI[tk3] { 594 | bash_store_data(ctx, &$tk1); 595 | bash_store_data(ctx, &$tk2); 596 | bash_store_data(ctx, &$tk3); 597 | } 598 | | TOK_IF[tk1] compound_list TOK_THEN[tk2] compound_list 599 | TOK_ELSE[tk3] compound_list TOK_FI[tk4] { 600 | bash_store_data(ctx, &$tk1); 601 | bash_store_data(ctx, &$tk2); 602 | bash_store_data(ctx, &$tk3); 603 | bash_store_data(ctx, &$tk4); 604 | } 605 | | TOK_IF[tk1] compound_list TOK_THEN[tk2] compound_list 606 | elif_clause TOK_FI[tk3] { 607 | bash_store_data(ctx, &$tk1); 608 | bash_store_data(ctx, &$tk2); 609 | bash_store_data(ctx, &$tk3); 610 | } 611 | ; 612 | 613 | group_command: TOK_BEGIN[tk1] compound_list TOK_END[tk2] { 614 | bash_store_data(ctx, &$tk1); 615 | bash_store_data(ctx, &$tk2); 616 | } 617 | ; 618 | 619 | arith_command: TOK_ARITH_CMD[tk1] { 620 | bash_store_data(ctx, &$tk1); 621 | } 622 | ; 623 | 624 | cond_command: TOK_COND_START[tk1] TOK_COND_CMD[tk2] TOK_COND_END[tk3] { 625 | bash_store_data(ctx, &$tk1); 626 | bash_store_data(ctx, &$tk2); 627 | bash_store_data(ctx, &$tk3); 628 | } 629 | ; 630 | 631 | elif_clause: TOK_ELIF[tk1] compound_list TOK_THEN[tk2] compound_list { 632 | bash_store_data(ctx, &$tk1); 633 | bash_store_data(ctx, &$tk2); 634 | } 635 | | TOK_ELIF[tk1] compound_list TOK_THEN[tk2] compound_list 636 | TOK_ELSE[tk3] compound_list { 637 | bash_store_data(ctx, &$tk1); 638 | bash_store_data(ctx, &$tk2); 639 | bash_store_data(ctx, &$tk3); 640 | } 641 | | TOK_ELIF[tk1] compound_list TOK_THEN[tk2] compound_list elif_clause { 642 | bash_store_data(ctx, &$tk1); 643 | bash_store_data(ctx, &$tk2); 644 | } 645 | ; 646 | 647 | case_clause:pattern_list 648 | | case_clause_sequence pattern_list 649 | ; 650 | 651 | pattern_list: newline_list pattern ')'[u1] compound_list { 652 | YYUSE($u1); 653 | } 654 | | newline_list pattern ')'[u1] newline_list { 655 | YYUSE($u1); 656 | } 657 | | newline_list '('[u1] pattern ')'[u2] compound_list { 658 | YYUSE($u1); 659 | YYUSE($u2); 660 | } 661 | | newline_list '('[u1] pattern ')'[u2] newline_list { 662 | YYUSE($u1); 663 | YYUSE($u2); 664 | } 665 | ; 666 | 667 | case_clause_sequence: pattern_list TOK_SEMI_SEMI[u1] { 668 | YYUSE($u1); 669 | } 670 | | case_clause_sequence pattern_list TOK_SEMI_SEMI[u1] { 671 | YYUSE($u1); 672 | } 673 | | pattern_list TOK_SEMI_AND[u1] { 674 | YYUSE($u1); 675 | } 676 | | case_clause_sequence pattern_list TOK_SEMI_AND[u1] { 677 | YYUSE($u1); 678 | } 679 | | pattern_list TOK_SEMI_SEMI_AND[u1] { 680 | YYUSE($u1); 681 | } 682 | |case_clause_sequence pattern_list TOK_SEMI_SEMI_AND[u1] { 683 | YYUSE($u1); 684 | } 685 | ; 686 | 687 | pattern: TOK_WORD[tk1] { 688 | bash_store_data(ctx, &$tk1); 689 | } 690 | | pattern '|'[u1] TOK_WORD[tk1] { 691 | YYUSE($u1); 692 | bash_store_data(ctx, &$tk1); 693 | } 694 | ; 695 | 696 | list: newline_list list0 697 | ; 698 | 699 | compound_list: list 700 | | newline_list list1 701 | ; 702 | 703 | list0: list1 '\n'[u1] newline_list { 704 | YYUSE($u1); 705 | } 706 | | list1 '&'[u1] newline_list { 707 | YYUSE($u1); 708 | } 709 | | list1 ';'[u1] newline_list { 710 | YYUSE($u1); 711 | } 712 | ; 713 | 714 | list1: list1 TOK_AND_AND[u1] newline_list list1 { 715 | YYUSE($u1); 716 | } 717 | | list1 TOK_OR_OR[u1] newline_list list1 { 718 | YYUSE($u1); 719 | } 720 | | list1 '&'[u1] newline_list list1 { 721 | YYUSE($u1); 722 | } 723 | | list1 ';'[u1] newline_list list1 { 724 | YYUSE($u1); 725 | } 726 | | list1 '\n'[u1] newline_list list1 { 727 | YYUSE($u1); 728 | } 729 | |pipeline_command 730 | ; 731 | 732 | simple_list_terminator: '\n'[u1] { 733 | YYUSE($u1); 734 | } 735 | | '\r'[u1] '\n'[u2] { 736 | YYUSE($u1); 737 | YYUSE($u2); 738 | } 739 | ; 740 | 741 | list_terminator: '\n'[u1] { 742 | YYUSE($u1); 743 | } 744 | | '\r'[u1] '\n'[u2] { 745 | YYUSE($u1); 746 | YYUSE($u2); 747 | } 748 | | ';'[u1] { 749 | YYUSE($u1); 750 | } 751 | ; 752 | 753 | newline_list: 754 | | newline_list '\n'[u1] { 755 | YYUSE($u1); 756 | } 757 | | newline_list '\r'[u1] '\n'[u2] { 758 | YYUSE($u1); 759 | YYUSE($u2); 760 | } 761 | ; 762 | 763 | simple_list: simple_list1 764 | | simple_list1 '&'[u1] { 765 | YYUSE($u1); 766 | } 767 | | simple_list1 ';'[u1] { 768 | YYUSE($u1); 769 | } 770 | ; 771 | 772 | simple_list1: simple_list1 TOK_AND_AND[u1] newline_list simple_list1 { 773 | YYUSE($u1); 774 | } 775 | | simple_list1 TOK_OR_OR[u1] newline_list simple_list1 { 776 | YYUSE($u1); 777 | } 778 | | simple_list1 TOK_AND_AND[u1]{ 779 | YYUSE($u1); 780 | } 781 | | simple_list1 TOK_OR_OR[u1]{ 782 | YYUSE($u1); 783 | } 784 | | simple_list1 '&'[u1] simple_list { 785 | YYUSE($u1); 786 | } 787 | | simple_list1 ';'[u1] simple_list { 788 | YYUSE($u1); 789 | } 790 | | pipeline_command 791 | ; 792 | 793 | pipeline_command: pipeline 794 | | TOK_BANG[u1] pipeline_command { 795 | YYUSE($u1); 796 | } 797 | | timespec pipeline_command 798 | | timespec list_terminator 799 | | TOK_BANG[u1] list_terminator { 800 | YYUSE($u1); 801 | } 802 | ; 803 | 804 | pipeline: pipeline '|'[u1] newline_list pipeline { 805 | YYUSE($u1); 806 | } 807 | | pipeline '|'[u1] { 808 | YYUSE($u1); 809 | } 810 | | pipeline TOK_BAR_AND[u1] newline_list pipeline { 811 | YYUSE($u1); 812 | } 813 | | command 814 | ; 815 | 816 | timespec: TOK_TIME[tk1] { 817 | bash_store_data(ctx, &$tk1); 818 | } 819 | | TOK_TIME[tk1] TOK_TIMEOPT[tk2] { 820 | bash_store_data(ctx, &$tk1); 821 | bash_store_data(ctx, &$tk2); 822 | } 823 | | TOK_TIME[tk1] TOK_TIMEOPT[tk2] TOK_TIMEIGN[tk3] { 824 | bash_store_data(ctx, &$tk1); 825 | bash_store_data(ctx, &$tk2); 826 | bash_store_data(ctx, &$tk3); 827 | } 828 | ; 829 | %% 830 | -------------------------------------------------------------------------------- /lib/test/detect_unit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../bash/bash_test.h" 5 | 6 | #define STR_LEN_ARGS(str) str, sizeof(str) - 1 7 | 8 | #define s_type_checks_(typename, has_attack, ...) \ 9 | s_type_checks( \ 10 | typename, (const struct detect_str[]){__VA_ARGS__}, \ 11 | sizeof((const struct detect_str[]){__VA_ARGS__}) / sizeof(const struct detect_str), \ 12 | has_attack) 13 | 14 | #define s_sqli_attacks(...) s_type_checks_("sqli", true, __VA_ARGS__) 15 | #define s_sqli_not_attacks(...) s_type_checks_("sqli", false, __VA_ARGS__) 16 | 17 | #define s_bash_attacks(...) s_type_checks_("bash", true, __VA_ARGS__) 18 | #define s_bash_not_attacks(...) s_type_checks_("bash", false, __VA_ARGS__) 19 | 20 | #define s_pt_attacks(...) s_type_checks_("pt", true, __VA_ARGS__) 21 | #define s_pt_not_attacks(...) s_type_checks_("pt", false, __VA_ARGS__) 22 | 23 | static void 24 | s_type_checks(const char *typename, const struct detect_str *tests, size_t ntests, bool has_attack) 25 | { 26 | struct detect *detect; 27 | 28 | CU_ASSERT_PTR_NOT_NULL_FATAL(detect = detect_open(typename)); 29 | for (size_t i = 0; i != ntests; i++) { 30 | uint32_t attack_types; 31 | 32 | CU_ASSERT_EQUAL(detect_start(detect), 0); 33 | CU_ASSERT_EQUAL(detect_add_data(detect, tests[i].str, tests[i].len, true), 0); 34 | CU_ASSERT_EQUAL(detect_has_attack(detect, &attack_types), has_attack); 35 | CU_ASSERT_EQUAL(detect_stop(detect), 0); 36 | } 37 | CU_ASSERT_EQUAL(detect_close(detect), 0); 38 | } 39 | 40 | static void 41 | Tsimplest(void) 42 | { 43 | CU_ASSERT_EQUAL(detect_init(), 0); 44 | CU_ASSERT_EQUAL(detect_deinit(), 0); 45 | CU_ASSERT_EQUAL(detect_init(), 0); 46 | CU_ASSERT_EQUAL(detect_deinit(), 0); 47 | } 48 | 49 | static int 50 | s_sqli_suite_init(void) 51 | { 52 | return (detect_init()); 53 | } 54 | 55 | static int 56 | s_sqli_suite_deinit(void) 57 | { 58 | return (detect_deinit()); 59 | } 60 | 61 | static void 62 | Tsqli_simplest(void) 63 | { 64 | struct detect *detect; 65 | 66 | CU_ASSERT_PTR_NOT_NULL_FATAL(detect = detect_open("sqli")); 67 | CU_ASSERT_EQUAL(detect_start(detect), 0); 68 | CU_ASSERT_EQUAL(detect_stop(detect), 0); 69 | CU_ASSERT_EQUAL(detect_close(detect), 0); 70 | } 71 | 72 | static void 73 | Tsqli_rce(void) 74 | { 75 | struct detect *detect; 76 | 77 | CU_ASSERT_PTR_NOT_NULL_FATAL(detect = detect_open("sqli")); 78 | CU_ASSERT_EQUAL(detect_start(detect), 0); 79 | CU_ASSERT_EQUAL(detect_add_data(detect, STR_LEN_ARGS("UP"), true), 0); 80 | CU_ASSERT_EQUAL(detect_stop(detect), 0); 81 | CU_ASSERT_EQUAL(detect_start(detect), 0); 82 | CU_ASSERT_EQUAL(detect_add_data(detect, STR_LEN_ARGS("SELECT SELECT"), true), 0); 83 | CU_ASSERT_EQUAL(detect_stop(detect), 0); 84 | printf("Next!\n"); 85 | CU_ASSERT_EQUAL(detect_start(detect), 0); 86 | CU_ASSERT_EQUAL(detect_add_data(detect, STR_LEN_ARGS("10 + 11 + 12 + 13"), true), 0); 87 | CU_ASSERT_EQUAL(detect_stop(detect), 0); 88 | CU_ASSERT_EQUAL(detect_close(detect), 0); 89 | } 90 | 91 | static void 92 | Tsqli_inj_in_table_name(void) 93 | { 94 | s_sqli_attacks({CSTR_LEN("1' where 1=1")}); 95 | } 96 | 97 | static void 98 | Tsqli_union(void) 99 | { 100 | s_sqli_attacks( 101 | {CSTR_LEN("1' union distinct select 1")}, 102 | {CSTR_LEN("1' union exec xp_cmdhshell 'ping127.0.0.1'")}, ); 103 | } 104 | 105 | static void 106 | Tsqli_operators(void) 107 | { 108 | // clang-format off 109 | s_sqli_attacks( 110 | {CSTR_LEN("1' RLIKE '.*'")}, 111 | {CSTR_LEN("1 MOD 1")}, 112 | {CSTR_LEN("1 XOR 1")}, 113 | {CSTR_LEN("1' REGEXP '.*'")}, 114 | {CSTR_LEN("1' + BINARY '1")}, 115 | {CSTR_LEN("1' INTO OUTFILE '1")}, 116 | {CSTR_LEN("1 AND 1 SOUNDS LIKE 1")}, 117 | {CSTR_LEN("1 + MATCH(col) AGAINST('text')")}, 118 | {CSTR_LEN("1 AND EXIST(SELECT 1)")}, 119 | {CSTR_LEN("1 AND xmlelement('user', " 120 | "login || ':' || pass).getStringVal()")}, 121 | {CSTR_LEN("1 AND FileToClob('/etc/passwd', " 122 | "'server')::html")}, 123 | {CSTR_LEN("1 AND U&'pgsql evade' uescape '!'")}, 124 | {CSTR_LEN("1 NOT BETWEEN 0 AND 1")}, 125 | {CSTR_LEN("1 AND ~1")}, 126 | ); 127 | // clang-format on 128 | } 129 | 130 | static void 131 | Tsqli_begin_end(void) 132 | { 133 | s_sqli_attacks({CSTR_LEN("1'; BEGIN UPDATE table_name SET column1 = value1 END")}); 134 | } 135 | 136 | static void 137 | Tsqli_waitfor(void) 138 | { 139 | // clang-format off 140 | s_sqli_attacks( 141 | {CSTR_LEN("1'; WAITFOR DELAY '02:00'")}, 142 | {CSTR_LEN("1'; WAITFOR TIME '02:00'")}, 143 | ); 144 | // clang-format on 145 | } 146 | 147 | static void 148 | Tsqli_top(void) 149 | { 150 | s_sqli_attacks({CSTR_LEN("SELECT TOP 5 * FROM table_name")}); 151 | s_sqli_attacks({CSTR_LEN("SELECT FIRST 5 * FROM table_name")}); 152 | } 153 | 154 | static void 155 | Tsqli_identifier_quote(void) 156 | { 157 | s_sqli_attacks({CSTR_LEN("SELECT * FROM `select`")}); 158 | s_sqli_attacks({CSTR_LEN("SELECT * FROM [select\nfrom]")}); 159 | } 160 | 161 | static void 162 | Tsqli_whitespace(void) 163 | { 164 | // clang-format off 165 | s_sqli_attacks( 166 | {CSTR_LEN("1\vor\v1")}, 167 | {CSTR_LEN("1\u00A0or\u00A01")}, 168 | {CSTR_LEN("1\000or\0001")}, 169 | ); 170 | // clang-format on 171 | } 172 | 173 | static void 174 | Tsqli_null(void) 175 | { 176 | s_sqli_attacks({CSTR_LEN("1-\\N union select 1")}); 177 | } 178 | 179 | static void 180 | Tsqli_left_func(void) 181 | { 182 | s_sqli_attacks({CSTR_LEN("1 AND left(a, 2) = 'ab'")}); 183 | } 184 | 185 | static void 186 | Tsqli_func(void) 187 | { 188 | s_sqli_not_attacks({CSTR_LEN("1; DBMS_LOCK.SLEEP()")}); 189 | } 190 | 191 | static void 192 | Tsqli_var_start_with_dollar(void) 193 | { 194 | s_sqli_attacks({CSTR_LEN("1 AND $func(a, 2) = 'ab'")}); 195 | } 196 | 197 | static void 198 | Tsqli_create_func(void) 199 | { 200 | s_sqli_attacks({CSTR_LEN("1; CREATE OR REPLACE FUNCTION SLEEP(int) " 201 | "RETURNS int AS '/lib/libc.so.6','sleep' " 202 | "language 'C' STRICT")}); 203 | } 204 | 205 | static void 206 | Tsqli_bit_num(void) 207 | { 208 | // clang-format off 209 | s_sqli_attacks( 210 | {CSTR_LEN("0b01UNION SELECT 1")}, 211 | {CSTR_LEN("B01UNION ALL SELECT 1")}, 212 | {CSTR_LEN("0b'01'UNION SELECT 1")}, 213 | {CSTR_LEN("B'01' UNION ALL SELECT 1")}, 214 | ); 215 | // clang-format on 216 | } 217 | 218 | static void 219 | Tsqli_join_wo_join_qual(void) 220 | { 221 | s_sqli_attacks({CSTR_LEN("SELECT * FROM (SELECT * FROM (SELECT 1) " 222 | "as t JOIN (SELECT 2)b)a")}); 223 | } 224 | 225 | static void 226 | Tsqli_empty_schema(void) 227 | { 228 | s_sqli_attacks({CSTR_LEN("SELECT 1 FROM master..sysdatabases")}); 229 | } 230 | 231 | static void 232 | Tsqli_asc_desc(void) 233 | { 234 | // clang-format off 235 | s_sqli_attacks( 236 | {CSTR_LEN("SELECT 1 ORDER BY 1 ASC")}, 237 | {CSTR_LEN("SELECT 1 ORDER BY 1 DESC")}, 238 | ); 239 | // clang-format on 240 | } 241 | 242 | static void 243 | Tsqli_shutdown(void) 244 | { 245 | s_sqli_attacks({CSTR_LEN("1; SHUTDOWN")}); 246 | } 247 | 248 | static void 249 | Tsqli_into_outfile(void) 250 | { 251 | s_sqli_attacks({CSTR_LEN("SELECT 1 FROM table_name INTO OUTFILE 'file'")}); 252 | } 253 | 254 | static void 255 | Tsqli_declare(void) 256 | { 257 | s_sqli_attacks( 258 | {CSTR_LEN("1; DECLARE name varchar(42)")}, 259 | {CSTR_LEN("1; DECLARE name CURSOR FOR select 1")}, 260 | {CSTR_LEN("1; DECLARE name varchar(42) = 'str'")}, ); 261 | } 262 | 263 | static void 264 | Tsqli_execute(void) 265 | { 266 | // clang-format off 267 | s_sqli_attacks( 268 | {CSTR_LEN("EXEC master.dbo.xp_cmdshell 'cmd'")}, 269 | {CSTR_LEN("EXEC (@s)")}, 270 | ); 271 | // clang-format on 272 | } 273 | 274 | static void 275 | Tsqli_nul_in_str(void) 276 | { 277 | s_sqli_attacks({CSTR_LEN("1\0' OR '")}); 278 | } 279 | 280 | static void 281 | Tsqli_drop(void) 282 | { 283 | // clang-format off 284 | s_sqli_attacks( 285 | {CSTR_LEN("DROP TABLE table_name")}, 286 | {CSTR_LEN("DROP FUNCTION func_name")}, 287 | {CSTR_LEN("DROP DATABASE db_name")}, 288 | ); 289 | // clang-format on 290 | } 291 | 292 | static void 293 | Tsqli_select(void) 294 | { 295 | s_sqli_attacks( 296 | {CSTR_LEN("(select 1) as t")}, 297 | {CSTR_LEN("SELECT lead(col, 0) OVER (ORDER BY col) FROM table_name")}, 298 | {CSTR_LEN("SELECT listagg(col,', ') WITHIN GROUP " 299 | "(ORDER BY col) from table_name")}, 300 | {CSTR_LEN("select 1, open, language, percent")}, ); 301 | } 302 | 303 | static void 304 | Tsqli_where(void) 305 | { 306 | s_sqli_attacks({CSTR_LEN("SELECT * FROM (table_name)")}); 307 | } 308 | 309 | static void 310 | Tsqli_string(void) 311 | { 312 | s_sqli_attacks({CSTR_LEN("SELECT _latin1 'str' COLLATE latin1_german2_ci a, n'str' b, " 313 | "x'str' c, _utf8'str' d")}); 314 | } 315 | 316 | static void 317 | Tsqli_use(void) 318 | { 319 | s_sqli_attacks({CSTR_LEN("USE db_name")}); 320 | } 321 | 322 | static void 323 | Tsqli_delete(void) 324 | { 325 | s_sqli_attacks({CSTR_LEN("DELETE LOW_PRIORITY " 326 | "FROM table_name WHERE 1=1 ORDER BY col ASC LIMIT 42")}); 327 | } 328 | 329 | static void 330 | Tsqli_0x(void) 331 | { 332 | s_sqli_attacks({CSTR_LEN("SELECT 0x")}); 333 | } 334 | 335 | static void 336 | Tsqli_print(void) 337 | { 338 | s_sqli_attacks({CSTR_LEN("print '1'")}); 339 | } 340 | 341 | static void 342 | Tsqli_load(void) 343 | { 344 | s_sqli_attacks({CSTR_LEN("LOAD DATA LOW_PRIORITY INFILE 'file'")}); 345 | } 346 | 347 | static void 348 | Tsqli_procedure_analyse(void) 349 | { 350 | s_sqli_attacks({CSTR_LEN("SELECT col FROM table_name " 351 | "PROCEDURE ANALYSE(7, 42);")}); 352 | } 353 | 354 | static void 355 | Tsqli_set(void) 356 | { 357 | s_sqli_not_attacks({CSTR_LEN("SET @t=1")}); 358 | } 359 | 360 | static void 361 | Tsqli_goto(void) 362 | { 363 | s_sqli_attacks({CSTR_LEN("1; GOTO label; SELECT 1")}); 364 | } 365 | 366 | static void 367 | Tsqli_call(void) 368 | { 369 | s_sqli_attacks({CSTR_LEN("call func()")}); 370 | } 371 | 372 | static void 373 | Tsqli_for_xml(void) 374 | { 375 | s_sqli_attacks({CSTR_LEN("SELECT 1 FOR XML PATH('')")}); 376 | } 377 | 378 | static void 379 | Tsqli_insert(void) 380 | { 381 | s_sqli_attacks( 382 | {CSTR_LEN("INSERT INTO table_name EXEC xp_cmdshell 'dir'")}, 383 | {CSTR_LEN("INSERT INTO table_name (col) VALUES (1)")}, ); 384 | } 385 | 386 | static void 387 | Tsqli_var(void) 388 | { 389 | s_sqli_attacks({CSTR_LEN("SELECT привет#")}); 390 | } 391 | 392 | static void 393 | Tsqli_open(void) 394 | { 395 | s_sqli_attacks({CSTR_LEN("OPEN tablecursor")}); 396 | } 397 | 398 | static void 399 | Tsqli_inner_select(void) 400 | { 401 | s_sqli_attacks({CSTR_LEN("1 union select 2 where 1=1), 3 where 1=1")}); 402 | } 403 | 404 | static void 405 | Tsqli_alter_database(void) 406 | { 407 | s_sqli_attacks({CSTR_LEN("ALTER DATABASE pubs SET RECOVERY SIMPLE")}); 408 | } 409 | 410 | static void 411 | Tsqli_backslash(void) 412 | { 413 | s_sqli_attacks({CSTR_LEN("SELECT \\1")}); 414 | } 415 | 416 | static void 417 | Tsqli_nl_in_str(void) 418 | { 419 | s_sqli_attacks({CSTR_LEN("SELECT '\n'")}); 420 | } 421 | 422 | static void 423 | Tsqli_buf(void) 424 | { 425 | s_sqli_attacks({CSTR_LEN("SELECT 0x4445434C415245204054207661726368" 426 | "617228323535292C4043207661726368617228343" 427 | "0303029204445434C415245205461626C655F4375" 428 | "72736F7220435552534F5220464F522073656C656" 429 | "37420612E6E616D652C622E6E616D652066726F6D" 430 | "207379736F626A6563747320612C737973636F6C7" 431 | "56D6E73206220776865726520612E69643D622E69" 432 | "6420616E6420612E78747970653D27752720616E6" 433 | "42028622E78747970653D3939206F7220622E7874" 434 | "7970653D3335206F7220622E78747970653D32333" 435 | "1206F7220622E78747970653D31363729204F5045" 436 | "4E205461626C655F437572736F722046455443482" 437 | "04E4558542046524F4D20205461626C655F437572" 438 | "736F7220494E544F2040542C4043205748494C452" 439 | "8404046455443485F5354415455533D3029204245" 440 | "47494E20657865632827757064617465205B272B4" 441 | "0542B275D20736574205B272B40432B275D3D2727" 442 | "223E3C2F7469746C653E3C7363726970742073726" 443 | "33D22687474703A2F2F777777302E646F7568756E" 444 | "716E2E636E2F63737273732F772E6A73223E3C2F7" 445 | "363726970743E3C212D2D27272B5B272B40432B27" 446 | "5D20776865726520272B40432B27206E6F74206C6" 447 | "96B6520272725223E3C2F7469746C653E3C736372" 448 | "697074207372633D22687474703A2F2F777777302" 449 | "E646F7568756E716E2E636E2F63737273732F772E" 450 | "6A73223E3C2F7363726970743E3C212D2D2727272" 451 | "94645544348204E4558542046524F4D2020546162" 452 | "6C655F437572736F7220494E544F2040542C40432" 453 | "0454E4420434C4F5345205461626C655F43757273" 454 | "6F72204445414C4C4F43415445205461626C655F4" 455 | "37572736F72")}); 456 | } 457 | 458 | static void 459 | Tsqli_if_else(void) 460 | { 461 | s_sqli_attacks( 462 | {CSTR_LEN("1'; IF (1=1) UPDATE table_name SET column1 = value1")}, 463 | {CSTR_LEN("1'; IF (1=1) UPDATE table_name " 464 | "SET column1 = value1 ELSE UPDATE " 465 | "table_name SET column1 = value1")}, 466 | {CSTR_LEN("SELECT IF(1=1,1, 0)")}, 467 | {CSTR_LEN("IF (1=1) THEN func(5); ELSE func(0); END IF;")}, ); 468 | } 469 | 470 | static void 471 | Tsqli_while(void) 472 | { 473 | s_sqli_attacks({CSTR_LEN("WHILE 1=1 SELECT 1")}); 474 | } 475 | 476 | static void 477 | Tsqli_semicolons_opt(void) 478 | { 479 | s_sqli_attacks({CSTR_LEN("1 SELECT 1 SELECT 2")}); 480 | } 481 | 482 | static void 483 | Tsqli_expr(void) 484 | { 485 | s_sqli_attacks({CSTR_LEN("-(1) WHERE 1=1")}); 486 | } 487 | 488 | static void 489 | Tsqli_var_start_with_num(void) 490 | { 491 | // clang-format off 492 | s_sqli_attacks( 493 | {CSTR_LEN("SELECT 1 FROM schema.1table_name")}, 494 | {CSTR_LEN("SELECT 1eUNION SELECT 1")}, 495 | {CSTR_LEN("SELECT 1e1UNION SELECT 1")}, 496 | ); 497 | // clang-format on 498 | } 499 | 500 | static void 501 | Tsqli_dot_e_dot(void) 502 | { 503 | s_sqli_attacks({CSTR_LEN("SELECT 1 from schema 9.e.table_name")}); 504 | } 505 | 506 | static void 507 | Tsqli_label(void) 508 | { 509 | s_sqli_not_attacks({CSTR_LEN("m1:")}); 510 | s_sqli_attacks({CSTR_LEN("m1: select 1")}); 511 | } 512 | 513 | static void 514 | Tsqli_comment(void) 515 | { 516 | s_sqli_attacks({CSTR_LEN("/*!SELECT*/ 1")}); 517 | } 518 | 519 | static void 520 | Tsqli_data_name(void) 521 | { 522 | // clang-format off 523 | s_sqli_attacks( 524 | {CSTR_LEN("SELECT \"1\" '2'")}, 525 | {CSTR_LEN("SELECT col FROM db.table")}, 526 | {CSTR_LEN("SELECT {ts '2013-03-31 00:00:00'}")}, 527 | {CSTR_LEN("SELECT replace('abc','b','d')")}, 528 | {CSTR_LEN("SELECT {db.table_name.id} from db.table_name")}, 529 | ); 530 | // clang-format on 531 | } 532 | 533 | static void 534 | Tsqli_regress_zero_realloc(void) 535 | { 536 | s_sqli_not_attacks({CSTR_LEN("\"''\"")}); 537 | } 538 | 539 | static void 540 | Tsqli_broken_from_select_list(void) 541 | { 542 | s_sqli_attacks( 543 | {CSTR_LEN("1) AS t WHERE 1=1")}, {CSTR_LEN("1) AS t1, table AS t2 WHERE 1=1")}, 544 | {CSTR_LEN("1) AS n FROM db.table WHERE 1=1")}, 545 | {CSTR_LEN("1) AS n1, name AS n2 FROM db.table WHERE 1=1")}, ); 546 | } 547 | 548 | static void 549 | Tsqli_table_name(void) 550 | { 551 | s_sqli_attacks( 552 | {CSTR_LEN("SELECT 1 FROM db:table")}, {CSTR_LEN("SELECT 1 FROM db:owner.object")}, 553 | {CSTR_LEN("SELECT 1 FROM db:owner.object.object")}, ); 554 | } 555 | 556 | static void 557 | Tbash_constraints(void) 558 | { 559 | CU_ASSERT_EQUAL(bash_lexer_test(), 0); 560 | } 561 | 562 | static void 563 | Tbash_simplest(void) 564 | { 565 | // clang-format off 566 | s_bash_attacks( 567 | {CSTR_LEN("ls -a")}, 568 | {CSTR_LEN("l\"s\" -a")}, 569 | {CSTR_LEN("l's' -a")}, 570 | {CSTR_LEN("l\\s -a")}, 571 | {CSTR_LEN("VAR=VAL ls")}, 572 | {CSTR_LEN("VAR=VAL l$1s")}, 573 | {CSTR_LEN("VAR=VAL l$name's'")}, 574 | {CSTR_LEN("VAR=VAL l${name}s")}, 575 | {CSTR_LEN("$-id")}, 576 | {CSTR_LEN("$*id")}, 577 | {CSTR_LEN("(aa=d;i$aa)")}, 578 | {CSTR_LEN("cat$IFS/etc/os-release")}, 579 | ); 580 | // clang-format on 581 | } 582 | 583 | static void 584 | Tbash_comment(void) 585 | { 586 | s_bash_not_attacks({CSTR_LEN("#ls -a")}, ); 587 | } 588 | 589 | static void 590 | Tbash_simplelist(void) 591 | { 592 | // clang-format off 593 | s_bash_attacks( 594 | {CSTR_LEN("ls;ls")}, 595 | {CSTR_LEN("ls&ls")}, 596 | {CSTR_LEN("ls&&ls")}, 597 | {CSTR_LEN("ls|ls")}, 598 | {CSTR_LEN("ls|&ls")}, 599 | {CSTR_LEN("ls||ls")}, 600 | {CSTR_LEN("ls|")}, 601 | {CSTR_LEN("time -p -- ls|ls")}, 602 | {CSTR_LEN("! ls|ls")}, 603 | ); 604 | // clang-format on 605 | } 606 | 607 | static void 608 | Tbash_commands(void) 609 | { 610 | // clang-format off 611 | s_bash_attacks( 612 | {CSTR_LEN("for test do echo i; done")}, 613 | {CSTR_LEN("for test; { echo i; }")}, 614 | {CSTR_LEN("for i in {1..5}; do echo i; done")}, 615 | {CSTR_LEN("for i in {1..5}; { echo i; }")}, 616 | {CSTR_LEN("for ((i=1; i<=10; ++i)) ; do echo $i ; done")}, 617 | {CSTR_LEN("for ((i=1; i<=10; ++i)) ; { echo $i ; }")}, 618 | {CSTR_LEN("select name do ls ; done")}, 619 | {CSTR_LEN("select name; do ls ; done")}, 620 | {CSTR_LEN("select name; { ls ; }")}, 621 | {CSTR_LEN("case 1 in 1) echo one;; 2) echo two;; esac")}, 622 | {CSTR_LEN("function a () { ( echo aaahh; ) }; a")}, 623 | {CSTR_LEN("coproc tee")}, 624 | {CSTR_LEN("if [ 1 -eq 2 ]; then echo equal ; else echo 'not equal' ; fi")}, 625 | {CSTR_LEN("(ls -a)")}, 626 | {CSTR_LEN("{ ls -a; }")}, 627 | {CSTR_LEN("/proc/self/exe -c id")}, 628 | {CSTR_LEN("sg root -c id")}, 629 | ); 630 | // clang-format on 631 | } 632 | 633 | static void 634 | Tbash_redirection(void) 635 | { 636 | // clang-format off 637 | s_bash_attacks( 638 | {CSTR_LEN("ls > file")}, 639 | {CSTR_LEN("ls > file")}, 640 | {CSTR_LEN("ls < file")}, 641 | {CSTR_LEN("ls 1> file")}, 642 | {CSTR_LEN("ls 2< file")}, 643 | {CSTR_LEN("ls {a}> file")}, 644 | {CSTR_LEN("ls {a}< file")}, 645 | {CSTR_LEN("ls >> file")}, 646 | {CSTR_LEN("ls 1>> file")}, 647 | {CSTR_LEN("ls {a}>> file")}, 648 | {CSTR_LEN("ls >| file")}, 649 | {CSTR_LEN("ls 1>| file")}, 650 | {CSTR_LEN("ls {a}>| file")}, 651 | {CSTR_LEN("ls <> file")}, 652 | {CSTR_LEN("ls 3<> file")}, 653 | {CSTR_LEN("ls {a}<> file")}, 654 | {CSTR_LEN("ls << file")}, 655 | {CSTR_LEN("ls 2<< file")}, 656 | {CSTR_LEN("ls {a}<< file")}, 657 | {CSTR_LEN("ls <<- file")}, 658 | {CSTR_LEN("ls 2<<- file")}, 659 | {CSTR_LEN("ls {a}<<- file")}, 660 | {CSTR_LEN("ls <<< file")}, 661 | {CSTR_LEN("ls <& 1")}, 662 | {CSTR_LEN("ls 2<& 1")}, 663 | {CSTR_LEN("ls {a}<& 1")}, 664 | {CSTR_LEN("ls >& 1")}, 665 | {CSTR_LEN("ls 2>& 1")}, 666 | {CSTR_LEN("ls {a}>& 1")}, 667 | {CSTR_LEN("ls <& file")}, 668 | {CSTR_LEN("ls 2<& file")}, 669 | {CSTR_LEN("ls {a}<& file")}, 670 | {CSTR_LEN("ls >& file")}, 671 | {CSTR_LEN("ls 2>& file")}, 672 | {CSTR_LEN("ls {a}>& file")}, 673 | {CSTR_LEN("ls <& -")}, 674 | {CSTR_LEN("ls 2<& -")}, 675 | {CSTR_LEN("ls {a}<& -")}, 676 | {CSTR_LEN("ls >& -")}, 677 | {CSTR_LEN("ls 2>& -")}, 678 | {CSTR_LEN("ls {a}>& -")}, 679 | {CSTR_LEN("ls &> file")}, 680 | {CSTR_LEN("ls 2&> file")}, 681 | {CSTR_LEN("ls {a}&> file")}, 682 | ); 683 | // clang-format on 684 | } 685 | 686 | static void 687 | Tbash_inj(void) 688 | { 689 | // clang-format off 690 | s_bash_attacks( 691 | {CSTR_LEN("\nls")}, 692 | {CSTR_LEN("\r\nls")}, 693 | {CSTR_LEN(";ls")}, 694 | {CSTR_LEN("|ls")}, 695 | {CSTR_LEN("||ls")}, 696 | {CSTR_LEN("&ls")}, 697 | {CSTR_LEN("&&ls")}, 698 | ); 699 | // clang-format on 700 | } 701 | 702 | static void 703 | Tbash_substitute(void) 704 | { 705 | // clang-format off 706 | s_bash_attacks( 707 | {CSTR_LEN("e$(FOO='BAR BAR BAR' echo ch)o test")}, 708 | {CSTR_LEN("foo <(FOO='BAR BAR BAR' ls)")}, 709 | {CSTR_LEN("c$()at /e??/p?????")}, 710 | {CSTR_LEN(";``id")}, 711 | ); 712 | } 713 | 714 | static void 715 | Tbash_globbing(void) 716 | { 717 | s_bash_attacks( 718 | {CSTR_LEN("/???/??t /???/??ss??")}, 719 | {CSTR_LEN("{cat,/etc/os-release}")}, 720 | ); 721 | } 722 | 723 | static void 724 | Tpt_boot_ini(void) 725 | { 726 | s_pt_attacks( 727 | {CSTR_LEN("../dir/../boot.ini")}, 728 | ); 729 | } 730 | 731 | static void 732 | Tpt_inj_start_with_sep(void) 733 | { 734 | s_pt_attacks( 735 | {CSTR_LEN("///../../etc///passwd")}, 736 | {CSTR_LEN("///..\\..\\etc///passwd")}, 737 | {CSTR_LEN("\\../\\../etc/\\passwd")}, 738 | ); 739 | } 740 | 741 | static void 742 | Tpt_travs_names_root(void) 743 | { 744 | s_pt_attacks( 745 | {CSTR_LEN("../\\windows/\\system32/\\drivers/\\etc/\\hosts")}, 746 | ); 747 | // clang-format on 748 | } 749 | 750 | int 751 | main(void) 752 | { 753 | CU_TestInfo generic_tests[] = { 754 | {"simplest", Tsimplest}, 755 | CU_TEST_INFO_NULL 756 | }; 757 | CU_TestInfo sqli_tests[] = { 758 | {"simplest", Tsqli_simplest }, 759 | {"rce", Tsqli_rce }, 760 | {"inj_in_table_name", Tsqli_inj_in_table_name }, 761 | {"union", Tsqli_union }, 762 | {"operators", Tsqli_operators }, 763 | {"begin_end", Tsqli_begin_end }, 764 | {"waitfor", Tsqli_waitfor }, 765 | {"top", Tsqli_top }, 766 | {"identifier_quote", Tsqli_identifier_quote }, 767 | {"whitespace", Tsqli_whitespace }, 768 | {"null", Tsqli_null }, 769 | {"left_func", Tsqli_left_func }, 770 | {"func", Tsqli_func }, 771 | {"var_start_with_dollar", Tsqli_var_start_with_dollar }, 772 | {"create_func", Tsqli_create_func }, 773 | {"bit_num", Tsqli_bit_num }, 774 | {"join_wo_join_qual", Tsqli_join_wo_join_qual }, 775 | {"empty_schema", Tsqli_empty_schema }, 776 | {"asc_desc", Tsqli_asc_desc }, 777 | {"shutdown", Tsqli_shutdown }, 778 | {"into_outfile", Tsqli_into_outfile }, 779 | {"declare", Tsqli_declare }, 780 | {"execute", Tsqli_execute }, 781 | {"nul_in_str", Tsqli_nul_in_str }, 782 | {"select", Tsqli_select }, 783 | {"drop", Tsqli_drop }, 784 | {"where", Tsqli_where }, 785 | {"string", Tsqli_string }, 786 | {"use", Tsqli_use }, 787 | {"delete", Tsqli_delete }, 788 | {"0x", Tsqli_0x }, 789 | {"print", Tsqli_print }, 790 | {"load", Tsqli_load }, 791 | {"procedure_analyse", Tsqli_procedure_analyse }, 792 | {"set", Tsqli_set }, 793 | {"goto", Tsqli_goto }, 794 | {"call", Tsqli_call }, 795 | {"for_xml", Tsqli_for_xml }, 796 | {"insert", Tsqli_insert }, 797 | {"var", Tsqli_var }, 798 | {"open", Tsqli_open }, 799 | {"inner_select", Tsqli_inner_select }, 800 | {"alter_database", Tsqli_alter_database }, 801 | {"backslash", Tsqli_backslash }, 802 | {"nl_in_str", Tsqli_nl_in_str }, 803 | {"buf", Tsqli_buf }, 804 | {"if_else", Tsqli_if_else }, 805 | {"while", Tsqli_while }, 806 | {"semicolons_opt", Tsqli_semicolons_opt }, 807 | {"expr", Tsqli_expr }, 808 | {"var_start_with_num", Tsqli_var_start_with_num }, 809 | {"dot_e_dot", Tsqli_dot_e_dot }, 810 | {"label", Tsqli_label }, 811 | {"data_name", Tsqli_data_name }, 812 | {"regress_zero_realloc", Tsqli_regress_zero_realloc }, 813 | {"comment", Tsqli_comment }, 814 | {"select_list", Tsqli_broken_from_select_list}, 815 | {"table_name", Tsqli_table_name }, 816 | CU_TEST_INFO_NULL 817 | }; 818 | CU_TestInfo bash_tests[] = { 819 | {"constraints", Tbash_constraints}, 820 | {"simplest", Tbash_simplest }, 821 | {"comment", Tbash_comment }, 822 | {"simplelist", Tbash_simplelist }, 823 | {"commands", Tbash_commands }, 824 | {"redirection", Tbash_redirection}, 825 | {"inj", Tbash_inj }, 826 | {"substitute", Tbash_substitute }, 827 | {"globbing", Tbash_globbing }, 828 | CU_TEST_INFO_NULL 829 | }; 830 | CU_TestInfo pt_tests[] = { 831 | {"boot_ini", Tpt_boot_ini }, 832 | {"inj_start_with_sep", Tpt_inj_start_with_sep}, 833 | {"travs_names_root", Tpt_travs_names_root }, 834 | CU_TEST_INFO_NULL 835 | }; 836 | // clang-format off 837 | CU_SuiteInfo suites[] = { 838 | {.pName = "generic", .pTests = generic_tests}, 839 | {.pName = "sqli", .pTests = sqli_tests, 840 | .pInitFunc = s_sqli_suite_init, .pCleanupFunc = s_sqli_suite_deinit}, 841 | {.pName = "bash", .pTests = bash_tests, 842 | .pInitFunc = s_sqli_suite_init, .pCleanupFunc = s_sqli_suite_deinit}, 843 | {.pName = "pt", .pTests = pt_tests, 844 | .pInitFunc = s_sqli_suite_init, .pCleanupFunc = s_sqli_suite_deinit}, 845 | CU_SUITE_INFO_NULL, 846 | }; 847 | // clang-format on 848 | CU_pRunSummary sum; 849 | 850 | if (CU_initialize_registry() != CUE_SUCCESS) 851 | return (EXIT_FAILURE); 852 | if (CU_register_suites(suites) != CUE_SUCCESS) 853 | return (EXIT_FAILURE); 854 | CU_basic_set_mode(CU_BRM_VERBOSE); 855 | CU_basic_run_tests(); 856 | 857 | if ((sum = CU_get_run_summary()) == NULL || sum->nSuitesFailed || sum->nTestsFailed 858 | || sum->nAssertsFailed) { 859 | CU_cleanup_registry(); 860 | return (EXIT_FAILURE); 861 | } 862 | CU_cleanup_registry(); 863 | return (EXIT_SUCCESS); 864 | } 865 | -------------------------------------------------------------------------------- /lib/sqli/sqli_lexer.re2c: -------------------------------------------------------------------------------- 1 | #include "sqli.h" 2 | #include "sqli_lexer.h" 3 | #include 4 | #include 5 | 6 | /*!max:re2c */ 7 | 8 | #define MAXBUFSIZ 1024 9 | #define MINBUFSIZ 32 10 | 11 | #define SSYM(ch) [ch] = ch 12 | 13 | static const unsigned char selfsyms[] = { 14 | SSYM('.'), SSYM(','), SSYM('('), SSYM(')'), 15 | SSYM('*'), 16 | SSYM(';'), SSYM('='), SSYM(':'), SSYM('{'), 17 | SSYM('}'), SSYM('-'), SSYM('+'), SSYM('~'), 18 | }; 19 | 20 | int 21 | sqli_get_token( 22 | struct sqli_detect_ctx *ctx, union SQLI_PARSER_STYPE *arg) 23 | { 24 | const unsigned char *end; 25 | unsigned char yych, yyaccept; 26 | int rv; 27 | 28 | if (ctx->lexer.pending[ctx->lexer.pending_first].tok > 0) { 29 | struct sqli_pending_token *token = 30 | &ctx->lexer.pending[ctx->lexer.pending_first]; 31 | int tok = token->tok; 32 | 33 | *arg = token->arg; 34 | memset(token, 0, sizeof(*token)); 35 | SQLI_PENDING_SHIFT(ctx->lexer.pending_first); 36 | return (tok); 37 | } 38 | 39 | if ((rv = detect_re2c_prepare_input(&ctx->lexer.re2c, &end, YYMAXFILL)) != 0) 40 | return (-rv); 41 | 42 | #define YYGETSTATE() ctx->lexer.state 43 | #define YYSETSTATE(st) \ 44 | ({ \ 45 | ctx->lexer.state = (st); \ 46 | }) 47 | #define YYGETCONDITION() ctx->lexer.condition 48 | #define YYSETCONDITION(cond) ({ctx->lexer.condition = (cond);}) 49 | #define RET(ctx, tok) \ 50 | do { \ 51 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); \ 52 | return (tok); \ 53 | } while (0) 54 | #define KEYNAME_SET(arg, _name_str) \ 55 | do { \ 56 | (arg)->data.value.str = _name_str; \ 57 | (arg)->data.value.len = sizeof(_name_str) - 1; \ 58 | } while (0) 59 | #define KEYNAME_SET_RET_EX(ctx, arg, _str, _name, _flags) \ 60 | do { \ 61 | KEYNAME_SET(arg, _str); \ 62 | (arg)->data.flags = (_flags); \ 63 | (arg)->data.tok = TOK_##_name; \ 64 | RET(ctx, TOK_##_name); \ 65 | } while (0) 66 | #define KEYNAME_SET_RET(ctx, arg, _name, _flags) \ 67 | KEYNAME_SET_RET_EX(ctx, arg, #_name, _name, _flags) 68 | #define RET_DATA_EX(toksuff, ctx, arg, _flags) \ 69 | do { \ 70 | (arg)->data.value = (ctx)->lexer.buf.data; \ 71 | (arg)->data.tok = TOK_##toksuff; \ 72 | (arg)->data.flags = (_flags)|SQLI_VALUE_NEEDFREE; \ 73 | detect_buf_reinit(&(ctx)->lexer.buf); \ 74 | RET(ctx, TOK_##toksuff); \ 75 | } while (0) 76 | #define RET_DATA(toksuff, ctx, arg) \ 77 | RET_DATA_EX(toksuff, ctx, arg, 0) 78 | #define RET_DATA_ERROR(ctx) detect_buf_deinit(&(ctx)->lexer.buf) 79 | 80 | /*!re2c 81 | re2c:define:YYCTYPE = "DETECT_RE2C_YYCTYPE"; 82 | re2c:define:YYCURSOR = DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c); 83 | re2c:define:YYMARKER = DETECT_RE2C_YYMARKER(&ctx->lexer.re2c); 84 | re2c:define:YYCTXMARKER = DETECT_RE2C_YYCTXMARKER(&ctx->lexer.re2c); 85 | re2c:define:YYLIMIT = end; 86 | re2c:define:YYCONDTYPE = SQLI_LEXER_CONDTYPE; 87 | re2c:define:YYFILL = "DETECT_RE2C_YYFILL(&ctx->lexer.re2c, &end, @@, YYMAXFILL);"; 88 | re2c:define:YYFILL@len = @@ ; 89 | re2c:define:YYFILL:naked = 1; 90 | re2c:yyfill:enable = 1; 91 | re2c:yyfill:check = 1; 92 | re2c:yych:conversion = 0; 93 | re2c:indent:top = 1; 94 | re2c:variable:yych = yych; 95 | re2c:variable:yyaccept = yyaccept; 96 | re2c:condprefix = sqli_; 97 | re2c:condenumprefix = sqli_; 98 | 99 | whitespace = [ \x01-\x1F\x7F\x80\x81\x88\x8D\x8F\x90\x98\x9D\xA0]|[\xC2][\xA0]; 100 | self = [,\.();=:{}~]; 101 | opchar = [!^&|%+\-*/<>]; 102 | opchar_nocomment = [!^&|%+<>=]; 103 | key_end = [^a-zA-Z0-9_]; 104 | 105 | <> { 106 | if (ctx->lexer.instring) { 107 | YYSETCONDITION(sqli_INSTRING); 108 | arg->data.flags = SQLI_DATA_NOSTART; 109 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 110 | goto sqli_INSTRING; 111 | } 112 | YYSETCONDITION(sqli_INITIAL); 113 | goto sqli_INITIAL; 114 | } 115 | "''"|'""'|'``' { 116 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 117 | goto sqli_INSTRING; 118 | } 119 | ['"`] => INITIAL { 120 | YYSETSTATE(-1); 121 | RET_DATA(DATA, ctx, arg); 122 | } 123 | ']' => INITIAL { 124 | YYSETSTATE(-1); 125 | RET_DATA(NAME, ctx, arg); 126 | } 127 | "''" { 128 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 129 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 130 | goto sqli_SQUOTE; 131 | } 132 | '""' { 133 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 134 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 135 | goto sqli_DQUOTE; 136 | } 137 | '``' { 138 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 139 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 140 | goto sqli_DQUOTE; 141 | } 142 | ['] => INITIAL { 143 | YYSETSTATE(-1); 144 | RET_DATA(DATA, ctx, arg); 145 | } 146 | ["] => INITIAL { 147 | YYSETSTATE(-1); 148 | RET_DATA(DATA, ctx, arg); 149 | } 150 | [`] => INITIAL { 151 | YYSETSTATE(-1); 152 | RET_DATA(DATA, ctx, arg); 153 | } 154 | [\]] => INITIAL { 155 | YYSETSTATE(-1); 156 | RET_DATA(NAME, ctx, arg); 157 | } 158 | [\x00] { 159 | if (ctx->lexer.re2c.fin && ctx->lexer.re2c.tmp_data_in_use && 160 | ctx->lexer.re2c.pos >= ctx->lexer.re2c.tmp_data + ctx->lexer.re2c.tmp_data_siz) { 161 | YYSETCONDITION(sqli_INITIAL); 162 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 163 | arg->data.flags |= SQLI_DATA_NOEND; 164 | YYSETSTATE(-1); 165 | RET_DATA(DATA, ctx, arg); 166 | } 167 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 168 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 169 | goto yy0; 170 | } 171 | .|[\n] { 172 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 173 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 174 | goto yy0; 175 | } 176 | [^] { 177 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 178 | YYSETSTATE(-1); 179 | RET_DATA_ERROR(ctx); 180 | RET(ctx, TOK_ERROR); 181 | } 182 | [0-9] { 183 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 184 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 185 | goto sqli_NUMBER; 186 | } else { 187 | detect_buf_deinit(&ctx->lexer.buf); 188 | RET(ctx, TOK_ERROR); 189 | } 190 | } 191 | [\.] => DECIMAL { 192 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 193 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 194 | goto sqli_DECIMAL; 195 | } else { 196 | detect_buf_deinit(&ctx->lexer.buf); 197 | RET(ctx, TOK_ERROR); 198 | } 199 | } 200 | [eE] => EXP { 201 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 202 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 203 | goto sqli_EXP; 204 | } else { 205 | detect_buf_deinit(&ctx->lexer.buf); 206 | RET(ctx, TOK_ERROR); 207 | } 208 | } 209 | [0-9] { 210 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 211 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 212 | goto sqli_NUMBER_OR_VAR; 213 | } else { 214 | detect_buf_deinit(&ctx->lexer.buf); 215 | RET(ctx, TOK_ERROR); 216 | } 217 | } 218 | [\.] => DECIMAL { 219 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 220 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 221 | goto sqli_DECIMAL; 222 | } else { 223 | detect_buf_deinit(&ctx->lexer.buf); 224 | RET(ctx, TOK_ERROR); 225 | } 226 | } 227 | [eE] => EXP_OR_VAR { 228 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 229 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 230 | goto sqli_EXP_OR_VAR; 231 | } else { 232 | detect_buf_deinit(&ctx->lexer.buf); 233 | RET(ctx, TOK_ERROR); 234 | } 235 | } 236 | [a-dA-Df-zF-Z_@$] => VAR { 237 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 238 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 239 | goto sqli_VAR; 240 | } else { 241 | detect_buf_deinit(&ctx->lexer.buf); 242 | RET(ctx, TOK_ERROR); 243 | } 244 | } 245 | [0-9] { 246 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 247 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 248 | goto sqli_DECIMAL; 249 | } else { 250 | detect_buf_deinit(&ctx->lexer.buf); 251 | RET(ctx, TOK_ERROR); 252 | } 253 | } 254 | [eE] => EXP { 255 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 256 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 257 | goto sqli_EXP; 258 | } else { 259 | detect_buf_deinit(&ctx->lexer.buf); 260 | RET(ctx, TOK_ERROR); 261 | } 262 | } 263 | [0-9] => EXP { 264 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 265 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 266 | goto sqli_EXP; 267 | } else { 268 | detect_buf_deinit(&ctx->lexer.buf); 269 | RET(ctx, TOK_ERROR); 270 | } 271 | } 272 | [a-zA-Z_@$] => VAR { 273 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 274 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 275 | goto sqli_VAR; 276 | } else { 277 | detect_buf_deinit(&ctx->lexer.buf); 278 | RET(ctx, TOK_ERROR); 279 | } 280 | } 281 | [0-9] { 282 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 283 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 284 | goto sqli_EXP; 285 | } else { 286 | detect_buf_deinit(&ctx->lexer.buf); 287 | RET(ctx, TOK_ERROR); 288 | } 289 | } 290 | [\.] => INITIAL { 291 | detect_buf_deinit(&ctx->lexer.buf); 292 | 293 | arg->data.value.str = (char *)&selfsyms['.']; 294 | arg->data.value.len = 1; 295 | arg->data.flags = SQLI_KEY_INSTR; 296 | arg->data.tok = '.'; 297 | YYSETSTATE(-1); 298 | RET(ctx, '.'); 299 | } 300 | [^] => INITIAL { 301 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 302 | YYSETSTATE(-1); 303 | RET_DATA(NUM, ctx, arg); 304 | } 305 | [0-9a-fA-F] { 306 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 307 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 308 | goto sqli_HEXNUMBER; 309 | } else { 310 | detect_buf_deinit(&ctx->lexer.buf); 311 | RET(ctx, TOK_ERROR); 312 | } 313 | } 314 | [^] => INITIAL { 315 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 316 | YYSETSTATE(-1); 317 | RET_DATA(NUM, ctx, arg); 318 | } 319 | [0-1] { 320 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 321 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 322 | goto yy0; 323 | } else { 324 | detect_buf_deinit(&ctx->lexer.buf); 325 | RET(ctx, TOK_ERROR); 326 | } 327 | } 328 | ['] => INITIAL { 329 | YYSETSTATE(-1); 330 | RET_DATA(DATA, ctx, arg); 331 | } 332 | [^] => INITIAL { 333 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 334 | YYSETSTATE(-1); 335 | RET_DATA(DATA, ctx, arg); 336 | } 337 | [0-9a-zA-Z_@$#]|[\xC0-\xDF][\x80-\xBF] { 338 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 339 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 340 | goto sqli_VAR; 341 | } else { 342 | detect_buf_deinit(&ctx->lexer.buf); 343 | RET(ctx, TOK_ERROR); 344 | } 345 | } 346 | [\.] { 347 | if (ctx->var_start_with_num) { 348 | YYSETCONDITION(sqli_INITIAL_AFTER_VAR); 349 | } else { 350 | YYSETCONDITION(sqli_INITIAL); 351 | } 352 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 353 | YYSETSTATE(-1); 354 | RET_DATA(NAME, ctx, arg); 355 | } 356 | [^] => INITIAL { 357 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 358 | YYSETSTATE(-1); 359 | RET_DATA(NAME, ctx, arg); 360 | } 361 | [\.] => INITIAL { 362 | YYSETSTATE(-1); 363 | arg->data.value.str = (char *)&selfsyms[DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]]; 364 | arg->data.value.len = 1; 365 | arg->data.flags = SQLI_KEY_INSTR; 366 | arg->data.tok = DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]; 367 | RET(ctx, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 368 | } 369 | '-' { 370 | assert(ctx->lexer.buf.data.len > 0); 371 | ctx->lexer.buf.data.len--; 372 | YYSETCONDITION(sqli_DASHCOMMENT); 373 | YYSETSTATE(-1); 374 | if (!ctx->lexer.buf.data.len) { 375 | detect_buf_deinit(&ctx->lexer.buf); 376 | goto sqli_DASHCOMMENT; 377 | } 378 | goto self_done; 379 | } 380 | [^] => INITIAL { 381 | assert(ctx->lexer.buf.data.len != 0); 382 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 383 | YYSETSTATE(-1); 384 | self_done: 385 | if (ctx->lexer.buf.data.len == 1) { 386 | unsigned char prev = ctx->lexer.buf.data.str[0]; 387 | 388 | if (prev < sizeof(selfsyms) / sizeof(selfsyms[0]) && 389 | selfsyms[prev] != 0) { 390 | 391 | detect_buf_deinit(&ctx->lexer.buf); 392 | 393 | arg->data.value.str = (char *)&selfsyms[prev]; 394 | arg->data.value.len = 1; 395 | arg->data.flags = SQLI_KEY_INSTR; 396 | arg->data.tok = prev; 397 | RET(ctx, prev); 398 | } 399 | } 400 | RET_DATA_EX(OPERATOR, ctx, arg, SQLI_KEY_INSTR); 401 | } 402 | opchar_nocomment { 403 | opchar_generic: 404 | if (!detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1])) { 405 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 406 | goto sqli_OPERATOR; 407 | } else { 408 | detect_buf_deinit(&ctx->lexer.buf); 409 | RET(ctx, TOK_ERROR); 410 | } 411 | } 412 | '-' { 413 | assert(ctx->lexer.buf.data.len > 0); 414 | if (ctx->lexer.buf.data.str[ctx->lexer.buf.data.len - 1] != '-') 415 | goto opchar_generic; 416 | ctx->lexer.buf.data.len--; 417 | YYSETCONDITION(sqli_DASHCOMMENT); 418 | YYSETSTATE(-1); 419 | if (!ctx->lexer.buf.data.len) { 420 | detect_buf_deinit(&ctx->lexer.buf); 421 | goto sqli_DASHCOMMENT; 422 | } 423 | goto operator_done; 424 | } 425 | '*' { 426 | assert(ctx->lexer.buf.data.len > 0); 427 | if (ctx->lexer.buf.data.str[ctx->lexer.buf.data.len - 1] != '/') 428 | goto opchar_generic; 429 | ctx->lexer.buf.data.len--; 430 | YYSETCONDITION(sqli_STARCOMMENT_PROBE); 431 | YYSETSTATE(-1); 432 | if (!ctx->lexer.buf.data.len) { 433 | detect_buf_deinit(&ctx->lexer.buf); 434 | goto sqli_STARCOMMENT_PROBE; 435 | } 436 | goto operator_done; 437 | } 438 | '/' { 439 | assert(ctx->lexer.buf.data.len > 0); 440 | if (ctx->lexer.buf.data.str[ctx->lexer.buf.data.len - 1] != '*') 441 | goto opchar_generic; 442 | // process unexpected "*/" construction as a space 443 | ctx->lexer.buf.data.len--; 444 | YYSETCONDITION(sqli_INITIAL); 445 | YYSETSTATE(-1); 446 | if (!ctx->lexer.buf.data.len) { 447 | detect_buf_deinit(&ctx->lexer.buf); 448 | goto sqli_INITIAL; 449 | } 450 | goto operator_done; 451 | } 452 | [^] => INITIAL { 453 | assert(ctx->lexer.buf.data.len != 0); 454 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 455 | YYSETSTATE(-1); 456 | operator_done: 457 | if (ctx->lexer.buf.data.len == 1) { 458 | unsigned char prev = ctx->lexer.buf.data.str[0]; 459 | 460 | if (prev < sizeof(selfsyms) / sizeof(selfsyms[0]) && 461 | selfsyms[prev] != 0) { 462 | 463 | detect_buf_deinit(&ctx->lexer.buf); 464 | 465 | arg->data.value.str = (char *)&selfsyms[prev]; 466 | arg->data.value.len = 1; 467 | arg->data.flags = SQLI_KEY_INSTR; 468 | arg->data.tok = prev; 469 | RET(ctx, prev); 470 | } 471 | } 472 | RET_DATA_EX(OPERATOR, ctx, arg, SQLI_KEY_INSTR); 473 | } 474 | [\r\n] => INITIAL { 475 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 476 | goto sqli_INITIAL; 477 | } 478 | [\x00] => INITIAL { 479 | // TODO: create UNCLOSED_COMMENT key 480 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 481 | goto sqli_INITIAL; 482 | } 483 | . => DASHCOMMENT { 484 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 485 | goto sqli_DASHCOMMENT; 486 | } 487 | [\x00] => INITIAL { 488 | // TODO: create UNCLOSED_COMMENT key 489 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 490 | goto sqli_INITIAL; 491 | } 492 | ('!' [0-9]{0,6})|("M!" [0-9]{0,6}) => INITIAL { 493 | // TODO: create EXECUTABLE_COMMENT key 494 | goto sqli_INITIAL; 495 | } 496 | [^] => STARCOMMENT { 497 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 498 | goto sqli_STARCOMMENT; 499 | } 500 | [\x00] => INITIAL { 501 | // TODO: create UNCLOSED_COMMENT key 502 | DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)--; 503 | goto sqli_INITIAL; 504 | } 505 | '*/' => INITIAL { 506 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 507 | goto sqli_INITIAL; 508 | } 509 | [^] => STARCOMMENT { 510 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 511 | goto sqli_STARCOMMENT; 512 | } 513 | whitespace { 514 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 515 | goto sqli_INITIAL; 516 | } 517 | '#' => DASHCOMMENT { 518 | DETECT_RE2C_UNUSED_BEFORE(&ctx->lexer.re2c); 519 | goto sqli_DASHCOMMENT; 520 | } 521 | 'SELECT'/key_end { 522 | KEYNAME_SET_RET(ctx, arg, SELECT, SQLI_KEY_READ|SQLI_KEY_INSTR); 523 | } 524 | 'UPDATE'/key_end { 525 | KEYNAME_SET_RET(ctx, arg, UPDATE, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 526 | } 527 | 'INSERT'/key_end { 528 | KEYNAME_SET_RET(ctx, arg, INSERT, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 529 | } 530 | 'EXECUTE'/key_end { 531 | KEYNAME_SET_RET(ctx, arg, EXECUTE, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 532 | } 533 | 'DROP'/key_end { 534 | KEYNAME_SET_RET(ctx, arg, DROP, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 535 | } 536 | 'DELETE'/key_end { 537 | KEYNAME_SET_RET(ctx, arg, DELETE, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 538 | } 539 | 'DISTINCT'/key_end { 540 | KEYNAME_SET_RET(ctx, arg, DISTINCT, SQLI_KEY_INSTR); 541 | } 542 | 'VARIADIC'/key_end { 543 | KEYNAME_SET_RET(ctx, arg, VARIADIC, SQLI_KEY_INSTR); 544 | } 545 | 'BETWEEN'/key_end { 546 | KEYNAME_SET_RET(ctx, arg, BETWEEN, SQLI_KEY_INSTR); 547 | } 548 | 'LIKE'/key_end { 549 | KEYNAME_SET_RET(ctx, arg, LIKE, SQLI_KEY_INSTR); 550 | } 551 | 'RLIKE'/key_end { 552 | KEYNAME_SET_RET(ctx, arg, RLIKE, SQLI_KEY_INSTR); 553 | } 554 | 'FROM'/key_end { 555 | KEYNAME_SET_RET(ctx, arg, FROM, SQLI_KEY_INSTR); 556 | } 557 | 'FOR'/key_end { 558 | KEYNAME_SET_RET(ctx, arg, FOR, SQLI_KEY_INSTR); 559 | } 560 | 'INTO'/key_end { 561 | KEYNAME_SET_RET(ctx, arg, INTO, SQLI_KEY_INSTR); 562 | } 563 | 'WHERE'/key_end { 564 | KEYNAME_SET_RET(ctx, arg, WHERE, SQLI_KEY_INSTR); 565 | } 566 | 'OR'/key_end { 567 | KEYNAME_SET_RET(ctx, arg, OR, SQLI_KEY_INSTR); 568 | } 569 | 'ON'/key_end { 570 | KEYNAME_SET_RET(ctx, arg, ON, SQLI_KEY_INSTR); 571 | } 572 | 'AS'/key_end { 573 | KEYNAME_SET_RET(ctx, arg, AS, SQLI_KEY_INSTR); 574 | } 575 | 'SET'/key_end { 576 | KEYNAME_SET_RET(ctx, arg, SET, SQLI_KEY_INSTR); 577 | } 578 | 'CROSS'/key_end { 579 | KEYNAME_SET_RET(ctx, arg, CROSS, SQLI_KEY_INSTR); 580 | } 581 | 'FULL'/key_end { 582 | KEYNAME_SET_RET(ctx, arg, FULL, SQLI_KEY_INSTR); 583 | } 584 | 'INNER'/key_end { 585 | KEYNAME_SET_RET(ctx, arg, INNER, SQLI_KEY_INSTR); 586 | } 587 | 'NATURAL'/key_end { 588 | KEYNAME_SET_RET(ctx, arg, NATURAL, SQLI_KEY_INSTR); 589 | } 590 | 'LEFT'/key_end { 591 | KEYNAME_SET_RET(ctx, arg, LEFT, SQLI_KEY_INSTR); 592 | } 593 | 'RIGHT'/key_end { 594 | KEYNAME_SET_RET(ctx, arg, RIGHT, SQLI_KEY_INSTR); 595 | } 596 | 'JOIN'/key_end { 597 | KEYNAME_SET_RET(ctx, arg, JOIN, SQLI_KEY_INSTR); 598 | } 599 | 'USING'/key_end { 600 | KEYNAME_SET_RET(ctx, arg, USING, SQLI_KEY_INSTR); 601 | } 602 | 'ORDER'/key_end { 603 | KEYNAME_SET_RET(ctx, arg, ORDER, SQLI_KEY_INSTR); 604 | } 605 | 'BY'/key_end { 606 | KEYNAME_SET_RET(ctx, arg, BY, SQLI_KEY_INSTR); 607 | } 608 | 'GROUP'/key_end { 609 | KEYNAME_SET_RET(ctx, arg, GROUP, SQLI_KEY_INSTR); 610 | } 611 | 'UNION'/key_end { 612 | KEYNAME_SET_RET(ctx, arg, UNION, SQLI_KEY_INSTR); 613 | } 614 | 'INTERSECT'/key_end { 615 | KEYNAME_SET_RET(ctx, arg, INTERSECT, SQLI_KEY_INSTR); 616 | } 617 | 'EXCEPT'/key_end { 618 | KEYNAME_SET_RET(ctx, arg, EXCEPT, SQLI_KEY_INSTR); 619 | } 620 | 'ALL'/key_end { 621 | KEYNAME_SET_RET(ctx, arg, ALL, SQLI_KEY_INSTR); 622 | } 623 | 'HAVING'/key_end { 624 | KEYNAME_SET_RET(ctx, arg, HAVING, SQLI_KEY_INSTR); 625 | } 626 | 'LIMIT'/key_end { 627 | KEYNAME_SET_RET(ctx, arg, LIMIT, SQLI_KEY_INSTR); 628 | } 629 | 'OFFSET'/key_end { 630 | KEYNAME_SET_RET(ctx, arg, OFFSET, SQLI_KEY_INSTR); 631 | } 632 | 'IS'/key_end { 633 | KEYNAME_SET_RET(ctx, arg, IS, SQLI_KEY_INSTR); 634 | } 635 | 'NOT'/key_end { 636 | KEYNAME_SET_RET(ctx, arg, NOT, SQLI_KEY_INSTR); 637 | } 638 | 'DIV'/key_end { 639 | KEYNAME_SET_RET(ctx, arg, DIV, SQLI_KEY_INSTR); 640 | } 641 | 'CASE'/key_end { 642 | KEYNAME_SET_RET(ctx, arg, CASE, SQLI_KEY_INSTR); 643 | } 644 | 'WHEN'/key_end { 645 | KEYNAME_SET_RET(ctx, arg, WHEN, SQLI_KEY_INSTR); 646 | } 647 | 'THEN'/key_end { 648 | KEYNAME_SET_RET(ctx, arg, THEN, SQLI_KEY_INSTR); 649 | } 650 | 'ELSE'/key_end { 651 | KEYNAME_SET_RET(ctx, arg, ELSE, SQLI_KEY_INSTR); 652 | } 653 | 'END'/key_end { 654 | KEYNAME_SET_RET(ctx, arg, END, SQLI_KEY_INSTR); 655 | } 656 | 'IN'/key_end { 657 | KEYNAME_SET_RET(ctx, arg, IN, SQLI_KEY_INSTR); 658 | } 659 | 'BOOLEAN'/key_end { 660 | KEYNAME_SET_RET(ctx, arg, BOOLEAN, SQLI_KEY_INSTR); 661 | } 662 | 'MODE'/key_end { 663 | KEYNAME_SET_RET(ctx, arg, MODE, SQLI_KEY_INSTR); 664 | } 665 | 'WAITFOR'/key_end { 666 | KEYNAME_SET_RET(ctx, arg, WAITFOR, SQLI_KEY_INSTR); 667 | } 668 | 'DELAY'/key_end { 669 | KEYNAME_SET_RET(ctx, arg, DELAY, SQLI_KEY_INSTR); 670 | } 671 | 'TIME'/key_end { 672 | KEYNAME_SET_RET(ctx, arg, TIME, SQLI_KEY_INSTR); 673 | } 674 | '||' { 675 | KEYNAME_SET_RET(ctx, arg, OR2, SQLI_KEY_INSTR); 676 | } 677 | 'AND'/key_end { 678 | KEYNAME_SET_RET(ctx, arg, AND, SQLI_KEY_INSTR); 679 | } 680 | '&&' { 681 | KEYNAME_SET_RET(ctx, arg, AND, SQLI_KEY_INSTR); 682 | } 683 | 'MOD'/key_end { 684 | KEYNAME_SET_RET(ctx, arg, MOD, SQLI_KEY_INSTR); 685 | } 686 | 'XOR'/key_end { 687 | KEYNAME_SET_RET(ctx, arg, XOR, SQLI_KEY_INSTR); 688 | } 689 | 'REGEXP'/key_end { 690 | KEYNAME_SET_RET(ctx, arg, REGEXP, SQLI_KEY_INSTR); 691 | } 692 | 'BINARY'/key_end { 693 | KEYNAME_SET_RET(ctx, arg, BINARY, SQLI_KEY_INSTR); 694 | } 695 | 'SOUNDS'/key_end { 696 | KEYNAME_SET_RET(ctx, arg, SOUNDS, SQLI_KEY_INSTR); 697 | } 698 | 'OUTFILE'/key_end { 699 | KEYNAME_SET_RET(ctx, arg, OUTFILE, SQLI_KEY_INSTR); 700 | } 701 | 'BEGIN'/key_end { 702 | KEYNAME_SET_RET(ctx, arg, BEGIN, SQLI_KEY_INSTR); 703 | } 704 | 'TOP'/key_end { 705 | KEYNAME_SET_RET(ctx, arg, TOP, SQLI_KEY_INSTR); 706 | } 707 | 'PERCENT'/key_end { 708 | KEYNAME_SET_RET(ctx, arg, PERCENT, SQLI_KEY_INSTR); 709 | } 710 | "\\N"/key_end { 711 | RET_DATA(NAME, ctx, arg); 712 | } 713 | 'CREATE'/key_end { 714 | KEYNAME_SET_RET(ctx, arg, CREATE, SQLI_KEY_INSTR); 715 | } 716 | 'REPLACE'/key_end { 717 | KEYNAME_SET_RET(ctx, arg, REPLACE, SQLI_KEY_INSTR); 718 | } 719 | 'FUNCTION'/key_end { 720 | KEYNAME_SET_RET(ctx, arg, FUNCTION, SQLI_KEY_INSTR); 721 | } 722 | 'RETURNS'/key_end { 723 | KEYNAME_SET_RET(ctx, arg, RETURNS, SQLI_KEY_INSTR); 724 | } 725 | 'LANGUAGE'/key_end { 726 | KEYNAME_SET_RET(ctx, arg, LANGUAGE, SQLI_KEY_INSTR); 727 | } 728 | 'STRICT'/key_end { 729 | KEYNAME_SET_RET(ctx, arg, STRICT, SQLI_KEY_INSTR); 730 | } 731 | 'DESC'/key_end { 732 | KEYNAME_SET_RET(ctx, arg, DESC, SQLI_KEY_INSTR); 733 | } 734 | 'ASC'/key_end { 735 | KEYNAME_SET_RET(ctx, arg, ASC, SQLI_KEY_INSTR); 736 | } 737 | 'SHUTDOWN'/key_end { 738 | KEYNAME_SET_RET(ctx, arg, SHUTDOWN, SQLI_KEY_INSTR); 739 | } 740 | 'DECLARE'/key_end { 741 | KEYNAME_SET_RET(ctx, arg, DECLARE, SQLI_KEY_INSTR); 742 | } 743 | 'EXEC'/key_end { 744 | KEYNAME_SET_RET(ctx, arg, EXECUTE, SQLI_KEY_WRITE|SQLI_KEY_INSTR); 745 | } 746 | 'TABLE'/key_end { 747 | KEYNAME_SET_RET(ctx, arg, TABLE, SQLI_KEY_INSTR); 748 | } 749 | 'MATCH'/key_end { 750 | KEYNAME_SET_RET(ctx, arg, MATCH, SQLI_KEY_INSTR); 751 | } 752 | 'AGAINST'/key_end { 753 | KEYNAME_SET_RET(ctx, arg, AGAINST, SQLI_KEY_INSTR); 754 | } 755 | 'COLLATE'/key_end { 756 | KEYNAME_SET_RET(ctx, arg, COLLATE, SQLI_KEY_INSTR); 757 | } 758 | 'USE'/key_end { 759 | KEYNAME_SET_RET(ctx, arg, USE, SQLI_KEY_INSTR); 760 | } 761 | 'IGNORE'/key_end { 762 | KEYNAME_SET_RET(ctx, arg, IGNORE, SQLI_KEY_INSTR); 763 | } 764 | 'LOW_PRIORITY'/key_end { 765 | KEYNAME_SET_RET(ctx, arg, LOW_PRIORITY, SQLI_KEY_INSTR); 766 | } 767 | 'QUICK'/key_end { 768 | KEYNAME_SET_RET(ctx, arg, QUICK, SQLI_KEY_INSTR); 769 | } 770 | 'PRINT'/key_end { 771 | KEYNAME_SET_RET(ctx, arg, PRINT, SQLI_KEY_INSTR); 772 | } 773 | 'EXIST'/key_end { 774 | KEYNAME_SET_RET(ctx, arg, EXIST, SQLI_KEY_INSTR); 775 | } 776 | 'UESCAPE'/key_end { 777 | KEYNAME_SET_RET(ctx, arg, UESCAPE, SQLI_KEY_INSTR); 778 | } 779 | 'LOAD'/key_end { 780 | KEYNAME_SET_RET(ctx, arg, LOAD, SQLI_KEY_INSTR); 781 | } 782 | 'DATA'/key_end { 783 | KEYNAME_SET_RET(ctx, arg, DATA2, SQLI_KEY_INSTR); 784 | } 785 | 'XML'/key_end { 786 | KEYNAME_SET_RET(ctx, arg, XML, SQLI_KEY_INSTR); 787 | } 788 | 'CONCURRENT'/key_end { 789 | KEYNAME_SET_RET(ctx, arg, CONCURRENT, SQLI_KEY_INSTR); 790 | } 791 | 'LOCAL'/key_end { 792 | KEYNAME_SET_RET(ctx, arg, LOCAL, SQLI_KEY_INSTR); 793 | } 794 | 'INFILE'/key_end { 795 | KEYNAME_SET_RET(ctx, arg, INFILE, SQLI_KEY_INSTR); 796 | } 797 | 'PROCEDURE'/key_end { 798 | KEYNAME_SET_RET(ctx, arg, PROCEDURE, SQLI_KEY_INSTR); 799 | } 800 | 'GOTO'/key_end { 801 | KEYNAME_SET_RET(ctx, arg, GOTO, SQLI_KEY_INSTR); 802 | } 803 | 'DATABASE'/key_end { 804 | KEYNAME_SET_RET(ctx, arg, DATABASE, SQLI_KEY_INSTR); 805 | } 806 | 'CALL'/key_end { 807 | KEYNAME_SET_RET(ctx, arg, CALL, SQLI_KEY_INSTR); 808 | } 809 | 'CURSOR'/key_end { 810 | KEYNAME_SET_RET(ctx, arg, CURSOR, SQLI_KEY_INSTR); 811 | } 812 | 'XML'/key_end { 813 | KEYNAME_SET_RET(ctx, arg, XML, SQLI_KEY_INSTR); 814 | } 815 | 'PATH'/key_end { 816 | KEYNAME_SET_RET(ctx, arg, PATH, SQLI_KEY_INSTR); 817 | } 818 | 'VALUES'/key_end { 819 | KEYNAME_SET_RET(ctx, arg, VALUES, SQLI_KEY_INSTR); 820 | } 821 | 'OPEN'/key_end { 822 | KEYNAME_SET_RET(ctx, arg, OPEN, SQLI_KEY_INSTR); 823 | } 824 | 'OVER'/key_end { 825 | KEYNAME_SET_RET(ctx, arg, OVER, SQLI_KEY_INSTR); 826 | } 827 | 'WITHIN'/key_end { 828 | KEYNAME_SET_RET(ctx, arg, WITHIN, SQLI_KEY_INSTR); 829 | } 830 | 'ALTER'/key_end { 831 | KEYNAME_SET_RET(ctx, arg, ALTER, SQLI_KEY_INSTR); 832 | } 833 | 'RECOVERY'/key_end { 834 | KEYNAME_SET_RET(ctx, arg, RECOVERY, SQLI_KEY_INSTR); 835 | } 836 | 'SIMPLE'/key_end { 837 | KEYNAME_SET_RET(ctx, arg, SIMPLE, SQLI_KEY_INSTR); 838 | } 839 | 'LOCK'/key_end { 840 | KEYNAME_SET_RET(ctx, arg, LOCK, SQLI_KEY_INSTR); 841 | } 842 | 'SHARE'/key_end { 843 | KEYNAME_SET_RET(ctx, arg, SHARE, SQLI_KEY_INSTR); 844 | } 845 | 'IF'/key_end { 846 | KEYNAME_SET_RET(ctx, arg, IF, SQLI_KEY_INSTR); 847 | } 848 | 'WHILE'/key_end { 849 | KEYNAME_SET_RET(ctx, arg, WHILE, SQLI_KEY_INSTR); 850 | } 851 | 'FIRST'/key_end { 852 | KEYNAME_SET_RET(ctx, arg, FIRST, SQLI_KEY_INSTR); 853 | } 854 | opchar => OPERATOR { 855 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 856 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 857 | goto sqli_OPERATOR; 858 | } 859 | [']|'_latin'[17]whitespace*[']|'n'[']|'x'[']|'_utf8'whitespace*['] => SQUOTE { 860 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 861 | goto sqli_SQUOTE; 862 | } 863 | ["] => DQUOTE { 864 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 865 | goto sqli_DQUOTE; 866 | } 867 | [`] => BQUOTE { 868 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 869 | goto sqli_BQUOTE; 870 | } 871 | [\[] => IQUOTE { 872 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 873 | goto sqli_IQUOTE; 874 | } 875 | [\.][0-9] => DECIMAL { 876 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 877 | detect_buf_add_char(&ctx->lexer.buf, '.'); 878 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 879 | goto sqli_DECIMAL; 880 | } 881 | '0x' => HEXNUMBER { 882 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 2048); 883 | detect_buf_add_char(&ctx->lexer.buf, '0'); 884 | detect_buf_add_char(&ctx->lexer.buf, 'x'); 885 | goto sqli_HEXNUMBER; 886 | } 887 | ("0b"|'b')[0-1] => BITVALUE { 888 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 889 | detect_buf_add_char(&ctx->lexer.buf, '0'); 890 | detect_buf_add_char(&ctx->lexer.buf, 'b'); 891 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 892 | goto sqli_BITVALUE; 893 | } 894 | ("0b'"|'b\'')[0-1] => QBITVALUE { 895 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 896 | detect_buf_add_char(&ctx->lexer.buf, '0'); 897 | detect_buf_add_char(&ctx->lexer.buf, 'b'); 898 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 899 | goto sqli_QBITVALUE; 900 | } 901 | '\\'|[0-9] { 902 | if (ctx->var_start_with_num) { 903 | YYSETCONDITION(sqli_NUMBER_OR_VAR); 904 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 905 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 906 | goto sqli_NUMBER_OR_VAR; 907 | } else { 908 | YYSETCONDITION(sqli_NUMBER); 909 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 910 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 911 | goto sqli_NUMBER; 912 | } 913 | } 914 | self { 915 | arg->data.value.str = (char *)&selfsyms[DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]]; 916 | arg->data.value.len = 1; 917 | arg->data.flags = SQLI_KEY_INSTR; 918 | arg->data.tok = DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]; 919 | RET(ctx, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 920 | } 921 | '-' => MINUS { 922 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, MAXBUFSIZ); 923 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 924 | goto sqli_MINUS; 925 | } 926 | [a-zA-Z_@$]|[\xC0-\xDF][\x80-\xBF] => VAR { 927 | detect_buf_init(&ctx->lexer.buf, MINBUFSIZ, 256); 928 | detect_buf_add_char(&ctx->lexer.buf, DETECT_RE2C_YYCURSOR(&ctx->lexer.re2c)[-1]); 929 | goto sqli_VAR; 930 | } 931 | "/*!" => INITIAL { 932 | goto sqli_INITIAL; 933 | } 934 | [\x00]|'-'[\x00]|'/'[\x00] { 935 | if (ctx->lexer.re2c.fin && ctx->lexer.re2c.tmp_data_in_use && 936 | ctx->lexer.re2c.pos >= ctx->lexer.re2c.tmp_data + ctx->lexer.re2c.tmp_data_siz) { 937 | return (0); 938 | } 939 | goto yy0; 940 | } 941 | [^] { 942 | RET(ctx, TOK_ERROR); 943 | } 944 | */ 945 | } 946 | -------------------------------------------------------------------------------- /lib/sqli/sqli_parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "sqli.h" 3 | 4 | #if !defined(YYUSE) 5 | #define YYUSE(arg) YY_USE(arg) 6 | #endif 7 | 8 | static void 9 | sqli_parser_error(struct sqli_detect_ctx *ctx, const char *s) 10 | { 11 | ctx->res.parse_error = true; 12 | } 13 | 14 | %} 15 | 16 | %define api.pure full 17 | %define api.push-pull push 18 | %define api.prefix {sqli_parser_} 19 | %parse-param {struct sqli_detect_ctx *ctx} 20 | %union { 21 | struct sqli_token_arg_data data; 22 | } 23 | %define lr.type lalr 24 | 25 | %type data data_name 26 | %type operator important_operator logical_operator 27 | %type join_type 28 | %type select_extra_tk 29 | %type union_tk 30 | %type func_args_modifier_tk 31 | %type post_expr_tk expr 32 | %type delete_modifier 33 | %type load_modifier data_xml 34 | 35 | %token TOK_START_DATA 36 | %token TOK_START_STRING 37 | %token TOK_START_RCE 38 | %token TOK_DISTINCT TOK_VARIADIC 39 | %token TOK_DATA TOK_NAME TOK_OPERATOR TOK_NUM 40 | %token '.' ',' '(' ')' '*' '[' ']' ';' '=' ':' '{' '}' '-' '+' '~' 41 | %token TOK_OR TOK_OR2 TOK_AND TOK_IS TOK_NOT TOK_DIV 42 | TOK_MOD TOK_XOR TOK_REGEXP 43 | TOK_BINARY TOK_SOUNDS TOK_OUTFILE TOK_MATCH TOK_AGAINST TOK_EXIST 44 | %token TOK_COLLATE TOK_UESCAPE 45 | %token TOK_FOR 46 | %token TOK_FROM TOK_INTO TOK_WHERE 47 | %token TOK_AS TOK_ON TOK_USING 48 | %token TOK_UNION TOK_INTERSECT TOK_EXCEPT TOK_ALL 49 | %token TOK_ORDER TOK_GROUP TOK_BY TOK_HAVING 50 | %token TOK_TOP TOK_PERCENT TOK_FIRST 51 | %token TOK_DESC TOK_ASC 52 | %token TOK_CROSS TOK_FULL TOK_INNER TOK_LEFT TOK_RIGHT 53 | %token TOK_LIMIT TOK_OFFSET 54 | %token TOK_NATURAL TOK_JOIN 55 | %token TOK_SELECT TOK_UPDATE TOK_INSERT TOK_EXECUTE TOK_DELETE 56 | %token TOK_DROP 57 | %token TOK_COMMA 58 | %token TOK_SET 59 | %token TOK_BETWEEN TOK_LIKE TOK_RLIKE TOK_IN TOK_BOOLEAN TOK_MODE 60 | %token TOK_CASE TOK_WHEN TOK_THEN TOK_ELSE TOK_BEGIN TOK_END 61 | %token TOK_WAITFOR TOK_DELAY TOK_TIME 62 | %token TOK_CREATE TOK_REPLACE TOK_FUNCTION TOK_RETURNS TOK_LANGUAGE TOK_STRICT 63 | %token TOK_SHUTDOWN 64 | %token TOK_DECLARE 65 | %token TOK_TABLE TOK_DATABASE 66 | %token TOK_USE 67 | %token TOK_WHILE 68 | %token TOK_IGNORE TOK_LOW_PRIORITY TOK_QUICK 69 | %token TOK_PRINT 70 | %token TOK_LOAD TOK_DATA2 TOK_XML TOK_CONCURRENT TOK_LOCAL TOK_INFILE 71 | %token TOK_PROCEDURE 72 | %token TOK_GOTO 73 | %token TOK_CALL 74 | %token TOK_CURSOR 75 | %token TOK_PATH 76 | %token TOK_VALUES 77 | %token TOK_OPEN 78 | %token TOK_OVER 79 | %token TOK_WITHIN 80 | %token TOK_ALTER TOK_RECOVERY TOK_SIMPLE 81 | %token TOK_LOCK TOK_SHARE 82 | %token TOK_IF 83 | %token TOK_FUNC 84 | %token TOK_ERROR 85 | 86 | %destructor { 87 | sqli_token_data_destructor(&$$); 88 | } 89 | 90 | %% 91 | 92 | context: start_data 93 | | start_string 94 | | start_rce 95 | ; 96 | 97 | start_data: TOK_START_DATA expr_cont 98 | | TOK_START_DATA noop_expr ','[u1] expr_cont { 99 | YYUSE($u1); 100 | } 101 | ; 102 | 103 | start_string: TOK_START_STRING { 104 | ctx->lexer.instring = true; 105 | } expr_cont 106 | ; 107 | 108 | data: TOK_DATA 109 | | data TOK_DATA { 110 | if ($1.flags == SQLI_VALUE_NEEDFREE) { 111 | if ($1.value.len + $2.value.len) { 112 | $$.value.str = realloc($1.value.str, $1.value.len + 113 | $2.value.len); 114 | } else { 115 | $$ = $1; 116 | } 117 | 118 | if ($$.value.str) { 119 | $1.flags = 0; 120 | } 121 | } else { 122 | $$.value.str = malloc($1.value.len + $2.value.len); 123 | memcpy($$.value.str, $1.value.str, $1.value.len); 124 | $$.flags = SQLI_VALUE_NEEDFREE; 125 | } 126 | 127 | if ($$.value.str && $2.value.str) { 128 | memcpy($$.value.str + $1.value.len, $2.value.str, $2.value.len); 129 | } 130 | $$.value.len = $1.value.len + $2.value.len; 131 | 132 | sqli_token_data_destructor(&$1); 133 | sqli_token_data_destructor(&$2); 134 | } 135 | ; 136 | 137 | data_name: data 138 | | TOK_NAME 139 | | TOK_DATA2 140 | | TOK_TABLE 141 | | TOK_BINARY 142 | | TOK_OPEN 143 | | TOK_LANGUAGE 144 | | TOK_PERCENT 145 | | '{'[u1] TOK_NAME[name] noop_expr '}'[u2] { 146 | YYUSE($u1); 147 | $$ = $name; 148 | YYUSE($u2); 149 | } 150 | /* Tokens-as-identifiers here */ 151 | ; 152 | 153 | expr_cont: 154 | | noop_expr after_exp_cont_op_noexpr after_exp_cont 155 | | noop_expr where_opt after_exp_cont_op_noexpr after_exp_cont 156 | ; 157 | 158 | update: TOK_UPDATE[tk1] colref_exact TOK_SET[tk2] expr_list { 159 | sqli_store_data(ctx, &$tk1); 160 | sqli_store_data(ctx, &$tk2); 161 | } 162 | ; 163 | 164 | sql_no_parens: 165 | select 166 | | update 167 | | begin_end 168 | | waitfor_delay 169 | | func 170 | | create_function 171 | | shutdown 172 | | declare 173 | | execute 174 | | drop 175 | | use 176 | | _delete 177 | | print 178 | | load 179 | | set 180 | | _goto 181 | | call 182 | | insert 183 | | open 184 | | alter 185 | | if_else 186 | | _while 187 | | _label 188 | ; 189 | 190 | sql_parens: sql_no_parens 191 | | '('[u1] sql_parens ')'[u2] {YYUSE($u1); YYUSE($u2);} 192 | ; 193 | 194 | colref_exact2: 195 | data_name { 196 | sqli_token_data_destructor(&$data_name); 197 | } 198 | | data_name[tname] '.'[u1] data_name[colname] { 199 | sqli_token_data_destructor(&$tname); 200 | sqli_token_data_destructor(&$colname); 201 | YYUSE($u1); 202 | } 203 | | data_name[dname] '.'[u1] data_name[tname] '.'[u2] data_name[colname] { 204 | sqli_token_data_destructor(&$dname); 205 | sqli_token_data_destructor(&$tname); 206 | sqli_token_data_destructor(&$colname); 207 | YYUSE($u1); 208 | YYUSE($u2); 209 | } 210 | | data_name[dname] '.'[u1] '.'[u2] data_name[colname] { 211 | sqli_token_data_destructor(&$dname); 212 | sqli_token_data_destructor(&$colname); 213 | YYUSE($u1); 214 | YYUSE($u2); 215 | } 216 | | '{'[u1] colref_exact '}'[u2] { 217 | YYUSE($u1); 218 | YYUSE($u2); 219 | } 220 | ; 221 | 222 | colref_exact: colref_exact2 223 | | data_name[dname] ':'[u1] colref_exact2 { 224 | sqli_token_data_destructor(&$dname); 225 | YYUSE($u1); 226 | } 227 | ; 228 | 229 | colref_asterisk2: 230 | data_name '.'[u1] '*'[u2] { 231 | sqli_store_data(ctx, &$data_name); 232 | YYUSE($u1); 233 | YYUSE($u2); 234 | } 235 | | data_name[dname] '.'[u1] data_name[tname] '.'[u2] '*'[u3] { 236 | sqli_store_data(ctx, &$dname); 237 | sqli_store_data(ctx, &$tname); 238 | YYUSE($u1); 239 | YYUSE($u2); 240 | YYUSE($u3); 241 | } 242 | ; 243 | 244 | colref_asterisk: colref_asterisk2 245 | | data_name[dname] ':'[u1] colref_asterisk2 { 246 | sqli_token_data_destructor(&$dname); 247 | YYUSE($u1); 248 | } 249 | ; 250 | 251 | name_list: colref_exact 252 | | name_list ','[u1] colref_exact {YYUSE($u1);} 253 | ; 254 | 255 | over_opt: 256 | | TOK_OVER[tk] '('[u1] sort_opt ')'[u2] { 257 | sqli_store_data(ctx, &$tk); 258 | YYUSE($u1); YYUSE($u2); 259 | } 260 | ; 261 | 262 | within_opt: 263 | | TOK_WITHIN[tk1] TOK_GROUP[tk2] '('[u1] sort_opt ')'[u2] { 264 | sqli_store_data(ctx, &$tk1); 265 | sqli_store_data(ctx, &$tk2); 266 | YYUSE($u1); YYUSE($u2); 267 | } 268 | ; 269 | 270 | logical_expr: noop_expr logical_operator noop_expr { 271 | sqli_store_data(ctx, &$logical_operator); 272 | } 273 | | noop_expr TOK_OR2[tk] noop_expr { 274 | sqli_token_data_destructor(&$tk); 275 | } 276 | | noop_expr TOK_IS[tk] TOK_NAME[name] { 277 | sqli_store_data(ctx, &$tk); 278 | sqli_token_data_destructor(&$name); 279 | } 280 | | noop_expr TOK_IS[tk1] TOK_NOT[tk2] TOK_NAME[name] { 281 | sqli_store_data(ctx, &$tk1); 282 | sqli_store_data(ctx, &$tk2); 283 | sqli_token_data_destructor(&$name); 284 | } 285 | | noop_expr '='[operator] noop_expr { 286 | sqli_token_data_destructor(&$operator); 287 | } 288 | | TOK_NOT[operator] logical_expr { 289 | sqli_store_data(ctx, &$operator); 290 | } 291 | | TOK_EXIST[operator] noop_expr { 292 | sqli_store_data(ctx, &$operator); 293 | } 294 | | noop_expr TOK_IN[tk] '('[u1] expr_list ')'[u2] { 295 | sqli_store_data(ctx, &$tk); 296 | YYUSE($u1); 297 | YYUSE($u2); 298 | } 299 | | noop_expr TOK_IN[tk] '('[u1] select ')'[u2] { 300 | sqli_store_data(ctx, &$tk); 301 | YYUSE($u1); 302 | YYUSE($u2); 303 | } 304 | | '('[u1] logical_expr ')'[u2] { 305 | YYUSE($u1); 306 | YYUSE($u2); 307 | } 308 | ; 309 | 310 | expr_common: 311 | func over_opt within_opt 312 | | noop_expr important_operator noop_expr { 313 | sqli_store_data(ctx, &$important_operator); 314 | } 315 | | noop_expr operator noop_expr { 316 | sqli_token_data_destructor(&$operator); 317 | } 318 | | '-'[operator] noop_expr { 319 | sqli_token_data_destructor(&$operator); 320 | } 321 | | '+'[operator] noop_expr { 322 | sqli_token_data_destructor(&$operator); 323 | } 324 | | '~'[operator] noop_expr { 325 | sqli_token_data_destructor(&$operator); 326 | } 327 | | noop_expr ':'[u1] '='[operator] noop_expr { 328 | YYUSE($u1); 329 | sqli_token_data_destructor(&$operator); 330 | } 331 | | TOK_BINARY[operator] noop_expr { 332 | sqli_store_data(ctx, &$operator); 333 | } 334 | | TOK_OUTFILE[operator] noop_expr { 335 | sqli_store_data(ctx, &$operator); 336 | } 337 | | TOK_MATCH[operator] noop_expr TOK_AGAINST[tk] noop_expr { 338 | sqli_store_data(ctx, &$operator); 339 | sqli_store_data(ctx, &$tk); 340 | } 341 | | waitfor_delay 342 | | '('[tk] select ')'[u1] alias_opt { 343 | sqli_store_data(ctx, &$tk); 344 | YYUSE($u1); 345 | } 346 | | '('[u1] expr_list ')'[u2] { 347 | YYUSE($u1); 348 | YYUSE($u2); 349 | } 350 | | '('[u1] expr_list { 351 | YYUSE($u1); 352 | } 353 | | '('[tk] error { 354 | sqli_token_data_destructor(&$tk); 355 | } 356 | | '('[u1] error ')'[u2] { 357 | YYUSE($u1); 358 | YYUSE($u2); 359 | } 360 | | TOK_CASE[tk1] TOK_WHEN[tk2] noop_expr TOK_THEN[tk3] noop_expr TOK_END[tk4] { 361 | sqli_store_data(ctx, &$tk1); 362 | sqli_store_data(ctx, &$tk2); 363 | sqli_store_data(ctx, &$tk3); 364 | sqli_store_data(ctx, &$tk4); 365 | } 366 | | TOK_CASE[tk1] noop_expr TOK_WHEN[tk2] noop_expr TOK_THEN[tk3] noop_expr TOK_END[tk4] { 367 | sqli_store_data(ctx, &$tk1); 368 | sqli_store_data(ctx, &$tk2); 369 | sqli_store_data(ctx, &$tk3); 370 | sqli_store_data(ctx, &$tk4); 371 | } 372 | | TOK_CASE[tk1] TOK_WHEN[tk2] noop_expr TOK_THEN[tk3] noop_expr TOK_ELSE[tk4] noop_expr TOK_END[tk5] { 373 | sqli_store_data(ctx, &$tk1); 374 | sqli_store_data(ctx, &$tk2); 375 | sqli_store_data(ctx, &$tk3); 376 | sqli_store_data(ctx, &$tk4); 377 | sqli_store_data(ctx, &$tk5); 378 | } 379 | | TOK_CASE[tk1] noop_expr TOK_WHEN[tk2] noop_expr TOK_THEN[tk3] noop_expr TOK_ELSE[tk4] noop_expr TOK_END [tk5] { 380 | sqli_store_data(ctx, &$tk1); 381 | sqli_store_data(ctx, &$tk2); 382 | sqli_store_data(ctx, &$tk3); 383 | sqli_store_data(ctx, &$tk4); 384 | sqli_store_data(ctx, &$tk5); 385 | } 386 | ; 387 | 388 | expr: important_operator noop_expr { 389 | $$ = $important_operator; 390 | } 391 | | operator noop_expr { 392 | $$ = $operator; 393 | } 394 | | logical_operator noop_expr { 395 | $$ = $logical_operator; 396 | } 397 | | '='[operator] noop_expr { 398 | $$ = $operator; 399 | } 400 | | TOK_IS[tk] TOK_NAME[name] { 401 | sqli_token_data_destructor(&$name); 402 | $$ = $tk; 403 | } 404 | | TOK_IS[tk1] TOK_NOT[tk2] TOK_NAME[name] { 405 | sqli_token_data_destructor(&$tk2); 406 | sqli_token_data_destructor(&$name); 407 | $$ = $tk1; 408 | } 409 | | expr post_exprs 410 | ; 411 | 412 | post_expr_tk: 413 | TOK_IN[tk] TOK_BOOLEAN[u1] TOK_MODE[u2] { 414 | $$ = $tk; 415 | $$.value = (struct detect_str){CSTR_LEN("IN_BOOLEAN_MODE")}; 416 | YYUSE($u1); 417 | YYUSE($u2); 418 | } 419 | | TOK_END 420 | ; 421 | 422 | post_expr: 423 | post_expr_tk[tk] { 424 | sqli_store_data(ctx, &$tk); 425 | } 426 | ; 427 | 428 | post_exprs: 429 | post_expr 430 | | post_exprs post_expr 431 | ; 432 | 433 | func_name: colref_exact 434 | | TOK_LEFT 435 | | TOK_DATABASE 436 | | TOK_IF 437 | | TOK_REPLACE 438 | | TOK_LIKE 439 | ; 440 | 441 | expr_list: 442 | noop_expr 443 | | expr_list ','[u1] noop_expr {YYUSE($u1);} 444 | ; 445 | 446 | expr_list_opt: 447 | | expr_list 448 | ; 449 | 450 | func_args_list: 451 | noop_expr 452 | | noop_expr TOK_AS[tk] data_name[type] { 453 | sqli_token_data_destructor(&$tk); 454 | sqli_token_data_destructor(&$type); 455 | } 456 | | noop_expr TOK_AS[tk] data_name[type] '('[u1] noop_expr ')'[u2] { 457 | sqli_token_data_destructor(&$tk); 458 | sqli_token_data_destructor(&$type); 459 | YYUSE($u1); 460 | YYUSE($u2); 461 | } 462 | | func_args_list ','[u1] noop_expr {YYUSE($u1);} 463 | ; 464 | 465 | func_args_modifier_tk: 466 | TOK_DISTINCT 467 | | TOK_ALL 468 | | TOK_VARIADIC 469 | ; 470 | 471 | func_args_modifier: 472 | func_args_modifier_tk[tk] { 473 | sqli_store_data(ctx, &$tk); 474 | } 475 | ; 476 | 477 | func_args_modifier_opt: 478 | | func_args_modifier 479 | ; 480 | 481 | func_distinct_opt: 482 | | TOK_DISTINCT 483 | ; 484 | 485 | func_args: '('[u1] func_distinct_opt ')'[u2] { 486 | YYUSE($u1); 487 | YYUSE($u2); 488 | } 489 | | '('[u1] func_distinct_opt '*'[u2] ')'[u3] { 490 | YYUSE($u1); 491 | YYUSE($u2); 492 | YYUSE($u3); 493 | } 494 | | '('[u1] func_distinct_opt func_args_modifier_opt 495 | func_args_list ')'[u2] { 496 | 497 | YYUSE($u1); 498 | YYUSE($u2); 499 | } 500 | ; 501 | 502 | func: func_name func_args 503 | ; 504 | 505 | noop_expr: expr_common 506 | | logical_expr 507 | | colref_exact 508 | | colref_asterisk 509 | | TOK_NUM { 510 | sqli_token_data_destructor(&$TOK_NUM); 511 | } 512 | | noop_expr post_exprs 513 | ; 514 | 515 | operator: TOK_OPERATOR 516 | | '~' 517 | | '+' 518 | | '-' 519 | | '*' 520 | | '.' 521 | | ':'[tk1] ':'[tk2] { 522 | sqli_token_data_destructor(&$tk1); 523 | $$ = $tk2; 524 | } 525 | ; 526 | 527 | logical_operator: TOK_OR 528 | | TOK_AND 529 | | TOK_NOT[tk1] TOK_IN[tk2] { 530 | sqli_token_data_destructor(&$tk1); 531 | $$ = $tk2; 532 | } 533 | | TOK_REGEXP 534 | | TOK_NOT[tk1] TOK_REGEXP[tk2] { 535 | sqli_token_data_destructor(&$tk1); 536 | $$ = $tk2; 537 | } 538 | | TOK_BETWEEN 539 | | TOK_NOT[tk1] TOK_BETWEEN[tk2] { 540 | sqli_token_data_destructor(&$tk1); 541 | $$ = $tk2; 542 | } 543 | | TOK_LIKE 544 | | TOK_NOT[tk1] TOK_LIKE[tk2] { 545 | sqli_token_data_destructor(&$tk1); 546 | $$ = $tk2; 547 | } 548 | | TOK_SOUNDS[tk1] TOK_LIKE[tk2] { 549 | sqli_token_data_destructor(&$tk1); 550 | $$ = $tk2; 551 | } 552 | | TOK_RLIKE 553 | | TOK_NOT[tk1] TOK_RLIKE[tk2] { 554 | sqli_token_data_destructor(&$tk1); 555 | $$ = $tk2; 556 | } 557 | | TOK_SOUNDS 558 | | TOK_INTO 559 | ; 560 | 561 | important_operator: TOK_DIV 562 | | TOK_MOD 563 | | TOK_XOR 564 | | TOK_BINARY 565 | | TOK_COLLATE 566 | | TOK_UESCAPE 567 | | TOK_USING 568 | ; 569 | 570 | select_distinct_opt: 571 | | TOK_DISTINCT 572 | | TOK_ALL 573 | ; 574 | 575 | select_arg: 576 | noop_expr alias_opt 577 | ; 578 | 579 | select_list: select_arg 580 | | select_list ','[u1] select_arg {YYUSE($u1);} 581 | ; 582 | 583 | top_opt: 584 | | TOK_TOP[tk] TOK_NUM[data] percent_opt { 585 | sqli_store_data(ctx, &$tk); 586 | sqli_store_data(ctx, &$data); 587 | } 588 | | TOK_TOP[tk] '('[u1] noop_expr ')'[u2] percent_opt { 589 | sqli_store_data(ctx, &$tk); 590 | YYUSE($u1); 591 | YYUSE($u2); 592 | } 593 | | TOK_FIRST[tk] TOK_NUM[data] { 594 | sqli_store_data(ctx, &$tk); 595 | sqli_store_data(ctx, &$data); 596 | } 597 | | TOK_FIRST[tk] '('[u1] noop_expr ')'[u2] { 598 | sqli_store_data(ctx, &$tk); 599 | YYUSE($u1); 600 | YYUSE($u2); 601 | } 602 | ; 603 | 604 | select_args: select_distinct_opt top_opt '*'[key] { 605 | sqli_store_data(ctx, &$key); 606 | } 607 | | select_distinct_opt top_opt select_list 608 | | top_opt select_distinct_opt '*'[key] { 609 | sqli_store_data(ctx, &$key); 610 | } 611 | | top_opt select_distinct_opt select_list 612 | ; 613 | 614 | as_opt: 615 | | TOK_AS[tk] { 616 | sqli_token_data_destructor(&$tk); 617 | } 618 | ; 619 | 620 | alias: as_opt data_name[data] { 621 | sqli_token_data_destructor(&$data); 622 | } 623 | ; 624 | 625 | alias_opt: 626 | | alias 627 | ; 628 | 629 | table_ref2: 630 | func 631 | | colref_exact 632 | | '('[u1] select ')'[u2] {YYUSE($u1); YYUSE($u2);} 633 | | joined_table 634 | | '('[u1] joined_table ')'[u2] {YYUSE($u1); YYUSE($u2);} 635 | ; 636 | 637 | join_type: 638 | TOK_CROSS 639 | | TOK_FULL 640 | | TOK_LEFT 641 | | TOK_RIGHT 642 | | TOK_INNER 643 | ; 644 | 645 | join_type_opt: 646 | | join_type[tk] { 647 | sqli_store_data(ctx, &$tk); 648 | } 649 | ; 650 | 651 | natural_opt: 652 | | TOK_NATURAL[tk] { 653 | sqli_store_data(ctx, &$tk); 654 | } 655 | ; 656 | 657 | join_qual: 658 | | TOK_USING[tk] '('[u1] name_list ')'[u2] { 659 | sqli_store_data(ctx, &$tk); 660 | YYUSE($u1); 661 | YYUSE($u2); 662 | } 663 | | TOK_ON[tk] noop_expr { 664 | sqli_store_data(ctx, &$tk); 665 | } 666 | ; 667 | 668 | joined_table: 669 | '('[u1] joined_table ')'[u2] {YYUSE($u1); YYUSE($u2);} 670 | | table_ref natural_opt join_type_opt TOK_JOIN[tk] table_ref join_qual { 671 | sqli_store_data(ctx, &$tk); 672 | } 673 | ; 674 | 675 | table_ref: table_ref2 alias_opt 676 | ; 677 | 678 | from_list: table_ref 679 | | from_list ','[u1] table_ref {YYUSE($u1);} 680 | ; 681 | 682 | from_opt: 683 | | TOK_FROM[key] from_list { 684 | sqli_store_data(ctx, &$key); 685 | } 686 | | TOK_FROM[key] '('[u1] from_list ')'[u2] { 687 | sqli_store_data(ctx, &$key); 688 | YYUSE($u1); 689 | YYUSE($u2); 690 | } 691 | ; 692 | 693 | where_opt: 694 | | TOK_WHERE[key] noop_expr { 695 | sqli_store_data(ctx, &$key); 696 | } 697 | ; 698 | 699 | group_opt: 700 | | TOK_GROUP[tk1] TOK_BY[tk2] func_args_list { 701 | sqli_store_data(ctx, &$tk1); 702 | sqli_store_data(ctx, &$tk2); 703 | } 704 | ; 705 | 706 | having_opt: 707 | | TOK_HAVING[tk1] noop_expr { 708 | sqli_store_data(ctx, &$tk1); 709 | } 710 | ; 711 | 712 | order: 713 | | TOK_DESC[tk] { 714 | sqli_store_data(ctx, &$tk); 715 | } 716 | | TOK_ASC [tk]{ 717 | sqli_store_data(ctx, &$tk); 718 | } 719 | ; 720 | 721 | sort_list: 722 | noop_expr order 723 | | sort_list ','[u1] noop_expr order {YYUSE($u1);} 724 | | error 725 | ; 726 | 727 | sort_opt: 728 | | TOK_ORDER[tk1] TOK_BY[tk2] sort_list { 729 | sqli_store_data(ctx, &$tk1); 730 | sqli_store_data(ctx, &$tk2); 731 | } 732 | ; 733 | 734 | select_extra_tk: 735 | TOK_LIMIT 736 | | TOK_OFFSET 737 | | TOK_FOR[tk] TOK_UPDATE[u1] { 738 | $$ = $tk; 739 | $$.value = (struct detect_str){CSTR_LEN("FOR_UPDATE")}; 740 | YYUSE($u1); 741 | } 742 | | TOK_FOR[tk] TOK_XML[u1] TOK_PATH[u2] '('[u3] data_name[elem] ')'[u4] { 743 | $$ = $tk; 744 | $$.value = (struct detect_str){CSTR_LEN("TOK_FOR TOK_XML TOK_PATH")}; 745 | YYUSE($u1); 746 | YYUSE($u2); 747 | YYUSE($u3); 748 | sqli_store_data(ctx, &$elem); 749 | YYUSE($u4); 750 | } 751 | ; 752 | 753 | select_extra: 754 | select_extra_tk expr_list_opt 755 | ; 756 | 757 | select_extras: select_extra 758 | | select_extras select_extra 759 | ; 760 | 761 | select_extras_opt: 762 | | select_extras 763 | ; 764 | 765 | procedure: 766 | TOK_PROCEDURE[tk1] func { 767 | sqli_store_data(ctx, &$tk1); 768 | } 769 | ; 770 | 771 | procedure_opt: 772 | | procedure 773 | ; 774 | 775 | lock_opt: 776 | | TOK_LOCK[tk1] TOK_IN[tk2] TOK_SHARE[tk3] TOK_MODE[tk4] { 777 | sqli_store_data(ctx, &$tk1); 778 | sqli_store_data(ctx, &$tk2); 779 | sqli_store_data(ctx, &$tk3); 780 | sqli_store_data(ctx, &$tk4); 781 | } 782 | ; 783 | 784 | select_after_where: 785 | group_opt having_opt sort_opt select_extras_opt procedure_opt lock_opt 786 | ; 787 | 788 | outfile_opt: 789 | | TOK_OUTFILE[tk] { 790 | sqli_store_data(ctx, &$tk); 791 | } 792 | ; 793 | 794 | into_opt: 795 | | TOK_INTO[tk] outfile_opt colref_exact { 796 | sqli_store_data(ctx, &$tk); 797 | } 798 | ; 799 | 800 | union_tk: 801 | TOK_UNION 802 | | TOK_INTERSECT 803 | | TOK_EXCEPT 804 | ; 805 | 806 | union_c: union_tk[tk] { 807 | sqli_token_data_destructor(&$tk); 808 | } 809 | ; 810 | 811 | all_distinct_opt: 812 | | TOK_ALL[tk] { 813 | sqli_store_data(ctx, &$tk); 814 | } 815 | | TOK_DISTINCT[tk] { 816 | sqli_store_data(ctx, &$tk); 817 | } 818 | ; 819 | 820 | select_parens: 821 | select 822 | | '('[u1] select_parens ')'[u2] {YYUSE($u1); YYUSE($u2);} 823 | ; 824 | 825 | percent_opt: 826 | | TOK_PERCENT[tk] { 827 | sqli_store_data(ctx, &$tk); 828 | } 829 | 830 | select: TOK_SELECT[tk] select_args into_opt from_opt 831 | where_opt select_after_where { 832 | sqli_store_data(ctx, &$tk); 833 | } 834 | | TOK_SELECT[tk] select_args from_opt into_opt 835 | where_opt select_after_where { 836 | sqli_store_data(ctx, &$tk); 837 | } 838 | | select union_c all_distinct_opt select_parens 839 | | select union_c all_distinct_opt execute 840 | ; 841 | 842 | begin_end: TOK_BEGIN[tk1] multiple_sqls TOK_END[tk2] { 843 | sqli_store_data(ctx, &$tk1); 844 | sqli_store_data(ctx, &$tk2); 845 | } 846 | ; 847 | 848 | waitfor_delay: TOK_WAITFOR[tk1] TOK_DELAY[tk2] data_name[data] { 849 | sqli_store_data(ctx, &$tk1); 850 | sqli_store_data(ctx, &$tk2); 851 | sqli_store_data(ctx, &$data); 852 | } 853 | | TOK_WAITFOR[tk1] TOK_TIME[tk2] data_name[data] { 854 | sqli_store_data(ctx, &$tk1); 855 | sqli_store_data(ctx, &$tk2); 856 | sqli_store_data(ctx, &$data); 857 | } 858 | ; 859 | 860 | or_replace_opt: 861 | | TOK_OR[tk1] TOK_REPLACE[tk2] { 862 | sqli_store_data(ctx, &$tk1); 863 | sqli_store_data(ctx, &$tk2); 864 | } 865 | ; 866 | 867 | create_function_body: TOK_AS[tk] data_name[obj_file] ','[u1] data_name[link_symbol] { 868 | sqli_store_data(ctx, &$tk); 869 | sqli_store_data(ctx, &$obj_file); 870 | YYUSE($u1); 871 | sqli_store_data(ctx, &$link_symbol); 872 | } 873 | | TOK_LANGUAGE[tk] data_name[lang_name] { 874 | sqli_store_data(ctx, &$tk); 875 | sqli_store_data(ctx, &$lang_name); 876 | } 877 | | TOK_STRICT[tk] { 878 | sqli_store_data(ctx, &$tk); 879 | } 880 | ; 881 | 882 | create_function_bodies: create_function_body 883 | | create_function_bodies create_function_body 884 | ; 885 | 886 | create_function: TOK_CREATE[tk1] or_replace_opt TOK_FUNCTION[tk2] func 887 | TOK_RETURNS[tk3] data_name[rettype] create_function_bodies { 888 | sqli_store_data(ctx, &$tk1); 889 | sqli_store_data(ctx, &$tk2); 890 | sqli_store_data(ctx, &$tk3); 891 | sqli_store_data(ctx, &$rettype); 892 | } 893 | ; 894 | 895 | shutdown: TOK_SHUTDOWN[tk] { 896 | sqli_store_data(ctx, &$tk); 897 | } 898 | ; 899 | 900 | var_list: data_name[name] { 901 | sqli_store_data(ctx, &$name); 902 | } 903 | | data_name[name] ','[u1] var_list { 904 | sqli_store_data(ctx, &$name); 905 | YYUSE($u1); 906 | } 907 | ; 908 | 909 | declare: TOK_DECLARE[tk] var_list as_opt data_name[type] { 910 | sqli_store_data(ctx, &$tk); 911 | sqli_store_data(ctx, &$type); 912 | } 913 | | TOK_DECLARE[tk] var_list as_opt data_name[type] '='[u1] noop_expr { 914 | sqli_store_data(ctx, &$tk); 915 | sqli_store_data(ctx, &$type); 916 | YYUSE($u1); 917 | } 918 | | TOK_DECLARE[tk] var_list as_opt data_name[type] '('[u1] TOK_NUM[len] ')'[u2] { 919 | sqli_store_data(ctx, &$tk); 920 | sqli_store_data(ctx, &$type); 921 | YYUSE($u1); 922 | sqli_store_data(ctx, &$len); 923 | YYUSE($u2); 924 | } 925 | | TOK_DECLARE[tk1] data_name[name] TOK_CURSOR[tk2] TOK_FOR[tk3] select_parens { 926 | sqli_store_data(ctx, &$tk1); 927 | sqli_store_data(ctx, &$name); 928 | sqli_store_data(ctx, &$tk2); 929 | sqli_store_data(ctx, &$tk3); 930 | } 931 | | TOK_DECLARE[tk] var_list as_opt data_name[type] '('[u1] TOK_NUM[len] ')'[u2] '='[u3] noop_expr { 932 | sqli_store_data(ctx, &$tk); 933 | sqli_store_data(ctx, &$type); 934 | YYUSE($u1); 935 | sqli_store_data(ctx, &$len); 936 | YYUSE($u2); 937 | YYUSE($u3); 938 | } 939 | ; 940 | 941 | param: data_name[value] { 942 | sqli_store_data(ctx, &$value); 943 | } 944 | | data_name[name] '='[u1] data_name[value] { 945 | sqli_store_data(ctx, &$name); 946 | YYUSE($u1); 947 | sqli_store_data(ctx, &$value); 948 | } 949 | ; 950 | 951 | param_list: param 952 | | param ','[u1] param_list { 953 | YYUSE($u1); 954 | } 955 | ; 956 | execute: 957 | TOK_EXECUTE[tk] func_name param_list { 958 | sqli_store_data(ctx, &$tk); 959 | } 960 | | TOK_EXECUTE[tk] '('[u1] data_name[name] ')'[u2] { 961 | sqli_store_data(ctx, &$tk); 962 | YYUSE($u1); 963 | sqli_store_data(ctx, &$name); 964 | YYUSE($u2); 965 | } 966 | ; 967 | 968 | drop: TOK_DROP[tk1] TOK_FUNCTION[tk2] func_name { 969 | sqli_store_data(ctx, &$tk1); 970 | sqli_store_data(ctx, &$tk2); 971 | } 972 | | TOK_DROP[tk1] TOK_TABLE[tk2] func_name { 973 | sqli_store_data(ctx, &$tk1); 974 | sqli_store_data(ctx, &$tk2); 975 | } 976 | | TOK_DROP[tk1] TOK_DATABASE[tk2] func_name { 977 | sqli_store_data(ctx, &$tk1); 978 | sqli_store_data(ctx, &$tk2); 979 | } 980 | ; 981 | 982 | use: 983 | TOK_USE[tk] data_name[database] { 984 | sqli_store_data(ctx, &$tk); 985 | sqli_store_data(ctx, &$database); 986 | } 987 | ; 988 | 989 | delete_modifier: 990 | TOK_IGNORE 991 | | TOK_LOW_PRIORITY 992 | | TOK_QUICK 993 | ; 994 | 995 | delete_modifier_opt: 996 | | delete_modifier[tk] { 997 | sqli_store_data(ctx, &$tk); 998 | } 999 | ; 1000 | 1001 | limit_op: TOK_LIMIT[tk] TOK_NUM[row_count] { 1002 | sqli_store_data(ctx, &$tk); 1003 | sqli_store_data(ctx, &$row_count); 1004 | } 1005 | ; 1006 | 1007 | _delete: TOK_DELETE[tk1] delete_modifier_opt TOK_FROM[key] from_list where_opt sort_opt limit_op { 1008 | sqli_store_data(ctx, &$tk1); 1009 | sqli_store_data(ctx, &$key); 1010 | } 1011 | | TOK_DELETE[tk1] delete_modifier_opt from_list TOK_FROM[key] from_list where_opt { 1012 | sqli_store_data(ctx, &$tk1); 1013 | sqli_store_data(ctx, &$key); 1014 | } 1015 | | TOK_DELETE[tk1] delete_modifier_opt TOK_FROM[key] from_list TOK_USING[tk2] from_list where_opt { 1016 | sqli_store_data(ctx, &$tk1); 1017 | sqli_store_data(ctx, &$key); 1018 | sqli_store_data(ctx, &$tk2); 1019 | } 1020 | | TOK_DELETE[tk1] top_opt TOK_FROM[key] from_list where_opt { 1021 | sqli_store_data(ctx, &$tk1); 1022 | sqli_store_data(ctx, &$key); 1023 | } 1024 | ; 1025 | 1026 | print: TOK_PRINT[tk] noop_expr { 1027 | sqli_store_data(ctx, &$tk); 1028 | } 1029 | ; 1030 | 1031 | load_modifier: 1032 | TOK_LOW_PRIORITY 1033 | | TOK_CONCURRENT 1034 | ; 1035 | 1036 | load_modifier_opt: 1037 | | load_modifier[tk] { 1038 | sqli_store_data(ctx, &$tk); 1039 | } 1040 | ; 1041 | 1042 | local_opt: 1043 | | TOK_LOCAL[tk] { 1044 | sqli_store_data(ctx, &$tk); 1045 | } 1046 | ; 1047 | 1048 | data_xml: 1049 | TOK_DATA2 1050 | | TOK_XML 1051 | ; 1052 | 1053 | load: 1054 | TOK_LOAD[tk1] data_xml[tk2] load_modifier_opt local_opt TOK_INFILE[tk3] data_name[file_name] { 1055 | sqli_store_data(ctx, &$tk1); 1056 | sqli_store_data(ctx, &$tk2); 1057 | sqli_store_data(ctx, &$tk3); 1058 | sqli_store_data(ctx, &$file_name); 1059 | } 1060 | ; 1061 | 1062 | set: TOK_SET[tk] noop_expr { 1063 | sqli_token_data_destructor(&$tk); 1064 | } 1065 | ; 1066 | 1067 | _goto: TOK_GOTO[tk] data_name[label] { 1068 | sqli_store_data(ctx, &$tk); 1069 | sqli_store_data(ctx, &$label); 1070 | } 1071 | ; 1072 | 1073 | call: TOK_CALL[tk] func { 1074 | sqli_store_data(ctx, &$tk); 1075 | } 1076 | ; 1077 | 1078 | insert: TOK_INSERT[tk1] TOK_INTO[tk2] colref_exact execute { 1079 | sqli_store_data(ctx, &$tk1); 1080 | sqli_store_data(ctx, &$tk2); 1081 | } 1082 | | TOK_INSERT[tk1] TOK_INTO[tk2] colref_exact '('[u1] name_list ')'[u2] TOK_VALUES[tk3] '('[u3] func_args_list ')'[u4] { 1083 | sqli_store_data(ctx, &$tk1); 1084 | sqli_store_data(ctx, &$tk2); 1085 | YYUSE($u1); 1086 | YYUSE($u2); 1087 | sqli_store_data(ctx, &$tk3); 1088 | YYUSE($u3); 1089 | YYUSE($u4); 1090 | } 1091 | ; 1092 | 1093 | open: TOK_OPEN[tk] data_name[name] { 1094 | sqli_store_data(ctx, &$tk); 1095 | sqli_store_data(ctx, &$name); 1096 | } 1097 | ; 1098 | 1099 | alter: TOK_ALTER[tk1] TOK_DATABASE[tk2] data_name[name] TOK_SET[tk3] TOK_RECOVERY[tk4] TOK_SIMPLE[tk5] { 1100 | sqli_store_data(ctx, &$tk1); 1101 | sqli_store_data(ctx, &$tk2); 1102 | sqli_store_data(ctx, &$name); 1103 | sqli_store_data(ctx, &$tk3); 1104 | sqli_store_data(ctx, &$tk4); 1105 | sqli_store_data(ctx, &$tk5); 1106 | } 1107 | ; 1108 | 1109 | if_else: TOK_IF[tk1] noop_expr sql_parens { 1110 | sqli_store_data(ctx, &$tk1); 1111 | } 1112 | | TOK_IF[tk1] noop_expr sql_parens TOK_ELSE[tk2] sql_parens { 1113 | sqli_store_data(ctx, &$tk1); 1114 | sqli_store_data(ctx, &$tk2); 1115 | } 1116 | | TOK_IF[tk1] noop_expr TOK_THEN[tk2] sql_parens semicolons_opt TOK_END[tk3] TOK_IF[tk4] { 1117 | sqli_store_data(ctx, &$tk1); 1118 | sqli_store_data(ctx, &$tk2); 1119 | sqli_store_data(ctx, &$tk3); 1120 | sqli_store_data(ctx, &$tk4); 1121 | } 1122 | | TOK_IF[tk1] noop_expr TOK_THEN[tk2] sql_parens semicolons_opt 1123 | TOK_ELSE[tk3] sql_parens semicolons_opt TOK_END[tk4] TOK_IF[tk5] { 1124 | sqli_store_data(ctx, &$tk1); 1125 | sqli_store_data(ctx, &$tk2); 1126 | sqli_store_data(ctx, &$tk3); 1127 | sqli_store_data(ctx, &$tk4); 1128 | sqli_store_data(ctx, &$tk5); 1129 | } 1130 | ; 1131 | 1132 | _while: TOK_WHILE[tk1] noop_expr sql_parens { 1133 | sqli_store_data(ctx, &$tk1); 1134 | } 1135 | ; 1136 | 1137 | _label: TOK_NAME[tk] ':'[u1] { 1138 | sqli_token_data_destructor(&$tk); 1139 | YYUSE(&$u1); 1140 | } 1141 | ; 1142 | 1143 | close_multiple_parens: ')'[u1] {YYUSE($u1);} 1144 | | close_multiple_parens ')'[u1] {YYUSE($u1);} 1145 | ; 1146 | 1147 | close_multiple_parens_opt: 1148 | | close_multiple_parens 1149 | ; 1150 | 1151 | start_rce: TOK_START_RCE start_rce_cont 1152 | ; 1153 | 1154 | semicolons: ';'[u1] {YYUSE($u1);} 1155 | | semicolons ';'[u1] {YYUSE($u1);} 1156 | ; 1157 | 1158 | semicolons_opt: 1159 | | semicolons 1160 | ; 1161 | 1162 | multiple_sqls: sql_parens semicolons_opt 1163 | | sql_parens semicolons_opt multiple_sqls 1164 | ; 1165 | 1166 | select_after_where_optunion: 1167 | select_after_where 1168 | | select_after_where union_c all_distinct_opt select_parens 1169 | | select_after_where union_c all_distinct_opt execute 1170 | ; 1171 | 1172 | after_exp_cont_op_noexpr: 1173 | select_after_where_optunion 1174 | ; 1175 | 1176 | after_exp_cont_op: 1177 | expr[tk] after_exp_cont_op_noexpr { 1178 | sqli_store_data(ctx, &$tk); 1179 | } 1180 | | alias_opt from_opt where_opt after_exp_cont_op_noexpr 1181 | ; 1182 | 1183 | after_exp_cont: 1184 | close_multiple_parens_opt semicolons_opt multiple_sqls 1185 | | close_multiple_parens after_exp_cont_op after_exp_cont 1186 | | close_multiple_parens alias_opt ','[u1] select_list from_opt where_opt after_exp_cont { 1187 | YYUSE($u1); 1188 | } 1189 | | close_multiple_parens alias_opt ','[u1] from_list where_opt after_exp_cont { 1190 | YYUSE($u1); 1191 | } 1192 | | after_exp_cont_op_noexpr close_multiple_parens after_exp_cont 1193 | ; 1194 | 1195 | start_rce_cont: close_multiple_parens_opt semicolons_opt multiple_sqls 1196 | | after_exp_cont_op_noexpr close_multiple_parens after_exp_cont 1197 | ; 1198 | 1199 | %% 1200 | --------------------------------------------------------------------------------