.:. [About] .:. [Blog] .:. [Twitter] .:. [Mastodon] .:. [GitHub] .:. [GameBoy Dev] .:.

BGGP3:
Amen Breaking Renoise

2022-08-25

+-----------+ 
|Preamble   |
+-----------+c o o o o o o o o
			
I've been debating if I should do a writeup for this crash I found in Renoise.
My hesitation is for 3 reasons:
  1. I was unable to leverage it and do anything cool with it.
  2. Renoise is closed source and I had difficulty doing triage via staring at assembly and debugging alone, therefore some of my "conclusions" at this point are only "informed assumptions".
  3. The crashes come from a lack of an XML parser, which Renoise is not currently interested in developing, so I was unable to merge any patches.
Despite that! I've decided it's worth recording my process. In hindsight, I learned a shit ton. So hopefully this writeup can expedite that learning process for some of you, or for me when I forget... So do a quick readup on the third annual Binary Golf Grand Prix (BGGP) to get some context, and come back to here if you're still interested in reading about my approach!
+-----------+
|Intro      |
+-----------+-Co o o o o o o o
			
You're collaborating with some friends on a new song in Renoise. You each take turns making changes to the track, downloading, adjusting, then reuploading the xrns file to a shared drive. Today the song has made its rounds back to you. You download the file, load it into renoise and start the song.

This plays:
Renoise crashes
An error appears:
x
Error 3:
BGGP 3

This was the goal I had my eyes set on. In accordance to the bggp rules, I wanted to make a Renoise song file that was 4096 bytes at the largest, that would load, play a sample, crash Renoise, then throw an error with the number 3, and in my wildest dreams, pop a shell.
It's good to dream.
However, before I could achieve this dream I knew I had to learn more about Renoise input files, fuzzing, and actually find my first crash.
+-----------+
|xrns       |
+-----------+--c o o o o o o o
			
Before fuzzing xrns files, I wanted to figure out how they worked, and how Renoise used them. The fileformat archive summed them up pretty well here.
TL;DR
An xrns file is just a zip file with a compressed xml file in it titled "Song.xml".
To verify this information I made a new renoise song, deleted all the tracks and instruments from it to get it as small as possible, then saved it.
To see if it behaved like a zip file I tried: `unzip song.xrns`
Sure enough, it output a file titled "Song.xml", the contents of which was a bunch of mark up language stuff.
I tried manually fiddling with these files a little bit. It turned out that if I changed the name of Song.xml and rezipped it into my xrns file, Renoise would no longer take it.
It seemed like there had to be a file named Song.xml. I went ahead and changed some values in the xml file too to see what would happen. Those changes were then changed in the song as well.
From this point it seemed like my best bet for getting a crash out of renoise would be to mess with the values in this file. There was only one caveat: a crash from a malformed input file will most likely cause a crash while it's being loaded, not while the song is playing, but I was eager to get started, and to see what was possible, so I started fuzzing!
+-----------+
|Fuzzing    |
+-----------+---Co o o o o o o
			
Fuzzing is an automated method of testing software by generating malformed inputs in the hopes of revealing vulnerabilities. Honggfuzz is a good fuzzer to start with.
This is the basic command it suggests starting out with:
$ honggfuzz -i input/ -o output/ -x -- /usr/bin/renoise ___FILE___
This standard command takes a folder of example inputs: `-i input/` generates a malformed file based on the input,
Passes this generated file to the specified program (in this case renoise): `-x -- /usr/bin/renoise ___FILE___`,
and if the program crashes, will output a file containing logs and memory information at the moment of crash into the specified output folder: `-o output/`
Unfortunately, this general case would not work for me. What I needed to happen was this:
In order to achieve this I came up with the following set of parameters for honggfuzz:
$ honggfuzz -n1 -t3 -i xml/ --pprocess_cmd fuzzy_xrns -x -s -- /usr/local/bin/renoise song.xrns
This command is pretty dense. I'll unpack it below:

