Rating: 5.0

First, I have to admit that I have not captured this flag!
But ... since I have just seen the other write-up and figured that I could retrieve the very same admin_k I thought it's worth writing this down
... it follows a little different no-crypto approach ;)

program setups rsa and elgamal keys to be used for encryption/decryption of a message and for signing.

Later it turns out that we can completly ignore the signing part ...

program runs 200 seconds until we hit SIGALRM ... this often means we might have to script the exploit, since manual in/output might take too long ... but let's just see

user can register or login
#### register:
if name == admin need to provide admin_k (which was read from file)
if the key is correct we get the flag!

-> goal get admin key!

for other users:

you enter a name (there will be a length check...)
then the rsa encrypted string will be:
msg = name + b'\x00' + admin_k
length check is on msg (means name + admin key + 1 null byte)
the check makes sure that size (aka number of bits of msg) does not exceed 700

from there we can get the size/len of the admin key by entering some names
and see when we get the 'Too long username' message
here it was a string with length (bytes) of 41 that passed, 42 will already be larger than 700 BITS
41 + 1 + admin = 700 -> 700 - 42*8 = 364 -> 364/8=45R4 admin key should have length 45

username\00admin_key
N 1 45

but again the msg is extended with data before encryption.
this time like this:
msg = msg + urandom(120 - len(msg))

it's easy to see that if length of message would equal 120 there would not be any random part
120 = len(name + b'\x00' + admin_k)
120 - 1 - 45 = 74 -> so choose name with N=74 bytes

mmmh 74 * 8 = 512 bits ... we had admin_k with 45 * 8 = 360 bits -> 512+360 > 700 !! bad :(

if we look further at the code, the other choice besides register is to login
to login you need a ticket which is the encrypted msg which was returned after registering
and the signature parts. if you enter false signature it does not matter only a warning is printed but program contines

from the code:

```
msg = cry.decrypt(ticket)
msg = long_to_bytes(msg)
name = msg.split(b'\x00')[0]
print('Welcome!{}'.format(name))
```

so after decrypt message is split again at the null byte and the name is returned
if we would have a message where admin_k does not contain random bytes and the name would be null as well
I guess after split() we would be greeted with the admin key that could be used to register with 'admin'

I tried this python code to see if I can generate suitable name string (still need to hope that input() would accept it)
```
admin = 'foobar'
name = b'\x00'*1
print('len: '+str(len(name + b'\x00' + admin)))
print('size: '+str(size(bytes_to_long(name + b'\x00' + admin))))

name = b'\x00'*74
print('len: '+str(len(name + b'\x00' + admin)))
print('size: '+str(size(bytes_to_long(name + b'\x00' + admin))))
gives us:
len: 8
size: 47
len: 81
size: 47
```
interesting ... we can have a 74 byte null (name) string with length 74, but size(bytes_to_long(name)) will be zero

so this would be the time to put everything into a script and see if it works but time run out and only a few manual tries were done but it seems it worked

So here are my (manual) steps
```
>>> nc=remote('47.75.53.178',9999)
>>> print(nc.recv())
b'welcome to Fantasy Terram\n65537\n140629472245999285098064316303866659629185812774218573790665303954294872600262448905882579177430447502384476296889455526009245187787784270910730776435647281906172951059742370845233593495596605880717057027739444519332751527512894864042942924679461080488348066457317250117451797086450925496865588827148175066791\n331594477686558113580787539285291719147\n144138144825272306357576043429581630357\n58183684653320143077677950713948783289\nPlease [r]egister or [l]ogin :>>'
>>> nc.send("r\n")
>>> print(nc.recv())
b'please input your username:>>'
>>> nc.send(b'\x00'*74)
>>> nc.send("\n")
>>> ans=nc.recv()
>>> print(ans)
### note we need to grep first line from ans -> our login ticket
b'**1150307703645571841691124872395986074445001716352354369904193119148817280160469269859716991052866383451320166269287258385015274222555291986480385108167877033070349751370633422865847917612573602863230184749721945543492207722907777189700351702934150482163081676826889438295076608552683903022157946512008773972**\n129390639790651743848770239302728311235\n109164253264931500125431647817682215432\nPlease [r]egister or [l]ogin :>>'
>>> nc.send("l\n")
>>> nc.sendline("1150307703645571841691124872395986074445001716352354369904193119148817280160469269859716991052866383451320166269287258385015274222555291986480385108167877033070349751370633422865847917612573602863230184749721945543492207722907777189700351702934150482163081676826889438295076608552683903022157946512008773972")
>>> print(nc.recv())
b'ticket:>>sig[0]'
>>> nc.sendline("123")
>>> nc.sendline("123")
>>> print(nc.recv())
b'sig[1]Wrong signature!\nWelcome!
b'1qsxcvghyu89olkmnbvd2#$%^&*()_OKMNBFRT:"><~&*r'
\nPlease [r]egister or [l]ogin :>>'

>>> nc.send("r\n")
>>> print(nc.recv())
b'please input your username:>>'
>>> nc.send("admin\n")
>>> print(nc.recv())
b"please impurt admin's key:>>"
>>> nc.sendline("1qsxcvghyu89olkmnbvd2#$%^&*()_OKMNBFRT:"><~&*r")
>>> print(nc.recv())
b'Liar! Get out of here!\n'
```

So what went wrong?
two things I guess ...
a) the string I send back is one char too long, remember we figured it should be 45 bytes the last r can be skipped and
b) the double quote shall be escaped when sending so it should have looked like:
``>>> nc.sendline("1qsxcvghyu89olkmnbvd2#$%^&*()_OKMNBFRT:\"><~&*")``

PharisaeusApril 17, 2018, 9:46 a.m.

Genius! :)