# Grille_me - Crypto/net (150)
How well do you know classic ciphers? Some coding may be required...
PEE151R4HEL.LCC8.2TFECE.EOT.901OTHN.ANT23P2RRAG.SNO0.O0TULE.
## Part One
The encoding given above is only the first part of the challenge. After staring at it for a while, I started to notice that the numbers given were the same as the IP address used in previous challenges along with `1204`, the month-day convention for ports previously used. With that it wasn't too hard to see that the characters could just be arranged in a grid and the first message read off the columns:
```PEE151R4HEL.LCC8.2TFECE.EOT.901OTHN.ANT23P2RRAG.SNO0.O0TULE.
pleaseconnectto18.205.93.120port1204forthetruechallenge```
## Part Two
The second part of the challenge comes when you connect to the service above. All you're given is a jumble of characters followed by a prompt. If you enter something wrong, the only response is then 'Invalid password'. After requesting several times, it becomes clear that some of the characters stay constant. A selection of different jumbles is shown below.
```YPWI2ZORASODSW4E9USR:9NYPWI2TORASODS6F3DUSR:U6YPWI1QORASODSGNX3USR:KZYPWI46ORASODSL2KAUSR:WHYPWIDGORASODSDSLKUSR:YKYPWIX6ORASODS4DPHUSR:0AYPWIXJORASODSY4Q8USR:BBYPWIJHORASODSIQDWUSR:1NYPWIOXORASODSMSZDUSR:C2YPWIR3ORASODSWHTSUSR:HBYPWI83ORASODS543RUSR:HFYPWI70ORASODS8DJ3USR:81YPWIWIORASODSLNCDUSR:NHYPWIT1ORASODSOGKVUSR:Z5YPWIJ7ORASODSLMHYUSR:CGYPWI9XORASODSGNV3USR:02YPWIC3ORASODSKXHTUSR:YFYPWIIUORASODSQVXSUSR:0F```
From here, I isolated the characters that were constant (`YPWIORASODSUSR:`), and anagramed them to what seems like the logical phrase `YOURPASSWORDIS:`. At this point, I was hopelessly lost on how to un-jumble the remaining 8 characters. Since there were only 8 characters, I chose the brute-force approach and wrote a script to attempt all `8! = 40320` possibilities.
```pythonif __name__ == '__main__':	invalid_ids = []	ans = b''	for index in range(40320):		try:			print(index)			positions = [4,5,13,14,15,16,21,22]			s = socket.socket()			s.settimeout(1)			s.connect(('18.205.93.120',1204))			val = s.recv(25)			count = 0			while len(val) != 25 and count < 5:				count += 1				val = val + s.recv(25-len(val))				time.sleep(1)			if count >= 10:				raise Exception('recv error')			if len(val) != 25:				print('error, got:', val)				break			pw = []			x = index			ans_positions = []			while len(positions) > 0:				i = x % len(positions)				x = x // len(positions)				pw.append(val[positions[i]])				ans_positions.append(positions[i])				positions = positions[:i] + positions[i+1:]
			s.send(bytes(pw)+b'\n')			res = s.recv(200)			print(val,bytes(pw),res)			if not res.startswith(b'Invalid'):				print(ans_positions)				break			s.close()		except Exception as e:			print(e)			invalid_ids.append(index)			print('invalid_ids =', invalid_ids)			time.sleep(5)	print('invalid ids:', invalid_ids)```
With this script running, I went to sleep and awoke to find that it hit on the ordering `[13, 4, 14, 21, 15, 5, 16, 22]`.
With the benefit of retrospect, it seems a little more obvious what the actual ordering was. With the numbers arranged in three rows, the order simply reads off as a diagonal pattern as shown below:
```0, 6 or 10, 17, 7 or 19, 1, 8, 9 or 12 or 18, 9 or 12 or 18, 2, 6 or 10, 7 or 19, 11, 3, 9 or 12 or 18, 20, 13, 4, 14, 21, 15, 5, 16, 22
0 . . . 1 . . . 2 . . . 3 . . . 4 . . . 5 . .. 6 . 7 . 8 . 9 .10 .11 .12 .13 .14 .15 .16 .. .17 . . .18 . . .19 . . .20 . . .21 . . .22```
## Part Three
After entering the correct password to part two, we're greeted with something we expected all along - a [Grille Cipher](https://en.wikipedia.org/wiki/Grille_(cryptography)) (see the challenge name). The response we're given looks like the following, but changes on every new iteration:
```### # #### ### ### ### #### ### ## #
EPEPALRUS9E2OSAOOWSNBOEEMROER8DN:WTE```
It turns out that this is a similar sentence, but the "password" this time is all contained in the last rotation of the grille. Knowing this, I wrote a quick function to solve the cipher and submitting the answer gives the flag.
```pythondef solve_chart(chart):	print(chart.decode('ascii'))	arr = chart.split(b'\n')	pw = []	for i in range(6):		for j in range(6):			print(i,j,chr(arr[j][5-i]),chr(arr[7+i][j]))			if arr[j][5-i] == ord(' '):				pw.append(arr[7+i][j])		print()	return bytes(pw)+b'\n'```
And my program output:
```0 0   E0 1 # P0 2 # E0 3 # P0 4 # A0 5 # L
1 0 # R1 1   U1 2 # S1 3   91 4 # E1 5   2
2 0   O2 1 # S2 2 # A2 3 # O2 4   O2 5 # W
3 0 # S3 1 # N3 2   B3 3 # O3 4 # E3 5 # E
4 0 # M4 1 # R4 2 # O4 3 # E4 4 # R4 5   8
5 0 # D5 1 # N5 2 # :5 3   W5 4 # T5 5 # E
b'EU92OOB8W\n'b'Have a flag: AOTW{m33t_m3_at_grillby_s}\n'```