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.

Wed Sep 12 14:42:09 UTC 2012

Funny Forkbomb

This shell script is a (single-forking-process) fork bomb in dash and busybox sh, but not in bash and ksh:

#!/bin/sh
S=`yes|head -n 32769`;while :;do exec<<S;done
$S  
S

Why? And why exactly 32769?

There you go.

Actually a neat trick to avoid tempfiles for here-documents, like bash uses...


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

Wed Sep 12 14:28:28 UTC 2012

Exploring the iPhone file system sandbox

There is a little known way to explore the iPhone file system without jailbreaking, at least on iPhone 4 and iOS 5.1.1:

  1. Take any app with file browser or FTP server functionality.
  2. Create a relative symbolic link to the file system root in its Documents folder:
    $ ifuse --appid APPID ~/mnt
    $ cd ~/mnt
    $ ln -snf ../../../../../.. root
    
  3. Open the app (or the FTP client)
  4. Enjoy!

Note that sandbox restrictions still apply; you will not be able to read another app's data this way.

I also did a brute force search for writable directories outside the app sandbox, and did not find any. I wrote this script for the purpose:

#!/usr/bin/perl

use strict;
use warnings;

use Net::FTP;

my $list_regex = qr/
        ^
        (?<type>d)
        (?<perms>\S+)
        \s+
        (?<links>\S+)
        \s+
        (?<user>\S+)
        \s+
        (?<group>\S+)
        \s+
        (?<size>\S+)
        \s+
        (?<date>\w\w\w\ .{8})
        \s+
        (?<name>.*)
        $
/x;

@ARGV == 5
        or die "Usage: $0 host port user pass root";
my ($host, $port, $user, $pass, $root) = @ARGV;

my $ftp = Net::FTP->new("$host:$port", Debug => 0, Passive => 1)
        or die "No ftp: $@";
$ftp->login($user, $pass)
        or die "No user/pass: $ftp->message";

open my $fh, ">", "ls-lR.txt"
        or warn ">ls-lR.txt: $!";

my @results = ();

my @queue = ($root);
while(@queue)
{
        my $item = shift @queue;
        print STDERR "[$item] found @{[scalar @results]} results\n";
        $ftp->mkdir($item . "/WRITETEST");
        my $list = $ftp->dir($item);
        print $fh "$item:\n";
        for(@$list)
        {
                print $fh "$_\n";
                /$list_regex/
                        or next;
                my $name = $+{name};
                next
                        if $name eq '.' or $name eq '..';
                if($name eq 'WRITETEST')
                {
                        push @results, $item;
                        $ftp->rmdir($item . "/WRITETEST");
                        next;
                }
                push @queue, "$item/$name";
        }
        print $fh "\n";
}

close $fh;

print "$_\n"
        for @results;

$ftp->quit;

UPDATE: This symlink hack has been fixed in iOS 6. I found a new way to create this link; however, apparently due to an issue in ideviceinstaller, doing this loses the Documents content of the app:

$ ideviceinstaller -o uninstall -o remove -o copy=. -a com.dspmobile.dbmeterpro
$ unzip -l com.dspmobile.dbmeterpro.ipa
$ mkdir -p "Payload/dB Meter Pro.app"
$ ln -snf ../../../../../.. "Payload/dB Meter Pro.app/root"
$ zip -0y com.dspmobile.dbmeterpro.ipa "Payload/dB Meter Pro.app/root"
$ ideviceinstaller -i com.dspmobile.dbmeterpro.ipa
$ ifuse --appid com.dspmobile.dbmeterpro ~/mnt
$ ln -snf "../dB Meter Pro.app/root" ~/mnt/root
$ fusermount -u ~/mnt

Posted by OpBaI | Permanent link | File under: ios, perl

Wed Apr 11 15:55:39 UTC 2012

Integer Overflows

A funny tale from PHP development: apparently, preventing integer overflows is hard. So, let's try to write code to allocate a 2D matrix of double...

unsigned int rows, cols;
// ...
if(rows < 1 || cols < 1)
        errx(1, "Invalid size");
double **M = calloc(rows * cols * sizeof(double), 1);

The inherent problem here is that the goal is to detect a miscalculation–by doing calculations. And many attempts failed in the past. So, how to best do this?

Standard Method: pre-verification of each step

if(rows < 1 || cols < 1)
        errx(1, "Invalid size");
