Linux Namespaces

November 23rd, 2014

Starting from kernel 2.6.24, there are 6 different types of Linux namespaces. Namespaces are useful in isolating processes from the rest of the system, without needing to use full low level virtualization technology.

  • CLONE_NEWIPC: IPC Namespaces: SystemV IPC and POSIX Message Queues can be isolated.
  • CLONE_NEWPID: PID Namespaces: PIDs are isolated, meaning that a PID inside of the namespace can conflict with a PID outside of the namespace. PIDs inside the namespace will be mapped to other PIDs outside of the namespace. The first PID inside the namespace will be ‘1’ which outside of the namespace is assigned to init
  • CLONE_NEWNET: Network Namespaces: Networking (/proc/net, IPs, interfaces and routes) are isolated. Services can be run on the same ports within namespaces, and “duplicate” virtual interfaces can be created.
  • CLONE_NEWNS: Mount Namespaces. We have the ability to isolate mount points as they appear to processes. Using mount namespaces, we can achieve similar functionality to chroot() however with improved security.
  • CLONE_NEWUTS: UTS Namespaces. This namespaces primary purpose is to isolate the hostname and NIS name.
  • CLONE_NEWUSER: User Namespaces. Here, user and group IDs are different inside and outside of namespaces and can be duplicated.

Let’s look first at the structure of a C program, required to demonstrate process namespaces. The following has been tested on Debian 6 and 7.

First, we need to allocate a page of memory on the stack, and set a pointer to the end of that memory page. We use alloca to allocate stack memory rather than malloc which would allocate memory on the heap.

void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);

Next, we use clone to create a child process, passing the location of our child stack ‘mem’, as well as the required flags to specify a new namespace. We specify ‘callee’ as the function to execute within the child space:

mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);

After calling clone we then wait for the child process to finish, before terminating the parent. If not, the parent execution flow will continue and terminate immediately after, clearing up the child with it:

while (waitpid(mypid, &r, 0) < 0 && errno == EINTR)
{
	continue;
}

Lastly, we’ll return to the shell with the exit code of the child:

if (WIFEXITED(r))
{
	return WEXITSTATUS(r);
}
return EXIT_FAILURE;

Now, let’s look at the callee function:

static int callee()
{
	int ret;
	mount("proc", "/proc", "proc", 0, "");
	setgid(u);
	setgroups(0, NULL);
	setuid(u);
	ret = execl("/bin/bash", "/bin/bash", NULL);
	return ret;
}

Here, we mount a /proc filesystem, and then set the uid (User ID) and gid (Group ID) to the value of ‘u’ before spawning the /bin/bash shell.
Read the rest of this entry »

Performing DNS Queries in Python

November 21st, 2014

dnspython provides a detailed interface into DNS. In its simplest form, it’s possible to perform queries in only a couple of lines of code. Here’s a commented example:

import dns.resolver #import the module
myResolver = dns.resolver.Resolver() #create a new instance named 'myResolver'
myAnswers = myResolver.query("google.com", "A") #Lookup the 'A' record(s) for google.com
for rdata in myAnswers: #for each response
    print rdata #print the data

The results in my case are:

173.194.125.3
173.194.125.7
173.194.125.4
173.194.125.8
173.194.125.9
173.194.125.5
173.194.125.2
173.194.125.0
173.194.125.6
173.194.125.1
173.194.125.14

Read the rest of this entry »

Nginx, SSL & php5-fpm on Debian Wheezy

October 11th, 2014

I decided to take a break from my love affair with Apache and set up a recent development project on Nginx. I’ve seen nothing but good things in terms of speed and performance from Nginx. I decided to set up a LEMP server (Linux, Nginx, MySQL, PHP), minus the MySQL as it’s already installed on my VM host server, and plus SSL. Here’s the full setup tutorial on Debian Wheezy:

Step #1 – Installing the packages

apt-get install nginx-extras mysql-client
apt-get install php5-fpm php5-gd php5-mysql php-apc php-pear php5-cli php5-common php5-curl php5-mcrypt php5-cgi php5-memcached

MySQL can be installed into the mix with a simple:

apt-get install mysql-server

Read the rest of this entry »

MySQL Master-Master Replication, Heartbeat, DRBD, Apache, PHP, Varnish MegaHOWTO

