Welcome to Timblog!

I'm Tim Johansson, and this is where I store everything that I write. You can browse through everything on this page, or you could choose a category that interests you in the top bar. If you have anything to say, don't hesitate to comment -- I love comments, too. Enjoy.

Getting Pretty URLs with phpwiki

The default for the phpwiki wiki software is to have links of the form /index.php/HomePage. This is ugly. To get Wikipedia-style permalinks, append the following to .htaccess and verify that you have the mod_rewrite apache module installed:

RewriteEngine On
RewriteRule ^index\.php/([^\.]*)$ /redir.php?dest=$1
RewriteRule ^([^\.]+)$ /index2.php/$1

Now, move your index.php to index2.php. Create a redir.php file that contains only a redirection:

<?
header( 'Location: /' . $_REQUEST['dest'] );
?>

I am aware that phpwiki does support native link prettifying. However, I couldn’t get it to work and had to solve it in some way.

Writing an MSN bot in Perl

A while ago, bot-depot.com maintained the handy MSN.pm module. Nowadays, though, the MSN protocol has evolved, and MSN.pm development has fallen behind. Trying to run the current module resulted in the error message “No expected reply recieved” (sic, not “No expected reply received”). Fortunately, there was an updated version to be found at botwork.com (MSN2.0.84.zip). It worked a bit better, at least:

$ perl echobot.pl 
Can't locate Digest/SHA1.pm in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl .) at lib/MSN/Notification.pm line 21.

Install Digest::SHA1 with CPAN, and:

$ perl echobot.pl 
MSN 2.0 (01/21/2005) Rev: 84  - Checksum: 60068-NS31944-SB22354
SERVER ERROR : Authentication Error: No response from Passport server

Searching the bot-depot forums, I found that the problem is that you need Crypt::SSLeay. Install it with CPAN, and the echobot.pl works fine, ready to be modified.

Amarok Script: Playlog

I just finished Playlog, my first Amarok plugin, written in Perl. I grew tired of last.fm having the sole properties of my listening-log, and such, this script everything that you listen to, along with the time that you listen to it, into two nice and handy MySQL tables. In the future, I plan to write some kind of analysis script for the data.

This was also my first piece of software to send in to kde-apps.org: Playlog.

Here is the Perl source code, in case you are interested. I release it under the GPL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/perl
 
use strict;
use DBI;
 
my $dbh = DBI->connect( 'DBI:mysql:database=playlog;host=localhost',
	'playlog', '',
	{ 'RaiseError' => 1 }
);
$dbh->do( 'CREATE TABLE IF NOT EXISTS songs ( song_id INT(11) NOT NULL AUTO_INCREMENT, song_artist VARCHAR(255), song_album VARCHAR(255), song_title VARCHAR(255), PRIMARY KEY (song_id) )' );
$dbh->do( 'CREATE TABLE IF NOT EXISTS playlog ( play_time INT(11), song_id INT(11) )' );
 
sub cleanup {
	$dbh->disconnect;
}
 
$SIG{'TERM'} = 'cleanup';
 
while ( <> ) {
	if ( m/^trackChange$/ ) {
		chomp( my $artist = `dcop amarok player artist` );
		chomp( my $album = `dcop amarok player album` );
		chomp( my $song = `dcop amarok player title` );
		my $id;
		my @s = &qsong( $artist, $album, $song );
		if ( ! $s[0] ) {
			$dbh->do( sprintf(
				'INSERT INTO songs ( song_artist, song_album, song_title ) VALUES ( %s, %s, %s )',
				$dbh->quote( $artist ),
				$dbh->quote( $album ),
				$dbh->quote( $song )
			) ) or die $dbh->errstr;
			@s = &qsong( $artist, $album, $song );
		}
		$dbh->do( sprintf(
			'INSERT INTO playlog ( play_time, song_id ) VALUES ( %d, %d )',
			time,
			$s[0]
		) );
	}
}
 
sub qsong {
	my ( $artist, $album, $song ) = @_;
	my $sth = $dbh->prepare( sprintf(
		'SELECT song_id FROM songs WHERE song_artist = %s AND song_album = %s AND song_title = %s',
		$dbh->quote( $artist ),
		$dbh->quote( $album ),
		$dbh->quote( $song )
	) ) or die $dbh->errstr;
	$sth->execute();
	return $sth->fetchrow_array();
}

Failure to Install GD with CPAN: LDS/GD-2.35.tar.gz

I decided to install the GD image library for Perl on my working station, and as usual, using CPAN. However, after running install GD, I was faced with an error.

make: *** [GD.o] Error 1
  LDS/GD-2.35.tar.gz
  /usr/bin/make -- NOT OK
