sanitize task->comm to avoid leaking escape codes
Subsystems (8)
PATCH v2 1/1 sanitize task->comm to avoid leaking escape codes
Through get_task_comm() and many direct uses of task->comm in the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.
This patch sanitizes task->comm to only contain printable characters when it is set. Additionally, it redefines get_task_comm so that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).
---
v2:
- don't use a helper #define, just fix the arguments and callers
- add missing ctype.h include that got lost during testing.
--- drivers/char/tty_audit.c | 2 +- fs/exec.c | 18 ++++++++++++++---- fs/proc/array.c | 4 ++-- include/linux/sched.h | 2 +- kernel/auditsc.c | 2 +- kernel/capability.c | 4 ++-- kernel/fork.c | 2 +- kernel/sys.c | 2 +- 8 files changed, 23 insertions(+), 13 deletions(-)
drivers/char/tty_audit.c +1 -1
@@ -75,7 +75,7 @@ static void tty_audit_log(const char *description, struct task_struct *tsk,"major=%d minor=%d comm=" , description, tsk->pid, uid, loginuid, sessionid, major, minor); -get_task_comm (name, tsk); +get_task_comm (name,sizeof (name), tsk);audit_log_untrustedstring (ab, name);audit_log_format (ab," data=" );audit_log_n_hex (ab, data, size);
fs/exec.c +14 -4
@@ -55,6 +55,7 @@#include <linux/fsnotify.h> #include <linux/fs_struct.h> #include <linux/pipe_fs_i.h> +#include <linux/ctype.h> #include <asm/uaccess.h> #include <asm/mmu_context.h>
@@ -934,17 +935,18 @@ static void flush_old_files(struct files_struct * files)spin_unlock (&files->file_lock); } -char *get_task_comm (char *buf,struct task_struct *tsk) +char *get_task_comm (char *buf,size_t len,struct task_struct *tsk) { -/* buf must be at least sizeof(tsk->comm) in size */ task_lock (tsk); -strncpy (buf, tsk->comm,sizeof (tsk->comm)); +strlcpy (buf, tsk->comm, len);task_unlock (tsk);return buf; }void set_task_comm (struct task_struct *tsk,char *buf) { +size_t i; +task_lock (tsk);/*
@@ -955,7 +957,15 @@ void set_task_comm(struct task_struct *tsk, char *buf)*/ memset (tsk->comm,0 ,TASK_COMM_LEN );wmb (); -strlcpy (tsk->comm, buf,sizeof (tsk->comm)); + +/* sanitize non-printable characters */ +for (i =0 ; buf[i] && i < (sizeof (tsk->comm) -1 ); i++) { +if (!isprint (buf[i])) + tsk->comm[i] ='?' ; +else + tsk->comm[i] = buf[i]; + } +task_unlock (tsk);perf_event_comm (tsk); }
fs/proc/array.c +2 -2
@@ -93,7 +93,7 @@ static inline void task_name(struct seq_file *m, struct task_struct *p)char *name;char tcomm[sizeof (p->comm)]; -get_task_comm (tcomm, p); +get_task_comm (tcomm,sizeof (tcomm), p);seq_printf (m,"Name:\t" ); end = m->buf + m->size;
@@ -393,7 +393,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } } -get_task_comm (tcomm, task); +get_task_comm (tcomm,sizeof (tcomm), task);sigemptyset (&sigign);sigemptyset (&sigcatch);
include/linux/sched.h +1 -1
@@ -2117,7 +2117,7 @@ extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned lonstruct task_struct *fork_idle (int );extern void set_task_comm (struct task_struct *tsk,char *from); -extern char *get_task_comm (char *to,struct task_struct *tsk); +extern char *get_task_comm (char *to,size_t len,struct task_struct *tsk);#ifdef CONFIG_SMP extern unsigned long wait_task_inactive (struct task_struct *,long match_state);
kernel/auditsc.c +1 -1
@@ -956,7 +956,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk/* tsk == current */ -get_task_comm (name, tsk); +get_task_comm (name,sizeof (name), tsk);audit_log_format (ab," comm=" );audit_log_untrustedstring (ab, name);
kernel/capability.c +2 -2
@@ -51,7 +51,7 @@ static void warn_legacy_capability_use(void)printk (KERN_INFO "warning: `%s' uses 32-bit capabilities" " (legacy support in use)\n" , -get_task_comm (name, current)); +get_task_comm (name,sizeof (name), current)); warned =1 ; } }
@@ -81,7 +81,7 @@ static void warn_deprecated_v2(void)printk (KERN_INFO "warning: `%s' uses deprecated v2" " capabilities in a way that may be insecure.\n" , -get_task_comm (name, current)); +get_task_comm (name,sizeof (name), current)); warned =1 ; } }
kernel/fork.c +1 -1
@@ -1403,7 +1403,7 @@ long do_fork(unsigned long clone_flags, count--;printk (KERN_INFO "fork(): process `%s' used deprecated " "clone flags 0x%lx\n" , -get_task_comm (comm, current), +get_task_comm (comm,sizeof (comm), current), clone_flags &CLONE_STOPPED ); } }
kernel/sys.c +1 -1
@@ -1535,7 +1535,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,set_task_comm (me, comm);return 0 ;case PR_GET_NAME : -get_task_comm (comm, me); +get_task_comm (comm,sizeof (comm), me);if (copy_to_user ((char __user *)arg2, comm,sizeof (comm)))return -EFAULT ;
Signature · 6 lines
1.7.1 -- Kees Cook Ubuntu Security Team
Quoted 6 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.
Quoted 6 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).Signed-off-by Kees Cook <[email protected]>
I've reviewed this patch briefly, Here is my personal concern...
On Linux, non-printable leaking is fundamental, only fixing task->comm doesn't solve syslog exploit issue. Probably all /proc/kmsg user should have escaping non-pritables code.
However, task->comm is one of most easy injection data of kernel, because we have prctl(PR_SET_NAME), attacker don't need root privilege. So, conservative assumption seems guard from crappy fault. Plus, this patch is very small and our small TASK_COMM_LEN lead that we don't need big performance concern.
So, I don't find demerit in this proposal. but I'm not security specialist, it's only personal thinking.
Am Freitag, den 25.06.2010, 08:56 +0900 schrieb KOSAKI Motohiro:
Quoted 19 lines
Quoted 6 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.Quoted 6 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).Signed-off-by Kees Cook <[email protected]>I've reviewed this patch briefly, Here is my personal concern...
On Linux, non-printable leaking is fundamental, only fixing
task->commdoesn't solve syslog exploit issue. Probably all/proc/kmsguser should have escaping non-pritables code.
Quoted 9 lines
However,
task->commis one of most easy injection data of kernel, because we haveprctl(PR_SET_NAME), attacker don't need root privilege. So, conservative assumption seems guard from crappy fault. Plus, this patch is very small and our smallTASK_COMM_LENlead that we don't need big performance concern.So, I don't find demerit in this proposal. but I'm not security specialist, it's only personal thinking.
Agree. I think a escaped printk should be a more generic solution.
Stefani
Quoted 26 lines · Stefani Seibold
Am Freitag, den 25.06.2010, 08:56 +0900 schrieb KOSAKI Motohiro:
Quoted 25 lines
Quoted 6 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.Quoted 6 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).Signed-off-by Kees Cook <[email protected]>I've reviewed this patch briefly, Here is my personal concern...
On Linux, non-printable leaking is fundamental, only fixing
task->commdoesn't solve syslog exploit issue. Probably all/proc/kmsguser should have escaping non-pritables code.However,
task->commis one of most easy injection data of kernel, because we haveprctl(PR_SET_NAME), attacker don't need root privilege. So, conservative assumption seems guard from crappy fault. Plus, this patch is very small and our smallTASK_COMM_LENlead that we don't need big performance concern.
Quoted 4 lines
Quoted 3 lines
So, I don't find demerit in this proposal. but I'm not security specialist, it's only personal thinking.
Agree. I think a escaped printk should be a more generic solution.
I think sanitizing inputs is more effective than sanitizing outputs. If ->comm is safe internally, then we don't have to filter it going out on printk, audit, /proc output, etc. There is a limited number of places where a process has control over an arbitrary string in kernel structures, so the places where they are set should be fixed instead of fixing every possible usage of it on output.
I wouldn't mind sanitizing printk also, but it's tangential to sanitizing task->comm when it is set.
-Kees
Signature · 2 lines
Kees Cook Ubuntu Security Team
Quoted 26 lines
Am Freitag, den 25.06.2010, 08:56 +0900 schrieb KOSAKI Motohiro:
Quoted 25 lines
Quoted 6 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.Quoted 6 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).Signed-off-by Kees Cook <[email protected]>I've reviewed this patch briefly, Here is my personal concern...
On Linux, non-printable leaking is fundamental, only fixing
task->commdoesn't solve syslog exploit issue. Probably all/proc/kmsguser should have escaping non-pritables code.However,
task->commis one of most easy injection data of kernel, because we haveprctl(PR_SET_NAME), attacker don't need root privilege. So, conservative assumption seems guard from crappy fault. Plus, this patch is very small and our smallTASK_COMM_LENlead that we don't need big performance concern.
Quoted 4 lines
Quoted 3 lines
So, I don't find demerit in this proposal. but I'm not security specialist, it's only personal thinking.
Agree. I think a escaped printk should be a more generic solution.
Is this possible? printk() doesn't know userland locale. how do it escape correctly? When we only concern task->comm, assuming ascii-only string is enough practical. but printk generic logic should allow non-ascii, I think.
I think userland reader process only know correct escaping way.
Quoted 8 lines · KOSAKI Motohiro
Quoted 8 lines
Am Freitag, den 25.06.2010, 08:56 +0900 schrieb KOSAKI Motohiro:
Quoted 7 lines
Quoted 7 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).
For the audit system, we want the real, unsanitized task->comm. We record it in a special format to the audit logs such that unprintable characters are included. We want it exactly this way for certification purposes as well as forensic evidence if someone was playing games. If you do sanitize it for other areas of the kernel, please give us a way to get the unsanitized text.
-Steve
Quoted 25 lines
Quoted 14 lines
Quoted 14 lines
Quoted 1 line
Signed-off-by Kees Cook <[email protected]>I've reviewed this patch briefly, Here is my personal concern...
On Linux, non-printable leaking is fundamental, only fixing
task->commdoesn't solve syslog exploit issue. Probably all/proc/kmsguser should have escaping non-pritables code.However,
task->commis one of most easy injection data of kernel, because we haveprctl(PR_SET_NAME), attacker don't need root privilege. So, conservative assumption seems guard from crappy fault. Plus, this patch is very small and our smallTASK_COMM_LENlead that we don't need big performance concern.Quoted 4 lines
Quoted 2 lines
So, I don't find demerit in this proposal. but I'm not security specialist, it's only personal thinking.
Agree. I think a escaped printk should be a more generic solution.
Is this possible?
printk()doesn't know userland locale. how do it escape correctly? When we only concerntask->comm, assuming ascii-only string is enough practical. but printk generic logic should allow non-ascii, I think.I think userland reader process only know correct escaping way.
Quoted 14 lines · KOSAKI Motohiro
Quoted 8 lines
Quoted 8 lines
Am Freitag, den 25.06.2010, 08:56 +0900 schrieb KOSAKI Motohiro:
Quoted 7 lines
Quoted 7 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).
Quoted 5 lines
For the audit system, we want the real, unsanitized
task->comm. We record it in a special format to the audit logs such that unprintable characters are included. We want it exactly this way for certification purposes as well as forensic evidence if someone was playing games. If you do sanitize it for other areas of the kernel, please give us a way to get the unsanitized text.
Probably this mail is offtopic. I think audit is unrelated with this discusstion. because when forensic, admins shouldn't believe task->comm at all. because 1) no path information, perhaps "ls" might mean "/home/attackers-dir/evil-script/ls" 2) easily obscured by prctl(PR_SET_NAME).
That said, audit have to logged following two point if task name is necessary.
1) exec
2) prctl(PRT_SET_NAME)
Thought ?
Quoted 7 lines · KOSAKI Motohiro
Quoted 6 lines
For the audit system, we want the real, unsanitized
task->comm. We record it in a special format to the audit logs such that unprintable characters are included. We want it exactly this way for certification purposes as well as forensic evidence if someone was playing games. If you do sanitize it for other areas of the kernel, please give us a way to get the unsanitized text.
Quoted 5 lines
Probably this mail is offtopic. I think audit is unrelated with this discusstion. because when forensic, admins shouldn't believe
task->commat all. because 1) no path information, perhaps "ls" might mean "/home/attackers-dir/evil-script/ls" 2) easily obscured byprctl(PR_SET_NAME).
No, its on-topic and we want that information unchanged.
Quoted 5 lines
That said, audit have to logged following two point if task name is necessary. 1) exec
2)prctl(PRT_SET_NAME)Thought ?
The audit system is capable of grabbing that information, too.
-Steve
Quoted 8 lines · KOSAKI Motohiro
Quoted 7 lines
Quoted 6 lines
For the audit system, we want the real, unsanitized
task->comm. We record it in a special format to the audit logs such that unprintable characters are included. We want it exactly this way for certification purposes as well as forensic evidence if someone was playing games. If you do sanitize it for other areas of the kernel, please give us a way to get the unsanitized text.
Quoted 7 lines
Quoted 5 lines
Probably this mail is offtopic. I think audit is unrelated with this discusstion. because when forensic, admins shouldn't believe
task->commat all. because 1) no path information, perhaps "ls" might mean "/home/attackers-dir/evil-script/ls" 2) easily obscured byprctl(PR_SET_NAME).No, its on-topic and we want that information unchanged.
Why?
I think I've described why admins should't see task->comm during forensic. Do you disagree this? or Do you have another viewpoint?
Can you help us clarify your point?
Quoted 7 lines
Quoted 5 lines
That said, audit have to logged following two point if task name is necessary. 1) exec
2)prctl(PRT_SET_NAME)Thought ?
The audit system is capable of grabbing that information, too.
ok. thanks good information :)
Quoted 5 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.
If an administrator has a vulnerable terminal emulator they have other problems.
Quoted 4 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).
This is a regression for tools that correctly handle unmutilated data.
fs/exec.c void set_task_comm(struct task_struct *tsk, char *buf) · line 964
Quoted 4 lines
fs/exec.c +4
-0+/* sanitize non-printable characters */ +for (i =0 ; buf[i] && i < (sizeof (tsk->comm) -1 ); i++) { +if (!isprint (buf[i])) + tsk->comm[i] ='?' ;
The kernel "isprint" isn't adequate for this. comm is set by the shell based on argv[0] usually which means that in normal situations it is a UTF-8 string.
Please do any filtering you must in the yama security module where it only affects that. One way to approach it without losing data within the module might be to use HTML style encoding within Yama so your own tools can undo the 'sanitizing' rather than losing information ?
Ideally you want to the dev/inode pair of the thing being executed printed as well - that will give real information for security purposes, while the ->comm data is much more convenient for general debugging and investigation than having to keep looking them up.
Alan
Quoted 8 lines · Alan Cox
Quoted 5 lines
Through
get_task_comm()and many direct uses oftask->commin the kernel, it is possible for escape codes and other non-printables to leak into dmesg, syslog, etc. In the worst case, these strings could be used to attack administrators using vulnerable terminal emulators, and at least cause confusion through the injection of \r characters.If an administrator has a vulnerable terminal emulator they have other problems.
Totally agreed.
Quoted 4 lines
Please do any filtering you must in the yama security module where it only affects that. One way to approach it without losing data within the module might be to use HTML style encoding within Yama so your own tools can undo the 'sanitizing' rather than losing information ?
I'm not interested in sanitizing this in Yama. The use of task->comm via printk was seen as a flaw. I didn't agree (see above about terminal), and suggested that if it was a flaw, it was a flaw with printk or task->comm itself. Since "fixing" both of those have been vetoed, I have no more interest in the filtering.
What I do have interest in is fixing get_task_comm's use of buffers, which is theoretically problematic in some future where someone accidentally calls it with a buffer smaller than sizeof(task->comm).
I'll send a patch that only fixes that and leaves out the filtering.
-Kees
Signature · 2 lines
Kees Cook Ubuntu Security Team
Quoted 3 lines
What I do have interest in is fixing get_task_comm's use of buffers, which is theoretically problematic in some future where someone accidentally calls it with a buffer smaller than
sizeof(task->comm).
Lots of things are theoretically problematic and kernel would take a week to boot if we covered them all 8)
Having a
struct task_name {
char [propersize];
}would produce the same code as far as I can tell and so typechecking though - so as you say it can be done sanely.
Alan
Quoted 7 lines
Quoted 4 lines
This patch sanitizes
task->commto only contain printable characters when it is set. Additionally, it redefinesget_task_commso that it is more obvious when misused by callers (presently nothing was incorrectly calling get_task_comm's unsafe use of strncpy).This is a regression for tools that correctly handle unmutilated data.
Quoted 8 lines
fs/exec.c void set_task_comm(struct task_struct *tsk, char *buf) · line 964
Quoted 4 lines
fs/exec.c +4
-0+/* sanitize non-printable characters */ +for (i =0 ; buf[i] && i < (sizeof (tsk->comm) -1 ); i++) { +if (!isprint (buf[i])) + tsk->comm[i] ='?' ;The kernel "isprint" isn't adequate for this. comm is set by the shell based on argv[0] usually which means that in normal situations it is a UTF-8 string.
Ah, I recall one use case. In past, some IBM folks talked about they want to map Java thread name to prctl(PR_SET_NAME). In such case, utf-8 name is very common.
So, I agree with you.
Thanks.