Saturday, August 25, 2012

Software flaw #3: CVE-2012-4298

CVE-2012-4298 is classical example of signedness vulnerability which I have explained in flaw #1. Affected software is Wireshark versions 1.8.x before 1.8.2.

Here's my analysis of the vulnerable code:
 
static void vwr_read_rec_data_ethernet(wtap *wth, guint8 *data_ptr, guint8 *rec,
int rec_size, int IS_TX)
{
...

// [1] msd_length is signed!
gint16 msdu_length,actual_octets; /* octets in frame */

...

// [2] msdu_length is initialized with external data (from received packet)
m_ptr = &(rec[0]); /* point to the data block */
s_ptr = &(rec[rec_size - vwr->STATS_LEN]); /* point to the stats block */
msdu_length = pntohs(&s_ptr[vwr->OCTET_OFF]);

...

/* [3] sanity checking is done but because msdu_length is signed, values
such as -1 will pass the check */
if (msdu_length > (rec_size - (int)vwr->STATS_LEN)) {
msdu_length = (rec_size - (int)vwr->STATS_LEN);
}

...

/* [4] wrongly validated data is casted to size_t (unsigned int) type and
used as memcpy parameter potentially causing overflow of buffer
pointed by data_ptr */
memcpy(&data_ptr[bytes_written], m_ptr, msdu_length);
...
}

Mitigation:

Declare msdu_length as guint16 instead of glint16

Monday, April 9, 2012

SSH remote port forwarding

Second kind of forwarding with ssh is so called remote port forwarding. This time the service available on ssh client can be forwarded to be available on ssh server. Suppose that we have machine with httpd (or any other TCP service) somewhere behind NAT and we want to make it available on our public standing ssh machine:

ssh-client$ ssh -R 8080:localhost:80 root@ssh-server-ip-addr

What it does is forwarding ssh-client's httpd server (port 80) to port 8080 on ssh-server.

From now on, connecting to ssh-server-ip-addr:8080 will effectively connect us with ssh-client:80

