Challenge - The game
Let's play another little game this year. Once again, as every year, I promise it is hardly obfuscated.
The file contained an obfuscated Perl script, neatly formatted to look like tetris. Of couse, when ran, it really was a tetris game. Since I am bad at tetris, but good in programming I chose to deobfuscate it.
First glance
eval eval '"'.
('['^'.').('['^'(').('`'|'%').('{'^
'^'^('`'|'+')).';'.'\\'.'$'.'|'.'='
.('`'|',').'\\'.'\\'.('`'|'%').'['.
'['^'(').('['^'+').('`'|',').('`'|'
'#'.('['^',').'#'.('['^',').'#'.('[
'`'|"\#").
'(')."\#".
'['^"\-").
'#'.("\`"|
'{'^"\*").
',').'#'.(
'*')).'#'.
'#'.("\;"&
'{'^"\,").
"'").'#'.(
'#'.("\`"|
'{'^"\*").
Fully zoomed out
The script begins with eval eval '"'. ...
. De-obfuscation was done by replacing the first eval
with print
. This way, we make use of the perl interpreter to do the hard work for us and then print it instead of executing it. Next I used perltidy to format the code. It was much better now:
use Term::ReadKey;
ReadMode 5;
$| = 1;
print "\ec\e[2J\e[?25l\e[?7l\e[1;1H\e[0;0r";
@FF = split //,
'####H#V#2#0#{#h#t#t#p#s#:#/#/#w#w#w#.#y#o#u#t#u#b#e#.#c#o#m#/#w#a#t#c#h#?#v#=#d#Q#w#4#w#9#W#g#X#c#Q#}####';
@BB = ( 89, 51, 30, 27, 75, 294 );
$w = 11;
$h = 23;
print( "\e[1;1H\e[103m"
. ( ' ' x ( 2 * $w + 2 ) )
. "\e[0m\r\n"
. (
( "\e[103m \e[0m" . ( ' ' x ( 2 * $w ) ) . "\e[103m \e[0m\r\n" ) x $h )
. "\e[103m"
. ( ' ' x ( 2 * $w + 2 ) )
. "\e[2;1H\e[0m" );
sub bl {
( $b, $bc, $bcc, $x, $y ) = @_;
for $yy ( 0 .. 2 ) {
for $xx ( 0 .. 5 ) {
print( "\e[${bcc}m\e["
. ( $yy + $y + 2 ) . ";"
. ( $xx + $x * 2 + 2 )
. "H${bc}" )
if ( ( ( $b & ( 0b111 << ( $yy * 3 ) ) ) >> ( $yy * 3 ) ) &
( 4 >> ( $xx >> 1 ) ) );
}
}
}
<...>
It even got a flag in it, HV20{https://www.youtube.com/watch?v=dQw4w9WgXcQ}, but that did not work. At least the video was nice. I even paused my CTF attempt to watch it fully 🙂
Next steps were to understand the code and see where the real flag was supposed to be. After a while I noticed that each building block was made out of a character from the fake flag. A bit more deciphering later, it occured to me that maybe the characters in the blocks will reveal the real flag. Meaning, the program might transform the fake flag into the real flag.
But again, I am bad at tetris. However in the meanwhile my understanding of the code was good enough to tweak it a litte. I changed all blocks to just a single square and removed the logic that clears completed lines. This way, I could easily play the game until the whole flag is shown and wouldn’t even need to remember the past blocks.
The modified code
# rig the building blocks
# @BB = ( 89, 51, 30, 27, 75, 294 );
@BB = ( 2, 2, 2, 2, 2, 2 );
<...>
sub _s {
( $b, $bc, $x, $y ) = @_;
for $yy ( 0 .. 2 ) {
for $xx ( 0 .. 5 ) {
<...>
}
}
$Q = 'QcXgWw9d4';
# don't clear lines
# @f = grep { / / } @f;
# unshift @f, ( " " x $w ) while ( @f < $h );
p();
}
The rigged playthrough
Then it was just parsing out the flag from the blocks in the game. This time, the flag was valid.
HV20{https://www.youtube.com/watch?v=Alw5hs0chj0}