October 8th, 2014

I created this HOWTO while building a new development environment today. The intention is to take a single Apache2/Varnish/MySQL environment and scale it to two servers, with one effectively a “hot-standby” – increase redundancy and continuity whilst maintaining current performance. This HOWTO is based on Linux Debian-76-wheezy-64-minimal 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64

Our current server has IP 192.168.201.1/24 and our new server has IP 192.168.201.7.

Section #1: Set up MySQL Master/Master Replication


First, we’ll set up MySQL master to master replication. In this configuration, data can be written and read from either host. Bear in mind that issues may exist with autoincrement fields when written to at the same time. There are other caveats with replication so ensure to research them along with how to deal with corruption and repair before considering this setup for a live application. Also be sure to be using the same version of MySQL on both servers – this may not always be necessary, however unless you are very familiar with any changes between versions, not doing so could spell disaster.

Read the rest of this entry »

Linux Challenge Blackbox #1

October 5th, 2014

I put together a small blackbox challenge this afternoon. Download it now:

Challenge starts here

The challenge covers some Linux file manipulation, C/ASM, GDB and filesystem. Please post questions or feedback in the comments. No spoilers! If you’ve got the master password, contact me privately through the form and if you’re correct I’ll post your details here.

Update 6th Oct 14:00 GMT

I’ve received a lot of questions and clarifications. Here are some hints for the first part.. πŸ™‚

  1. Β The download file is hidden on this page. It’s not hard to find!
  2. Linux “file” command is helpful
  3. Make sure you have GDB installed and know how to use it

And for the second part…

  1. I <3 AES 256!

The final key is a 16 byte string padded out to 32 bytes.

The challenge has now been solved, and an excellent and very detailed solution posted by Reader Remi Pommarel (repk at triplefau dot lt). Here is Remi’s solution:

Spoiler Inside: Challenge Solution SelectShow

Please feel free to submit your own solutions!

Linux iproute2 multiple default gateways

October 5th, 2014

This article describes a Linux server set up with 2 interfaces (eth0) and (eth1). Each interface has a separate ISP, network details and default gateway. eth0 has two sets of network details on the same interface and so a virtual interface (eth0:0) must be created to handle the second IP.

By default, Linux only allows for one default gateway. Let’s see what happens if we try to use multiple uplinks with 1 default gateway. Assume eth0 is assigned 192.168.1.2/24 and eth1 is assigned 172.16.1.5/16. Let’s say our default gateway is 192.168.1.1 (which of course is found through eth0) but there’s also a 172.16.0.1 gateway on eth1 which we can’t enter as Linux only allows for the one.

Our routing table now looks like this:

root@www1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.16.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth1

If a packet comes in to us, routed through the 172.16.0.1 gateway from say 8.8.8.8, our machine will receive it. When it tries to reply to 8.8.8.8 however, it runs down the routing table and sees that it’s not local to eth0 or eth1 and therefore will get routed out through the default gateway (192.168.1.1) – the problem is, this is the wrong gateway and so the target machine will ignore our response due to it being out of sequence and from the wrong IP.

Using iproute2, Linux can track multiple routing tables and therefore multiple default gateways. If the packet comes in through one interface and to one IP, it will go out via a specific default gateway. The script to achieve this is as follows:

Read the rest of this entry »

Simple Ready to Roll Linux Backup Script

September 12th, 2014

I’d built a Linux backup BASH shell script a while ago that I’ve been using, and wanted to share it today. This is a simple and easy to configure script, useful for backing up and scheduling multiple hosts, as well as handling file and MySQL backups, and flexibly allowing multiple days or copies to be retained.

The full source is available here

The global configuration is performed at the top of the script:

#config
MAIN_DIR="/home/sysbackups"
BACKUP_DIR="${MAIN_DIR}/backups"
LOG_DIR="${MAIN_DIR}/logs"

RSYNC="time nice -19 rsync"
SSH=ssh
MYSQLDUMP="time nice -19 mysqldump"
GZIP=gzip
SCP=scp
CAT=cat
RM=rm
CP=cp
RSYNC_ARGS="-arplogu --delete --stats"
TODAY=`date +%Y%m%d`
LOG="${LOG_DIR}/log_error_${TODAY}.log"

