Hack.lu 2015 Writeups
2015
2015 Perl golf
Goal: Write a Perl program that takes a parameter as input and outputs a filtered version with alternating upper/lower case letters of the (english) alphabet. Non-letter characters have to be printed but otherwise ignored.
Example:
Input Hello World! Hallo Welt! Output HeLlO wOrLd! HaLlO wElT!
Rules:
- You have 1 second.
- You have 45 (ASCII) chars.
- Do not flood the server.
The first thing is to find how the server will use your script.
Input isn't read from stdin but is expected as the first argument, so you can try locally with
perl -e 'print @ARGV[0]' 'Hello World! Hallo Welt!' Hello World! Hallo Welt!
Then come with a strategy to convert letters as asked and squeeze it.
Here is our solution: (37 chars)
$_=pop;s/\pL.*?(\pL|$)/\u\L$&/g;print
But here is actually how Fluxfingers designed this challenge: they tried internally, kept the best one-liner they could come with, which was 38 chars long, and extended the limit to 45 to give some room to the participants.
The best solution Fluxfingers had in mind: (38 chars)
$_=pop;s/\pL/uc$&^$"^($x^=$")/ge/print
So we were one char shorter \o/
A little word of explanation of our solution:
$_=pop;s/\pL.*?(\pL|$)/\u\L$&/g;print $_= assign to the Perl "current var", so we don't have to mention it later pop; we need @ARGV[0], pop is shorter s/ / /g; search and replace, g=repeat \pL.* \pL We want one letter followed optionally by some stuff followed by one letter \w captures too much (also [0-9_]) so we rely on that Unicode magic \pL So first captured letter needs to be in uppercase and second captured letter needs to be lowercase .*? Problem with .* is that it captures too much, including letters. One could write [^a-zA-Z]* but that's not golfing anymore Adding the "?" reduces the "greediness" of the expression to capture as few chars as possible (\pL|$) An issue wit \pL only is that it fails if there is an odd number of letters in the string So here we say one letter, or end of string, to capture that corner case. \u\L$& $& is what was captured by the regex, \u means first char uppercase, \L means lowercase for the remaining letters print Should I explain?
After the CTF, we got it even shorter, now 36 chars!:
$_=uc pop;s/\pL.*?\pL/\u\L$&/g;print uc Set whole input as uppercase \pL No more dirty (\pL|$) trick here
The trick is to avoid the corner case of odd number of letters by setting the whole input uppercase first
Alternative solution of 36 chars, optimizing what was discussed here:
$_=lc pop;s/\pL/$&^($u^=$")/ge;print $_= assign to the Perl "current var", so we don't have to mention it later lc Set whole input as lowercase pop; we need @ARGV[0], pop is shorter s/ / /ge search and replace, g=repeat, e=evaluate again the result \pL We want one letter: \w captures too much (also [0-9_]) so we rely on that Unicode magic \pL $& $& is what was captured by the regex ^ $" xor it with $" which evaluates to " ". The effect is that a letter XORed by 0x20 toggles its case. ^($u^=$") Actually that's to be done only half of the time so we xor with a variable that is constantly xored with " " so $u value is successively 0x20, 0x00, 0x20, 0x00 etc print Should I explain?
UPDATE: someone managed to get down to 34 chars!:
print pop=~s/\pL/lc$&^($u^=$")/ger r perform non-destructive substitution and return the new value
Actually we can go even further for some of those solutions if we tolerate that the script doesn't always work.
One can simply reload multiple times the script till the server provides a compatible input to get the flag.
E.g. 29 chars and 28 chars:
$_=pop;s/\w.*?\w/\u$&/g;print print pop=~s/\w.*?\w/\u$&/gr
worked after a few reloads on the sentence "body. want Law the help of and, being. a, graduates"
2015 PHP Golf
Same as for Perl Golf, but now we've 62 chars max.
The lazy solution: (57 chars)
<?=`echo '$argv[1]'|perl -pe 's/\pL.*?(\pL|$)/\u\L$&/g'`;
Apparently Fluxfingers solution was:
<?=preg_filter("/\pL/e",'($0|" ")^chr($m^=32)',$argv[1]);