Running make test
  Can't test without successful make
Running make install
  Make had returned bad status, install seems impossible
Failed during this command:
 LDS/GD-2.35.tar.gz                           : make NO

Unable to find the cause, I turned to apt-get, which installed GD without any problems.

$ sudo apt-cache search libgd perl 
libgd-gd2-perl - Perl module wrapper for libgd - gd2 variant
libgd-graph-perl - Graph Plotting Module for Perl 5
libgd-text-perl - Text utilities for use with GD
ruby1.8 - Interpreter of object-oriented scripting language Ruby 1.8
calamaris - log analyzer for Squid or Oops proxy log files
libgd-barcode-perl - Library to create barcode images (GD::Barcode)
libgd-gd1-noxpm-perl - Perl module wrapper for libgd (old version against GD 1.8.x)
libgd-gd1-perl - Perl module wrapper for libgd (old version against GD 1.8.x)
libgd-gd2-noxpm-perl - Perl module wrapper for libgd - gd2 variant without XPM support
libgd-graph3d-perl - Create 3D Graphs with GD and GD::Graph
libgd-perl - Perl module wrapper for libgd
libgd-securityimage-perl - Security image (captcha) generator.
libgdk-pixbuf-perl - Perl module for the gdkpixbuf library
$ sudo apt-get install libgd-perl

Done!

Haiku #42: Selling Out

Who, me? You don’t say
I’m merely playing my part
Lyrics started it

By having no recurring visitors, I just convinced myself that I’m allowed to do like this. Don’t mind the advertisements; and if you do, I suppose you wouldn’t come back anyway. I’m going all-organic search engine whore.

Nightwish Lyrics, Dark Passion Play

I don’t want this to degenerate into some cheesy lyrics site, but there currently seems to be an unsatisfied demand for the lyrics of the latest Nightwish album, Dark Passion Play. So far, I’ve transcribed The Poet and the Pendulum, Amaranth and Cadence of her Last Breath from the promo CD.

The real lyrics have been released by Nightwish, but I will keep my interpretations for historical reasons.

(more…)

PHP: Calculate FRES and SMOG of a Text

A few days ago, I set up a site where students can upload their essays for money (Uppsatslotto.se: Tjäna pengar på dina uppsatser), and wanted a nice way for analyzing texts. I found two in particular, FRES (Flesch Reading Ease Score) and SMOG (Simple Measure of Gobledygook). Slightly modified excerpts from the respective Wikipedia articles (licensed under the GNU Free Document License):

The Flesch/Flesch–Kincaid Readability Tests are readability tests designed to indicate how difficult a reading passage is to understand. There are two tests, the Flesch Reading Ease, and the Flesch–Kincaid Grade Level. Although they supposedly use the same measures, just placed into a different scale, the results of the two tests do not always correlate (a text with a better score on the Reading Ease test over another text may end up with a worse score on the Grade Level test). Both sytems were devised by Rudolf Flesch. In the Flesch Reading Ease test, higher scores indicate material that is easier to read; lower numbers mark harder-to-read passages. The formula for the Flesch Reading Ease Score (FRES) test is 206.835 - 1.015 * W/Se - 84.6 * Sy/W where W/Se is the average number of words per sentence and Sy/W is the average number of syllables per word.

SMOG (Simple Measure Of Gobbledygook) is a readability formula that estimates the years of education needed to understand a piece of writing. SMOG is widely used, particularly for checking health messages. The precise SMOG formula yields an outstandingly high 0.985 correlation with the grades of readers who had 100% comprehension of test materials. SMOG was published by G. Harry McLaughlin in 1969 as a more accurate and more easily calculated substitute for the Gunning-Fog Index. The SMOG of a text can be calculated by: 1.0430 * sqrt( 30 * Psy/Se ) + 3.1291.

The following code is a PHP implementation for calculating the required values (number of words, number of sentences, number of syllables, and number of polysyllabic words) and putting it all together using the above mentioned formulae.

1
2
3
4
5
6
7
8
9
10
11
12
// Number of words: number of space series or linebreaks + 1
$wc = preg_match_all( '/[ \r]/', preg_replace( '/ +/', ' ', $text ), $tmp );
// Number of syllables: vowels not followed by another vowel. Quite accurate approximation.
$syc = preg_match_all( '/[aeiouy][^aeiouy]/', $text, $tmp );
// Number of polysyllabic words (>=3 syllables): Vowel, non-spaces, vowel, non-spaces, vowel (or more non-spaces-vowel)
$psyc = preg_match_all( '/[aeiouy]([^ ]*[aeiouy]){2,}/', $text, $tmp );
// Number of sentences: Number of periods, exclamation marks, question marks and linebreaks
$sec = preg_match_all( '/[.!?\r]/', $essayf, $tmp );
// Flesch Reading Ease Score
$fres = 206.835 - 1.015 * ( $wc / $sec ) - 84.6 * ( $syc / $wc );
// Simple Measure of Gobbledygook
$smog = 1.043 * sqrt( $psyc * ( 30 / $sec ) ) + 3.1291;