The utilities that you see listed are all required to be installed: rsync gzip scp time nice cat mysqldump.

The directory structure for backups is a master directory, which in this case is /home/sysbackups, a directory for the actual backups to be placed, in this case /home/sysbackups/backups and a directory for log files, in this case /home/sysbackups/logs. These directories should exist prior to running the script.

The usage of ‘nice’ is to ensure the backups are as resource friendly as possible, and ‘time’ allows for timing data to be provided within the log files created.

Each backup set is defined lower down in the ‘startEntry’ function. Taking the first as an example:

10 )
 STARTED=1
 START="Local: vm1"
 HOST="192.168.1.50"
 B_RSYNC_USER=root
 B_RSYNC=1
 B_RSYNC_LIMIT=4096
 B_RSYNC_DIR=( "/var/www" "/var/spool/cron" "/etc" "/home" )
 B_MYSQLDUMP=1
 B_MULTIPLEDAYS_DB=3
 B_MYSQLDUMP_USER="root"
 B_MYSQLDUMP_MYSQLUSER="root"
 B_MYSQLDUMP_PASS="password"
 B_MYSQLDUMP_HOST="127.0.0.1"
 B_MYSQLDUMP_TMP="/tmp/mysqldump-backup.sql"
 B_MYSQLDUMP_GZIPAFTER=1
 B_MYSQLDUMP_DATABASES=( "all-databases" )
 ;;

The ‘START’ variable defines the “friendly name” of the machine for log purposes, and the ‘HOST’ variable defines it’s IP or hostname.

Setting ‘B_RSYNC’ to 1 instructs the script to execute the rsync routines for file backup. Setting ‘B_MYSQLDUMP’ to 1 allows us to back up MySQL databases from on the host.

Rsync options

B_RSYNC_USER defines the SSH user to connect to the host as
B_RSYNC_LIMIT defines the limit in kbps for the transferB_RSYNC_DIR is an array of directories to back up

Mysqldump options

B_MYSQLDUMP_USER defines the SSH user to connect to the host as
B_MYSQLDUMP_MYSQLUSER and B_MYSQLDUMP_PASS define the MySQL username and password to connect with
B_MYSQLDUMP_HOST defines the MySQL host to connect to, relative to the HOST variable.
B_MYSQLDUMP_TMP defines a temporary location for the mysql backup on the host
B_MYSQLDUMP_GZIPAFTER defines whether the MySQL backups should be GZipped before being transferred
B_MYSQLDUMP_DATABASES is an array of database names to be backed up, with “all-databases” being hopefully self explanatory

B_MULTIPLEDAYS_DB defines the number of database copies to keep and B_MULTIPLEDAYS defines the number of file sets to keep.

As we have defined this backup set as case ’10’ in the script, to execute it, we simply run: /path/to/backup.script.sh 10

This can be cronned to run on a daily basis.

Lastly, as connections to hosts are made via SSH, either a password will need to be entered on each run manually, or SSH keys can be set up.

Feel free to reply with changes or comments.

My 5 step offshore interviewing process

July 15th, 2014

Often being called on to assemble and manage teams of designers or coders in particular through projects, I’ve developed a few tips and tricks in 12+ years of off-shoring which I wanted to share. They may be obvious to some. With experience, I’ve found that most off shore contractors are hard working and keen to grow but can sometimes be challenging or even impossible to work with.

  • Can a job applicant read?

Usually, the job description will contain the old test, “please include the word ‘blah’ at the top of your reply to prove you’ve read this description”. This makes sure that the applicant has read the description rather than just posted a cut and paste canned application to multiple jobs. Sometimes, applicants respond to questions that they prepare rather than actually reading and understanding your questions. I was hiring someone to work on some Google AdWords campaigns. I asked, “can you let me have some stats/supporting evidence/case study on any recent previous job – click through rates, impressions, conversions and metrics.” What I’m asking is, what did you do, and what were the results? The applicant on the other hand, seemed to answer the question, “can you tell me anything y0u can think of about some of your past work?” As an applicant, if you can’t read my requirements and queries accurately, how can I work with you?

If you want to save yourself the hassle, have a look at our php programmer services.
Read the rest of this entry »

A new method for network tunnelling: tundeep