At this point we start fuzzing, and honggfuzz finds a number of crashes. I gravitate towards the crash I'm familiar with: SIGSEGV, the error for a memory access violation.
+-----------+
|"Triage"   |
+-----------+----c o o o o o o
			
I'm going to try and keep this part relatively brief.
Most of the SIGSEGV crashes had a crash log in Renoise that looked like this:


This looks to me like something is trying to grab memory at [(nil)]. Just to check and see if I can get any more information I run it through gdb as well.
At the time of the crash one can see the problem pretty clearly:


On line 0xd74143, renoise tries to run the following line of code:
mov rdi, QWORD PTR [rax]
And if we check what's in the register $rax, we can see it's 0x0, which is an inaccessible memory location. Hence the SIGSEGV.
The question for me then is: "why is that 0x0?" I then spent a month, on and off, trying to answer that question by running it through gdb, staring at the dissassembly in radare and ghidra:

This was a bit out of my reach, and is where I'm going to call it for this portion of the writeup. Please reach out to me on twitter though if you see any flaws in my logic / process though! Would love to hear any leads.
This is where a new part of the challenge began: seeing how small I could make the crash.
+-----------+
|Binary Golf|
+-----------+-----Co o o o o o
			

Working with a zip (xrns) seemed like a good strategy for making my crash as small as possible. I started off by seeing how small I could get the physical contents of the zip. I did this via some trial and error: remove stuff from the file, recompress it, throw it into renoise, see if it crashes. Funny thing about this was it didn't end until I had just one line left in the `Song.xml` file:
<RenoiseSong></RenoiseSong>
It turned out that Renoise would attempt to load any file with the `RenoiseSong` XML tag in it, then crash.
After discussing ways to see if I could get this file even smaller, Retr0id pointed out that renoise may accept self-closing tags, which it does! Allowing me to make my `Song.xml` file just the single line: `<RenoiseSong/>`.
This is small enough where I'm not even getting compression out of zipping it. Here's a hexdump of the xrns file before messing with the binary at all:
00000000  50 4b 03 04 0a 00 00 00  00 00 d0 75 1f 55 e7 f7  |PK.........u.U..|
00000010  04 74 0e 00 00 00 0e 00  00 00 08 00 1c 00 53 6f  |.t............So|
00000020  6e 67 2e 78 6d 6c 55 54  09 00 03 48 74 0f 63 4b  |ng.xmlUT...Ht.cK|
00000030  74 0f 63 75 78 0b 00 01  04 00 00 00 00 04 00 00  |t.cux...........|
00000040  00 00 3c 52 65 6e 6f 69  73 65 53 6f 6e 67 2f 3e  |..<RenoiseSong/>|
00000050  50 4b 01 02 1e 03 0a 00  00 00 00 00 d0 75 1f 55  |PK...........u.U|
00000060  e7 f7 04 74 0e 00 00 00  0e 00 00 00 08 00 18 00  |...t............|
00000070  00 00 00 00 01 00 00 00  a4 81 00 00 00 00 53 6f  |..............So|
00000080  6e 67 2e 78 6d 6c 55 54  05 00 03 48 74 0f 63 75  |ng.xmlUT...Ht.cu|
00000090  78 0b 00 01 04 00 00 00  00 04 00 00 00 00 50 4b  |x.............PK|
000000a0  05 06 00 00 00 00 01 00  01 00 4e 00 00 00 50 00  |..........N...P.|
000000b0  00 00 00 00                                       |....|
000000b4
			
