Secure Coding

Many software projects are not using secure coding practices, and thus many mistakes are quite common and lead to vulnerabilities.

These can then be used by “hackers” in order to gain access your data, and—worse—the data of your users.

This blog will demonstrate common vulnerabilities, and show methods to prevent them. They will be shown in all sorts of applications—from admin scripts over web applications to iPhone apps anything can become topic, as any of these can impact your security.

Tue Apr 2 19:22:36 UTC 2013

Shell scripts on Linux and dropping privileges

Assuming you want to make a shell script that runs as root, but after setting up stuff, drops privileges to the calling user and runs a specified command: how would one best achieve this?

In C, what you would do is somewhat like this:

setuid(getuid());
execvp(argv[1], &argv[1]);

This drops set-user-id rights, then chains to the program specified in $1 and forwards the command line arguments to it without causing any extra shell parsing.

But how to do this in a shell script?

The user ID problem

First of all: shell scripts can't be setuid. We need another mechanism to start them with root privileges. The tool of our choice shall be Sudo. This, however, changes the effective and real UID to the target UID, and stores the calling user ID in $SUDO_USER. Our first attempt then may be:

su "$SUDO_USER" -c "$*"

Avoiding argument parsing

This however fails: it causes another level of argument parsing:

[root@grawp ~]# set -- 'echo' '"' '-x'
[root@grawp ~]# "$@"
" -x
[root@grawp ~]# su rpolzer -c "$*"
zsh:1: unmatched "

So let's try to find something better...

[root@grawp ~]# su rpolzer -c "$@"

Right, -c only takes a single argument... but the argument is a full fledged shell script!

[root@grawp ~]# su rpolzer -c 'exec "$@"' sh "$@"
su: invalid option -- 'x'
[root@grawp ~]# su rpolzer -- -c 'exec "$@"' sh "$@"
" -x

It worked! Now we also want this shell to have some sensible preset variables, so we want to read .profile and such, as sudo has stripped most environment variables for security reasons... also, we totally want to avoid danger of possible option parsing. Also, what if the user's shell is not Bourne compatible and doesn't do $@? So let's do it:

[root@grawp ~]# su -s '/bin/sh' - rpolzer -- -l -c 'exec "$@"' -- "$@"
" -x

Therefore, the solution is:

su -s '/bin/sh' - "$SUDO_USER" -- -l -c 'exec "$@"' -- "$@"

And here is a full script (download here) to abstract away this mess (I call it asuser):

#!/bin/sh

user=$1
shift

case "$user" in
    ''|-*)
        echo >&2 "Bad/evil user name."
        exit 1
        ;;
esac