September 10th, 2013

I’ve been frustrated during several pen tests lately at the lack of a tool to tunnel through a network in the way that I want to.

Consider the following network:

[Attacker 
192.168.200.40 (eth0)]
|
|
[192.168.200.41(eth0) 
VICTIM 1 
10.0.0.5(eth1)]
|
|
[10.0.0.10(eth0) 
VICTIM2 
10.10.10.20(eth1)]
|
|
[10.10.10.21(eth0) 
VICTIM3]

Once we’ve compromised VICTIM1 we have a number of current choices to tunnel deeper into the network. As far as I’m aware, these are:

  1. Metasploit’s ‘autoroute’ module.
    Advantages: This is a great tool that does exactly what I want. It tunnels traffic through the victim so that the attacker appears to be on the victim’s network.
    Disadvantages: Works great, but only works from within metasploit. No use for running external tools, scans, or layer 2 protocols. Metasploit Pro has a VPN tunneling feature that looks ideal although not all of us can afford it πŸ˜‰
  2. Metasploit’s portfwd module/iptables/simpleproxy.
    Advantages: Quick and easy
    Disadvantages: Only forwards specified single layer 3 UDP/TCP ports, and each port must be forwarded individually.
  3. Proxychains/ssh -D SOCKS tunnelling.
    Advantages: This is my current preferred method wherever possible. Easy and reasonably flexible
    Disadvantages: Proxychains is a hack in itself, and only supports layer 3 TCP.
  4. Implement a VPN server and set up bridging on your victim.
    Advantages: Will do exactly what we want
    Disadvantages: Disastrous idea, requires config and install on victim, possibly reboot or interface reconfiguration/bridging, very unstealthy

Currently, my preferred method is a mixture of the above depending on the scenario. What I’ve always wanted though, is a method to bring up a local interface on the remote network, that I can interact with as if I was directly connected, running any tools I wish including ARP scans and poisoning.

Introducing TUNDEEP… [Get tundeep now]

For the next release I’m planning compression mode, packet mangling, and a code cleanup as well as any bug fixes that arise.

Multithreaded TCP Proxy Tunnel Code

August 18th, 2013

Further to my earlier article, I went ahead and developed this application. Here’s a beta!

File: tcp_tun.c
Version: 0.3-beta
Title: TCP reassembling client-server application
Date: 17 Aug 13
Author: Adam Palmer <adam [AT] sasdataservicesΒ [DOT] com>
URL: http://www.adampalmer.me/iodigitalsec/
Read the rest of this entry »

PHP Local and Remote File Inclusion (LFI, RFI) Attacks

August 15th, 2013

PHP supports the ability to ‘include’ or ‘require’ additional files within a script. If unsanitized data is passed to such functions, an attacker may be able to get remote code execution access to the server. A typical include block might look something like this:

<?php
require("config/settings.inc.php");
require("lib/db.lib.php");
require("lib/parser.lib.php");
include("contrib/users/user.contrib.php");
die("This is a test");
?>

Now, it’s also possible to dynamically require or include files based on variables or user input, say for example:
Read the rest of this entry »

Wireless Exploit Device Code

April 26th, 2013

The wireless network service exploiter that was written for the Automatic Wireless Hacking Station is as follows. This is of course to be used for research and testing purposes only, and not for anything illegal.

Update: You can download the code here.

This tool runs fake HTTP, POP3, and IMAP servers as well as their SSL counterparts. It is part of the wireless MITM device for usage in situations without internet connectivity. It dynamically handles SSL certificate generation, logs GPS co-ordinates if they’re available in /run/gpsdata and logs any credentials to a sqlite3 database. It will respond to the iDevice and Blackberry internet connectivity test and it can also optionally deliver a message to any POP3 victims.

Going Retro With PHP – A Hits Counter Step By Step

January 11th, 2012

Going Retro With PHP – A Hits Counter Step By Step

It wasn’t all that long ago that the web was littered with little “number of visitor” indicators. The point of these was either to show how popular your site was, or more likely just because the site owner had no administrator level site statistics program running. Now most sites have a sophisticated statistics system or other means of counting visitors and these visible counters have become less popular. It will still be a good learning experience to try making a script for counting visitors from scratch.