This file is 180 bytes. To get this even smaller, I had to learn more about zip files. Fortunately, there's a bunch of resources about zips, and a lot of people have messed with them. Wikipedia was pretty great for this, as well as xcellerator's writeup for bggp2, and Ange Albertini's zip poster. To get started editing hex files you can use vim + xxd, which is a fun tip I learned from yuu! Or you can just use ghex or something like that. For brevities sake, it may be worth going ahead and clicking some of those links to get a better understanding of zip for yourself. I'm going to stick to the specific changes that were necessary for this project. I got started messing around with the different fields to see what I could get away with changing, and then followed the logic that that field may also be able to be removed. What I quickly noticed from looking at ZIP's Wikipedia page, is that zip's have a lot of required fields, but also a lot of redundency. The Local File Header even has a section called the "Extra Field", and I was finding that that data could be removed after changing around the "Extra Field Length" as well.
In our case we're looking to remove the red bytes and alter the purple bytes (if you're color blind that's remove the brown bytes and alter the blue bytes):
00000000  50 4b 03 04 0a 00 00 00  00 00 d0 75 1f 55 e7 f7  |PK.........u.U..|
00000010  04 74 0e 00 00 00 0e 00  00 00 08 00 1c 00 53 6f  |.t............So|
00000020  6e 67 2e 78 6d 6c 55 54  09 00 03 48 74 0f 63 4b  |ng.xmlUT...Ht.cK|
00000030  74 0f 63 75 78 0b 00 01  04 00 00 00 00 04 00 00  |t.cux...........|
00000040  00 00 3c 52 65 6e 6f 69  73 65 53 6f 6e 67 2f 3e  |..<RenoiseSong/>|
00000050  50 4b 01 02 1e 03 0a 00  00 00 00 00 d0 75 1f 55  |PK...........u.U|
00000060  e7 f7 04 74 0e 00 00 00  0e 00 00 00 08 00 18 00  |...t............|
00000070  00 00 00 00 01 00 00 00  a4 81 00 00 00 00 53 6f  |..............So|
00000080  6e 67 2e 78 6d 6c 55 54  05 00 03 48 74 0f 63 75  |ng.xmlUT...Ht.cu|
00000090  78 0b 00 01 04 00 00 00  00 04 00 00 00 00 50 4b  |x.............PK|
000000a0  05 06 00 00 00 00 01 00  01 00 4e 00 00 00 50 00  |..........N...P.|
000000b0  00 00 00 00                                       |....|
000000b4
			
Below is a hexdump after those changes. The changed values are highlighted.
00000000  50 4b 03 04 0a 00 00 00  00 00 d0 75 1f 55 e7 f7  |PK.........u.U..|
00000010  04 74 0e 00 00 00 0e 00  00 00 08 00 00 00 53 6f  |.t............So|
00000020  6e 67 2e 78 6d 6c 3c 52  65 6e 6f 69 73 65 53 6f  |ng.xml<RenoiseSo|
00000030  6e 67 2f 3e 50 4b 01 02  1e 03 0a 00 00 00 00 00  |ng/>PK..........|
00000040  d0 75 1f 55 e7 f7 04 74  0e 00 00 00 0e 00 00 00  |.u.U...t........|
00000050  08 00 00 00 00 00 00 00  01 00 00 00 a4 81 00 00  |................|
00000060  00 00 53 6f 6e 67 2e 78  6d 6c 50 4b 05 06 00 00  |..Song.xmlPK....|
00000070  00 00 01 00 01 00 36 00  00 00 34 00 00 00 00 00  |......6...4.....|
00000080
			
In order by which the highlighted values appear, we've altered: Having squeezed out 52 bytes by removing the entirety of the extra fields, we have what I've called the final product for now!
+-----------+
|Scoring    |
+-----------+------c o o o o o
			
My total score is my file size (4096-128) + 1024 points for my writeup = 4992 points. No code exec, and no printed 3's... it's not a winner, but I learned a hell of a lot. BGGP3 opened up a huge door of side projects that I'll begin documenting on this site as well. Many thanks to everyone I've linked to above, and all the homies I didn't, you know who you are! Thanks for believing in me! <3
Also please reach out if you see any glaring errors, typos, or ways that I could make the file smaller, or anything like that at all. I really won't take it personal and I'm always down to learn something new!