# Kerning - Reversing - 150 Points

## Challenge description:

Made on an arch machine, by awg

"kerning" file from https://drive.google.com/open?id=1Ye5I5yUfuH_-
ntAJMnd_ikAdb9DnszOD

## Lookin at it

Looks to be the usual reversing binary. Run the binary with the flag as the
argument, and it will either tell you right or wrong.

```bash  
~/kerning$ ./kerning  
Usage: ./kerning WPI{some_Fl4g-to 7ry}  
```  
```bash  
~/kerning$ ./kerning WPI{YOLOSWAGWHATSINTHEBAG}  
Wrong  
```  
  
  
First examination of the main function reveals loading
/usr/share/fonts/TTF/FreeSans.ttf, and then doing an md5sum on it. If this
md5sum does not match 22f8930d33f395544eb0034b7de24f41, the process will exit.  
  
![main font md5 check](https://i.imgur.com/eQByKIu.png)  
![obj.fonthash](https://i.imgur.com/5QAI3iN.png)  
  
The challenge description "Made on an arch machine" is somewhat of a hint, as
the file matches the one in the official arch repo.
https://www.archlinux.org/packages/extra/any/ttf-freefont/  
  
  
  
  
We can also see a loop with some calls to various STB_truetype functions.
https://github.com/nothings/stb/blob/master/stb_truetype.h  
  
![stb calls](https://i.imgur.com/Zse4ytF.png)  
  
We can guess here that its probably rendering something using FreeSans as a
font. That something further appears to be the flag we enter, as it loops
through argv[1] as it is calling the STB functions.  

  
  
After all of that is done, there is a loop where the rendered text output has
vertical slices of it hashed (using the domd5 function). Those hashes are
compared to known values in obj.texthashes. If any of the hashes mismatch, it
printfs "Wrong\n" and exits.  
![md5 check loop](https://i.imgur.com/KKqnZDE.png)  
  
Because these are only vertical slices, the changing a character will not
effect the md5sums of (most of) the slices before/to the left of it. The loop
only exits on the first instance of a wrong vertical slice, so we can do
something similar to a timing attack on this. (we could also do a timing
attack, but thats more effort)  
  
  
## Getting the flag  
  
We can count the number of times it goes through the loop as an indicator if
we got the correct sequence of letters. The loop exits at the first "wrong"
vertical slice. A timing attack would work on this, but it is much simpler to
use ltrace to count the number of calls to MD5_Final.

```python  
#! /usr/bin/python2  
from pwn import *

curstring = 'WPI{'

while curstring[-1] is not '}':  
top = [0, 0]  
for i in range(0, 127):  
c = chr(i)  
teststring = curstring + c  
s = process(["ltrace", "-e", "MD5_Final", "./kerning", teststring])  
res = s.recvall().count("MD5_Final")  
s.close()

if res >= top[0]:  
top[0] = res  
top[1] = c  
curstring += top[1]  
print(str(top[0]) + " " + top[1] + " " + curstring)  
```

This works for the first few words, but the first character of the third word
has a bunch of different possibilities that all result the same score. This is
due to the character 'i' fitting entirely within one vertical slice, and
requiring both 'i' and the character after it to be correct in order for that
vertical slice to work.

![multiple possibilities](https://i.imgur.com/841zzDe.png)

I remade my "bruteforcer" to keep a list of all the attempts that shared the
same highscore. This got through that issue and ran perfectly.

```python  
#! /usr/bin/python2  
from pwn import *

curlist = [['WPI{', 0]]

maxscore = 0

while maxscore < 165: ## was origionally just while True, but this check just
makes sure the program doesnt run on forever  
#Populate list  
tlist = []  
for j in curlist:  
tlist.append(j)  
for i in range(0, 127):  
tlist.append([j[0]+chr(i), 0])

#score list  
jlist = []  
for j in tlist:  
if j[1] == 0: #only calculate score if not already calculated  
s = process(["ltrace", "-e", "MD5_Final", "./kerning", j[0]])  
j[1] = s.recvall().count("MD5_Final")  
s.close()  
if maxscore < j[1]: maxscore = j[1]  
jlist.append(j)

#remove anything that isnt tied with the high score  
curlist = [j for j in jlist if j[1] >= maxscore]  
print(curlist)

```

After we let it do its thing for a few minutes, we get the answer.

![Script working](https://i.imgur.com/QKmNaif.png)

WPI{I_majored_in_typography}  

Original writeup
(https://github.com/roboman2444/writeups2018/tree/master/wpictf2018/kerning).