A simple function to detect a valid page load might look like this

function log_page_load(){
    $invalid_list = '/(googlebot|crawler)/i'; //set a regular expression that detects invalid agents like googlebot
    if (preg_match($invalid_list, $_SERVER['HTTP_USER_AGENT'])){
    // it is a bot
    }else{
    // it is probably a browser
    }
}

As you can see we are erring on the side of caution, making browsers the default for most possible strings. That is because there are probably countless possible browsers now with cell phones and other devices included. There’s no easy way to tell bot from human computer user there may be no perfect method that is always right. There definitely are techniques that are pretty accurate compared to what I show above but they are beyond the scope of this article.

Once we have decided the request is a real pageview we can get on with the reading and saving of data.

$filename = "counterfile.dat";
if(file_exists ($counter_file)){
    $counter_file = fopen($filename, 'r+');
    $total_count = trim(fread($counter_file, filesize($filename)));
}else{
    $counter_file = fopen($filename, 'w');
    $total_count = 0;
}
//other code here
...
//save file
ftruncate($counter_file,0);
fwrite($counter_file, $total_count);
fclose($counter_file);

First we opened the file, but since it may not exist we check that, and create it if it doesn’t (opening with w will do that). Otherwise it should work fine to read it. To save it we erase it (ftruncate($counter_file,0)) and write new data, which is just a number. In the next example let’s combine all that we have covered.

function log_page_load(){
    $filename = "counterfile.dat";
    if(file_exists ($counter_file)){
        $counter_file = fopen($filename, 'r+');
        $total_count = trim(fread($counter_file, filesize($filename)));
    }else{
        $counter_file = fopen($filename, 'w');
        $total_count = 0;
    }
    $invalid_list = '/(googlebot|crawler)/i'; //set a regular expression that detects invalid agents like googlebot
    if (!preg_match($invalid_list, $_SERVER['HTTP_USER_AGENT'])){
        //it is a brower. probably. increment and save.
        $total_count++;
        ftruncate($counter_file,0);
        fwrite($counter_file, $total_count);
    }
    fclose($counter_file);
    if(!trim($total_count))
        $total_count = 0; //let's make sure it is a 0 if it somehow was set to a blank string
    return $total_count;
}
echo "val= ". log_page_load(); //each load you get 1, 2, 3, 4

so there you have it, define the function and either capture the return value for later or print it as shown.
Isn’t there something missing though? Most of the counters are graphical since they are used on sites that have no scripting. How can we get graphics instead of just numbers as a text string? There are many ways, but try this on for size.

$page_loads = log_page_load(); //logs once and also return the new value
$page_l_str = strval($page_loads);
$icons = "";
for($i = 0; $i < strval($page_l_str); $i++){
    $icons .= '<imr src="images/' . $page_l_str[$i] . '.gif" />';
}
echo $icons;

that will produce image tags with the pattern <imr src=”images/1.gif” /> and so on. $page_l_str[$i] means get the letter with index $i.
Of course you might also wish to zero padd it or instead of images use <li>$page_l_str[$i]</li>, there are a limitless variety of possibilities with modern css and php.

To take this a step further you might want to build in counting of visitors instead on top of pageviews. That would be done by recording $_SERVER[‘REMOTE_ADDR’] and checking if the visitor has been there recently. You could use 2 counters for hits and unique hits, or some other combination. That’s all I will cover here though so that concludes the article on hit counters, I hope it has been informative.

Custom Math High and Low Functions in PHP

January 11th, 2012

This should be a good beginner tutorial, especially if you have used some functions and if statements and haven’t gone much further. I will explain the concept as we go along, first let’s review the Ternary Operator / Operators that we will use.

$x = $y < 0 ? -1 : 1;

That is sort of a quick “what is the sign” test for signed numbers. If means if y is less than 0 x is negative 1, otherwise it is positive 1. 0 is considered +1 in that case. Following so far? You just need to understand the basic rules so you can understand what is happening in a complex line of code.

$x = $y == 0 ? 0 : ($y < 0 ? -1 : 1);

In that case it mean if y is 0 x is 0, otherwise x is either -1 or +1. Let’s use that principal to make a function that accepts 3 arguments and tells us whether argument 1 is within the bounds of arguments 2 and 3.