HTS Realistic 5: Find the Hidden Hash

Damn Telemarketers!: Telemarketers are invading peoples privacy and peace and quiet. Get the password for the administrative section of the site to delete their database and return the privacy of their victims!

Realistic mission 5 of Hack This Site is quite fun, since it involves several vulnerabilities. You are supposed to clear a spammer’s database.

From: spiffomatic64

Message: Yo! This is Spiffomatic64 from Hackthissite.org! I’m a bit of a hacker myself as you can see, but I recently came upon a problem I couldn’t resolve…..
Lately I’ve been getting calls day and night from the telemarketing place. I’ve gone to their website and hacked it once deleting all of their phone numbers so they wouldn’t call me anymore. That was a temporary fix but they put their database back up, this time with an encrypted password. When I hacked them I noticed everything they used was 10 years out of date and the new password seems to be a ‘message digest’. I have done some research and I think it could be something like a co called hash value. I think you could somehow reverse engineer it or brute force it. I also think it would be a good idea to look around the server for anything that may help you.

Don’t just skim that through; read everything that Spiffomatic has to say. It’s quite helpful. Now, for their site. As you’ve probably seen, it consists of four visible sections: Home, News, Database and Contact.

The Home page contains nothing but an image and few email addresses, and the Contact page is equally useless. Don’t forget to check the source anyway, though, just to be sure. The Database page has nothing but a password input field. Inputting anything just gives you a “invalid password” message. Since entering “\”, “‘” and “”" characters still gives you the same message, we can assume that this field is not vulnerable to SQL injections.

We can’t find any vulnerability from the News page, either. However, the news items contain valuable information. We find that the administrator’s girlfriend’s name is Haley, which invites us to try that name as a password. Unfortunately, it didn’t work. We are also told something about “zapp”, which I have no idea of what it is. Next, we are told that they had some problems with Google:

Google was grabbing links it shouldn’t be so I have taken extra precautions.

Now, how do you take extra precautions when search engines are grabbing links that they shouldn’t? Well, you edit robots.txt to disallow it. Let’s take a look at the robots.txt of Compu-Global-Hyper-Mega-Net: http://www.hackthissite.org/missions/realistic/5/robots.txt.

Disallow: /lib/
Disallow: /secret/

Nice to know. Check those folders out, and you will find that directory listing is not disabled. Begin with secret/, since it seems most interesting. There are two files: admin.php and admin.bak.php. The prior is the script that validates passwords, and the latter seems to be a backup of the prior. However, when we access it, we get:

error matching hash 3184342944a094dd5dbe6fccaeb8dc96

Note that the specific hash might be different for you. As Spiffomatic64 said, this is a message digest (MD). Probably, it’s the message digest of the password that you are going to enter. But how are we supposed to know the algorithm of the hashing function? Remember the other directory disallowed from robots.txt? Let’s check lib/.

There is only one file here–”hash”. The hashing algorithm, perhaps? Download the file and open it in your favourite text-editor. Uh-oh. A lot of bogus characters, followed by:

Error: MDupdate MD already done.
Error: MDupdate called with illegal count value %d.
3.4.4 [FreeBSD] 20050518
MIC

In most cases when you encounter such a file, using a hex editor lets you read parts of it. There are many, for example XVI32 for Windows and hexedit for Linux. Now, look for something that you recognize. You will probably find this part in the file:

do_global_ctors_aux
/usr/src/lib/csu/i386-elf/crtn.S
md4.c
MDblock
md4driver.c

Which reveals that the encryption algorithm used is MD4. Use any MD4 collision finder to crack the hash. I’ve written a simple MD4 collision finder in perl, which you can use. If you don’t happen to like Perl, there is a lot of free tools available out there, including online reverse-lookup databases.

Going back to the “Database” page and entering the password completes the challenge.

MD4 Hash Collision Finder in Perl

I’m trying currently my skills at Hack This Site, and found that I am supposed to crack an MD4 hash. I wrote the following Perl script to do it for me:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use Digest::MD4 md4_hex;
 
my $goal = '3184342944a094dd5dbe6fccaeb8dc96'; # hash to find
my @chars = ( a..z, 0..9 ); # characters to use
my $minlen = 1; # minimum length of string
my $maxlen = 8; # maximum length of string
 