if(rows > SIZE_MAX / (size_t) cols)
        errx(1, "Integer overflow");
size_t rc = rows * cols;
if(rc > SIZE_MAX / sizeof(double))
        errx(1, "Integer overflow");
size_t n = rc * sizeof(double);
double **M = calloc(rc, 1);

What are we doing there? We basically verify before each calculation step that the calculation will neither overflow nor divide by zero. We use the fact that the division operator / rounds downwards. Also, an integer division cannot overflow. So the following equations hold:

rows        <=  SIZE_MAX / (size_t) cols
rows * cols <= (SIZE_MAX / (size_t) cols) * cols
rows * cols <=  SIZE_MAX - {value between 0 and cols-1}
rows * cols <=  SIZE_MAX

And thus is proven this step causes no integer overflow.

The problem with this method is that it is tedious and leads to quite complex code.

Interesting Method: pre-verification in a single step

if(SIZE_MAX / (size_t) rows / (size_t) cols / sizeof(double) < 1)
        errx(1, "Integer overflow");
double **M = calloc((size_t) rows * (size_t) cols * sizeof(double), 1);

So, what is this now? Why does this trick guarantee no overflow? Let's work on this equation too.

Theorems for integer division "/":

  a >= b   =>   a/c >= b/c

  Proof:

    Write a as (a/c) * c + (a%c) with (a%c) between 0 and c-1.
    Write b as (b/c) * c + (b%c) with (b%c) between 0 and c-1.

    Then it follows:

                      a >= b
      (a/c) * c + (a%c) >= (b/c) * c + (b%c)
    ((a/c) - (b/c)) * c >= (b%c) - (a%c)

    If the theorem were not true, then (a/c) < (b/c).
    This means that (a/c) < (b/c) <= -1. We get:

    -1 * c >= ((a/c) - (b/c)) * c >= (b%c) - (a%c)
        -c >= (b%c) - (a%c)
         c <= (a%c) - (b%c)

    However, this cannot be fulfilled for any (a%c) and (b%c) in the range from
    0 to c-1. q.e.d.

Furthermore:

  a < b*c  =>   a/c < b

  Proof:

    Let's prove the equivalent relation

    a/c >= b   =>   a >= b*c

    Write a as (a/c) * c + (a%c) with (a%c) between 0 and c-1.

    Then it follows:

                (a/c) >= b
            (a/c) * c >= b*c
    (a/c) * c + (a%c) >= b*c + (a%c)
                    a >= b*c + (a%c)
                    a >= b*c

    q.e.d.

// Let's define:
r := (size_t) rows
c := (size_t) cols
d := sizeof(double)

// Assuming no overflow takes place, then we get using the above mentioned theorems:
SIZE_MAX             >= d*c*r        | / r
SIZE_MAX / r         >= d*c          | / c
SIZE_MAX / r / c     >= d            | / d
SIZE_MAX / r / c / d >= 1

// Assuming an overflow takes place, then we get using the above mentioned theorems:
SIZE_MAX             <  d*c*r        | / r
SIZE_MAX / r         <  d*c          | / c
SIZE_MAX / r / c     <  d            | / d
SIZE_MAX / r / c / d <  1

Therefore, the check above always yields the correct result.

Ridiculous Method: floating point post-verification

What actually happens on overflow? The first bits get cut off. We easily see that the overflown value is at most half the correct value, as cutting off a binary number's first bit always reduces it to less than half. So, we can do this:

if(rows < 1 || cols < 1)
        errx(1, "Invalid size");
size_t ni = (size_t) rows * (size_t) cols * sizeof(double);
float nf = (float) rows * (float) cols * sizeof(double);
if(ni < 0.8f * nf)
        errx(1, "Integer overflow");
double **M = calloc(ni, 1);

We know that on overflow, ni will be smaller than 0.5 times the correct value. So, as long as our calculation of nf is accurate enough so that 0.8f * nf is between 0.5 times the correct value, and the correct value itself, this actually works!

By standard methods, you find that a single calculation step for float of addition, multiplication, division, or rounding an input introduces a relative error of at most 10^-6, as long as no negative values or denormals are involved anywhere. How many such errors do we need so that 0.8 becomes 1.0 or 0.5?

223143 of them. We'll never hit THAT in a buffer size calculation...


Posted by OpBaI | Permanent link | File under: code, c