function custom_clamp_check($number, $low, $high){
    return $number > $high ? false : ($number < $low ? false : true);
}

Think about that function like this, if the first statment is true, we return false, if it isn’t we check the second statment. The function returns false unless the $number is between $high and $low or equal to either. This is intentionally written to work well with floats and integers. Other variable types should not be used.

PHP has built in min and max functions like in this example.

$x = min($y, 0);
$x = max(5, 12);

That just gives you the higher number of the two numbers put into the function. Actually you can do max(1, 4, 99, -5) since it allows more arguments, or max(array(1, 4, 99, -5)) with the same result. It returns the highest of whatever is put in. I don’t know if min/max have many pros or cons over statements like we used earlier, but some users have said min/max are slower and less efficient. Regardless of that this article is focusing on using operators in statements to construct the desired functions, even if only for learning purposes.

Our final function is a clamp function, it clamps the value entered to be equal to $high, equal to $low, or between the two.

function custom_clamp($number, $low, $high){
    return $number > $high ? $high : ($number < $low ? $low : $number);
}

You should now understand how the function works and what the results will be. custom_clamp(1.0115, 1.0, 0.0) would give you 1.0, the exact high value you put in. These functions are useful for many things, and as I described earlier we can use integers or floats. If a user entered a comment we could check if it’s length is within the range we want.

if(custom_clamp_check(strlen($_GET['comment']), 20, 512) == false){
    return "Your comment must be 20 to 512 chars in length.";
}

If the user must also enter a number within 1 and 10 and we want to make sure there are no other numbers possible, we could force it with the clamp function.

$entered_number = custom_clamp(intval($_GET['number']), 1, 10);

I added intval to make this even more strict, since users could have entered 1.0002 and we probably only want whole numbers. The final use shows how useful the function can be in math. The goal is to keep the product above 0 since dividing by zero is not acceptable.

$angle = 1.0 / custom_clamp($radius * pi(), 0.0000001, 1.0);

PHP – What in the world is CType

January 7th, 2012

Apparently the name comes from ctype.h, a file in the C programming language. The PHP engine can execute the code from that file in it’s ctype functions. Does the c in ctype stand for classification, or character maybe? I don’t know, but according to PHP, these functions “are always preferred over regular expressions, and even to some equivalent str_* and is_* functions.” because they are executed from the executable functions (from ctype.h) and are therefore faster.

$is_it_alphanumeric = ctype_alnum("Carnival101"); //true
$is_it_alphanumeric_too = ctype_alnum("Carnival*_*");//false
$is_it_alphanumeric_also = ctype_alnum("Carnival1.01"); //false

ctype_alnum() checks if the string is totally alphanumeric. The first of those was the only one, a decimal is still not going to return true. Checking if things are alphanumeric is actually pretty useful, but it’s probably more common to check if it is an unsigned integer for example.

$is_it_digit = ctype_digit("05909090"); //true
$is_it_digit_too = ctype_digit(-4); //false
$is_it_digit_also = ctype_digit(4.5); //false

So only 0s to 9s will get you a true result. Positive integers will also work. ctype_alpha() is the same as those 2 but for letters only. Going back to ctype_alnum, it also accepts integers but “If an integer between -128 and 255 inclusive is provided, it is interpreted as the ASCII value of a single character ” So if you aren’t interested in ASCII tests don’t pass integers to ctype_alnum(). That rule applies to the next function as well, called ctype_print();

ctype_print("asdfnrt"); // false

ctype_print() only returns true if the characters are printable and not control characters. This is a confusing classification, since spaces would be allowed and tabs not. Only use it if you are looking for a very specific type of check. ctype_graph() might be more clear, as it is the same but spaces are not valid, no white space is allowed. ctype_cntrl() is the opposite of ctype_print(), the characters must be control characters to get it to return true.

ctype_lower() returns true for lowercase letters only
ctype_punct() returns true for punctuation only
ctype_space() returns true for white space only (tab, space, newline and so on)
ctype_xdigit() returns true for hexidecimal characters only (comes in useful for checking if the string is a color code for example)
ctype_upper returns true for uppercase letters only

For all these functions, empty strings will give you false in new versions of PHP, but true in older versions (< 5.1). Passing something other than an integer or string will return false.