OverTheWire Advent Bonanza 2019: [Day 1] 7110

About

This post is part of a series in which I’ll post writeups for all challenges of the OverTheWire Advent Bonanza 2019 CTF. To see the intro, click here

Overview

So apparently Santa got stuck on an island and is sending SMSes to Rudolph, and we have got key logger logs from his cellphone. We have a few known SMSes and an unknown one, probably containing the flag. When we download the .tar.gz file, we get a few files: a C header file, and a few known SMSes in CSV and text format, and an unknown SMS CSV file. The header reviles the following mapping of the keys of Santa’s cellphone:

  • [0] Key 0 -> ” 0″
  • [1] Key 1 -> “.,’?!\”1-()@/:”
  • [2] Key 2 -> “abc2”
  • [3] Key 3 -> “def3”
  • [4] Key 4 -> “ghi4”
  • [5] Key 5 -> “jkl5”
  • [6] Key 6 -> “mno6”
  • [7] Key 7 -> “pqrs7”
  • [8] Key 8 -> “tuv8”
  • [9] Key 9 -> “wxyz9”
  • [10] Key * -> “@/:_;+&%*[]{}”
  • [11] Key # -> T9, T9_CAPS, ABC, ABC_CAPS
  • [100] Key LEFT -> LEFT
  • [101] Key RIGHT -> RIGHT
  • [102] Key UP -> UP
  • [103] Key DOWN -> DOWN
  • [104] Key CALL_ACCEPT -> CALL_ACCEPT
  • [105] Key CALL_REJECT -> CALL_REJECT

So let us begin decoding our SMSes

SMS 1

So, let’s start by looking at the resulting SMS:

date: 1999-11-23 03:01:10
to: 00611015550117
text: rudolf where are you brrr

And we also look at the CSV:

945918410125,100
945918410538,100
945918411013,100
945918411409,100
...

Now the first column seems to be some sort of timestamp, and the second one seems to be the actual key pressed. So lets just ignore the timestamp for now, and just print out every key pressed:

$ python parse.py sms1.csv
LEFT LEFT LEFT LEFT # # 7 7 7 8 8 3 6 6 6 5 5 5 3 3 3 0 9 4 4 3 3 7 7 7 3 3 0 2 7 7 7 3 3 0 9 9 9 6 6 6 8 8 0 2 2 7 7 7 7 7 7 7 7 7 LEFT LEFT 0 0 6 1 1 0 1 5 5 5 0 1 1 7 LEFT

So looking at that, we may recognize that our target phone number is in this CSV, namely 00611015550117, surrounded by two LEFTs each. In between there’s a huge chunk of key presses, so lets look into those a bit more.

We can see that some keys are repeated more than once. When we think back to old cellphones, we could assume that that cycles through the characters mapped to that specific key. So let’s try that on the huge chunk of key presses:

$ python parse.py sms1.csv
 rudolf where are you bTraceback (most recent call last):
 ...
 IndexError: string index out of range

Well, that didn’t quite work out. Let’s see why: The message ends in three ‘r’s. Because our code doesn’t care about the timestamp, it interpreted this as 9 consecutive presses of the 7 Key. But there’s no interpretation of 9x 7 Key, so we crash. The solution is that for a key press to be counted as a consecutive key press, it needs to happen in about 0.75 seconds after the previous one. With this implemented, we get a perfect reconstruction of the SMS:

$ python parse.py sms1.csv
 rudolf where are you brrr

SMS 2

The real SMS is:

date: 1999-11-23 03:04:11
to: 00611015550117
text: its too damn cold here and im out of eggnog lul

Let’s try our parse script from before:

$ python parse.py sms2.csv
 its fuckingTraceback (most recent call last):
 ...
 IndexError: list index out of range

So, it seems our parser doesn’t like bad words and crashed. Jokes aside, how did that ‘f***ing’ get in there? Let’s implement an failsafe when an invalid key press is encountered:

$ python parse.py sms2.csv
 its fuckin
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 Encountered invalid keypress 101!
 4too damn cold here and im out of eggnog lul

We see that we encounter a few RIGHT presses. Could it be that RIGHT is functioning as a backspace? Let’s implement that:

$ python parse.py sms2.csv
its too damn cold here and im out of eggnog lul

And we reconstructed the SMS perfectly!

SMS 3

This is the last known SMS:

date: 1999-11-23 03:06:39
to: 00611015550117
text: sorry bout my last 2msg but i could really need your help bud :*

Let’s try our old script:

$ python parse.py sms3.csv
 Encountered invalid keypress 102!
 Encountered invalid keypress 102!
 Encountered invalid keypress 102!
 Encountered invalid keypress 102!
 Encountered invalid keypress 102!
 Encountered invalid keypress 102!
 ...
 Encountered invalid keypress 103!
 sorri bout my last 2msg but i could realy need your help buy

OK, not quite and there’re a lot of UPs and DOWNs. So, let’s think a little: On old cellphones you could usually cycle between menus while writing an SMS. Maybe UP and DOWN cycle through the menus? So lets have an integer, which increments when pressing UP and decrements when pressing DOWN, and only care about key presses when that integer is 0:

$ python parse.py sms3.csv
 sorri bout my last 2msg but i could realy need your help bud :*

And again a perfect reconstruction! (The differences are intended, nothing to worry about)

EDIT: Thanks to Thuf1r from the CTF Discord, I realized I made a mistake. The UP and DOWN keys don’t actually cycle menus, but actually move the cursor back and forth. Because that doesn’t affect the flag, I wont fix the error.

SMS 4 (The unknown)

So let’s try our script on the unknown SMS:

$ python parse.py sms4.csv
 alright pal hers ye flag good lucj entering it with those hooves lol its aotw{l3ts_dr1nk_s0m3_eggn60g_y0u_cr4zy_d33r}

And we’ve got the flag!

aotw{l3ts_dr1nk_s0m3_eggn60g_y0u_cr4zy_d33r}

And here’s my (uggly) script:

#!/usr/bin/python
import csv
import sys

mappings = {0: " 0", 1: ".,'?!\"1-()@/:", 2: "abc2", 3: "def3", 4: "ghi4", 5: "jkl5", 6: "mno6", 7: "pqrs7", 8: "tuv8", 9: "wxyz9", 10: "@/:_;+&%*[]{}"}
 
with open(sys.argv[1], "r") as f:
	c = csv.reader(f, delimiter=',')
	
	cnt = 0
	lk = -1
	lt = 0
	m = 0
	
	s = ""
	
	for r in c:
		cnt += 1
		if lk == -1 and cnt < 7: continue
		
		t = int(r[0])
		k = int(r[1])
		
		if k == 100: break
		
		if m == 0:
			if (k != lk or t - lt > 750) and lk < 100:
				if lk >= 0: s +=mappings[lk][(cnt - 2) % len(mappings[lk])]
				cnt = 1
				lk = k
			
			lt = t
		
		if m == 0 and k == 101:
			s = s[:-1]
			cnt = 0
			lk = -2
			lt = 0
			continue
		elif k == 102: 
			m += 1
			cnt = 0
			lk = -2
			lt = 0
			continue
		elif k == 103:
			m -= 1
			cnt = 0
			lk = -2
			lt = 0
			continue
		elif m == 0 and k > 100:
			print("\nEncountered invalid keypress %d!" % k)
			cnt = 0
			continue
	
	if lk < 100: s +=mappings[lk][(cnt - 2) % len(mappings[lk])]
	print(s)

Leave a Reply

Your email address will not be published. Required fields are marked *