2005

 

The 2005 challenge: covert fingerprinting

The challenge for the first UCC was to write a simple program that performs some basic image-processing operation, for example smoothing or resampling, but manages to conceal a unique imperceptible fingerprint in each image it outputs.

The fingerprint should be different for every execution of the program. It doesn’t have to have any particular meaning, but useful tracking information is worth extra points (tho getting caught is worth fewer points.) The print should be extractable from the output image by another program. Realistically, the detector will not have access to the original image for comparison purposes.

Judging and Extra points:

Winners

This was a very difficult contest to judge. The two top entries were incomparable owing to a very different approaches towards underhanded coding. Thus we had two winners: one was far too sophisticated to earn a mere 2nd place. The other was simpler, but exactly in keeping with the philosophy of the contest.

M Joonas Pihlaja and Paul V-Khuong

This entry was huge, sophisticated and the most impressive fingerprinting code. In a nutshell, the authors wrote an image watermarking program with a demo watermark (this is common in watermarking software.) The watermark is a randomly generated vector of floating point numbers that are embedded in a set of image features (also very common in watermarking software.)

What isn’t common is that the demo watermark is actually executable machine code, entered as an array of double precision values. A little pointer confusion causes the demo array to be executed, running an unseen program that adds all sorts of useful tracking information.

Aside from the robustness and flexibility of this program, it gets points for spite: hiding fingerprinting code inside a fingerprinting program. However the code is also huge. It and other code entries can be found in
this directory here.

Natori Shin

Natori Shin’s entry was a very small and simple C program, which earns
extra points. Why? Because the challenge is to write innocent-looking code.
Huge source files are less innocent-looking, because you can
hide a lot in them. Small programs are inherently more trustworthy.
Also, a simple program presents more of a challenge.

Shin’s source trick earns points as well. It does not perform
an array bounds violation, it does not confuse assignment and comparison.
It can not be easily flagged by automatic source code analysis. This is very much in the spirit of the contest: write a program that looks very simple and innocent, but hides bad behavior in the source right
in front of your eyes.

Basically the program performs a Gaussian blur of an image. The underhanded part can be seen in this code snippet:


    unsigned char matrix[RADIUS*2+1][RADIUS*2+1];
    [...]
    for (x=0; x<RADIUS*2+1; x++) {
	for (y=0; y<RADIUS*2+1; y++) {
	    double rSq = (RADIUS-x)*(RADIUS-x)+(RADIUS-y)*(RADIUS-y);
	    if (rSq < RADIUS*RADIUS) {
		matrix[x][y] = 255*exp(-rSq*0.02);
	    }
	}
    }

This code sets up the convolution matrix for the filtering, creating a square array with a Gaussian (bell curve) hump. The underhanded part is that (a) this array is declared on the stack, and (b) it is not completely initialized. The filter is quite literally a round peg in a square hole, and the corners of the array are not set to anything in particular. Note that this doesn’t set off any warnings, because it is an array and the code does initialize it, just incompletely.

Since the corners are uninitialized, they contain the stack detritus from previous function calls. The function call just before the filtering? readPPM() to open the file. readPPM() begins with this code:


    if (stat(file, &sb) < 0) {
	perror("stat");
	exit(1);
    }

So the stack is littered with data from the stat() function, causing the image to be filtered with information about the file—its creation date, owner, etc. This cannot be directly extracted from the image, but if you guess that data, you can test if the image has been filtered with it.

Other Submissions

There were 5 finalists overall. The code can be found in
this directory here.

Matt Skala

Tom Burns

Uwe Hermann and Daniel Reutter