Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[BUG] Two vulnerabilities in zsh
- X-seq: zsh-workers 45843
- From: Aaron Esau <arinerron@xxxxxxxxxxxxxx>
- To: "zsh-workers@xxxxxxx" <zsh-workers@xxxxxxx>
- Subject: [BUG] Two vulnerabilities in zsh
- Date: Tue, 19 May 2020 06:48:14 +0000
- Cc: Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- List-unsubscribe: <mailto:zsh-workers-unsubscribe@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
- Reply-to: Aaron Esau <arinerron@xxxxxxxxxxxxxx>
Another win for AFL!
There are two vulnerabilities in zsh <5.8. I'll request a couple CVE IDs if that's okay.
I contacted Peter <p.w.stephenson@xxxxxxxxxxxx> about these a few days ago. Neither have common, practical attack scenarios ("oh no! someone with a shell could pop a shell!"), so I'm disclosing them here.
I did a little analysis on each vulnerability. The more severe one appears to be some form of memory corruption, but it's decently complex, and I couldn't find the root cause. I think it'll take someone more experienced than me to find it. But, I was able to track down the null dereference bug. :)
Frankly, I'm not a C developer, but I think I at least know how to fix the null dereference vuln, so I added a section with a patch there.
Thanks for the awesome shell!
Sincerely,
Aaron Esau
https://aaronesau.com/
------ #1 :: null dereference in check_colon_subscript in subst.c ------
A denial of service vulnerability exists in zsh <5.8 due to a null dereference in check_colon_subscript in subst.c.
## Reproduction Steps
I was able to reproduce this bug on every zsh version I tested. I am running zsh 5.8 (x86_64-pc-linux-gnu) on Arch Linux.
1. Start zsh and execute the following (including quotes):
"${: :${{{\"{{i use arch btw}}"
2. Observe that the command causes a segmentation fault. The stack trace is:
$rip 0x5555555eb22e => movzx eax, BYTE PTR [rax]
[#1] 0x5555555ed61e => prefork()
[#2] 0x555555583815 => mov rsi, r12
[#3] 0x555555588e01 => mov rbx, QWORD PTR [rbx]
[#4] 0x55555558c32f => pop rcx
[#5] 0x55555558c6d1 => mov eax, DWORD PTR [rip+0x9e8c1] # [rip+0x9e8c1] => 0x55555562af98
[#6] 0x55555558e051 => execlist()
[#7] 0x55555558e564 => execode()
[#8] 0x5555555a43d4 => loop()
[#9] 0x5555555a7d36 => zsh_main()
## Analysis
The following code in check_colon_subscript in subst.c checks if the value at a pointer obtained from a call to parse_subscript is NULL:
*endp = parse_subscript(str, 0, ':');
if (!*endp) {
/* No trailing colon? */
*endp = parse_subscript(str, 0, '\0');
if (!*endp)
return NULL;
}
However, the pointer itself can be NULL. In parse_subscript in lex.c, if err != 0, the returning variable, s, is set to NULL:
err = dquote_parse(endchar, sub);
...
if (err) {
...
s = NULL;
} else {
s += toklen;
}
..
return s;
The function dquote_parse in lex.c returns NULL on many error-related conditions.
## Patch
diff --git Src/subst.c Src/subst.c
index 90b5fc121..ac12c6d0e 100644
--- Src/subst.c
+++ Src/subst.c
@@ -1571,10 +1571,10 @@ check_colon_subscript(char *str, char **endp)
}
*endp = parse_subscript(str, 0, ':');
- if (!*endp) {
+ if (endp && !*endp) {
/* No trailing colon? */
*endp = parse_subscript(str, 0, '\0');
- if (!*endp)
+ if (endp && !*endp)
return NULL;
}
sav = **endp;
------ #2 :: memory corruption in ?? ------
A memory corruption vulnerability exists in zsh <5.8 which may enable arbitrary code execution or memory disclosure via unspecified methods.
## Reproduction Steps
I am running zsh 5.8 (x86_64-pc-linux-gnu) on Arch Linux, kernel version 5.6.11-arch1-1.
1. Execute the following PoC command:
echo $'******** **********************$\\\n(>$' | zsh
2. Observe that the command causes a segmentation fault. The stack trace is:
$rip 0x5555555eb22e => movzx eax, BYTE PTR [rax]
[#1] 0x5555555ed61e => prefork()
[#2] 0x555555583815 => mov rsi, r12
[#3] 0x555555588e01 => mov rbx, QWORD PTR [rbx]
[#4] 0x55555558c32f => pop rcx
[#5] 0x55555558c6d1 => mov eax, DWORD PTR [rip+0x9e8c1] # [rip+0x9e8c1] => 0x55555562af98
[#6] 0x55555558e051 => execlist()
[#7] 0x55555558e564 => execode()
[#8] 0x5555555a43d4 => loop()
[#9] 0x5555555a7d36 => zsh_main()
## Analysis
The segmentation fault is caused by a mov instruction which dereferences a $rax, a pointer to invalid memory:
* 0x555555588bac: setne bl
* 0x555555588baf: jmp 0x555555588bd9
* 0x555555588bb1: nop DWORD PTR [rax+0x0]
=>0x555555588bb8: mov rdi, QWORD PTR [rax+0x10]
* 0x555555588bbc: addr32 call 0x5555555f27f0 <has_token>
* 0x555555588bc2: test eax, eax
* 0x555555588bc4: je 0x555555589bc0
* 0x555555588bca: mov rsi, QWORD PTR [rbp+0x0]
* 0x555555588bce: xor edx, edx
At the segfault, the \$r[a-z]{2} registers are:
$rax : 0x00007fff007ffff7
$rbx : 0x00007ffff7fbe601 => 0xa8772e2a3a000000
$rcx : 0x00007ffff7fbe608 => 0x00007ffff7fbe5a8 => 0x0000000000000000
$rdx : 0x00007ffff7fbe5a8 => 0x0000000000000000
$rsp : 0x00007fffffffbef0 => 0x0000000100000099
$rbp : 0x00007ffff7fbe5f0 => 0x00007fff007ffff7
$rsi : 0x00007ffff7fbe578 => 0x0000000000000000
$rdi : 0x00007ffff7fbe5f0 => 0x00007fff007ffff7
$rip : 0x0000555555588bb8 => mov rdi, QWORD PTR [rax+0x10]
So, again, the register $rax points to invalid memory. I stepped backward and found that the value in the register comes from the stack:
=>0x555555588bef: mov rax, QWORD PTR [rbp+0x0]
* 0x555555588bf3: test rax, rax
* 0x555555588bf6: jne 0x555555588bb8
I set a breakpoint at 0x555555588bef, then set watchpoints on both $rbp and—as least significant 4 bytes of the address appear to be overwritten—the address $rbp-0x4.
It was really hard to trace back and find what overwrites that memory. It appears as though the lower bytes of the address are overwritten before the instruction that first triggered the second watchpoint in __memmove_avx_unaligned_erms:
=>0x7ffff7d4d26f: mov QWORD PTR [rdi+rdx*1-0x8], rcx
However, neither execlist in exec.c nor any of the functions it calls appear to execute glibc's memmove function. Perhaps another glibc function internally calls __memmove_avx_unaligned_erms? To be continued.
Messages sorted by:
Reverse Date,
Date,
Thread,
Author