Writeup for: binarygolf/BGGP#165
I set out to cover as many of the BGGP challenges as I could and went on quite a journey to get there!
Initially I started out with this structure, planning to randomly output a challenge and replace ls later with perhaps a :(){ :|:& };: fork bomb:
a=(ls cp\ $0\ X\ \&\&\ echo\ 4 curl\ https://binary.golf/5/5 echo\ 6);eval ${a[RANDOM%5]}Next I wanted to add a polyglot, knowing a zip file didn't have to have the PK header starting at byte 0, it could be anywhere in the file, which gave a lot of flexibility:
a=(ls cp\ $0\ X\ \&\&\ echo\ 4 curl\ https://binary.golf/5/5 echo\ 6);eval ${a[RANDOM%5]}#PK<binary>The <binary> part would be the bytecode for an empty file named 2. However, despite best efforts I couldn't make it work to the right hand side of a # character that was to the right hand side of some Shell. It would always return "cannot execute binary file: Exec format error" message and no output.
Having the bytecode for a zip with an empty file named 2 was also 100 bytes, far too big. I wondered if we could have an empty zip and if perhaps zip files could have sort of metadata area in the file to store text?
I was very pleased to find an empty zip was only 22 bytes and even more pleased to find you can indeed have a metadata comment in a zip file and adding 2 as the comment only added 1 byte!!!
The format was:
#PK<empty zip binary>2Note the initial # to stop Shell executing this line. This would output 2 if you ran:
└─▶ unzip -zq 6.zip
2However, the next challenge I hit was trying to make this a palindrome. This was a problem because reversing the zip bytecode caused an invalid zip.
After a LOT of experimenting I managed to get a format where the zip was both a palindrome and a valid zip:
#PK + EOCD + empty zip 00's + comment byte length:1 + 2 + reverse(#PK + EOCD + empty zip 00's + comment byte length:1)
The hex bytes and an explanation of key parts of this are:
# P K 2 K P #
23 50 4b 05 06 [16 x 00's] 01 00 32 00 01 [16 x 00's] 06 05 4b 50 23
^ ^ ^ ^ ^ ^
| | | | | | +-- Palindrome continues...
| | | | | +-- Zip comment byte 2 and middle point of palindrome
| | | | +-- Zip comment length of 1 byte in little endian
| | | +-- Zip zeros for disc numbers, record counts, sizes, offsets
| | +-- Zip EOCD start
| +-- Zip start signature
+-- Shell comment char
With that sorted, I worked on the Shell, restructuring it to get it shorter:
s(){ sleep $@; };ls;s 3;cp $0 X&&echo 4;s 3;curl https://binary.golf/5/5;s 3;echo 6;#Line 3 needed to be the reverse of this so the file overall was a palindrome:
#;6 ohce;3 s;5/5/flog.yranib//:sptth lruc;3 s;4 ohce&&X 0$ pc;3 s;sl;} ;@$ peels {)(sJamming it all together, the code now looked like this:
s(){ sleep $@; };ls;s 3;cp $0 X&&echo 4;s 3;curl https://binary.golf/5/5;s 3;echo 6;#
#PK<binary>2<binary>KP#
#;6 ohce;3 s;5/5/flog.yranib//:sptth lruc;3 s;4 ohce&&X 0$ pc;3 s;sl;} ;@$ peels {)(sThe next problem however was on line 3, as it seems Bash will read the syntax of lines before it figures out it's a comment line and should be ignored! So it meant it was trying to parse the syntax of line 3, even though it was a comment and as } was parsed before { it was breaking! Uggh, baffling.
I decided to let go of the idea of having randomly chosen challenge or attempting to save bytes by using a function for sleep and instead just sleep 3 between each challenge part:
ls;sleep 3;cp $0 X&&echo 4;sleep 3;curl https://binary.golf/5/5;sleep 3;echo 6;#
#PK<binary>2<binary>KP#
#;6 ohce;3 peels;5/5/flog.yranib//:sptth lruc;3 peels;4 ohce&&X 0$ pc;3 peels;slThis worked now! The Shell was executed on line 1, line 2 was a valid zip that had a 2 in metadata comment and was palindromic and line 3 was the reverse of line 1 to make the whole file a palindrome.
I was close to finishing this - all 6 challenges in one entry and it was only around 220-ish bytes, amazing! Time for one last aspect - crashing!
Straight away I knew couldn't use a fork bomb, as that contained } and { characters, which are a problem when that reversed order, even in a comment.
After some research I came across kill -11 $$ which sends SIGSERV which causes a segmentation fault crash, and it worked as I hoped, it would crash the shell progarm that was running the script, eg sh.
The almost finished entry consisted of this (the final touch was that a space needed before the # char to ensure the kill command functioned with the params):
cp $0 X&&echo 4;sleep 3;curl https://binary.golf/5/5;sleep 3;echo 6;sleep 3;kill -11 $$ #
#PK<binary>2<binary>KP#
# $$ 11- llik;3 peels;6 ohce;3 peels;5/5/flog.yranib//:sptth lruc;3 peels;4 ohce&&X 0$ pcThis was 225 bytes, pretty good! But I realised there's actually no point in sleeping at all between outputs for the challenges, that doesn't seem to be required, so I got rid of them and now it's just 179 bytes!
The final entry now looks like this:
cp $0 X&&echo 4;curl https://binary.golf/5/5;echo 6;kill -11 $$ #
#PK<binary>2<binary>KP#
# $$ 11- llik;6 ohce;5/5/flog.yranib//:sptth lruc;4 ohce&&X 0$ pcUsing unzip to see the comment and then executing it as a Shell script, it'll output as following:
└─▶ unzip -zq 6.zip
2
└─▶ ./6.zip
4
Another #BGGP5 download!! @binarygolf https://binary.golf
6
Segmentation fault (core dumped)This in order is - unzip showing the comment satisfies BGGP 2.
Then running ./6.zip we execute BGGP 4 (also replicates the 6.zip file as X file), downloading for BGGP 5, outputting 6 for BGGP 6 and finially crashing for BGGP 3.
It's also a palindrome cover BGGP 1!
Now I really need some sleep! :-D