0x01 概述


前段时间监控到公司站点的手机号是否存在的接口被单IP高频率访问,站点解析到腾讯云上,LB后是20+腾讯云主机做Nginx代理,使用Filebeat+ELK收集监控此类异常行为,当前最新的是6.2,有些改动,我还是用5.5版本。

ELK可以当作一个MVC模型,logstash是controller层,Elasticsearch是一个model层,kibana是view层。

使用三台机器,配置和角色如下:

10.59.0.248(32核+64G内存) Logstash Elasticsearch(Master) Kibana

10.59.0.116(24核+32G内存) Logstash Elasticsearch(Data)

10.211.0.107(64核+64G内存) Logstash Elasticsearch(Data)

 

0x02 Filebeat


Filebeat的性能消耗要比logstash小的多,配置如下:

[root@VM_1_216_centos filebeat]# cat filebeat.yml | grep -v "^\s*#" | grep -v "^$"

filebeat.prospectors:

- type: log

  enabled: true

  paths:

    - /马赛克/*.log

  tail_files: true

output.logstash:

  hosts: ["10.59.0.116:5044","10.59.0.248:5044","10.211.0.71:5044"]

  loadbalance: true  #如无该配置默认仅向一台机器发送日志,该机器如果Down掉之后,才会切换其他的机器

data/registry记录了文件读取的offset,如果文件data/registry不存在,则会重新发送文件。

配置tail_files: true 仅发送新的数据

 

0x03 Logstash


在logstash-5.4.0/bin/logstash.lib.sh文件第一行添加

export JAVA_HOME=/usr/local/jdk1.8

所遇到的问题:

1)

ELK部署运行后,非常常见的一个现象是429错误,如下所示:

[2018-04-04T09:08:16,479][INFO ][logstash.outputs.elasticsearch] retrying failed action with response code: 429 ({"type"=>"es_rejected_execution_exception", "reason"=>"rejected execution of org.elasticsearch.transport.TransportService$7@4e1276c3 on EsThreadPoolExecutor[bulk, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@4892020c[Running, pool size = 24, active threads = 24, queued tasks = 200, completed tasks = 4786420]]"})

表示Elasticsearch处理不过来了。

在ES5.0以后,Elasticsearch将bulk、flush、get、index、search等线程池完全分离,自身的写入不会影响其他功能的性能。

来查询一下ES当前的线程情况:

Get http://elasticsearch.cdeledu.com/_nodes/stats/thread_pool

其中

"bulk": {

    "threads": 24,

    "queue": 74,

    "active": 24,

    "rejected": 666890,

    "largest": 24,

    "completed": 4817519

},

最需要关注的是rejected。当某个线程池active==threads时,表示所有线程都在忙,那么后续新的请求就会进入queue中,即queue>0,一旦queue大小超出限制,那么elasticsearch进程将拒绝请求(bulk HTTP状态码429),相应的拒绝次数就会累加到rejected中。

解决方案为优化logstash.yml的参数,修改batch.size参数为3000,增加每次发送的事件数,从而降低调用ES的频率。另外修改worker/output.workers为CPU数。

logstash.yml配置如下:

[root@localhost config]# cat logstash.yml | grep -v "^\s*#" | grep -v "^$"

pipeline.workers: 32

pipeline.output.workers: 32

pipeline.batch.size: 3000

pipeline.batch.delay: 5

2)

增加Logstash JVM内存,Logstash报错内存溢出

java.lang.OutOfMemoryError: Java heap space

需要修改logstash启动文件bin/logstash

export LS_JAVA_OPTS=" -Xmx8g -Xms8g"

Logstash配置文件如下:

input {

    beats {

        port => 5044

      }

}



filter{

    grok{

        match => {

            "message" => "%{IP:client-ip}(,\s)?(?<proxy-ip>[\d\.,\s]*) (%{USER:ident}|-) (%{USER:auth}|-) \[%{HTTPDATE:timestamp}\] \"(?:%{WORD:verb} %{URIPATH:uri}%{DATA:parameter}(?: HTTP/%{NUMBER:http_version})?|-)\" %{NUMBER:status-code} %{NUMBER:bytes} \"(%{GREEDYDATA:referer}|-)\" \"(%{GREEDYDATA:user-agent}|-)\" (%{BASE16FLOAT:response_time}|-) (%{BASE16FLOAT:request_time}|-) \"(%{GREEDYDATA:cookie}|-)\" \[%{GREEDYDATA:servername}\]"

        }

    }

    if ([uri] =~ "\.(js|css)$"){

        drop {}

    }

    date {

        match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]

        target => "@timestamp"

    }

    ruby {

        code => "event.set('timestamp', event.get('@timestamp').time.localtime + 8*60*60); event.set('@timestamp', event.get('timestamp'))"

    }

    if ([verb] == "POST")

    {

        mutate {

            add_field => {"dynamic" => 1}

        }

    }

    else if ([verb] == "GET" and [parameter])

    {

        mutate {

            add_field => {"dynamic" => 1}

        }

    }

    else {

        mutate {

            add_field => {"dynamic" => 0}

        }

    }

    mutate {

        remove_field => [ "message" ]

    }

}



output {

    if "_grokparsefailure" not in [tags] {

        elasticsearch {

            hosts => "10.59.0.248:9200"

            index => "nginx_%{+YYYY.MM.dd}"

        }

    }

}

这里grok正则匹配非常耗费性能,可以使用dissect替换。

 

0x04 ELasticsearch


ElasticSearch是一个基于Lucene的搜索服务器,Lucene是一个开源的全文检索引擎工具包(类似于Java api),而Elasticsearch底层是基于这些包,对其进行了扩展,提供了比Lucene更为丰富的查询语言,可以非常方便的通过Elasticsearch的HTTP接口与底层Lucene交互。Elasticsearch是Lucene面向企业搜索应用的扩展,极大的缩短研发周期。

在if [ -x “$JAVA_HOME/bin/java” ]; then上添加两行

export JAVA_HOME=/usr/local/jdk1.8

export PATH=$JAVA_HOME/bin:$PATH

启动时遇到其他问题汇总:

1) 启动 elasticsearch 如出现异常  can not run elasticsearch as root

解决方法:创建ES 账户,修改文件夹 文件 所属用户 组

2) 启动异常:ERROR: bootstrap checks failed

system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk

解决方法:在elasticsearch.yml中配置bootstrap.system_call_filter为false,注意要在Memory下面:

bootstrap.system_call_filter: false

3) 启动后,如果只有本地可以访问,尝试修改配置文件 elasticsearch.yml中network.host(注意配置文件格式不是以 # 开头的要空一格, : 后要空一格)

为 network.host: 0.0.0.0

默认端口是 9200

注意:关闭防火墙 或者开放9200端口

4) ERROR: bootstrap checks failed

max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]

max number of threads [1024] for user [lishang] likely too low, increase to at least [2048]

解决方法:切换到root用户,编辑limits.conf 添加类似如下内容

vim /etc/security/limits.conf

添加如下内容:

* soft nofile 65536

* hard nofile 131072

* soft nproc 2048

* hard nproc 4096

5)

max number of threads [1024] for user [lish] likely too low, increase to at least [2048]

解决:切换到root用户,进入limits.d目录下修改配置文件。

vi /etc/security/limits.d/90-nproc.conf

修改如下内容:

* soft nproc 1024

#修改为

* soft nproc 2048

6)

max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]

解决:切换到root用户修改配置sysctl.conf,添加下面配置:

vm.max_map_count=655360

并执行命令:

sysctl -p

然后,重新启动elasticsearch,即可启动成功。

Elasticsearch Master配置文件如下:

cluster.name: logstash_sec

node.name: "node-1"

node.master: true

node.data: false

discovery.zen.ping.unicast.hosts: ["10.59.0.248", "10.211.0.71", "10.59.0.116"]

network.host: 0.0.0.0

path.data: /soft/elasticsearch55/data

path.logs: /soft/elasticsearch55/logs

http.enabled: true

http.cors.enabled: true

http.cors.allow-origin: "*"

bootstrap.mlockall: true

thread_pool.bulk.queue_size: 3000

 

0x05 ELasticsearch plugins


1) Head插件

yum install git -y

git clone git://github.com/mobz/elasticsearch-head.git

下载Node.js:

wget https://nodejs.org/dist/v4.6.1/node-v4.6.1-linux-x64.tar.gz

tar zxvf node-v4.6.1-linux-x64.tar.gz

配置node.js环境变量:

vim /etc/profile:

export PATH=/soft/elasticsearch-5.4.0/node-v4.6.1-linux-x64/bin:$PATH

执行source /etc/profile使环境变量生效

查看当前head插件目录下有无node_modules/grunt目录:

没有则执行命令创建:

npm install grunt --save

安装head插件:

npm install

安装grunt:

npm install -g grunt-cli

编辑Gruntfile.js

文件93行添加

hostname:'0.0.0.0'

检查head根目录下是否存在base文件夹,没有:将 _site下的base文件夹及其内容复制到head根目录下

修改elasticsearch.yml,添加:

http.cors.enabled: true

http.cors.allow-origin: "*"

启动grunt server:在head下运行

grunt server -d

访问head插件:http://localhost:9100

 

2) bigdesk插件

bigdesk是elasticsearch的一个集群监控工具,可以通过它来查看es集群的各种状态,如:cpu、内存使用情况,索引数据、搜索情况,http连接数等。

安装步骤:

git clone https://github.com/hlstudio/bigdesk

cd bigdesk/_site/

python -m SimpleHTTPServer

 

0x06 ELasticsearch 优化


1) shard与Replicas

shard不能修改,一个node不要超过2个shard。

replica只会参与读操作,它的主要作用就是提高集群错误恢复的能力,并且可以在集群建立之后变更。

2) 索引存储

最好是使用SSD,没有的话,最好将es数据节点配置多个数据存储路径,尽量避免使用远程文件系统存储,如NFS 或 SMB。

3) “refresh_interval”: “30s”

优化点: 减少刷新频率,降低潜在的写磁盘性能损耗

另外如果需要一次加载较大的数据量进 index 里面时,可以先禁用 refresh ,把 index.refresh_interval 设置成为 -1 ,把 index.number_of_replicas 设置成 0。暂时把多个shard副本关闭,这样做可以大大加快索引速度。当初始化索引完成,可以将 index.refresh_interval 和 index.number_of_replicas 设置回原来的值。

4) translog优化

Lucene只有在commit的时候才会把之前的变更持久化存储到磁盘(每次操作都写到磁盘的话,代价太大),在commit之前如果出现故障,上一次commit之后的变更都会丢失

为了防止数据丢失,Lucene会把变更操作都记录在translog里,在出现故障的时候,从上次commit起记录在translog里的变更都可以恢复,尽量保证数据不丢失

Lucene的flush操作就是执行一次commit,同时开始记录一个新的translog,所以translog是用来记录从上次commit到下一次commit之间的操作的

flush操作的频率是通过translog的大小控制的,当translog大小达到一定值的时候就执行一次flush,对应参数为index.translog.flush_threshold_size,默认值是512mb,这里调整为1gb,减少flush的次数

translog本身是文件,也需要存储到磁盘,它的存储方式通过index.translog.durability和index.translog.sync_interval设定。默认情况下,index.translog.durability=request,意为每次请求都会把translog写到磁盘。这种设定可以降低数据丢失的风险,但是磁盘IO开销会较大

这里采用异步方式持久化translog,每隔30秒写一次磁盘

{

  "index": {

      "translog": {

          "flush_threshold_size": "1gb",

          "sync_interval": "30s",

          "durability": "async"

      }

  }

}

5) 429错误

增加bulk的queue大小

thread_pool.bulk.queue_size: 3000

改配置会增加JVM内存,修改config/jvm.options

-Xms8g

-Xmx8g

建议配置为物理内存的一半,因为文件系统缓存是为了缓冲磁盘的IO操作。至少确保有一半机器的内存保留给操作系统,并且JVM内存不要超过32G。

6)

index.merge.scheduler.max_thread_count: 1

index由多个shard组成,每个shard又分成很多segment,segment是index数据存储的最小单位。segment比较多的时候会影响搜索性能,ES通过merge对小的segment进行合并,优化查询性能。但是合并过程中会消耗较多磁盘IO,会影响查询性能。Elasticsearch 5 采用了多线程去执行merge,可以通过修改index.merge.scheduler.max_thread_count 来动态调整这个线程数,默认的话是通过下面公式去计算:

Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2))

要注意的是如果你是用HDD而非SSD的磁盘的话,最好是用单线程为妙。

 

另外也可以手工进行merge操作,这里有3个参数可以用

max_num_segments 期望merge到多少个segments,1的意思是强行merge到1个segment

only_expunge_deletes 只做清理有deleted的segments,即瘦身

flush 清理完执行一下flush,默认是true

你可以用下面的URL来执行强行的merge

[root@localhost elasticsearch55]# curl -XPOST "http://localhost:9200/nginx_2018.04.12/_forcemerge?max_num_segments=1"

{"_shards":{"total":5,"successful":5,"failed":0}}

7) 避免内存交换

设置为true来锁住内存不进行swapping,因为当jvm开始swapping时es的效率会降低。

bootstrap.mlockall: true

[待补充]

 

0x07 告警配置


告警方面可以选择Elastalert

这里我以单IP高频请求api监控为例,查询语句如下:

{

  "size": 0,

  "query": {

    "bool": {

      "must": [

        {

          "range": {

            "timestamp": {

              "from": "%d",

              "to": "%d"

            }

          }

        },

        {

          "term": {

            "status-code": {

              "value": "200"

            }

          }

        },

        {

          "term": {

            "dynamic": {

              "value": 1

            }

          }

        }                                  

      ]

    }

  },

  "aggs": {

    "group_by_clientip": {

      "terms": {

        "field": "client-ip",

        "order": {

          "_count": "desc"

        },

        "min_doc_count": %d

      },

      "aggs": {

        "group_by_servername": {

          "terms": {

            "field": "servername",

            "size": 2,

            "order": {

              "_count": "desc"

            }

          }

        }

      }

    }

  }

}

 

参考文章:

https://cloud.tencent.com/developer/article/1006124

https://www.jianshu.com/p/9b872a41d5bb

Lshell用于构建一个受限的Shell环境,由Python编写,其功能如下:
1)限制可以使用的命令
2)记录用户执行的命令
3)限制可以访问的目录
4)限制环境变量
…..

安装步骤:
下载源码https://github.com/ghantoos/lshell
python setup.py install –no-compile –install-scripts=/usr/bin/
cp /etc/lshell.conf /usr/local/etc/

配置:
默认的配置文件中有Default配置,另外有可以创建[username]或者[grp:groupname]来定制用户的个性配置。
配置的优先级如下:
a.User配置
b.Group配置
c.Default配置

修改用户的shell环境:
chsh -s /usr/bin/lshell user_name

配置文件参数:

# lshell.py configuration file
#
# $Id: lshell.conf,v 1.27 2010-10-18 19:05:17 ghantoos Exp $

[global]
##  log directory (default /var/log/lshell/ )
logpath         : /var/log/lshell/
##  set log level to 0, 1, 2, 3 or 4  (0: no logs, 1: least verbose,
##                                                 4: log all commands)
loglevel        : 2
##  configure log file name (default is %u i.e. username.log)
#logfilename     : %y%m%d-%u
#logfilename     : syslog

##  in case you are using syslog, you can choose your logname
#syslogname      : myapp

##  Set path to sudo noexec library. This path is usually autodetected, only
##  set this variable to use alternate path. If set and the shared object is
##  not found, lshell will exit immediately. Otherwise, please check your logs
##  to verify that a standard path is detected.
##
##  while this should not be a common practice, setting this variable to an empty
##  string will disable LD_PRELOAD prepend of the commands. This is done at your
##  own risk, as lshell becomes easily breached using some commands like find(1)
##  using the -exec flag.
#path_noexec     : /usr/libexec/sudo_noexec.so

## include a directory containing multiple configuration files. These files
## can only contain default/user/group configuration. The global configuration will
## only be loaded from the default configuration file.
## e.g. splitting users into separate files
#include_dir     : /etc/lshell.d/*.conf

[default]
##  a list of the allowed commands without execution privileges or 'all' to
##  allow all commands in user's PATH
##
##  if  sudo(8) is installed and sudo_noexec.so is available, it will be loaded
##  before running every command, preventing it from  running  further  commands
##  itself. If not available, beware of commands like vim/find/more/etc. that
##  will allow users to execute code (e.g. /bin/sh) from within the application,
##  thus easily escaping lshell. See variable 'path_noexec' to use an alternative
##  path to library.
allowed         : ['ls', 'echo','ll']

##  A list of the allowed commands that are permitted to execute other
##  programs (e.g. shell scripts with exec(3)). Setting this variable to 'all'
##  is NOT allowed. Warning do not put here any command that can execute
##  arbitrary commands (e.g. find, vim, xargs)
##
##  Important: commands defined in 'allowed_shell_escape' override their
##  definition in the 'allowed' variable
#allowed_shell_escape        : ['man','zcat']

##  a list of forbidden character or commands
forbidden       : [';', '&', '|','`','>','<', '$(', '${']

##  a list of allowed command to use with sudo(8)
##  if set to ´all', all the 'allowed' commands will be accessible through sudo(8)
#sudo_commands   : ['ls', 'more']

##  number of warnings when user enters a forbidden value before getting 
##  exited from lshell, set to -1 to disable.
warning_counter : 2

##  command aliases list (similar to bash’s alias directive)
aliases         : {'ll':'ls -l'}

##  introduction text to print (when entering lshell)
#intro           : "== My personal intro ==\nWelcome to lshell\nType '?' or 'help' to get the list of allowed commands"

##  configure your promt using %u or %h (default: username)
#prompt          : "%u@%h"

##  set sort prompt current directory update (default: 0)
#prompt_short    : 0

##  a value in seconds for the session timer
#timer           : 5

##  list of path to restrict the user "geographicaly"
##  warning: many commands like vi and less allow to break this restriction
#path            : ['/home/bla/','/etc']

##  set the home folder of your user. If not specified the home_path is set to 
##  the $HOME environment variable
#home_path       : '/home/bla/'

##  update the environment variable $PATH of the user
#env_path        : ':/usr/local/bin:/usr/sbin'

##  a list of path; all executable files inside these path will be allowed 
#allowed_cmd_path: ['/home/bla/bin','/home/bla/stuff/libexec']

##  add environment variables
#env_vars        : {'foo':1, 'bar':'helloworld'}

##  allow or forbid the use of scp (set to 1 or 0)
#scp             : 1

## forbid scp upload
#scp_upload       : 0

## forbid scp download
#scp_download     : 0

##  allow of forbid the use of sftp (set to 1 or 0)
##  this option will not work if you are using OpenSSH's internal-sftp service
#sftp            : 1

##  list of command allowed to execute over ssh (e.g. rsync, rdiff-backup, etc.)
#overssh         : ['ls', 'rsync']

##  logging strictness. If set to 1, any unknown command is considered as 
##  forbidden, and user's warning counter is decreased. If set to 0, command is
##  considered as unknown, and user is only warned (i.e. *** unknown synthax)
strict          : 0

##  force files sent through scp to a specific directory
#scpforce        : '/home/bla/uploads/'

##  Enable support for WinSCP with scp mode (NOT sftp)
##  When enabled, the following parameters will be overridden:
##    - scp_upload: 1 (uses scp(1) from within session)
##    - scp_download: 1 (uses scp(1) from within session)
##    - scpforce - Ignore (uses scp(1) from within session)
##    - forbidden: -[';']
##    - allowed: +['scp', 'env', 'pwd', 'groups', 'unset', 'unalias']
#winscp: 0

##  history file maximum size 
#history_size     : 100

##  set history file name (default is /home/%u/.lhistory)
#history_file     : "/home/%u/.lshell_history"

##  define the script to run at user login
#login_script     : "/path/to/myscript.sh"

## disable user exit, this could be useful when lshell is spawned from another
## none-restricted shell (e.g. bash)
#disable_exit      : 0

配置例子:
foo:
1)可以访问/usr和/var,无法访问/usr/local
2)可以执行除了su以外的命令
3)家目录/home/users

bar:
1)可以访问/usr和/etc,无法访问/usr/local
2)除了default的命令还可以执行ping,无法执行ls
3)开启strict(1:表示每个unknown命令都会减少warning counter的数量;0:针对unknown命令只是提醒,不会减少warning counter的数量)

配置文件如下:

# CONFIGURATION START
[global]
logpath         : /var/log/lshell/
loglevel        : 2

[default]
allowed         : ['ls','pwd']
forbidden       : [';', '&', '|'] 
warning_counter : 2
timer           : 0
path            : ['/etc', '/usr']
env_path        : ':/sbin:/usr/foo'
scp             : 1 # or 0
sftp            : 1 # or 0
overssh         : ['rsync','ls']
aliases         : {'ls':'ls --color=auto','ll':'ls -l'}

[grp:users]
warning_counter : 5
overssh         : - ['ls']

[foo]
allowed         : 'all' - ['su']
path            : ['/var', '/usr'] - ['/usr/local']
home_path       : '/home/users'

[bar]
allowed         : + ['ping'] - ['ls'] 
path            : - ['/usr/local']
strict          : 1
scpforce        : '/home/bar/uploads/'
# CONFIGURATION END

CVE
CVE-2012-1493

漏洞危害
攻击者可以获取Root权限

漏洞影响如下产品和版本
* BIG-IP LTM 版本9.x, 10.x和11.x
* BIG-IP GTM 版本 9.x, 10.x和11.x
* BIG-IP ASM 版本 9.x, 10.x和11.x
* BIG-IP Link Controller 版本 9.x, 10.x和11.x
* BIG-IP PSM 版本 9.x, 10.x和11.x
* BIG-IP WOM 版本 10.x and 11.x
* BIG-IP APM 版本 10.x and 11.x
* BIG-IP Edge Gateway 版本 10.x和11.x
* BIG-IP Analytics 版本 11.x
* Enterprise Manager 版本 1.x和2.x

漏洞描述
F5的SSH对外开放且在上述版本可以使用私钥直接登录F5设备。

私钥如下

-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQC8iELmyRPPHIeJ//uLLfKHG4rr84HXeGM+quySiCRgWtxbw4rh
UlP7n4XHvB3ixAKdWfys2pqHD/Hqx9w4wMj9e+fjIpTi3xOdh/YylRWvid3Pf0vk
OzWftKLWbay5Q3FZsq/nwjz40yGW3YhOtpK5NTQ0bKZY5zz4s2L4wdd0uQIBIwKB
gBWL6mOEsc6G6uszMrDSDRbBUbSQ26OYuuKXMPrNuwOynNdJjDcCGDoDmkK2adDF
8auVQXLXJ5poOOeh0AZ8br2vnk3hZd9mnF+uyDB3PO/tqpXOrpzSyuITy5LJZBBv
7r7kqhyBs0vuSdL/D+i1DHYf0nv2Ps4aspoBVumuQid7AkEA+tD3RDashPmoQJvM
2oWS7PO6ljUVXszuhHdUOaFtx60ZOg0OVwnh+NBbbszGpsOwwEE+OqrKMTZjYg3s
37+x/wJBAMBtwmoi05hBsA4Cvac66T1Vdhie8qf5dwL2PdHfu6hbOifSX/xSPnVL
RTbwU9+h/t6BOYdWA0xr0cWcjy1U6UcCQQDBfKF9w8bqPO+CTE2SoY6ZiNHEVNX4
rLf/ycShfIfjLcMA5YAXQiNZisow5xznC/1hHGM0kmF2a8kCf8VcJio5AkBi9p5/
uiOtY5xe+hhkofRLbce05AfEGeVvPM9V/gi8+7eCMa209xjOm70yMnRHIBys8gBU
Ot0f/O+KM0JR0+WvAkAskPvTXevY5wkp5mYXMBlUqEd7R3vGBV/qp4BldW5l0N4G
LesWvIh6+moTbFuPRoQnGO2P6D7Q5sPPqgqyefZS
-----END RSA PRIVATE KEY-----

保存到my_private_key.txt

chomd 600 my_private_key.txt 
ssh -i my_private_key.txt -l root ipaddress

MSF插件

exploit/linux/ssh/f5_bigip_known_privkey

修复建议
https://support.f5.com/csp/article/K13600

monit和supervisor都是进程管理工具,不过进程管理只是monit的功能之一,monit是一个开源的轻量级监控工具,功能十分强大。可以从多个层面进行监控,可以自动维护进程,发送邮件报警等。

系统监控:进程状态,系统负载,cpu负载,内存占用等。
进程监控:monit可以监控守护进程,当被监控进程异常退出时,可以自动被拉起。
文件系统:Monit可以监控本地文件、目录、文件系统的变化,包括时间戳、校验值、大小的变化。例如,可以监控文件sha1以及md5的值,来监控文件是否发生变化
网络监控:monit可以监控网络连接,支持TCP、UDP、Unix domain sockets以及HTTP、SMTP等。

程序安装:
yum install monit -y
配置文件:/etc/monit.conf

常用命令:

monit -t # 配置文件检测
monit # 启动monit daemon
monit -c /var/monit/monitrc # 启动monit daemon时指定配置文件
monit reload # 当更新了配置文件需要重载
monit status # 查看所有服务状态
monit status nginx # 查看nginx服务状态
monit stop all # 停止所有服务
monit stop nginx # 停止nginx服务
monit start all # 启动所有服务
monit start nginx # 启动nginx服务
monit -V # 查看版本

配置告警联系人
set alert 776711462@qq.com
下面是常用的几个功能:
1)监控文件
Nginx的配置文件HASH变化则直接reload

check file nginx.conf path /usr/local/nginx/conf/nginx.conf
    if changed sha1 checksum
    then exec "/usr/local/nginx/sbin/nginx -s reload"

这里也可以指定HASH值

check file nginx.conf path /usr/local/nginx/conf/nginx.conf
    if failed checksum and expect the sum 144f738eee9c0c0bb0b1e62c785e4a76 then alert

监控文件的修改时间,比如DB文件如果15分钟没有修改可能系统服务出现问题。监控文件的权限、属主、属组、大小等。

check file database with path /data/mydatabase.db
    if failed permission 700 then alert
    if failed uid data then alert
    if failed gid data then alert
    if timestamp > 15 minutes then alert
    if size > 100 MB then exec "/my/cleanup/script" as uid dba and gid dba

2)监控进程
监控Nginx进程:

# 提供主进程pid文件
check process nginx with pidfile /usr/local/nginx/logs/nginx.pid
    # 进程启动命令,必须写绝对路径
    start program = "/usr/local/nginx/sbin/nginx" with timeout 30 seconds
    # 进程关闭命令
    stop program  = "/usr/local/nginx/sbin/nginx -s stop"
# 端口状态检测,当状态返回异常,则重启服务。
  if failed host 192.168.192.120 port 80 protocol http then restart
# 当端口状态异常,报警    
  if failed host 192.168.192.120 port 80 protocol http then alert
# 在5个监视周期中,重启了服务3次,则超时不再监视。 因为如果重启了多次不成功,很有可能继续重启下去也不会成功,避免一直无效的重启,白白消耗系统资源影响主机上其他进程的工作,这时应该通知人工处理。
  if 3 restarts within 5 cycles then timeout
# 如果在5个监视周期内,该服务的CPU使用率都超过90%则告警。       
  if cpu usage > 90% for 5 cycles then alert
# 设置分组,可选
   group server
#   可选的ssl端口的监控,如果有的话
#    if failed port 443 type tcpssl protocol http
#       with timeout 15 seconds
#       then restart

监控SSH进程:

check process sshd with pidfile /var/run/sshd.pid
   start program  "/etc/init.d/sshd start"
   stop program  "/etc/init.d/sshd stop"
   if failed port 22 protocol SSH then restart
   if 5 restarts within 5 cycles then timeout

监控apache进程:

  check process apache with pidfile /usr/local/apache/logs/httpd.pid
    start program = "/etc/init.d/httpd start" with timeout 60 seconds
    stop program  = "/etc/init.d/httpd stop"
    if cpu > 60% for 2 cycles then alert
    if cpu > 80% for 5 cycles then restart
    if totalmem > 200.0 MB for 5 cycles then restart
    if children > 250 then restart
    if loadavg(5min) greater than 10 for 8 cycles then stop
    if failed host www.tildeslash.com port 80 protocol http and request "/somefile.html" then restart
    if failed port 443 type tcpssl protocol http with timeout 15 seconds then restart
    if 3 restarts within 5 cycles then unmonitor
    depends on apache_bin
    group server

3)系统负载监控

  check system $HOST
    if loadavg (1min) > 4 then alert
    if loadavg (5min) > 2 then alert
    if cpu usage > 95% for 10 cycles then alert
    if memory usage > 75% then alert
    if swap usage > 25% then alert

4)监控脚本返回值

check program myscript with path /usr/local/bin/myscript.sh
    if status != 0 then alert

5)监控网卡状态

  check network public with interface eth0
    if failed link then alert
    if changed link then alert
    if saturation > 90% then alert
    if download > 10 MB/s then alert
    if total upload > 1 GB in last hour then alert

6)监控远程主机服务

通过发出ping测试来检查远程主机的可用性,并检查来自web服务器的响应的内容。

  check host myserver with address 192.168.192.120
    if failed ping then alert
    if failed port 3306 protocol mysql with timeout 15 seconds then alert
    if failed port 80 protocol http and request /1.html with content = "123" then alert

7)监控文件系统

check filesystem datafs with path /dev/sdb1
 start program = "/bin/mount /data"
 stop program = "/bin/umount /data"
 if failed permission 660 then unmonitor
 if failed uid root then unmonitor
 if failed gid disk then unmonitor
 if space usage > 80% for 5 times within 15 cycles then alert
 if space usage > 99% then stop
 if inode usage > 30000 then alert
 if inode usage > 99% then stop
 group server

 

Supervisor是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。

安装supervisor
pip install supervisor

通过运行echo_supervisord_conf程序生成supervisor的初始化配置文件
echo_supervisord_conf > /etc/supervisor/supervisord.conf

supervisor的配置参数较多,下面介绍一下常用的参数配置,详细的配置及说明,分号(;)开头的配置表示注释。

[unix_http_server]
file=/tmp/supervisor.sock   ;UNIX socket 文件,supervisorctl 会使用
;chmod=0700                 ;socket文件的mode,默认是0700
;chown=nobody:nogroup       ;socket文件的owner,格式:uid:gid

;[inet_http_server]         ;HTTP服务器,提供web管理界面
;port=127.0.0.1:9001        ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
;username=user              ;登录管理后台的用户名
;password=123               ;登录管理后台的密码

[supervisord]
logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB        ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
logfile_backups=10           ;日志文件保留备份数量默认10,设为0表示不备份
loglevel=info                ;日志级别,默认info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false               ;是否在前台启动,默认是false,即以 daemon 的方式启动
minfds=1024                  ;可以打开的文件描述符的最小值,默认 1024
minprocs=200                 ;可以打开的进程数的最小值,默认 200

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord

; [program:xx]是被管理的进程配置参数,xx是进程的名称
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run  ; 程序启动命令
autostart=true       ; 在supervisord启动的时候也自动启动
startsecs=10         ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true     ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
startretries=3       ; 启动失败自动重试次数,默认是3
user=tomcat          ; 用哪个用户启动进程,默认是root
priority=999         ; 进程启动优先级,默认999,值小的优先启动
redirect_stderr=true ; 把stderr重定向到stdout,默认false
stdout_logfile_maxbytes=20MB  ; stdout 日志文件大小,默认50MB
stdout_logfile_backups = 20   ; stdout 日志文件备份数,默认是10
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
stopasgroup=false     ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false     ;默认为false,向进程组发送kill信号,包括子进程

;包含其它配置文件
[include]
files = relative/directory/*.ini    ;可以指定一个或多个以.ini结束的配置文件

下面是一个监控Jboss进程的例子:

[program:jboss]
command=/web/webserver/jboss/anquan2/bin/catalina.sh run
stdout_logfile=/web/webserver/jboss/anquan2/logs/catalina.out
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true

启动supervisor
supervisord -c /etc/supervisor/supervisord.conf

控制进程
1)交互终端
supervisord启动成功后,可以通过supervisorctl客户端控制进程,启动、停止、重启。运行supervisorctl命令,不加参数,会进入supervisor客户端的交互终端,并会列出当前所管理的所有进程。

[root@server144 logs]# supervisorctl 
jboss                            RUNNING   pid 6949, uptime 0:01:02
supervisor> stop jboss 
jboss: stopped

2)bash终端

[root@server144 logs]# supervisorctl status
jboss                            RUNNING   pid 7018, uptime 0:00:35
[root@server144 logs]# supervisorctl start jboss
jboss: started

3)Web界面

PS:
Supervisor只能管理非daemon的进程,也就是说Supervisor不能管理守护进程。否则提示Exited too quickly (process log may have details)异常。

 

0x01 SSL配置


首先生成私钥文件

openssl genrsa -des3 -out ssl.key 1024

然后他会要求你输入这个key文件的密码。不推荐输入。因为以后要给nginx使用。每次reload nginx配置时候都要你验证这个PAM密码的。
由于生成时候必须输入密码。你可以输入后 再删掉。

mv ssl.key xxx.key
openssl rsa -in xxx.key -out ssl.key
rm xxx.key

然后根据这个key文件生成证书请求文件,CSR是一个证书签名请求,是客户的服务器软件所生成的一串文本字符。服务器在向CA注册的过程中首先要在WEB服务器上生成CSR,并把这串字符提供给证书认证中心。
openssl req -new -key ssl.key -out ssl.csr
最后根据这2个文件生成crt证书文件
openssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt
最后使用到的文件是key和crt文件。

然后修改Nginx配置

    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      ssl.crt;
        ssl_certificate_key  ssl.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }

首次配置需要重启nginx,reload不生效。
这里比较重要的配置是:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;
ssl_ciphers HIGH:!aNULL:!MD5;

0x02 SSL证书返回错误问题排查


运维同学反馈在IE 8和XP的环境下使用https访问chinaacc.com的时候,返回了cdeledu.com的证书。先验证下:
果然是提示证书不安全,然后查看证书,果然是访问chinaacc.com的时候,返回了cdeledu.com的证书。

经过了解,原来是服务器上配置了多个域名的SSL证书,使用低版本浏览器的时候默认返回了配置的第一个证书。
在HTTP协议中,请求的域名作为主机头(Host)放在HTTP Header中,所以服务器端知道应该把请求引向哪个域名,但是早期的SSL做不到这一点,因为在SSL握手的过程中,根本不会有Host的信息,所以服务器端通常返回的是配置中的第一个可用证书。因而一些较老的环境,可能会产生多域名分别配好了证书,但返回的始终是同一个。
直到后来出现了SNI(Server Name Indication),是一项用于改善SSL/TLS的技术,在SSLv3/TLSv1中被启用。它允许客户端在发起SSL握手请求时(具体说来,是客户端发出SSL请求中的ClientHello阶段),就提交请求的Host信息,使得服务器能够切换到正确的域并返回相应的证书。
这里我们抓一下SSL握手的包,看一下ClientHello的内容
发现使用Chrome访问的,证书正常,此时ClientHello中有SNI,如下图所示:

在XP系统使用IE浏览器的时候返回证书错误,发送的ClientHello包中没有SNI,所以会返回配置的第一个证书。
然后我在Window7下使用IE8测试,返回证书是正常的。看来跟操作系统也有关系。
需要注意SNI需要客户端和服务端同时支持才行。例如查看Nginx时候支持

[root@VM_1_112_centos sbin]# ./nginx -V
nginx version: nginx/1.6.3
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
TLS SNI support enabled

TLS SNI support enabled表示支持SNI。

0x03 Nginx多SSL配置不生效问题


今天运维同事反馈XP系统IE6不能正常访问Https站点的问题。
低版本的IE默认配置是SSLv2和SSLv3。

抓包先看了下,握手失败。

然后用nmap跑了下证书支持的加密套件:

[root@server120 ]# nmap --script ssl-enum-ciphers -p 443 chinaacc.com

Starting Nmap 6.47 ( http://nmap.org ) at 2017-09-01 10:34 CST
Nmap scan report for chinaacc.com (59.151.113.79)
Host is up (0.034s latency).
PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers: 
|   SSLv3: No supported ciphers found

发现站点不支持SSLv3。
然后看了下SSL配置
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
发现没有配置SSLv3
修改配置
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;
然后用nmap扫描了下发现还是不行,配置没有生效。

原因是如果一个反向代理Nginx上配置了多个https域名的话,默认会走第一个的SSL配置。
例如我的配置如下:

    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      xx.crt;
        ssl_certificate_key  xx.key;
        ssl_protocols TLSv1;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }

    server {
        listen       443 ssl;
        server_name  192.168.192.120;

        ssl_certificate      xx.crt;
        ssl_certificate_key  xx.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }

第一个server配置ssl_protocols TLSv1;
第二个server配置ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
然后用nmap测试一下

[root@server144 ~]# nmap --script ssl-enum-ciphers -p 443 192.168.192.120

Starting Nmap 5.51 ( http://nmap.org ) at 2017-09-01 16:35 CST
Nmap scan report for localhost (192.168.192.120)
Host is up (0.00066s latency).
PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers: 
|   TLSv1.0
|     Ciphers (13)
|       TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA
|       TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
|       TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
|       TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA
|       TLS_RSA_WITH_AES_128_CBC_SHA
|       TLS_RSA_WITH_AES_256_CBC_SHA
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
|     Compressors (1)
|_      uncompressed
MAC Address: 52:54:00:26:BE:A2 (QEMU Virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 91.34 seconds

可以看到只支持TLSv1,走的是第一个ssl_protocols的配置。

解决方案:
修改第一个SSL配置中

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;

SQL Server 2008R2版本实现方案:
1)数据库事务日志
在SQL Server数据库事务日志中,记录了每个事务的数据变更操作的详细信息。
2)Audit
之前做过的数据库蜜罐,针对单表监控增删改查行为并告警,就是使用的audit。
文章链接:

MSSQL数据库蜜罐测试


3)SQL Profiler
SQL Profiler是微软从SQL Server 2000开始引入的数据库引擎跟踪工具,具有使用界面操作的接口、使用SQL语句创建接口以及使用SMO编程创建接口。
4)Extended Event
Extended Event更加轻量级,性能消耗比SQL Profiler大幅降低,因此对用户系统性能影响也大幅减轻。对系统性能和吞吐量影响均在0.01%左右。

综合考虑可靠性、可维护性、系统开销和影响来看,使用Extended Event实现审计日志的方法是最优的选择。所以这里我们采用最优的方案Extended Event。

创建Extended Event Session并启用:

USE master
GO

CREATE EVENT SESSION [svrXEvent_User_Define_Testing] ON SERVER 
ADD EVENT sqlserver.sql_statement_completed
( 
	ACTION 
	( 
		sqlserver.database_id,
		sqlserver.session_id, 
		sqlserver.username, 
		sqlserver.client_hostname,
		sqlserver.client_app_name,
		sqlserver.sql_text, 
		sqlserver.plan_handle,
		sqlserver.tsql_stack,
		sqlserver.is_system,
		package0.collect_system_time
	) 
	WHERE sqlserver.username <> N'NT AUTHORITY\SYSTEM'
		AND sqlserver.username <> 'sa'
		AND sqlserver.is_system = 0		
)
ADD TARGET package0.asynchronous_file_target
( 
	SET 
		FILENAME = N'C:\Temp\svrXEvent_User_Define_Testing.xel', 
		METADATAFILE = N'C:\Temp\svrXEvent_User_Define_Testing.xem',
		MAX_FILE_SIZE = 10,
		MAX_ROLLOVER_FILES = 500
)
WITH (
	EVENT_RETENTION_MODE = NO_EVENT_LOSS,
	MAX_DISPATCH_LATENCY = 5 SECONDS,
    STARTUP_STATE=ON
);
GO


-- We need to enable event session to capture event and event data 
ALTER EVENT SESSION [svrXEvent_User_Define_Testing]
ON SERVER STATE = START;
GO

扩展事件创建完毕并启动以后,发生在SQL Server数据库服务端的所有sql_statement_completed事件信息都会被扩展事件异步滚动记录在日志文件svrXEvent_User_Define_Testing.xel文件中

每个事件以XML格式单行写入日志文件,因此我们可以采用SQL Server提供的动态管理函数sys.fn_xe_file_target_read_file来读取和分析日志文件。
SQL Server DMF sys.fn_xe_file_target_read_file ( path, mdpath, initial_file_name, initial_offset )
以下是使用DMF全量读取所有审计日志文件记录的例子:

USE master
GO

SELECT *
FROM sys.fn_xe_file_target_read_file('C:\Temp\svrXEvent_User_Define_Testing*.xel', 
		'C:\Temp\svrXEvent_User_Define_Testing*.xem', null, null)

结果如下:

详细的日志信息在Event_data字段中,使用下面的查询语句获取更为详细的信息:

-- This is SQL 2008R2
;WITH events_cte
AS (
	SELECT
		[event_data] = T.C.query('.'),
		[event_name] = T.C.value('(event/@name)[1]','varchar(100)'),
		[event_time] = DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), CURRENT_TIMESTAMP),T.C.value('(event/@timestamp)[1]','datetime2')),
		[client app name] = T.C.value('(event/action[@name="client_app_name"]/value/text())[1]', 'sysname'),
		[client host name] = T.C.value('(event/action[@name="client_hostname"]/value/text())[1]', 'sysname'),
		[database_id]= T.C.value('(event/action[@name="database_id"]/value/text())[1]', 'int'),
		[cpu time (ms)] = T.C.value('(event/data[@name="cpu"]/value/text())[1]', 'bigint'),
		[logical reads] = T.C.value('(event/data[@name="reads"]/value/text())[1]', 'bigint'),
		[logical writes] = T.C.value('(event/data[@name="writes"]/value/text())[1]', 'bigint'),
		[duration (ms)] = T.C.value('(event/data[@name="duration"]/value/text())[1]', 'bigint'),
		[row count] = T.C.value('(event/data[@name="row_count"]/value/text())[1]', 'bigint'),
		[sql_text] = T.C.value('(event/action[@name="sql_text"]/value/text())[1]','nvarchar(max)'),
		[session_id] = T.C.value('(event/action[@name="session_id"]/value/text())[1]','int'),
		[user_name] = T.C.value('(event/action[@name="username"]/value/text())[1]','sysname'),
		[is_system] = T.C.value('(event/action[@name="is_system"]/value/text())[1]','sysname'),
		[query_timestamp] = T.C.value('(event/action[@name="collect_system_time"]/value/text())[1]','bigint'),
		[query_time] = DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), CURRENT_TIMESTAMP),T.C.value('(event/action[@name="collect_system_time"]/text/text())[1]','datetime2'))
	FROM sys.fn_xe_file_target_read_file('C:\Temp\svrXEvent_User_Define_Testing*.xel', 
		'C:\Temp\svrXEvent_User_Define_Testing*.xem', null, null)
		CROSS APPLY (SELECT CAST(event_data as XML) AS event_data) as T(C)
)
SELECT 
	
	cte.session_id,
	--cte.query_timestamp,
	--cte.[event_time],
	cte.[query_time],
	--cte.[event_name],
	cte.user_name,
	[database_name] = db.name,
	cte.[database_id],
	cte.[client host name],
	
	cte.[logical reads],
	cte.[logical writes],
	cte.[cpu time (ms)],
	cte.[duration (ms)],
	--cte.[plan_handle],
	cte.sql_text,
	sql_text_hash = CHECKSUM(cte.sql_text),
	cte.[client app name],
	cte.[event_data],
	cte.is_system
FROM events_cte as cte
	LEFT JOIN sys.databases as db
	on cte.database_id = db.database_id
ORDER BY [query_time] ASC
;

 

结果如下:

从这个结果集中,我们可以很清楚的知道每一条SQL语句执行的详细情况,包括:用户名、执行时间点、客户机名、逻辑读、逻辑写、CPU消耗、执行时间消耗、查询语句详情等非常重要的信息。
另外也可以传入initial_file_name和initial_offset来实现从某个日志文件的特定offset(文件内容偏移量)开始读取。

 

参考文章:
http://mysql.taobao.org/monthly/2017/06/06/
http://mysql.taobao.org/monthly/2017/07/06/
http://mysql.taobao.org/monthly/2017/08/08/
https://github.com/elastic/beats/issues/149

看到同程安全应急响应中心发的文章XSS Trap – XSS DNS防护的简单尝试的思路,很多人都是使用网络上的XSS Platform,比较常用的就那几个,另外还用公开的搭建代码搭建XSS Platform,可以通过搜索引擎收集到一些。那么如果企业中有Local DNS,那么就可以配置这些域名的解析到指定服务器,然后返回自己的JS文件,然后查看自己的XSS Platform就可以知道XSS的位置。

域名列表:https://sec.ly.com/xsspt.txt
参考文章:http://sec.ly.com/blogdetail?id=17

最近nginx官网公布了nginx1.13.4最新的ngx_http_mirror_module模块,利用mirror模块,业务可以将线上实时访问流量拷贝至其他环境,该流量可用于压测或者旁路做一些检测。

mirror模块配置分为两部分,源地址和镜像地址配置,配置位置可以为nginx配置文件的http, server, location上下文,配置示例为:

# original配置
location / {
    mirror /mirror;
    mirror_request_body off;
    proxy_pass http://127.0.0.1:9502;
}

# mirror配置
location /mirror {
    internal;
    proxy_pass http://127.0.0.1:8081$request_uri;
    proxy_set_header X-Original-URI $request_uri;
}

 

1)original配置
location /指定了源uri为/
mirror /mirror指定镜像uri为/mirror
mirror_request_body off | on 指定是否镜像请求body部分,此选项与proxy_request_buffering、fastcgi_request_buffering、scgi_request_buffering和 uwsgi_request_buffering冲突,一旦开启mirror_request_body为on,则请求自动缓存;
proxy_pass 指定上游server的地址

2)mirror配置
internal 指定此location只能被“内部的”请求调用,外部的调用请求会返回”Not found” (404)
proxy_pass 指定上游server的地址
proxy_set_header 设置镜像流量的头部

整个请求流程为:

1)curl向nginx 80端口发起GET / HTTP请求
2)nginx将请求转发至upstream 9502端口的original PHP脚本,nginx本地端口为51637
3)nginx将请求镜像发至upstream 8081端口的mirror PHP脚本,nginx本地端口为51638
4)original发送响应response to client至nginx
5)nginx将响应转发至curl,curl将响应展示到终端
6)mirror将响应发送至nginx,nginx丢弃。

参考文章:
https://segmentfault.com/p/1210000010610500/read

 

1、CRLF
需要注意的地方:
a)rewrite, return, add_header, proxy_set_header or proxy_pass中
b)使用了$uri和$document_uri,因为这两个参数会进行URL解码,正确配置应该是$request_uri。

c)变量,例如(?P<myvar>[^.]+).

这里先测试一下$uri
添加一条配置

location /sectest {
  return 302 https://$host$uri;
}

结果如下:

 

修改配置为

location /sectest {
  return 302 https://$host$request_uri;
}

结果如下:

测试一下匹配变量导致的CRLF
添加

    location ~ /v1/((?<action>[^.]*)\.json)?$ {
        add_header X-Action $action;
        return 200 "OK";
    }

结果如下:

应该修改正则为

    location ~ /v1/((?<action>[^.\s]*)\.json)?$ {
        add_header X-Action $action;
        return 200 "OK";
    }

2、HTTP头覆盖
如果location有add_header,那么以location为准。如果location没有add_header,则继承Http和server块的add_header内容。
官方配置例子如下:

server {
  listen 80;
  add_header X-Frame-Options "DENY" always;
  location / {
      return 200 "index";
  }

  location /new-headers {
    # Add special cache control
    add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate" always;
    add_header Pragma "no-cache" always;

    return 200 "new-headers";
  }
}

如果访问/,响应头中有X-Frame-Options

GET / HTTP/1.0

HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:28:33 GMT
Content-Type: application/octet-stream
Content-Length: 5
Connection: close
X-Frame-Options: DENY

index

如果访问/new-headers,响应头中没有X-Frame-Options

GET /new-headers HTTP/1.0


HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:29:46 GMT
Content-Type: application/octet-stream
Content-Length: 11
Connection: close
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache

new-headers

 

3、alias导致的任意文件读取
错误配置示例如下:

location /files {
  alias /home/;
}

这里如果访问http://example.com/files/readme.txt,就可以获取/home/readme.txt文件。
如果访问http://example.com/files../etc/passwd就可以读取/etc/passwd

需要注意,这里只能添加一个../,也就是跳到上层的目录,这里我修改nginx的配置如下:

        location /files {
                alias /home/elk/;
        }

修复建议:
location和alias的最后必须都带/或者都不带/

Gixy介绍
开源程序https://github.com/yandex/gixy用来检测Nginx配置中存在的问题
安装使用:

pip install gixy
gixy /etc/nginx/nginx.conf

检查项如下:

[ssrf] Server Side Request Forgery
[http_splitting] HTTP Splitting
[origins] Problems with referrer/origin validation
[add_header_redefinition] Redefining of response headers by "add_header" directive
[host_spoofing] Request's Host header forgery
[valid_referers] none in valid_referers
[add_header_multiline] Multiline response headers

参考文章:
https://www.leavesongs.com/PENETRATION/nginx-insecure-configuration.html
https://mp.weixin.qq.com/s?__biz=MzIzOTQ5NjUzOQ==&mid=2247483699&idx=1&sn=6f0394df7be9aafd65c12002c2bb4f10&chksm=e9287d07de5ff41165757618d932021e1b8e036fd0c1b8305e38ad693097cf05e37b76928eb5&mpshare=1&scene=23&srcid=0714xbWwfcwuCe7XA9oIQryo#rd