if [ $# -eq 0 ]; then
    exec su - "$user"
else
    exec su -s '/bin/sh' - "$user" -- -l -c 'exec "$@"' -- "$@"
fi

NOTE: $SUDO_USER still should to be checked to be sane. Especially, it should better not start with a dash... although normally its value can be assumed sane as it comes from sudo, in security critical applications this should not be taken for granted. What if someone does have a user name starting with a dash... the /etc/passwd format allows it, but sure, it would break many tools, such as sudo.

As for why one would need this:

One application

#!/bin/sh

exec ip netns "$SUDO_USER" exec asuser "$SUDO_USER" "$@"

would be a neat script to run a given command in a user's private network namespace.


Posted by OpBaI | Permanent link | File under: unix, shell

Thu Sep 27 13:58:04 UTC 2012

iPhone Safari crash (other browsers too...)

It doesn't go much simpler than this:

<!DOCTYPE html>
<html>
        <meta http-equiv="refresh" content="3;URL=tel:(4MB of X)">
        <title>DIE, iPhone, DIE!</title>
        <body>
                <h1>DIE, iPhone, DIE!</h1>
                <p>
                        <img src="http://whitsblog.com/wp-content/uploads/2012/05/Rick-astley-never-gonna-give-you-up.jpg">
                        <br>
                        Never gonna browse the web!
                </p>
        </body>
</html>

Test link

Even Force Quit won't stop it... but the watchdog timer, or the reboot trick (hold both buttons for a few seconds) will.

But this is a HUGE file...

No problem.

My browser is crashing .............

I warned you about crash bro!!!!!

I told you dog!

It keeps happening

I told you man

I TOLD you about crash!

But seriously: how to remove it?

Ah... yes. Safari is so nice and even saves the open tabs before crashing the phone.

Either you can be fast and close the tab before it fully loaded.

Or, you can enable and disable airplane mode browsing mode in settings, clear Safari's cache, open Safari, close the tab, disable airplane mode again (thanks to Sergeij2000 on the Heise forum for this hint).

Or, you can enable and disable private browsing mode in Safari's settings, and when asked whether you want to close all tabs, confirm (thanks to ticaki on the Heise forum for this hint).

UPDATE: Try this link to get an idea of why it seems to crash. This one is so short it does not cause a crash, however, it does cause display corruption (dialog text overwriting dialog buttons) on the iPhone. Apparently the crash bug of the iPhone is in formatting the confirmation message dialog box... and it then ends up looking like this:

UPDATE: This vulnerability is not new, Apple should be aware it for 3 years now and hasn't fixed it yet. See: CVE-2009-3271


Posted by OpBaI | Permanent link | File under: ios

Wed Sep 12 14:59:03 UTC 2012

Scrubbing Metadata is Not Easy

Take some random MP3 file.

$ dd if=/dev/urandom bs=1k count=1024 | lame -r -s 44.1 -m s - test.mp3
1024+0 records in
1024+0 records out
1048576 bytes (1.0 MB) copied, 0.18162 s, 5.8 MB/s
Assuming raw pcm input file
LAME 3.99.5 64bits (http://lame.sf.net)
Using polyphase lowpass filter, transition band: 16538 Hz - 17071 Hz
Encoding <stdin> to test.mp3
Encoding as 44.1 kHz stereo MPEG-1 Layer III (11x) 128 kbps qval=3

Add some ID3 tags to it...

$ perl -e 'printf "TAGThis is tag number %-105d\xFF", $_ for reverse 1..8' >> test.mp3
$ id3v2 -l test.mp3 
id3v1 tag info for test.mp3:
Title  : This is tag number 1            Artist:                               
Album  :                                 Year:     , Genre: Unknown (255)
Comment:                               
test.mp3: No ID3v2 tag

Try remuxing it using ffmpeg, and instruct ffmpeg to remove metadata (see manpage):

$ ffmpeg -i test.mp3 -map_metadata -1 -c:a copy test-2.mp3 
ffmpeg version 0.11.1 Copyright (c) 2000-2012 the FFmpeg developers
  built on Jun  9 2012 13:50:13 with gcc 4.7.0 20120505 (prerelease)
  configuration: --prefix=/usr --enable-libmp3lame --enable-libvorbis --enable-libxvid --enable-libx264 --enable-libvpx --enable-libtheora --enable-libgsm --enable-libspeex --enable-postproc --enable-shared --enable-x11grab --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libschroedinger --enable-libopenjpeg --enable-librtmp --enable-libpulse --enable-libv4l2 --enable-gpl --enable-version3 --enable-runtime-cpudetect --disable-debug --disable-static
  libavutil      51. 54.100 / 51. 54.100
  libavcodec     54. 23.100 / 54. 23.100
  libavformat    54.  6.100 / 54.  6.100
  libavdevice    54.  0.100 / 54.  0.100
  libavfilter     2. 77.100 /  2. 77.100
  libswscale      2.  1.100 /  2.  1.100
  libswresample   0. 15.100 /  0. 15.100
  libpostproc    52.  0.100 / 52.  0.100
[mp3 @ 0x237a100] max_analyze_duration 5000000 reached at 5015510
Input #0, mp3, from 'test.mp3':
  Metadata:
    title           : This is tag number 1          
    artist          :                               
    album           :                               
    date            :     
    comment         :                               
  Duration: 00:00:05.98, start: 0.000000, bitrate: 129 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s
Output #0, mp3, to 'test-2.mp3':
  Metadata:
    TSSE            : Lavf54.6.100
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, 128 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
size=      95kB time=00:00:06.00 bitrate= 129.2kbits/s    
video:0kB audio:94kB global headers:0kB muxing overhead 0.466835%

Metadata is in input, but not in output. Looks like it worked, right? To make absolutely sure, let's do it again:

$ ffmpeg -i test-2.mp3 -map_metadata -1 -c:a copy test-3.mp3 
ffmpeg version 0.11.1 Copyright (c) 2000-2012 the FFmpeg developers
  built on Jun  9 2012 13:50:13 with gcc 4.7.0 20120505 (prerelease)
  configuration: --prefix=/usr --enable-libmp3lame --enable-libvorbis --enable-libxvid --enable-libx264 --enable-libvpx --enable-libtheora --enable-libgsm --enable-libspeex --enable-postproc --enable-shared --enable-x11grab --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libschroedinger --enable-libopenjpeg --enable-librtmp --enable-libpulse --enable-libv4l2 --enable-gpl --enable-version3 --enable-runtime-cpudetect --disable-debug --disable-static
  libavutil      51. 54.100 / 51. 54.100
  libavcodec     54. 23.100 / 54. 23.100
  libavformat    54.  6.100 / 54.  6.100
  libavdevice    54.  0.100 / 54.  0.100
  libavfilter     2. 77.100 /  2. 77.100
  libswscale      2.  1.100 /  2.  1.100
  libswresample   0. 15.100 /  0. 15.100
  libpostproc    52.  0.100 / 52.  0.100
[mp3 @ 0x259c100] max_analyze_duration 5000000 reached at 5015510
Input #0, mp3, from 'test-2.mp3':
  Metadata:
    encoder         : Lavf54.6.100
  Duration: 00:00:06.00, start: 0.000000, bitrate: 129 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s
Output #0, mp3, to 'test-3.mp3':
  Metadata:
    TSSE            : Lavf54.6.100
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, 128 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
size=      95kB time=00:00:06.00 bitrate= 129.1kbits/s    
video:0kB audio:94kB global headers:0kB muxing overhead 0.467454%

Indeed, my Title tag is no longer displayed by ffmpeg. Now the output file of this should be twice as clean, right? Let's see with ffprobe...

$ ffprobe test-3.mp3 
ffprobe version 0.11.1 Copyright (c) 2007-2012 the FFmpeg developers
  built on Jun  9 2012 13:50:13 with gcc 4.7.0 20120505 (prerelease)
  configuration: --prefix=/usr --enable-libmp3lame --enable-libvorbis --enable-libxvid --enable-libx264 --enable-libvpx --enable-libtheora --enable-libgsm --enable-libspeex --enable-postproc --enable-shared --enable-x11grab --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libschroedinger --enable-libopenjpeg --enable-librtmp --enable-libpulse --enable-libv4l2 --enable-gpl --enable-version3 --enable-runtime-cpudetect --disable-debug --disable-static
  libavutil      51. 54.100 / 51. 54.100
  libavcodec     54. 23.100 / 54. 23.100
  libavformat    54.  6.100 / 54.  6.100
  libavdevice    54.  0.100 / 54.  0.100
  libavfilter     2. 77.100 /  2. 77.100
  libswscale      2.  1.100 /  2.  1.100
  libswresample   0. 15.100 /  0. 15.100
  libpostproc    52.  0.100 / 52.  0.100
[mp3 @ 0x12df240] max_analyze_duration 5000000 reached at 5015510
Input #0, mp3, from 'test-3.mp3':
  Metadata:
    encoder         : Lavf54.6.100
  Duration: 00:00:06.00, start: 0.000000, bitrate: 129 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s

Clean indeed.

$ id3v2 -l test-3.mp3 
id3v1 tag info for test-3.mp3:
Title  : This is tag number 3            Artist:                               
Album  :                                 Year:     , Genre: Unknown (255)
Comment:                               
test-3.mp3: No ID3v2 tag

Trust no one.


Posted by OpBaI | Permanent link | File under: fileformats