mirror of
https://github.com/autistic-symposium/sec-pentesting-toolkit.git
synced 2025-08-13 17:05:27 -04:00
Add old writeups
This commit is contained in:
parent
06365916d8
commit
1b774c9add
89 changed files with 4052 additions and 688 deletions
53
CTFs_and_WarGames/CTFs_Writeups/Hack.lu/300_peace_pipe.py
Normal file
53
CTFs_and_WarGames/CTFs_Writeups/Hack.lu/300_peace_pipe.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import socket
|
||||
|
||||
PORTc = 1432
|
||||
PORTm = 1434
|
||||
PORTw = 1433
|
||||
HOST = 'wildwildweb.fluxfingers.net'
|
||||
|
||||
def peace_pipe():
|
||||
|
||||
""" Get the magic message from some user to calculate rm """
|
||||
|
||||
# create sockets
|
||||
sm = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sw = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# connect to w
|
||||
sw.connect((HOST, PORTw))
|
||||
sw.recv(4096)
|
||||
sw.send(b'makawee')
|
||||
sw.recv(4096)
|
||||
sec = sw.recv(4096)
|
||||
tw = sec.split("did this:")[1].split("\n")[1].strip()
|
||||
print "\nMagic from w to m: " + tw
|
||||
|
||||
# connect to m
|
||||
sm.connect((HOST, PORTm))
|
||||
sm.recv(4096)
|
||||
sm.send(b'mrblack')
|
||||
sm.recv(4096)
|
||||
sec = sm.recv(4096)
|
||||
tm = sec.split("did this:")[1].split("\n")[1].strip()
|
||||
print "\nMagic from m to w: " + tm
|
||||
|
||||
# send w's magic to m's
|
||||
sm.send(tw)
|
||||
print sm.recv(4096)
|
||||
|
||||
# send m's magic to get the token
|
||||
sw.send(tm)
|
||||
token = sw.recv(4096)
|
||||
token = token.split('\n')[1].strip()
|
||||
print "Token is: " + token
|
||||
|
||||
# finally, send token back to m
|
||||
sm.send(token)
|
||||
print sm.recv(4096)
|
||||
|
||||
sm.close()
|
||||
sw.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
peace_pipe()
|
257
CTFs_and_WarGames/CTFs_Writeups/Hack.lu/README.md
Normal file
257
CTFs_and_WarGames/CTFs_Writeups/Hack.lu/README.md
Normal file
|
@ -0,0 +1,257 @@
|
|||
# The Peace Pipe at Hack.lu's Final CTF 2014
|
||||
|
||||
|
||||
## Understanding the Problem
|
||||
|
||||
The problem starts with this weird story:
|
||||
|
||||
After a long day, you sit around a campfire in the wild wild web with a few Sioux you met today.
|
||||
To celebrate friendship one of them takes out his wooden peace pipe and minutes later everyone seems to be pretty dizzy.
|
||||
You remember that their war chief "Makawee" started something to say about a secret tipi filled with fire-water (the good stuff). But when he noticed your interest he immediately stopped talking.
|
||||
You recall that "Makawee" spoke with "Wahkoowah" about that issue, but it ended with a fight.
|
||||
Since then Makawee wouldn't talk to Wahkoowah anymore. While they argued "Chapawee" wrote something down.
|
||||
Maybe you can exploit their dizzyness to find out the location of the tipi.
|
||||
|
||||
Then it gives us three *ports* in the *host*. With the first one, we talk to **Chapawee**:
|
||||
|
||||
wildwildweb.fluxfingers.net 1432
|
||||
|
||||
With the second, we talk to **Wankoowah**:
|
||||
|
||||
wildwildweb.fluxfingers.net 1433
|
||||
|
||||
|
||||
Finally, with the third, we talk to **Makawee**:
|
||||
|
||||
wildwildweb.fluxfingers.net 1434
|
||||
|
||||
It was obvious that this game was about fooling our fellow *stoned* native-Americans.
|
||||
|
||||
### A Dialogue with Chapawee
|
||||
|
||||
When we *netcat* to **Chapawee** he answers:
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1432
|
||||
Hi I'm Chapawee. I know the truth about the stars
|
||||
Say stars for more
|
||||
```
|
||||
|
||||
We answer *stars* and get a funny menu:
|
||||
|
||||
```sh
|
||||
I can tell you the truth about
|
||||
* constellation
|
||||
* namestar [starname] [key_of_truth] Adds a public key to a user.
|
||||
Existing users cannot be
|
||||
overwritten. Input is
|
||||
[a-f0-9]{1,700}.
|
||||
* showstar [starname] Reads the public key from the
|
||||
database.
|
||||
```
|
||||
|
||||
The first option *constellation*, shows a very interesting scheme:
|
||||
|
||||

|
||||
|
||||
Choosing the options **namestar** we are able to pick a (new) name to add a key. Picking the option **showstar** we are able to see the key for some name (for example, for Wahkoowar, Makawee, or any new name we had added before).
|
||||
|
||||
So, from the above scheme, we know:
|
||||
|
||||
1. How a **message** (t) is created with someone's public key, a **random rational number** (r_w), and a given **modulo number** (p). The only unknown here is r_w, which is a rational number (Q). This mean that any plans to brute force the messages wouldn't work (however, if r_w was an integer, this task could be achieved).
|
||||
|
||||
2. Everyone has a private key that is modulo p. We never learn anything about anyone's private keys. We just know that they could be of the order of p (which is a really large number, ~1E2048).
|
||||
|
||||
3. Wahkoowah and Makawee have a shared secret key. The way they share this key without knowing each other's private key is by this tricky transformation:
|
||||
|
||||

|
||||
|
||||
Notice that we can move the multiplications' modulo operation to the end, due to [this propriety](http://en.wikipedia.org/wiki/Modular_arithmetic#Congruence_relation).
|
||||
|
||||
|
||||
In conclusion, all we need to do is to convince Wahkoowah that we are Makawee (by telling him we are Makawee, so he can use his public key, and by sending him a correct *t_m*). If this works, he will give us a token. Then, if we send this token to Makawee, we get our flag.
|
||||
|
||||
|
||||
|
||||
### A Dialogue with Wankoowah
|
||||
|
||||
Now, let's see what Wankoowah has to say:
|
||||
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1433
|
||||
Hi, I'm Wahkoowah. Who are you? Too foggy...
|
||||
```
|
||||
|
||||
We try a couple of possibilities to check the outputs:
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1433
|
||||
Hi, I'm Wahkoowah. Who are you? Too foggy...
|
||||
noone
|
||||
Hi noone
|
||||
Cannot find it...
|
||||
Ncat: Broken pipe.
|
||||
|
||||
$ nc wildwildweb.fluxfingers.net 1433
|
||||
Hi, I'm Wahkoowah. Who are you? Too foggy...
|
||||
makawee
|
||||
Oh its you, Im so sorry. Can we talk now?
|
||||
This is your key of truth
|
||||
50e7e1957c1786a9442f0c9f372ec19f74f52839e9e38849b47438153f9d2483213a43ad2d988fab4a8707922060aaefe6504a70637596fbcf9d58362b23e5d5e2177fd4e919b80437bab51eda931e065b6d66fce343d7cb2b7c1ca26214792d461895095ae58354af0dec6e63869007e23835892f26aabc96fe3d9084a829b4d6c5b92c6f3e0dd9a70cbd5c72d6434f2b94d21c3b0c58a288c140642b813ffb1b632bc358b3a6af0124902acd8792202c848de7f9d5d98bee51ca69040c8a2457ad3fa6276d6510701b9a875df612e035322cad06579a0a11f5e7cb4ebb7b69171c38585fc0f4fe07b0c889442397029d05dc801026a0648d7aa8c847420e9c
|
||||
With magic I did this:
|
||||
922a7f4b150eb83eab929e2a44bcbbb45435851262a6e7b84d2777d995ffbc315a2e57a580f4982797b45efde6d30b493880ecea33fe26e6c8ff636b75b7cb3f647f0c6f606249bc48ef09bd20738cf472bf47c7f52b9e11afcefc1548155637b0d2054d37cd74301e534208408074938ae4e7b54ef50fa0a39cb090dd34de7a4040024ba2394bac62262ccda529d2d69effe24338f0ec1b842539d2b89b081fa77a266a7c9f62c25d2a1ee1af3da8054d79d87ae88da61b8333e1fc195d2957341458700a3be70c98e1a8ab35bfe527ff6a2f255c66d753d03c59404993f1ed295a722bf1d0241eec9c01efe06e3cd5b845e84de3d29de17f9b68351bdc2d65
|
||||
We continue our conversation, right?
|
||||
|
||||
|
||||
```
|
||||
|
||||
The *magic* is the message *t_w*, created with Makawee's public key. Wahkoowah then ask for *t_m*...
|
||||
|
||||
|
||||
### A Dialogue with Makawee
|
||||
|
||||
Let's see what Makawee has to say:
|
||||
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1434
|
||||
Hi, I'm, Makawee, and you are? Too bright here...
|
||||
noone
|
||||
noone ... do I know you?
|
||||
Cannot find it...
|
||||
|
||||
Ncat: Broken pipe.
|
||||
|
||||
$ nc wildwildweb.fluxfingers.net 1434
|
||||
Hi, I'm, Makawee, and you are? Too bright here...
|
||||
wahkoowah
|
||||
I dont talk to you anymore. That thing with my daughter...
|
||||
|
||||
Ncat: Broken pipe.
|
||||
```
|
||||
|
||||
Mmmm, we need to make Makawee use Wankoowah's key without him knowing it!
|
||||
|
||||
Since Chapawee allows us to add keys to names, let's create some name with Wahkoowah's key (say "mrwhite") and send this to Makawee:
|
||||
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1432
|
||||
Hi I'm Chapawee. I know the truth about the stars
|
||||
Say stars for more
|
||||
stars
|
||||
|
||||
I can tell you the truth about
|
||||
* stars
|
||||
* constellation
|
||||
* namestar [starname] [key_of_truth] Adds a public key to a user.
|
||||
Existing users cannot be
|
||||
overwritten. Input is
|
||||
[a-f0-9]{1,700}.
|
||||
* showstar [starname] Reads the public key from the
|
||||
database.
|
||||
|
||||
namestar mrwhite 218b783ec5676cbddd378ceb724820444599f22cdcfda0a5a195b3a8fbf4ab5c915703420ad3b84531c54b838b23858fb84fcaf04d4932d4b9ef861c7ae9b635c9d3f56dfb100aa47297afcd94df41efa9f5ecba6483c5328e43ec457027ee4efcecefa094a83945106d7da1878c1f47516c2f2578170eeb36955d8bd16e0d106f9e2effe9debff41e551db4ac2e87bc8a9378d8eadb042bee18f4ad72ab721833a27154a7318b8cbe6f98fb3c82da32d1688fdcdb718fb15d9d5e6276b037cef62d953c09b23ebe90d0b13f61cd1643e5e1b0a433d5e2522ec5a028817891b6df444e983e1e0ff2356044fea67c616dce6b4bd53b17ea8bc51ef816ab8f2d9e
|
||||
Add the star to the sky...
|
||||
Set the star for mrwhite: 218b783ec5676cbddd378ceb724820444599f22cdcfda0a5a195b3a8fbf4ab5c915703420ad3b84531c54b838b23858fb84fcaf04d4932d4b9ef861c7ae9b635c9d3f56dfb100aa47297afcd94df41efa9f5ecba6483c5328e43ec457027ee4efcecefa094a83945106d7da1878c1f47516c2f2578170eeb36955d8bd16e0d106f9e2effe9debff41e551db4ac2e87bc8a9378d8eadb042bee18f4ad72ab721833a27154a7318b8cbe6f98fb3c82da32d1688fdcdb718fb15d9d5e6276b037cef62d953c09b23ebe90d0b13f61cd1643e5e1b0a433d5e2522ec5a028817891b6df444e983e1e0ff2356044fea67c616dce6b4bd53b17ea8bc51ef816ab8f2d9e
|
||||
```
|
||||
|
||||
Sending it to Makawee:
|
||||
```sh
|
||||
$ nc wildwildweb.fluxfingers.net 1434
|
||||
Hi, I'm, Makawee, and you are? Too bright here...
|
||||
mrwhite
|
||||
mrwhite ... do I know you?
|
||||
Disguise does not help
|
||||
```
|
||||
|
||||
Oh no, the plan did not work! We can't send **exactly** Wahkoowah's key! We need to be even more tricky...
|
||||
|
||||
|
||||
## Crafting a Solution
|
||||
|
||||
### Master in Disguising
|
||||
|
||||
Every key in this problem is given by *mudulus p*. This means that we have infinite values that map to the same original key. My first attempt was to multiply the original key by p, so that, when it receives the modulo operation, it circles once more returning to the original value.
|
||||
|
||||
It didn't work. The reason is that p is too large. When multiplied by the key (that is large itself) we loose precision and we don't go back to the original value. We need to keep the values in the same scale!
|
||||
|
||||
Let's take a look again at the way the messages are generated:
|
||||
|
||||

|
||||
|
||||
We notice that the public key is exponentiated by r_m. It means that, if r_m is an even number, two values of the public key are mapped to the same value of the final message: +pubk and -pubk.
|
||||
|
||||
That's all we need! We are going to disguise Makawee by creating a *star* with the negative value of Wahkoowah's key.
|
||||
|
||||
|
||||
### Automatizing the Process and getting the Flag!
|
||||
|
||||
|
||||
All right, now we know how to make Wahkoowah and Makawee talk and how to get *t_m* and *t_w*. We are ready to generate the token that will lead us to the flag.
|
||||
|
||||
Notice again that since these messages are generated with random numbers, they will differ each time. However, we know from above that they carry unique information that leads to a common key (and the flag). I wrote the following script to automatize the process:
|
||||
|
||||
```python
|
||||
import socket
|
||||
|
||||
PORTm = 1434
|
||||
PORTw = 1433
|
||||
HOST = 'wildwildweb.fluxfingers.net'
|
||||
|
||||
def peace_pipe():
|
||||
|
||||
""" Get the magic message from some user to calculate rm """
|
||||
# create sockets
|
||||
sm = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sw = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# connect to w
|
||||
sw.connect((HOST, PORTw))
|
||||
sw.recv(4096)
|
||||
sw.send(b'makawee')
|
||||
sw.recv(4096)
|
||||
sec = sw.recv(4096)
|
||||
tw = sec.split("did this:")[1].split("\n")[1].strip()
|
||||
print "\nMagic from w to m: " + tw
|
||||
|
||||
# connect to m
|
||||
sm.connect((HOST, PORTm))
|
||||
sm.recv(4096)
|
||||
sm.send(b'mrblack')
|
||||
sm.recv(4096)
|
||||
sec = sm.recv(4096)
|
||||
tm = sec.split("did this:")[1].split("\n")[1].strip()
|
||||
print "\nMagic from m to w: " + tm
|
||||
|
||||
# send w's magic to m's
|
||||
sm.send(tw)
|
||||
print sm.recv(4096)
|
||||
|
||||
# send m's magic to get the token
|
||||
sw.send(tm)
|
||||
token = sw.recv(4096)
|
||||
token = token.split('\n')[1].strip()
|
||||
print "Token is: " + token
|
||||
|
||||
# finally, send token back to m
|
||||
sm.send(token)
|
||||
print sm.recv(4096)
|
||||
|
||||
sm.close()
|
||||
sw.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
peace_pipe()
|
||||
```
|
||||
|
||||
|
||||
Running it leads us to the flag:
|
||||
```sh
|
||||
python 300_peace_pipe.py
|
||||
|
||||
Magic from w to m: 2f2f5d280871947836e9b5665986c1b75e732d88ae3d464b65d24ea7e41c33c491060379ac4f3dc4a7231f43d6a11b5bfd3a780d8ac46bd1a4cfd99ac041434cb82c5941f17e68a4f180101ece166a1b4da6ea32d62455bd7472892ed9b67fe2122e0b331048e4a11d98422f04ec3063a3652a0e1a90e13a740905bb3a22c9b5e39d1e0fa97f10bff34d76243b9211afd1131b0f6e33d4d99c8069c462677ce67401214c943fee13252060aa02b8b1525ed0af8c9aa5ad5dee64dbb0c275dd6147754c7dfaf3218caf35d7837925215a04bb315e91441306ef0d29f0da733b7e4ac92b500dc522de11c5f5af58248ed5f762b854f40f0adf4b681a937d17a1c0
|
||||
|
||||
Magic from m to w: e9eedf64931d5f77f5d061a0f411f9d385144f33fe1419905fdb24a0537cc205a7f99e083f37f98af8553795f1a71f83b7924620790845c3a48bb71a9b70a0f9e5ab95dda40ec4e229bc6a6cd146779de74b7237e42d01e2538c093407165afc79776bbd9bcdefa1d9af27a39f17610b4b9060c2b0ca5203457061facdc68257433253366937cef469261492ac81c177f42f10beea386ddfa09069a5fa2ae2e39a41eeecebdba622b79231cd5f206d0a70c71aa3eb5f706a16c99173f79f97e7f3408b544df556e3779f6d49441c04d33438b9604392f90bca6c2a8c3181b12ec5d492ef2184b9db69fdd1b6247150e3b55f8ee65d113c5350b4b097abadddc9
|
||||
Bit more truth is missing
|
||||
|
||||
Token is: 5QAWhcwSaQicM8LitDGz6To69sBtsO8ASL27zxql8hW8aziveW0B0epJz2PKIFo/K4A=
|
||||
I knew you are able to see IT. Lets get drunk, I tell you where
|
||||
flag{FreeBoozeForEverone-Party!}
|
||||
```
|
|
@ -0,0 +1,337 @@
|
|||
#!/usr/bin/env python
|
||||
import random
|
||||
import gmpy
|
||||
import os
|
||||
import asyncio
|
||||
import base64
|
||||
import shutil
|
||||
import traceback
|
||||
import tempfile
|
||||
import argparse
|
||||
import aiopg
|
||||
import logging
|
||||
import collections
|
||||
import pyasn1_modules.rfc3447
|
||||
import pyasn1.codec.ber.encoder
|
||||
import concurrent.futures
|
||||
|
||||
|
||||
TARGET_USERNAME = "deputies"
|
||||
DB = {'database': 'wiener', 'user': TARGET_USERNAME}
|
||||
ADMIN_NAME = "sheriff"
|
||||
AUTH_KEYS_PATH = os.path.join(
|
||||
"/", "home", TARGET_USERNAME, ".ssh", "authorized_keys")
|
||||
EXECUTOR = concurrent.futures.ProcessPoolExecutor()
|
||||
|
||||
auth_keys_lock = asyncio.Lock()
|
||||
logging.getLogger('asyncio').setLevel(logging.ERROR)
|
||||
prng = random.SystemRandom()
|
||||
messages = {
|
||||
'welcome': ("Well if it isn't another one of those shave tails again. "
|
||||
"Don't you dare think this is gonna be an easy job here, we "
|
||||
"take no coffee boilers just so ya' know. If you're sure you "
|
||||
"want to join our deputy ranks, just apply here. If you got "
|
||||
"any problems just lemme know by typing 'h'."),
|
||||
'access': ('Oh and you can find all lockers by going '
|
||||
'to the "Secure Sheriff\'s Huddle" with the number 1427 on the '
|
||||
'door and tell the doorman you are a "deputies". Of course you '
|
||||
'are not a "sheriff"!'),
|
||||
'new_private_key': ("Here is your locker code. Don't you dare loose it or "
|
||||
"I'm gonna knock you galley west!"),
|
||||
'new_public_key': "And that's your lock.",
|
||||
'key_validity': ("Your locker code is only valid for 30 minutes. If you "
|
||||
"don't need it, I'm gonna trash your lock, boy!"),
|
||||
'new_username': ("I'm gonna call you %s from now on. I don't wanna hear "
|
||||
"any bellyaching about it!"),
|
||||
'get_public_get': "Here this is %s's lock. Why do you need it, hmm?",
|
||||
'get_pubkey_query': "So whose lock do you wanna see? ",
|
||||
'invalid_user': "Don't know him, get lost!",
|
||||
'cmd_unknwown': "What? No way you get THAT!",
|
||||
'unkown_error': "We seem to be having some problems here. We'll fix it.",
|
||||
'help_prefix': "These are the questions you can ask me:",
|
||||
}
|
||||
|
||||
|
||||
def get_message(key, *params, with_newline=True):
|
||||
nl = '\n' if with_newline else ''
|
||||
return ((messages[key] % params) + nl).encode('utf-8')
|
||||
|
||||
#
|
||||
# Actions
|
||||
#
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def do_help(reader, writer, conn):
|
||||
"""
|
||||
Ask the sheriff for help. He might not react too friendly...
|
||||
"""
|
||||
writer.write(get_message('help_prefix'))
|
||||
out = []
|
||||
for key, handler in actions.items():
|
||||
out.append("%s: %s" % (key, handler.__doc__.strip()))
|
||||
writer.writelines([(s + '\n').encode("utf-8") for s in out])
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def do_register(reader, writer, conn):
|
||||
"""
|
||||
Get a lock and a locker code. This way you can also unlock the shared locker for everyone!
|
||||
"""
|
||||
uname, id_ = yield from new_username(conn)
|
||||
priv_key, pub_key = yield from get_keypair(uname)
|
||||
with (yield from auth_keys_lock):
|
||||
with open(AUTH_KEYS_PATH, "ab") as f:
|
||||
f.seek(0, 2)
|
||||
f.write(pub_key + b'\n')
|
||||
writer.write(get_message('new_private_key'))
|
||||
writer.write(priv_key)
|
||||
writer.write(get_message('new_public_key'))
|
||||
writer.write(pub_key + b"\n")
|
||||
writer.write(get_message('new_username', uname.lower()))
|
||||
writer.write(get_message('key_validity'))
|
||||
writer.write(get_message('access'))
|
||||
cur = yield from conn.cursor()
|
||||
try:
|
||||
yield from cur.execute(
|
||||
'UPDATE users SET public_key=%s, username=%s WHERE id=%s',
|
||||
(pub_key.decode("utf-8"), uname, id_))
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def do_list(reader, writer, conn):
|
||||
"""
|
||||
Get a list of all guys that have locks.
|
||||
"""
|
||||
cur = yield from conn.cursor()
|
||||
out = []
|
||||
try:
|
||||
yield from cur.execute('SELECT username FROM users')
|
||||
for username, in (yield from cur.fetchall()):
|
||||
out.append(("- %s\n" % username).encode("utf-8"))
|
||||
finally:
|
||||
cur.close()
|
||||
writer.write(b"".join(out))
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def do_get_pubkey(reader, writer, conn):
|
||||
"""
|
||||
Get the lock for a particular guy. Why would you need that?
|
||||
"""
|
||||
writer.write(get_message('get_pubkey_query', with_newline=False))
|
||||
uname = (yield from reader.readline()).decode("utf-8").strip().lower()
|
||||
cur = yield from conn.cursor()
|
||||
try:
|
||||
yield from cur.execute(
|
||||
'SELECT public_key FROM users WHERE username=%s',
|
||||
(uname,))
|
||||
result = yield from cur.fetchone()
|
||||
finally:
|
||||
cur.close()
|
||||
if result:
|
||||
pub_key, = result
|
||||
writer.write(get_message('get_public_get', uname))
|
||||
writer.write((pub_key + '\n').encode("utf-8"))
|
||||
else:
|
||||
writer.write(get_message('invalid_user'))
|
||||
|
||||
|
||||
actions = collections.OrderedDict()
|
||||
actions['h'] = do_help
|
||||
actions['r'] = do_register
|
||||
actions['l'] = do_list
|
||||
actions['p'] = do_get_pubkey
|
||||
|
||||
#
|
||||
# Helpers
|
||||
#
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def new_username(conn):
|
||||
cur = yield from conn.cursor()
|
||||
try:
|
||||
yield from cur.execute(
|
||||
'INSERT INTO users DEFAULT VALUES RETURNING id')
|
||||
new_id = yield from cur.fetchone()
|
||||
new_user = 'user%d' % new_id
|
||||
finally:
|
||||
cur.close()
|
||||
return new_user, new_id
|
||||
|
||||
|
||||
def asn1_encode_priv_key(N, e, d, p, q):
|
||||
key = pyasn1_modules.rfc3447.RSAPrivateKey()
|
||||
dp = d % (p - 1)
|
||||
dq = d % (q - 1)
|
||||
qInv = gmpy.invert(q, p)
|
||||
assert (qInv * q) % p == 1
|
||||
key.setComponentByName('version', 0)
|
||||
key.setComponentByName('modulus', N)
|
||||
key.setComponentByName('publicExponent', e)
|
||||
key.setComponentByName('privateExponent', d)
|
||||
key.setComponentByName('prime1', p)
|
||||
key.setComponentByName('prime2', q)
|
||||
key.setComponentByName('exponent1', dp)
|
||||
key.setComponentByName('exponent2', dq)
|
||||
key.setComponentByName('coefficient', qInv)
|
||||
ber_key = pyasn1.codec.ber.encoder.encode(key)
|
||||
pem_key = base64.b64encode(ber_key).decode("ascii")
|
||||
out = ['-----BEGIN RSA PRIVATE KEY-----']
|
||||
out += [pem_key[i:i + 64] for i in range(0, len(pem_key), 64)]
|
||||
out.append('-----END RSA PRIVATE KEY-----\n')
|
||||
out = "\n".join(out)
|
||||
return out.encode("ascii")
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def ssh_encode_pub_key(keypath, uname):
|
||||
p = yield from asyncio.create_subprocess_exec(
|
||||
"ssh-keygen", "-y", "-f", keypath, stdout=asyncio.subprocess.PIPE)
|
||||
out = yield from p.stdout.read()
|
||||
return out.strip() + b' ' + uname.encode("ascii")
|
||||
|
||||
|
||||
def get_prime(size):
|
||||
while True:
|
||||
val = prng.getrandbits(size)
|
||||
if gmpy.is_prime(val):
|
||||
return val
|
||||
|
||||
|
||||
def test_key(N, e, d):
|
||||
msg = (N - 1) >> 1
|
||||
c = pow(msg, e, N)
|
||||
if pow(c, d, N) != msg:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def create_parameters(size=2048):
|
||||
p = get_prime(size // 2)
|
||||
q = get_prime(size // 2)
|
||||
N = p * q
|
||||
phi_N = (p - 1) * (q - 1)
|
||||
while True:
|
||||
d = prng.getrandbits(size // 5)
|
||||
e = int(gmpy.invert(d, phi_N))
|
||||
if (e * d) % phi_N == 1:
|
||||
break
|
||||
|
||||
assert test_key(N, e, d)
|
||||
return N, e, d, p, q
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_keypair(uname):
|
||||
loop = asyncio.get_event_loop()
|
||||
params_coro = loop.run_in_executor(EXECUTOR, create_parameters)
|
||||
N, e, d, p, q = yield from params_coro
|
||||
priv_key = asn1_encode_priv_key(N, e, d, p, q)
|
||||
with tempfile.NamedTemporaryFile() as priv_file:
|
||||
priv_file.write(priv_key)
|
||||
priv_file.flush()
|
||||
pub_key = yield from ssh_encode_pub_key(priv_file.name, uname)
|
||||
return priv_key, pub_key
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def cleanup_old_users(conn):
|
||||
print("Cleanup task is running now.")
|
||||
while True:
|
||||
cur = yield from conn.cursor()
|
||||
yield from cur.execute("BEGIN")
|
||||
try:
|
||||
yield from cur.execute('SELECT COUNT(*) FROM users')
|
||||
old_count, = yield from cur.fetchone()
|
||||
yield from cur.execute(
|
||||
"DELETE FROM users WHERE creation_date < "
|
||||
"CURRENT_TIMESTAMP - interval '30 minutes' "
|
||||
"AND username != %s OR public_key IS NULL",
|
||||
(ADMIN_NAME,))
|
||||
yield from cur.execute('SELECT COUNT(*) FROM users')
|
||||
new_count, = yield from cur.fetchone()
|
||||
yield from cur.execute(
|
||||
'SELECT public_key FROM users WHERE username != %s',
|
||||
(ADMIN_NAME,))
|
||||
with (yield from auth_keys_lock):
|
||||
AUTH_KEYS_PATH_NEW = AUTH_KEYS_PATH + '.new'
|
||||
with open(AUTH_KEYS_PATH_NEW, "wb") as f:
|
||||
for public_key, in (yield from cur.fetchall()):
|
||||
f.write(public_key.encode("utf-8"))
|
||||
f.write(b'\n')
|
||||
shutil.move(AUTH_KEYS_PATH_NEW, AUTH_KEYS_PATH)
|
||||
print("Deleted %d old keys" % (old_count - new_count))
|
||||
except Exception:
|
||||
yield from cur.execute("ROLLBACK")
|
||||
raise
|
||||
else:
|
||||
yield from cur.execute("COMMIT")
|
||||
finally:
|
||||
cur.close()
|
||||
yield from asyncio.sleep(15 * 60)
|
||||
|
||||
|
||||
def cleanup_done(t, conn):
|
||||
POOL.release(conn)
|
||||
e = t.exception()
|
||||
if e:
|
||||
traceback.print_exception(e.__class__, e, e.__traceback__)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def handle(reader, writer):
|
||||
conn = yield from POOL.acquire()
|
||||
cur = yield from conn.cursor()
|
||||
yield from cur.execute("BEGIN")
|
||||
try:
|
||||
writer.write(get_message('welcome'))
|
||||
while True:
|
||||
writer.write(b"Command: ")
|
||||
cmd = (yield from reader.readline()).decode("utf-8").strip()
|
||||
if not cmd and reader.at_eof():
|
||||
writer.close()
|
||||
break
|
||||
try:
|
||||
yield from (actions[cmd](reader, writer, conn))
|
||||
except KeyError:
|
||||
writer.write(get_message('cmd_unknwown'))
|
||||
except Exception as e:
|
||||
writer.write(get_message('unkown_error'))
|
||||
traceback.print_exception(e.__class__, e, e.__traceback__)
|
||||
except Exception:
|
||||
yield from cur.execute("ROLLBACK")
|
||||
else:
|
||||
yield from cur.execute("COMMIT")
|
||||
finally:
|
||||
cur.close()
|
||||
POOL.release(conn)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--port', '-p', metavar='PORT', type=int,
|
||||
required=True)
|
||||
args = parser.parse_args()
|
||||
server = asyncio.start_server(handle, port=args.port)
|
||||
server_task = asyncio.Task(server)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# Initialize connection pool
|
||||
POOL = loop.run_until_complete(aiopg.create_pool(maxsize=100, **DB))
|
||||
|
||||
# Cleanup task setup
|
||||
cleanup_conn = loop.run_until_complete(POOL.acquire())
|
||||
cleaner_task = asyncio.Task(cleanup_old_users(cleanup_conn))
|
||||
cleaner_task.add_done_callback(lambda t: cleanup_done(t, cleanup_conn))
|
||||
try:
|
||||
print("Starting main loop, accepting connections now.")
|
||||
loop.run_forever()
|
||||
finally:
|
||||
EXECUTOR.shutdown()
|
||||
server.close()
|
||||
loop.run_until_complete(POOL.clear())
|
Loading…
Add table
Add a link
Reference in a new issue