この記事はfreee Developers Advent Calendar 2020 20日目の記事です。
freee PSIRT専属エンジニアのEiji Sugiuraです。
クリスマスまで1週間を切りましたね。
この時期に思い出すのは、まだ子供達が小さかった頃のクリスマスです。
その年の12/24 21時すぎ、子供達が寝静まったあと、準備をはじめました。
ラッピングの材料を広げ、Amazonで調達しておいたプレゼントの中身が入った箱を開けて、さて包もうとしたときのことです。
ん?
息子に贈るはずだったNINTENDO DSのソフトにしては、固いパッケージ、
CDのパッケージのような...
いや、実際のところ自分で頼んだCDだったわけですが...。プレゼントが荷物として届いたことを気づかせないため、無造作に段ボール箱を積み上げた中に箱も開けずに隠していたことが仇となりました。
Amazonのアカウント情報には、1週間以上前に欠品のためにキャンセルのお知らせが来てました。
教訓としては、荷物は届いたら中身を確認しよう、Amazonだったらアカウント情報の配送記録見ようね、です。
ということで、ログは保存したら中身を精査しよう、AmazonだったらElasticsearch serviceでSIEMを作ってみようね、という話をします。
SOCって大変
freeeでは、SOC*1を自分たちで運用し、24hours 7days休むことなく以下を実施しています。
freeeでは、社内のcyber securityへの対処を行うCSIRT*4と、service siteのcyber securityへの対処を行うPSIRT*5が組織されており、SOC業務を専任で担当しています。 securityを担保する体制としては、かなり恵まれています。しかし、それでも、毎日すべてのログを精査して、優先度をつけて全て対応なんてことをやっていると、すぐに時間が溶けてしまいます。
ある日のSOC業務
12月に入ったある日、WAFのログを見ていると、以下のようなアクセスを見つけました。
これが、適当なpathにGET requestを飛ばしている/_vti_bin/owssvr.dllだけならば気にしません。悪意あるclientをweb serverに、たどり着く前に止めてくれてよかった!と思うだけです。
妙なのは、通常のアクセスである/dealsが直前に行われていることです。
もしかして?と思い、web serverのaccess logを見てみたところ、少し時間帯はずれていますが、通常の利用が行われていました。
これは良くありません。お客様のサービス利用をip blacklistが止めてしまっていることに、なりますからね。
念のため、同じ時間帯のDeepSecurityのログを見てみたところ、
WAFのip blacklistでblockされるよりも前に、同様のアクセスをDeepSecurityがresetしていることも確認できました。
さて、ここまでで分かったことは、以下のような事象が起きていることです。
- あるIPから、/_vti_bin/owssvr.dllへのアクセスをDeepSecurityがreset
- 同じIPから、同じ時刻帯に、通常のサービス利用を行われている
- 同じIPからのアクセスが、WAFのip blacklistによってblock
これらを割り出すには、バラバラに記録された3つのログを行き来しながら、grepをかけて状況をあぶりだす必要があります。 作業に熟練したとしても、結構な時間がかかってしまいます。
解決する方法の一つは、SIEM*6を導入することです。
ログを一つの入れ物に入れておき、attributeとvalueを揃えておく*7ことで、全てのログに横串を通して検索することが可能になります。
ということで、今回、SIEMとして利用するのは、SIEM on Amazon Elasticsearch Serviceです。
元々、AWS WAF、CloudTrail、GuardDuty、VPC flow logなど、AWSのmanaged serviceには対応していますが、今回はDeepSecurityのログをここに統合していきます。
以下では、こちらの手順に従って、SIEM on Amazon Elasticsearch serviceを稼働していることを前提とします。
es-loaderでDeepSecurityのログを取り込む
簡単に仕組みを以下に図示しておきます。
以下の処理を順に行って、DeepSecurityのログをSIEMに取り込んでいきます。
- ec2 instance上で動作しているDeepSecurity が、syslogでlocalhostの/var/log/dsa.logにログを出力
- td-agent/fluentdで/var/log/dsa.log → s3 bucketに転送
- es-loaderでs3を読み取って、Elasticsearchにloadする
以降では、これらを順に設定していきます。
DeepSecurity SaaSでのlocalhostへのsyslog転送設定
まず、DeepSecurity SaaSの管理画面にloginし、Adminitration -> System Settings -> Event Forwardingで、localhostにsyslogを転送する設定を行なっておきます。
Local Syslog profileは、以下のように localhost=127.0.0.1の514/udpに送信する設定で作成したものです。 このとき、formatにはCommonEventFormat、Agent should forward logsには Directory to the Syslog Serverを指定し、ログをDeepSecurity SaaSを介さずに直接送信する設定を行っておきます。
詳しくは、公式のmanualを参照してください。
rsyslogで/var/log/dsa.logにDeepSecurityのログを保存
次に、rsyslogを用いて、CEF:やLEEF:を含むログをDeepSecurityのログとして、/var/log/dsa.logに保存します。
/etc/rsyslog.d/ds_agent.conf
$FileCreateMode 0644 :syslogtag, contains, "CEF:" /var/log/dsa.log & stop :syslogtag, contains, "LEEF:" /var/log/dsa.log & stop
この設定によって、/var/log/dsa.logに、以下のようなログが記載されていきます。
Dec 3 15:08:33 ip-10-0-XXX-XXX CEF: 0|Trend Micro|Deep Security Agent|20.0.0.1194|1002751|Disallowed Resources|10|cn1=2387187 cn1Label=Host ID dvc=10.0.32.44 TrendMicroDsTenant=production freee TrendMicroDsTenantId=XXXXXX dmac=06:9C:B6:19:29:18 smac=06:06:2A:80:00:05 TrendMicroDsFrameType=IP src=10.0.XXX.XXX dst=10.0.XXX.XXX in=1584 cs3=DF 0 cs3Label=Fragmentation Bits proto=TCP spt=24920 dpt=8080 cs2=0x00 ACK PSH cs2Label=TCP Flags cnt=1 act=Reset cn3=23 cn3Label=DPI Packet Position cs5=6813 cs5Label=DPI Stream Position cs1=2-Disallowed cs1Label=DPI Note cs6=8 cs6Label=DPI Flags TrendMicroDsPacketData=R0VUIC9...
td-agent/fluendからS3へのlog転送
/var/log/dsa.logをtd-agentでtailして、S3にJSON形式でログを転送します。
/etc/td-agent/conf.d/ds_agent.conf
<source> @type tail format none path /var/log/dsa.log pos_file /var/log/td-agent/.dsa.pos tag ds_agent.* </source> <filter ds_agent.**> @type record_transformer @id ds_agent_record_modifier enable_ruby true <record> hostname "#{Socket.gethostname}" timestamp ${time.strftime('%FT%T%:z')} tag ${tag} </record> </filter> <match ds_agent.**> @type s3 @id ds_agent_s3 s3_bucket ${BUCKET_NAME} s3_region ${REGION} path ds_agent/ s3_object_key_format %{path}%{time_slice}_${hostname}_%{index}.%{file_extension} time_slice_format %Y/%m/%d/%H timezone Asia/Tokyo output_time false output_tag false <buffer tag,time> @type file path /var/log/td-agent/buffer/s3_ds_agent flush_mode interval flush_interval 1m flush_at_shutdown true </buffer> </match>
ec2 instanceからのs3への書き込みは、instance profileで許可をしてあげると良いです。
ここまでの設定で、S3 bucketのds_agent/YYYY/MM/DD/HH_hostname_NN.gzに以下の形式でログが保存されていきます。
{ "message":"Oct 28 14:46:28 ip-10-0-XX-XX CEF: 0|Trend Micro|Deep Security Agent|20.0.0.1194|1000552|Generic Cross Site Scripting(XSS) Prevention|10|cn1=2312751 cn1Label=Host ID dvc=10.0.XX.XX TrendMicroDsTenant=tenant name TrendMicroDsTenantId=XXXXXX dmac=0E:37:8C:B4:47:10 smac=0E:28:7B:CF:58:50 TrendMicroDsFrameType=IP src=10.0.XX.XX dst=10.0.YY.YY in=5770 cs3=DF 0 cs3Label=Fragmentation Bits proto=TCP spt=54056 dpt=80 cs2=0x00 ACK PSH cs2Label=TCP Flags cnt=1 act=IDS:Log cn3=1472 cn3Label=DPI Packet Position cs5=60218 cs5Label=DPI Stream Position cs1=XSS Attack cs1Label=DPI Note cs6=8 cs6Label=DPI Flags TrendMicroDsPacketData=IDwh...YXku\\=\\=", "hostname":"ip-10-0-XX-XX", "timestamp":"2020-10-28T14:46:28+09:00" } { "message":"Nov 28 10:33:58 ip-10-0-XX-XX CEF: 0|Trend Micro|Deep Security Agent|20.0.0.1194|3002831|Unix - Syslog|6|cn1=2313513 cn1Label=Host ID dvc=10.0.XX.XX TrendMicroDsTenant=tenant name TrendMicroDsTenantId=XXXXXX cs1=Non standard syslog message (size too large) cs1Label=LI Description fname=/var/log/messages shost=ip-10-0-XX-XX msg=Nov 28 10:33:57 ip-10-0-XX-XX error: message:Could not push log to Elasticsearch: {\"took\"\\=>329, \"errors\"\\=>true, ...", "hostname":"ip-10-0-XX-XX", "timestamp":"2020-11-28T10:33:58+09:00" }
elasticsearchでのlog-deepsecurity templateの定義
ElasticserachがJSON形式で送られてくるログを解釈する際のmappingを定義しておきます。今回は元々log-*のtemplateで共通のものは定義されているため、deepsecurityで特有のものだけを定義しておけば良いです。
PUT _template/log-deepsecurity { "log-deepsecurity" : { "index_patterns" : [ "log-deepsecurity*" ], "mappings" : { "properties" : { "cloud.account" : { "type" : "object" }, "event.severity" : { "type" : "integer" }, "event.original" : { "type" : "text" }, "event.count" : { "type" : "integer" }, "timestamp" : { "type" : "date" } } } } }
es_loader側の設定
es_loader lambda functionに、DeepSecurityのCEF形式のログを解釈させるため、user.iniに以下を定義します。
[deepsecurity] index = log-deepsecurity s3_key = ds_agent format = json script_ecs = event.action destination.ip destination.port destination.mac destination.bytes source.ip source.port source.mac source.bytes network.transport event.action server.name file.path event.count rule.category host.id event.original event.action = act destination.ip = dst destination.port = dpt destination.mac = dmac destination.bytes = out source.ip = src source.port = spt source.mac = smac source.bytes = in network.transport = proto server.name = hostname file.path = fname event.count = cnt rule.category = cs1 host.id = cn1 event.original = msg
次に、es_loader lambda functionに、deepsecurityのlogを解釈する siem/sf_deepsecurity.py が存在していることを確認してください。 ここまでの設定で、Elasticsearchにログがloadされていくはずです。
なお、ここまでのes_loaderの機能追加は、以下のPRでdevelop branchに既にmergeされています。
SIEMを用いたSOC業務
Amazon Elasticsearch serviceを用いて構築したSIEMで、先ほど解析した事象を見てみましょう。
ログ解析は1画面で!
ElasticsearchのDiscoveryで同じIP、同じ時間帯のログを表示させてみると、以下の通りです。
これらから、以下のことが言えます。
- AWS WAF fulllogから、通常のアクセスが行われている
- AWS WAFは、/_vti_bin/owssvr.dllをALLOWし、通している
- DeepSecurityで、/_vti_bin/owssvr.dllに対するアクセスを検知しResetしている
- 直後の/MSOffice/cltreq.aspについては、何も検知されずALLOWしている
- その後、25分ほどアクセスが行われていない = ここでip blacklistへの登録が行われたと推測
- ip blacklistによって、あるIPからのアクセスを全てBLOKしている
ipや時間帯のような簡単な絞り込みで、AWS WAF fulllogやDeepSecurityのtimelineが1画面で表示されるため、解析作業が捗るようになりました。
その後の対応
実は、今回DeepSecurityで検知された以下のアクセスは、Microsoft Office 2000/XpのWebディスカッション機能を有効化していると、発生します。
GET /_vti_bin/owssvr.dll?UL=...
残念ながら、Webディスカッション機能が用いるURIについてのMicrosoft社の一次情報は、既に削除されているようです。
お客様に以下を連絡し、
- お客様がオフィスで利用されているWindows端末から、上記のアクセスが生じていること
- おそらく、上記のアクセスを他のsiteにも実施したことが原因で、オフィスのipがblacklistに登録されてしまい、結果としてfreeeへのアクセスが行えなくなっていること
PC環境を更新していただくことで、対応を終了しました。
まとめ
Amazon Elasticsearch serviceを用いて構築したSIEMにDeepSecurityのログを統合することで、毎日のSOC業務が、メチャメチャはかどるようになりました。
SIEMは商用製品も多数ありますが、freeeのサービス規模だと、年間数千万円を超えるコストがかかってしまいます。AWSのmanaged serviceを利用して構築した今回のSIEMは、かなりの節約になりました。オススメです。
おわりに
SIEMのdashboardみたいなキレイな画面は目を引くのですが、実際に業務を回す人にとっては、ログに横串を通して解析できることの方が意味があるんですよね。
あ、息子のクリスマスプレゼントは、夜中に車で駆けずり回ってNINTENDO 3DSを調達しました。
明日は、freeeのインフラ部隊を率いる浅羽さんです!
freeeでは、securityを担保する仕組みを一緒に作り上げていく仲間を求めています。
*1:Security operation Center
*2:Web Application Firewall = HTTP request filterです。正規表現やip blacklist、ratelimitを用いることができます。
*3:Intrusion Prevention System = packet capture + stream再構築 + signature match
*4:Computer Security Incident Response Team
*5:Product Security Incident Response Team
*6:Security Information and Event Management = セキュリティに関係するログを集めて解析をする仕組み
*7:今回はElastic Common Schemaに統一します。
ECS Field Reference | Elastic Common Schema (ECS) Reference [8.0] | Elastic