for ( $minlen..$maxlen ) {
	print 'Length: ', $_, "\n";
	&checkStrings($_);
}
 
sub checkStrings {
	my ( $n, $s ) = ( (shift) - 1, shift );
	for ( @chars ) {
		if ( $n ) {
			checkStrings($n, $s . $_ );
		} else {
			print $s, $_, "\n" if md4_hex( $s . $_ ) eq $goal;
		}
	}
}

&checkStrings is a recursive function that checks all strings built from @chars of a certain number of characters. The script can easily be changed to cater for other message digest or hashing algorithms; simply change the function in line 19 from md4_hex() to whatever function you want. It could even be something like reverse(), although I hope that you’ve got a better way for finding such a string.

The output of the above configuration is:

Length: 1
Length: 2
Length: 3
Length: 4
Length: 5
c6a18
Length: 6
Length: 7
Length: 8

Note that the script keeps going after a successful collision–there could be several hits.

HTS Realistic 4: UNION ALL the Products

Fischer’s Animal Products: A company slaughtering animals and turning their skin into overpriced products sold to rich bastards! Help animal rights activists increase political awareness by hacking their mailing list.

So I finally got around to write a walkthrough/guide for Hack This Site realistic mission 4. Your objective is to get the email addresses of the subscribers to the news letter of Fischer’s Animal Products.

From: SaveTheWhales

Message: Hello, I was referred to you by a friend who says you know how to hack into computers and web sites - well I was wondering if you could help me out here. There’s this local store who is killing hundreds of animals a day exclusively for the purpose of selling jackets and purses etc out of their skin! I have been to their website and they have an email list for their customers. I was wondering if you could somehow hack in and send me every email address on that list? I want to send them a message letting them know of the murder they are wearing. Just reply to this message with a list of the email addresses. Please? Their website is at http://www.hackthissite.org/missions/realistic/4/. Thanks so much!!

Start by investigating every part of Fischer’s the site. There are essentially two parts which might be vulnerable. The most visible one is the email form. A clearly visible input-field, where you just add your email address and are given a “Email added successfully” message. As you’ve seen through other missions containing SQL injections, the first step is attempting to get out of the string. Try registering an email address containing apostrophes, both single and double.

Error inserting into table “email”! Email not valid! Please contact an administrator of Fischer’s.

Unsuccessful. However, we got an important piece of information: the table name is “email”.

Now for the other part of the website; the product lists. There are two product lists, “fur coats” and “alligator accessories” (how this would have anything with whales to do is beyond me). If you’ve been as observant as you should be, you’ve noticed that both are the same file–products.php–with the category ID as an argument.

What do we want to accomplish? If we wanted to select something else from that table, we could attempt to change the WHERE part of the SELECT statement by changing the category argument to something like “1 OR categpory = 2″ (which happens to give you both categories of products on one page). However, we want to add information from another table: the “email” table. This is were the MySQL command UNION comes in very handy. Using UNION, we can merge the results of two SELECT statements into one. For example, we could:

SELECT * FROM table1 UNION ALL SELECT * FROM table2;

The result would be getting all rows from table1 and all rows from table2. Note that this assumes that the number of columns in table1 and table2 are equal. If they are not, the command will not work. UNION ALL is used instead of simply UNION in order to preserve duplicate rows. It is good practice to use UNION ALL in order to avoid unexpected errors. Let’s assume that the initial query could be something like this:

SELECT * FROM products WHERE category = 1;

We also want the rows from the email table. Therefore, we’ll try looking for another category: 1 UNION ALL SELECT * FROM email, resulting in the following final query:

SELECT * FROM products WHERE category = 1 UNION ALL SELECT * FROM email;

Which is exactly what we want. However, this results in nothing of value. Remember the assumption made earlier when we UNIONed table1 and table2? They must be of the same number of columns. We can assume that “email” has fewer columns than “products” does, since the products table should be more advanced. Therefore, we add columns to the email table:

SELECT * FROM products WHERE category = 1 UNION ALL SELECT *, NULL FROM email;

NULL means nothing–it is just an empty column. This doesn’t work either, so we’ll have to keep adding NULLs until we get some results. It will finally work at three NULLs:

SELECT * FROM products WHERE category = 1 UNION ALL SELECT *, NULL, NULL, NULL FROM email;

Below the category 1 products, you can see ten broken images. Viewing the source-code, you will find that the sources of these are email addresses! Rearranging the column order will give you a more eligible format.

SELECT * FROM products WHERE category = 1 UNION ALL SELECT NULL, *, NULL, NULL FROM email;

Just copy the list and email it to SaveTheWhales!

« Later PostsEarlier Posts »
FireStats iconAnvänder FireStats