Use cases for this functionality:
- remote system administering of machine behind NAT (see my serverfault's answer)
- encrypted forwarding service to another machine

Vulnerability analysis with gdbserver

Here's my workflow when I'm doing vulnerability analysis under debugger. Thanks to gdbserver I can debug on target machine (OS image launched in Virtualbox) and use gvim and pyclewn just by dropping statically compiled gdbserver into target machine.

ON WORKING MACHINE:

# in case you don't already have it
apt-get install dpkg-dev

# get source of your gdb (includes gdbserver)
apt-get source gdb

# compile gdbserver
cd gdb-x.y/gdb/gdbserver/
LDFLAGS=-static ./configure
make

# sent it to target machine (for example:)
scp ./gdbserver root@192.168.x.y:

ON TARGET MACHINE:

# compile your binary (exim4 for me)
cd exim-4.69/
cp src/EDITME Local/Makefile
vi Local/Makefile
set/modify BIN_DIRECTORY
set/modify CONFIGURE_FILE
set EXIM_USER
set EXIM_GROUP
comment out EXIM_MONITOR
add: CFLAGS += -g somewhere in the file
make
make install

ON WORKING MACHINE:

# copy binary from target machine to your working machine:
cd exim-4.69/src
scp 192.168.x.y:/usr/local/exim/bin/exim-4.69-2 ./

ON TARGET MACHINE:

# start gdbserver:
./gdbserver :3332 /usr/local/exim/bin/exim-4.69-2 -d -bd -oX 3333

OR attach to existing process:

./gdbserver :3332 --attach

ON WORKING MACHINE:

cd exim-4.69/src
gdb exim-4.69-2
target remote 192.168.x.y:3332
break main
continue

OR you could do it from gvim + pyclewn:

cd exim-4.69/src
pyclewn
e exim.c
Cmapkeys
Cfile exim-4.69-2
Ctarget remote 192.168.x.y:3332
Cbreak main
Ccontinue

Friday, March 16, 2012

Software flaw #2: integer overflow vulnerability

Following code illustrates integer overflow condition:

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

unsigned char *createTable(unsigned int w, unsigned int h, unsigned char *initialRow);

int main(int argc, char **argv)
{
    unsigned char row[2] = { 'a' };
    unsigned char *wholeTable;
    unsigned int user_provided_w = 0x400;
    unsigned int user_provided_h = 0x1000001;

    wholeTable = createTable(user_provided_w, user_provided_h, row);

    free(wholeTable);
    return 0;
}

unsigned char *createTable(unsigned int w, unsigned int h, unsigned char *initialRow)
{
    unsigned int n;
    int i;
    unsigned char *buf;

    n = w * h;
    buf = (char *)malloc(n);
    if(!buf)
        return NULL;

    for(i=0; i<h; ++i)
        memcpy(&buf[i*w], initialRow, w);

    return buf;
}
 
The purpose of createTable(...) function is to take width and height and an initial row and create table in which all rows are initialized with initialRow. However we can observe that there can be integer overflow condition (bolded line), when width and height will be big enough. Lets assume width = 0x400 and height = 0x1000001 in this situation n will be equal to 1024 (in decimal), so only 1024 bytes will be allocated. Following for loop will be iterated 0x1000001 times so heap buffer overflow will occur.

Saturday, February 18, 2012

Comparing two directory structures

 Sometimes when I'm copying important directory structure (with cp -rp) I want to make sure that each and every file was copied correctly, so I compare source directory structure with it's copy this way:

DIRR=<copied-dir-structure>; diff -u <(cd <path-where-source-dir-lies>; find "$DIRR" -type f -exec ls -al {} \;) <(cd <path-where-copied-dir-lies>; find "$DIRR" -type f -exec ls -al {} \;)

Monday, February 13, 2012

SSH local port forwarding

Recently I advised on serverfault.com how to do local port forwarding with ssh:

On remote machine with sshd server, start a service that you would like to give access to:
 echo "hello" | nc -l -p 2222
On local machine initiate port forwarding:
 ssh -L 1234:localhost:2222 root@remoteserver.com
try it (from local machine):
 nc localhost 1234
If you will see "hello" that means port forwarding worked as expected.

 Use cases for this kind of functionality:
 - tunneling otherwise insecure TCP traffic
 - accessing services behind firewall when only sshd access is available

Tuesday, January 31, 2012

Hardening LAMP stack (part 1)

This article begins series of posts which are meant to create very SECURE but also easy to manage web server on Linux box. I start from default Debian 6.0 Stable installation and move on configuring various parts of the system.

We start from:

APACHE - PUTTING IT IN CHROOT JAIL

# compile apache2
  ./configure --prefix=/usr/local/apache --disable-userdir --disable-include
 make
 make install

# prepare new root environment for apache
  mkdir -p /srv/chroot/apache/

# create /usr/local directory in new environment
  mkdir -p /srv/chroot/apache/usr/local

# move built and installed apache to new environment
  mv /usr/local/apache /srv/chroot/apache/usr/local

# link it under old installation directory for easy updates
  ln -s /srv/chroot/apache/usr/local/apache /usr/local/apache

# stisfy all apache's library dependencies by copying it to new /lib directory
  mkdir -p /srv/chroot/apache/lib
 ldd /chroot/apache/usr/local/apache/bin/httpd
 (copy it to new /lib)

# copy strace (and it's dependencies) for debug purposes (remove it in production)
  mkdir -p /srv/chroot/apache/bin
 cp `which strace` /srv/chroot/apache/bin
 ldd `which strace`
 (copy it to new /lib)

# first launch (with strace), probably something will be missing
 chroot /srv/chroot/apache /bin/strace /usr/local/apache/bin/httpd

# name resolution
  mkdir -p /srv/chroot/apache/etc
 cp /etc/nsswitch.conf /srv/chroot/apache/etc/  #make sure that access to passwd is set to 'files'
 cp /lib/libnss_files.so.2 /srv/chroot/apache/lib

# dns name resolution
  cp /lib/libnss_dns.so.2 /srv/chroot/apache/lib
 cp /etc/hosts /srv/chroot/apache/etc
 cp /etc/resolv.conf /srv/chroot/apache/etc

# create special devices which apache uses
  mkdir /srv/chroot/apache/dev
 mknod -m 666 /srv/chroot/apache/dev/null c 1 3
 mknod -m 666 /chroot/apache/dev/zero c 1 5
 mknod -m 644 /chroot/apache/dev/random c 1 8

# create /tmp directory
  mkdir /srv/chroot/apache/tmp
 chmod +t /srv/chroot/apache/tmp
 chmod 777 /srv/chroot/apache/tmp

# create /etc/passwd & /etc/group files
  echo "www-data:x:33:33:Apache:/:/sbin/nologin" > /srv/chroot/apache/etc/passwd
 echo "www-data:x:33:" > /srv/chroot/apache/etc/group

# prepare /var directory in new environment (in jail) for test-site
  mkdir -p /srv/chroot/apache/var/www
 cd /srv/chroot/apache/var/www
 mkdir -p test-site/bin
 mkdir -p test-site/cgi-bin
 mkdir -p test-site/data
 mkdir -p test-site/htdocs
 mkdir -p test-site/logs

# link it for easier updates of future web applications
 ln -s /srv/chroot/apache/var/www /var/www

# second launch, now it should start, if not, use strace to find out why
 chroot /srv/chroot/apache /usr/local/apache/bin/httpd

APACHE - PRELIMINARY HARDENING 

# proper files & directories privilages
  chown -R root:root /usr/local/apache
 find /srv/chroot/apache/usr/local/apache -type d | xargs chmod 755
 find /srv/chroot/apache/usr/local/apache -type d | xargs chmod g-s
 find /srv/chroot/apache/usr/local/apache -type f | xargs chmod 644
 find /srv/chroot/apache/usr/local/apache/bin -type f | xargs chmod 744

# configuration and logs can be read only by root
  chmod -R go-r /srv/chroot/apache/usr/local/apache/conf
 chmod -R go-r /srv/chroot/apache/var/www/site-test/logs

Friday, January 13, 2012

Software flaw #1: Signed conversion vulnerability

To better understand this kind of flaw let's consider following code:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int read_from_file()
{
    char size;
    char buf[32];
    buf[1] = 0;
    int fd;

    if((fd = open("fileSubverted", O_RDONLY)) == -1) {
        perror("[open file]");
        return -1;
    }

    if(read(fd, &size, sizeof(char)) == -1) {
        perror("[read size]");
        return -1;
    }
    if(size > 32) {
        fprintf(stderr, "Too big size\n");
        return -1;
    }

    unsigned char s = size;
    if(read(fd, buf, s) == -1) {
        perror("[read content]");
        return -1;
    }

    return 0;
}

int main()
{
    if(read_from_file() == -1)
        exit(1);

    return 0;
}
 
* the above code should work without an explicit casting of size variable to unsigned int type (the bolded line) but in my environment it doesn't - without this cast I get EDEFAULT (Bad address) error from read function.

Problem lies in implicit conversion of 'size' variable form char type to size_t (unsigned int) type, if user provides negative value for 'size' (let's say -2) it will pass the 'if(size > 32)' check and it will be converted to size_t type while invoking read(...) function which will be 0x000000fe in 2's complement representation. Provided that file is bigger than 32 bytes, 'buf' buffer overflow will occur.

Attack:

Commands below will generate file that will cause application to crash (Segmentation fault), due to buffer overflow which overwrites return address from read_from_file function:

echo -e '\xfe'`perl -e 'print "A"x250'` > maliciousFile

or in pure Bash:

echo -e '\xfe'`printf 'A%.0s' {1..255}` > maliciousFile

Countermeasures:

1) Best in this situation is to declare size variable as unsigned char

2) Change validation condition to: if( size < 0 || size > 32) { //generate error }

Sunday, January 1, 2012

Simple intrusion detection using standard UNIX commands

After I'm done with configuring and hardening Linux server I'm doing "reference of the server" by writing output from several crucial commands to my laptop:

ps -x -o user,command
netstat -natuw
find / -uid 0 -perm -4000 -print
find / -size +10000k -print
crontab -u root -l

Then using this simple script on regular basis, I have chance to detect compromised boxes:

#!/bin/bash

function usage
{
    echo "Usage: $0 <hostname>"
}

function getSrvAddr
{
    case $1 in
        'hostname1' ) echo "ssh user@srv1.addr"
            ;;
        'hostname2' ) echo "ssh user@srv2.addr"
            ;;
        * ) echo -n ""
    esac
}

HOSTNAME=$1
REFERENCE_PATH=~/lab/configs/myServers/${HOSTNAME}
SERVER=`getSrvAddr "$HOSTNAME"`
CMDS=(
    "ps -x -o user,command"
    "netstat -natuw"
    "find / -uid 0 -perm -4000 -print"
    "find / -size +10000k -print"
    "crontab -u root -l"
)

if [ -z "$SERVER" ]; then
    usage
    exit 1
fi

i=0
for FILE in ${REFERENCE_PATH}/*; do
    echo
    echo "########################################################################"
    echo  ${SERVER} ${CMDS[$i]}
    echo "########################################################################"
    echo
    diff -w -u <(sort $FILE) <(${SERVER} ${CMDS[$i]} 2> /dev/null | sort)
    i=$((i+1))
done

Basically, what the script does is compare server's initial output from several UNIX commands with it's current output. Using this script I can easily extend it for more commands and more servers. It's very simple method of intrusion detection (and by no mean 100% reliable!) but it's good addition to other mechanisms that should be in place.