English:
I do not remember when, but certain day I decided to look for vulnerabilities in the FreeBSD kernel. As it was a long time ago, I do not remember almost any detail of the process, I just remember I found one NULL pointer dereference without reading the source code or using fuzzing techniques. Nowadays that is not one of the most interesting vulnerabilities because is necessary some tweaks to be vulnerable and to allow exploitation but my goal with this post is also to write a bit about the discovery process.
Português:
Não lembro exatamente quando, mas um certo dia, decidi começar a procurar vulnerabilidades no kernel do FreeBSD. Como faz muito tempo, não lembro de quase nenhum detalhe do processo, apenas lembro, que sem ler o código-fonte e sem usar técnicas de fuzzing, acabei descobrindo um NULL pointer dereference. Nos dias atuais essa não é uma das vulnerabilidade mais interessantes porque precisa de alguns ajustes para estar vulnerável e permitir a exploração, mas meu objetivo nesse post é também escrever um pouco sobre o processo de descoberta.
English:
I do not remember when, but certain day I decided to look for vulnerabilities in the FreeBSD kernel. As it was a long time ago, I do not remember almost any detail of the process, I just remember I found one NULL pointer dereference without reading the source code or using fuzzing techniques. Nowadays that is not one of the most interesting vulnerabilities because is necessary some tweaks to be vulnerable and to allow exploitation but my goal with this post is also to write about the discovery process.
The problem
The problem affects some file systems and it can be triggered by an unprivileged user if the sysctl vfs.usermount is enabled (set to 1). This vulnerability allows an attacker to execute arbitrary code and consequently privilege escalation. To be able to exploit it, the security feature security.bsd.map_at_zero also needs to be enabled (set to 1). At the time I wrote an exploit that escalates the privilege to root. After escalating the privileges the exploit needs to fix what was corrupted to avoid any further problems. To trigger the problem, one just needs to run the following commands:
mkdir ABCD EFGH
mount -t unionfs ABCD EFGH
mount -t unionfs EFGH ABCD
umount -f EFGH
df
As it can be seen below, a page fault will occur during the execution of the command df.
While writing that post I ran the string of commands and the error was triggered in the FreeBSD version 7.4, 8.4, 9.4, 10.3, 11.0 RC1 e PC-BSD 10.3. I did not analyse the problem in detail to try to understand what is going on but I believe the crash happens because when running the command df the kernel accesses the structure that stores the information about the mounted file systems and somehow this structure was corrupted by the command umount.
In the FreeBSD version 7 and 8 is also possible to trigger a NULL pointer dereference using the file system nullfs, as it can be seen in the screenshot below. An error is returned in other versions while running the command umount.
Error while running the string of commands using nullfs in a FreeBSD version 9.4
When I discovered that problem I used the file system vmblock. With a similar string of commands is also possible to trigger a NULL pointer dereference using it. The only difference is that the command umount receives ABCD rather than EFGH. Using vmblock the error was triggered in almost all versions mentioned above. I did not run the commands in a FreeBSD 7.4 and 8.4.
The modules of unionfs, nullfs and vmblock need to be previously loaded in the kernel, if not the command mount will not work. In the PC-BSD, the only Desktop system I ran the string of commands, the sysctl vfs.usermount comes enabled by default. I believe the modules of unionfs and nullfs are not popular but I am sure vmblock is a very one. That file system is used to improve the interface between a virtual machine and a hypervisor, as example, adding features like copy and paste from virtual machine to host and vice-versa.
The hypothesis
As stated earlier, that problem was found without reading the source code or fuzzing techniques. I just asked, what will happen if I do that? It was that question that took me to discover the problem. I do not remember details of the UFS and VFS (Virtual File System), but I wondered, how does the kernel keep the information of the mounted file systems? I do not know if it really is but I thought it could be a structure like a list and knowing a bit how lists work, I immediately tried to run operations out of the order, not expected and because of that the problem was triggered. I tried to create a hole in a mentally created list and when I ran the command df, boom! Kernel Panic. If the information are stored using a list, I don’t know, but I found that problem using that hypothesis.
Conclusion
The FreeBSD Security Team is aware of that problem since a long time ago but they do not consider a security problem, that’s why that is a very old and known issue. It is stated in the official documentation [1][2] that the usage of umount with the parameter -f can corrupt data. Then if one enables the sysctl vfs.usermount, it’s allowed unprivileged users mount file system as well as escalate privileges or causing a denial of service. Today that is not so important but when I found it the security feature security.bsd.map_at_zero was not so popular. This feature started to come enabled by default on FreeBSD version 8 [3], as it can be seen below:
The feature is disabled by default in FreeBSD 7 and lower, and must be enabled by setting the sysctl(8) variable security.bsd.map_at_zero to. In FreeBSD 8 and later feature is enabled by default.
When coming across a problem like a challenge in a CTF (Capture The Flag), a recommendation is to have a well defined and good hypothesis before spending time trying to solve it. Other example is black-box web security assessment where the analyst does not have access to the source code and is necessary through inputs to interact with the application and trying to understand what the application is doing with the data and look for a way to subvert the application to your own benefit. The time can be better spent trying to validate the hypothesis.
References
1 – https://www.freebsd.org/doc/handbook/mount-unmount.html
2 – https://www.freebsd.org/doc/handbook/usb-disks.html
3 – https://www.freebsd.org/security/advisories/FreeBSD-EN-09:05.null.asc
Português:
Não lembro exatamente quando, mas um certo dia, decidi começar a procurar vulnerabilidades no kernel do FreeBSD. Como faz muito tempo, não lembro de quase nenhum detalhe do processo, apenas lembro, que sem ler o código-fonte e sem usar técnicas de fuzzing, acabei descobrindo um NULL pointer dereference. Nos dias atuais esta não é uma das vulnerabilidade mais interessantes porque precisa de alguns ajustes para estar vulnerável e permitir a exploração mas meu objetivo nesse post é também escrever um pouco sobre o processo de descoberta.
O problema
O problema afeta alguns sistemas de arquivos e pode ser acionado por um usuário não privilegiado caso a sysctl vfs.usermount estiver habilitada (com o valor 1). Quando explorada, permite a um atacante executar códigos arbitrários e, consequentemente, escalação de privilégio. Para poder explorá-la, o recurso de segurança security.bsd.map_at_zero também deve estar habilitado (com o valor 1). Naquele tempo, eu escrevi um exploit que quando executado escalava os privilégios de um usuário para root. Depois de escalar os privilégios, o exploit precisa corrigir o que foi corrompido para evitar erros que force a máquina a reiniciar. Para acionar o problema, basta executar os seguintes comandos em ordem:
mkdir ABCD EFGH
mount -t unionfs ABCD EFGH
mount -t unionfs EFGH ABCD
umount -f EFGH
df
Como pode ser visto abaixo, um page fault ocorrerá durante a execução do comando df.
Enquanto escrevia esse post eu testei a sequência de comandos e o erro foi acionado no FreeBSD versão 7.4, 8.4, 9.4, 10.3, 11.0 RC1 e PC-BSD 10.3. Não analisei o problema detalhadamente para tentar entender o que realmente está acontecendo, mas acredito que o crash acontece porque ao executar o comando df o kernel acessa a estrutura que armazena as informações sobre os sistemas de arquivos montados e ela estava de alguma forma corrompida com a execução do umount.
No FreeBSD versão 7 e 8 também é possível acionar um NULL pointer dereference utilizando o sistema de arquivos nullfs, como pode ser visto no screenshot abaixo. Nas outras versões um erro é retornado ao executar o comando umount, informando que o dispositivo ainda está em uso.
Erro ao executar sequência de comandos usando nullfs em um FreeBSD versão 9.4
Inicialmente, quando descobri esse problema eu utilizei o sistema de arquivo da VMware, o vmblock. Com uma sequência de comandos similar também é possível acionar um NULL pointer dereference usando vmblock. A única diferença é que o comando umount recebe como argumento ABCD ao invés de EFGH. O problema foi acionado em quase todas as versões mencionadas acima. Não realizei os testes usando vmblock nas versões 7.4 e 8.4.
Os módulos do unionfs, nullfs e vmblock precisam estar previamente carregados para o comando mount funcionar e o problema ser acionado. No PC-BSD, o único sistema operacional Desktop que testei, a sysctl vfs.usermount vem por padrão com seu valor habilitado. Os módulos unionfs e nullfs não acredito que sejam amplamente utilizado, ao contrário do vmblock. Esse sistema de arquivos é necessário para melhorar a interface entre uma máquina virtual e o host, por exemplo, adicionar recursos como copiar e colar da máquina virtual para o host e vice-versa.
A Hipótese
Como afirmado no início desse artigo, esse problema foi descoberto sem ler o código-fonte e sem usar técnicas de fuzzing. Simplesmente me perguntei, o que acontece se eu fizer isso? Foi essa pergunta que me levou a descobrir o problema. Não lembro os detalhes da implementação do UFS e do VFS (Virtual File System), mas eu pensei, como o kernel mantém as informações dos sistemas de arquivos montados? Não sei se realmente é assim mas eu assumi que a estrutura de dados utilizada poderia ser uma lista e com um pouco de conhecimento de como listas são implementadas, imediatamente tentei executar operações fora do esperado, fora do padrão e com isso o problema foi acionado. Tentei criar um buraco removendo um nó na minha lista criada mentalmente e quando executei o df, boom! Kernel Panic. Se as informações são realmente armazenadas em uma lista, eu não sei, o importante é que descobri esse problema com essa hipótese.
Conclusão
O time de segurança do FreeBSD sabe, há muito tempo, da existência desse problema, mas não considera como um problema de segurança, por isso não há correção disponível. Inclusive, afirma-se na documentação oficial [1][2] que a utilização do comando umount com o parâmetro -f pode corromper dados. Se você habilita a sysctl vfs.usermount, saiba que você está permitindo usuários não privilegiados a montar sistemas de arquivos e também a escalar os privilégios ou causar uma negação de serviço. Hoje esse problema pode parecer não tão importante, mas quando o encontrei o recurso de segurança security.bsd.map_at_zero não era tão difundido como hoje. Esse recurso começou a vir habilitado por padrão no FreeBSD versão 8 [3], como pode ser visto abaixo:
The feature is disabled by default in FreeBSD 7 and lower, and must be enabled by setting the sysctl(8) variable security.bsd.map_at_zero to. In FreeBSD 8 and later feature is enabled by default.
Ao se deparar com um problema como em um challenge de um CTF (Capture The Flag), uma recomendação é ter uma hipótese bem definida antes de investir tempo tentando resolver. Outro exemplo são as análises web black-box em que o analista não tem acesso ao código-fonte e através de inputs precisa interagir com a aplicação e tentar entender o que ela faz com o dado e assim procurar uma forma de subvertê-la em seu benefício. O tempo pode ser melhor investido validando um boa hipótese.
Referências
1 – https://www.freebsd.org/doc/handbook/mount-unmount.html
2 – https://www.freebsd.org/doc/handbook/usb-disks.html
3 – https://www.freebsd.org/security/advisories/FreeBSD-EN-09:05.null.asc