mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 05:45:59 +03:00
Lua: Added support for scripting to @inspectFile.
This commit is contained in:
parent
4cecdf4c5b
commit
f64c7c39e8
@ -9,7 +9,6 @@
|
||||
*
|
||||
*/
|
||||
#include <limits.h>
|
||||
#include <apr_lib.h>
|
||||
|
||||
#include "modsecurity.h"
|
||||
#include "msc_logging.h"
|
||||
@ -18,15 +17,6 @@
|
||||
|
||||
#include "msc_lua.h"
|
||||
|
||||
static const char *configuration_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) {
|
||||
if (filename == NULL) return NULL;
|
||||
// TODO Support paths on operating systems other than Unix.
|
||||
if (filename[0] == '/') return filename;
|
||||
|
||||
return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename,
|
||||
strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))),
|
||||
filename, NULL);
|
||||
}
|
||||
|
||||
/* -- Directory context creation and initialisation -- */
|
||||
|
||||
@ -1297,7 +1287,7 @@ static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) {
|
||||
static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1,
|
||||
const char *p2)
|
||||
{
|
||||
const char *filename = configuration_relative_path(cmd->pool, cmd->directive->filename, p1);
|
||||
const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
|
||||
return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL);
|
||||
}
|
||||
#endif
|
||||
|
@ -244,7 +244,7 @@ static const struct luaL_Reg mylib[] = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int lua_execute(msc_script *script, modsec_rec *msr, msre_rule *rule, char **error_msg) {
|
||||
int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg) {
|
||||
apr_time_t time_before;
|
||||
lua_State *L = NULL;
|
||||
int rc;
|
||||
@ -288,7 +288,12 @@ int lua_execute(msc_script *script, modsec_rec *msr, msre_rule *rule, char **err
|
||||
/* Execute main() */
|
||||
lua_getglobal(L, "main");
|
||||
|
||||
if (lua_pcall(L, 0, 1, 0)) {
|
||||
/* Put the parameter on the stack. */
|
||||
if (param != NULL) {
|
||||
lua_pushlstring(L, param, strlen(param));
|
||||
}
|
||||
|
||||
if (lua_pcall(L, ((param != NULL) ? 1 : 0), 1, 0)) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Lua: Script execution failed: %s", lua_tostring(L, -1));
|
||||
return -1;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ struct msc_script_part {
|
||||
|
||||
char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool);
|
||||
|
||||
int DSOLOCAL lua_execute(msc_script *script, modsec_rec *msr, msre_rule *rule, char **error_msg);
|
||||
int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <apr_lib.h>
|
||||
|
||||
/* NOTE: Be careful as these can ONLY be used on static values for X.
|
||||
* (i.e. VALID_HEX(c++) will NOT work)
|
||||
*/
|
||||
@ -1176,3 +1178,28 @@ char *modsec_build(apr_pool_t *mp) {
|
||||
build_type,
|
||||
atoi(MODSEC_VERSION_RELEASE));
|
||||
}
|
||||
|
||||
int is_empty_string(const char *string) {
|
||||
unsigned int i;
|
||||
|
||||
if (string == NULL) return 1;
|
||||
if (strlen(string) == 0) return 1;
|
||||
|
||||
for(i = 0; i < strlen(string); i++) {
|
||||
if (!isspace(string[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) {
|
||||
if (filename == NULL) return NULL;
|
||||
// TODO Support paths on operating systems other than Unix.
|
||||
if (filename[0] == '/') return (char *)filename;
|
||||
|
||||
return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename,
|
||||
strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))),
|
||||
filename, NULL);
|
||||
}
|
||||
|
@ -78,4 +78,8 @@ int DSOLOCAL ansi_c_sequences_decode_inplace(unsigned char *input, int len);
|
||||
|
||||
char DSOLOCAL *modsec_build(apr_pool_t *mp);
|
||||
|
||||
int DSOLOCAL is_empty_string(const char *string);
|
||||
|
||||
char DSOLOCAL *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename);
|
||||
|
||||
#endif
|
||||
|
@ -1966,7 +1966,7 @@ static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) {
|
||||
acting_actionset = rule->chain_starter->actionset;
|
||||
}
|
||||
|
||||
rc = lua_execute(rule->script, msr, rule, &my_error_msg);
|
||||
rc = lua_execute(rule->script, NULL, msr, rule, &my_error_msg);
|
||||
if (rc < 0) {
|
||||
msr_log(msr, 1, "%s", my_error_msg);
|
||||
return -1;
|
||||
|
@ -1484,7 +1484,7 @@ static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp,
|
||||
msc_script *script = (msc_script *)action->param_data;
|
||||
char *my_error_msg = NULL;
|
||||
|
||||
if (lua_execute(script, msr, rule, &my_error_msg) < 0) {
|
||||
if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) {
|
||||
msr_log(msr, 1, "%s", my_error_msg);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1237,17 +1237,58 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||||
}
|
||||
|
||||
/* inspectFile */
|
||||
|
||||
static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) {
|
||||
char *filename = (char *)rule->op_param;
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
if ((filename == NULL)||(is_empty_string(filename))) {
|
||||
*error_msg = apr_psprintf(rule->ruleset->mp, "Operator @inspectFile requires parameter.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename);
|
||||
|
||||
#ifdef WITH_LUA
|
||||
// TODO Write & use string_ends(s, e).
|
||||
if (strlen(rule->op_param) > 4) {
|
||||
char *p = filename + strlen(filename) - 4;
|
||||
if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a'))
|
||||
{
|
||||
msc_script *script = NULL;
|
||||
|
||||
/* Compile script. */
|
||||
*error_msg = lua_compile(&script, filename, rule->ruleset->mp);
|
||||
if (*error_msg != NULL) return -1;
|
||||
|
||||
rule->op_param_data = script;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rule->op_param_data == NULL) {
|
||||
// TODO Verify the script exists and that we have
|
||||
// the rights to execute it.
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||||
char **error_msg)
|
||||
{
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
if (rule->op_param_data == NULL) {
|
||||
/* Execute externally, as native binary/shell script. */
|
||||
char *script_output = NULL;
|
||||
char const *argv[5];
|
||||
const char *approver_script = rule->op_param;
|
||||
const char *target_file = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
msr_log(msr, 4, "Executing %s to inspect %s.", approver_script, target_file);
|
||||
|
||||
argv[0] = approver_script;
|
||||
@ -1275,6 +1316,20 @@ static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_va
|
||||
|
||||
/* No match. */
|
||||
return 0;
|
||||
} else {
|
||||
/* Execute internally, as Lua script. */
|
||||
char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||||
msc_script *script = (msc_script *)rule->op_param_data;
|
||||
int rc;
|
||||
|
||||
rc = lua_execute(script, target, msr, rule, error_msg);
|
||||
if (rc < 0) {
|
||||
/* Error. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* validateByteRange */
|
||||
@ -1867,7 +1922,7 @@ void msre_engine_register_default_operators(msre_engine *engine) {
|
||||
/* inspectFile */
|
||||
msre_engine_op_register(engine,
|
||||
"inspectFile",
|
||||
NULL,
|
||||
msre_op_inspectFile_init,
|
||||
msre_op_inspectFile_execute
|
||||
);
|
||||
|
||||
|
@ -339,9 +339,8 @@
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Add one line to your configuration to load libxml2:
|
||||
<filename moreinfo="none">LoadFile
|
||||
/usr/lib/libxml2.so</filename></para>
|
||||
<para>Add one line to your configuration to load libxml2: <filename
|
||||
moreinfo="none">LoadFile /usr/lib/libxml2.so</filename></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
@ -420,13 +419,13 @@
|
||||
"allow" rules or to correct any false positives in the Core rules as they
|
||||
are applied to your site.</para>
|
||||
|
||||
<para><emphasis>Note</emphasis></para>
|
||||
|
||||
<note>
|
||||
<para>It is highly encouraged that you do not edit the Core rules files
|
||||
themselves but rather place all changes (such as
|
||||
<literal>SecRuleRemoveByID</literal>, etc...) in your custom rules file.
|
||||
This will allow for easier upgrading as newer Core rules are released by
|
||||
Breach Security on the ModSecurity website.</para>
|
||||
</note>
|
||||
|
||||
<section>
|
||||
<title><literal>SecAction</literal></title>
|
||||
@ -690,7 +689,7 @@ SecAuditLogStorageDir logs/audit
|
||||
<listitem>
|
||||
<para><literal moreinfo="none">K</literal> - This part contains a
|
||||
full list of every rule that matched (one per line) in the order
|
||||
they were matched.</para>
|
||||
they were matched. Supported as of v2.5.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
@ -1798,8 +1797,13 @@ ServerAlias www.app2.com
|
||||
optional parameter is the list of actions whose meaning is identical to
|
||||
that of <literal>SecRule</literal>.</para>
|
||||
|
||||
<para><emphasis>Syntax:</emphasis> SecRuleScript /path/to/script.lua
|
||||
[ACTIONS]</para>
|
||||
<para><emphasis>Syntax:</emphasis> <literal>SecRuleScript
|
||||
/path/to/script.lua [ACTIONS]</literal></para>
|
||||
|
||||
<para>The first parameter, path to the script, can be absolute or
|
||||
relative to the configuration file in which
|
||||
<literal>SecRuleScript</literal> resides. This allows you to place your
|
||||
script in the same folder as the configuration files using them.</para>
|
||||
|
||||
<para>Example script:</para>
|
||||
|
||||
@ -1819,8 +1823,10 @@ function main()
|
||||
var2 = m.getvar("REQUEST_URI", "normalisePath");
|
||||
|
||||
-- Retrieve one variable, applying several transformation functions.
|
||||
-- Notice how the second parameter is now a list.
|
||||
var3 = m.getvar("ARGS:p", { "lowercase", "compressWhitespace" } );
|
||||
-- The second parameter is now a list. You should note that m.getvar()
|
||||
-- requires the use of comma to separate collection names from
|
||||
-- variable names. This is because only one variable is returned.
|
||||
var3 = m.getvar("ARGS.p", { "lowercase", "compressWhitespace" } );
|
||||
|
||||
-- If you want this rule to match return a string
|
||||
-- containing the error message. It is a good idea to mention
|
||||
@ -1830,6 +1836,13 @@ function main()
|
||||
-- Otherwise, simply return null.
|
||||
return null;
|
||||
end</programlisting>
|
||||
|
||||
<note>
|
||||
<para>Go to <ulink url="http://www.lua.org">http://www.lua.org</ulink>
|
||||
to find more about the Lua programming language. The reference manual
|
||||
too is available online, at <ulink
|
||||
url="http://www.lua.org/manual/5.1/">http://www.lua.org/manual/5.1/</ulink>.</para>
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@ -2412,7 +2425,7 @@ SecRule <emphasis>ENV:tag</emphasis> "suspicious"</programlisting>
|
||||
|
||||
<para>Example:</para>
|
||||
|
||||
<programlisting format="linespecific">SecRule REMOTE_ADDR "<emphasis>@geoLookup</emphasis>" chain,drop,msg:'Non-UK IP address'
|
||||
<programlisting format="linespecific">SecRule REMOTE_ADDR "<emphasis>@geoLookup</emphasis>" "chain,drop,msg:'Non-UK IP address'"
|
||||
SecRule GEO:COUNTRY_CODE "!@streq UK"</programlisting>
|
||||
</section>
|
||||
|
||||
@ -3913,27 +3926,42 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \
|
||||
<title><literal>exec</literal></title>
|
||||
|
||||
<para><emphasis>Description:</emphasis> Executes an external
|
||||
script/binary supplied as parameter.</para>
|
||||
script/binary supplied as parameter. As of v2.5, when the support for
|
||||
Lua scripting is enabled, and the parameter supplied to
|
||||
<literal>exec</literal> is a Lua script (detected by the
|
||||
<filename>.lua</filename> extension) the script will be processed
|
||||
<emphasis>internally</emphasis>. This means you will get direct access
|
||||
to the internal request context from the script. Please read the
|
||||
<literal>SecRuleScript</literal> documentation for more details on how
|
||||
to write Lua scripts.</para>
|
||||
|
||||
<para><emphasis>Action Group:</emphasis> Non-Disruptive</para>
|
||||
|
||||
<para>Example:</para>
|
||||
|
||||
<programlisting format="linespecific">SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
|
||||
"log,<emphasis>exec:/usr/local/apache/bin/test.sh</emphasis>,phase:1"</programlisting>
|
||||
<programlisting format="linespecific"># The following is going to execute /usr/local/apache/bin/test.sh
|
||||
# as a shell script on rule match.
|
||||
SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
|
||||
"log,<emphasis>exec:/usr/local/apache/bin/test.sh</emphasis>"
|
||||
|
||||
<para><emphasis>Note</emphasis></para>
|
||||
# The following is going to process /usr/local/apache/conf/exec.lua
|
||||
# internally as a Lua script on rule match, provided ModSecurity was
|
||||
# compiled with Lua support enabled.
|
||||
SecRule ARGS:p attack log,<emphasis>exec:/usr/local/apache/conf/exec.lua</emphasis></programlisting>
|
||||
|
||||
<para>This directive does not effect a primary action if it exists. This
|
||||
action will always call script with no parameters, but providing all
|
||||
information in the environment. All the usual CGI environment variables
|
||||
will be there. You can have one binary executed per filter match.
|
||||
Execution will add the header mod_security-executed to the list of
|
||||
request headers. You should be aware that forking a threaded process
|
||||
results in all threads being replicated in the new process. Forking can
|
||||
therefore incur larger overhead in multithreaded operation. The script
|
||||
you execute must write something (anything) to stdout. If it doesn't
|
||||
ModSecurity will assume execution didn't work.</para>
|
||||
<note>
|
||||
<para>This directive does not effect a primary action if it exists.
|
||||
This action will always call script with no parameters, but providing
|
||||
all information in the environment. All the usual CGI environment
|
||||
variables will be there. You can have one binary executed per filter
|
||||
match. Execution will add the header mod_security-executed to the list
|
||||
of request headers. You should be aware that forking a threaded
|
||||
process results in all threads being replicated in the new process.
|
||||
Forking can therefore incur larger overhead in multithreaded
|
||||
operation. The script you execute must write something (anything) to
|
||||
stdout. If it doesn't ModSecurity will assume execution didn't
|
||||
work.</para>
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@ -4847,11 +4875,37 @@ SecRule ARGS:route "!<emphasis>@endsWith %{REQUEST_ADDR}</emphasis>" t:none,deny
|
||||
|
||||
<para><emphasis>Description:</emphasis> Executes the external
|
||||
script/binary given as parameter to the operator against every file
|
||||
extracted from the request.</para>
|
||||
extracted from the request. As of v2.5, if the supplied filename is not
|
||||
absolute it is treated as relative to the directory in which the
|
||||
configuration file resides. Also as of v2.5 if the filename is
|
||||
determined to be a Lua script (based on its extension) and the Lua
|
||||
support is compiled in, the script will be processed by the internal
|
||||
engine. As such it will have full access to the ModSecurity
|
||||
context.</para>
|
||||
|
||||
<para>Example:</para>
|
||||
<para>Example of using an external binary/script:</para>
|
||||
|
||||
<programlisting format="linespecific">SecRule FILES_TMPNAMES "<emphasis>@inspectFile</emphasis> /opt/apache/bin/inspect_script.pl"</programlisting>
|
||||
<programlisting format="linespecific"># Execute external script to validate uploaded files.
|
||||
SecRule FILES_TMPNAMES "<emphasis>@inspectFile</emphasis> /opt/apache/bin/inspect_script.pl"</programlisting>
|
||||
|
||||
<para>Example of using Lua script:</para>
|
||||
|
||||
<programlisting>SecRule FILES_TMPNANMES "@inspectFile <emphasis>inspect.lua</emphasis>"</programlisting>
|
||||
|
||||
<para>Script <filename>inspect.lua</filename>:</para>
|
||||
|
||||
<programlisting>function main(filename)
|
||||
-- Do something to the file to verify it. In this example, we
|
||||
-- read up to 10 characters from the beginning of the file.
|
||||
local f = io.open(filename, "rb");
|
||||
local d = f:read(10);
|
||||
f:close();
|
||||
|
||||
-- Return null if there is no reason to believe there is ansything
|
||||
-- wrong with the file (no match). Returning any text will be taken
|
||||
-- to mean a match should be trigerred.
|
||||
return null;
|
||||
end</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
Loading…
x
Reference in New Issue
Block a user