Ostiary is distributed under the GPL (GNU Public License).
By far the most common attacks in the wild are 'buffer overflow' exploits, where an application is tricked into reading more data than it has room for, and ends up overwriting important data, like its own code. This can be suprisingly difficult to protect against if you have to deal with variable kinds of data.
But Ostiary only reads one type of data - an SHA256 hash, 32 bytes. That's it. Take a look at 'Recv_With_Timeout()' in 'ost_main.c'. That's where Ostiary reads data from the client (or attacker). It refuses to read more than 32 bytes. There is no way for an attacker to inject more than 32 bytes, no matter what they do, short of actual occult forces.
Note further that Ostiary doesn't do anything with those 32 bytes but compare them to other hashes (also of fixed size). It never executes them. Either the data the attacker sent matches a valid hash, or it doesn't. There are no "SQL Injection" or "parameter tampering" attacks possible.
Moreover, Ostiary never allocates anything dynamically. All data is stored in static, fixed-size buffers. No attacker can use Ostiary to fill up the RAM and crash the box or anything like that. Now, the commands that Ostiary runs (in response to user requests) may open up holes (like starting up ssh when there's a known ssh exploit) but Ostiary itself is effectively unhackable.
Note that if someone gets a valid password (see below), even if they can't log in, they might be able to keep firing off scripts, eventually consuming too many resources and bringing the machine down. It's important to write your scripts with this in mind.
In addition, the client doesn't get any acknowledgement that a command was accepted or not. Presumably valid users can verify this for themselves. An attacker, even if they somehow guess the right passphrase to enable ssh instead of killing it, then has to port scan to find the port that sshd is on. It makes the job of attacking that much harder.
To keep things as simple as possible, the protocol does not permit you to pass arbitrary data to the command. However, the IP address of the client is passed to the command as an argument, so the command can take conditional action based on this information.
Yes, but Ostiary only stores up to 128 bad addresses. Moreover, it uses a 'clock cache' algorithm to hopefully store the most useful ones. Now, an attacker with 129 addresses available to him can defeat the cache, but that's not necessarily a big problem.
First off, brute force hash guessing isn't terribly useful (see below). Second, there's a configuration option where Ostiary can kill itself if the IP cache fills up. You may not be able to use Ostiary then, but more importantly the attacker can't, either. Remember, "First, do no harm."
Note, on today's Internet with portscans happening all the time, it could easily happen that 128 different attackers will probe the port you've chosen for Ostiary in a relatively short amount of time. (I don't recommend putting ostiaryd on any common port like 80 or 22...) If you really care, you can edit "ost.h", increase the number of bad ips it stores, and recompile.
Because blocking known bad IP's is a firewall's job. The cache is there just to help provide some defense against new attackers. If you have known attackers, block them at the firewall. That will keep them from attacking ostiaryd and other ports on your system.
Optionally, you can compile ostiaryd with tcp wrappers support, and control access with the /etc/hosts.allow and hosts.deny files.
Of course not, but all of the probable attacks are DOS (Denial Of Service) attacks rather than actual security breaches. Here are some ways to deny legitimate users the ability to use Ostiary:
A DDOS, which floods a host with enourmous amounts of bogus traffic, is just about impossible for a mere program to deal with. Basically, if an attacker stuffs your pipe full of garbage, you're screwed. Nothing I can do about it. You'll need to talk to your ISP or reconfigure your routers.
This takes a lot less resources, and is fairly easy to implement. An attacker can just keep trying to connect to the ostiaryd port, and since ostiaryd sleeps five seconds between trying connections, they can tie up the port pretty well, making it unlikely a legit user could get in.
If I added a 'time last connected' field to the cache, and checked more often for connections, but blocked ones connecting 'too fast', it might help. But an attacker could just keep upping the ante, eventually devolving to case #1 above. I figure it's best to keep the CPU load as low as possible.
Another possibility is adding threads and/or forking processes to handle multiple simultaneous requests. For my own purposes, this is overkill, and gets in the way of making Ostiary as simple as possible. However, a high-traffic, heavily-attacked site might consider coding this in.
Hard to do since SYN cookies came around. The attacker would have send a spoofed SYN packet claiming to be from the IP they wanted to lock out. Then, they'd have to guess the SYN cookie number for the SYN/ACK packet. Doable, particularly on some operating system versions, but not easy. And it's not strictly an Ostiary vulnerability, it's inherent in the TCP protocol.
If an attacker is completely in control of communications (they own the routers you're talking through) they can block your commands, but without the passwords, they can't do any more than lock out IP addresses.
Now, that's not to say that it's impossible for an attacker to subvert Ostiary. But it's plenty hard, and you can make it almost arbitrarily hard. To be able to use Ostiary for their nefarious goals, attackers would have to do one of the following:
This may or may not be possible. If someone manages to make pigs out of sausage, so to speak, Ostiary is screwed. They could reverse the hash and deduce your passwords, once they'd captured a packet with a seed and the corresponding packet you had HMAC'd with your password. On the bright side, if someone manages to do this, they'll have lots of far more attractive targets than poor little Ostiary.
Even if someone finds an easy way to generate collisions in SHA256, that doesn't invalidate its use in HMAC, at least as we are using it here. Assuming the attacker doesn't know the secret password, they don't know what the IV (initialization vector) of the hash is, so the known forms of collision attack won't work against this method. Indeed, the way HMAC works, there are two different IVs and this is exponentially harder.
Brute-force guessing of the hash is, um, extremely unlikely to be useful to an attacker. Assuming you have 8 commands configured, the odds of any one hash being correct are about in 2^248. Assuming one guess per second, on average an attacker will need to guess continuously for over 90 trillion trillion trillion years. (Presumably you will check your logs before the sun burns out and notice that something's amiss.) Even then, the attacker won't know what command will be run. It might be the 'kill' command, at which point they don't get to guess anymore. Even a replay attack (see the section on /dev/urandom) has somewhat better odds.
This can be a serious weakness. If you choose easily-guessed passwords, an attacker may be able to guess them (duh). If they sniff the seed sent by the server, and your reply, they can try, brute-force style, to figure out if any of the words in their dictionary produce that hash. This is obviously much quicker than trying guesses directly against the Ostiary server. On a PII/2GHz, I hit around 630,000 guesses/second in a quickly-hacked test program. (See "find-secret.c" in the 'tests' directory.) Note that this problem scales pretty much linearly with the number of CPUs you put on it, so a Beowulf cluster could churn through guesses very quickly.
If you're clever about password choice (pick a long phrase, maybe misspell a few words and throw in some punctuation, use words in multiple languages, etc.) a brute-force attack could still take a long time.
Of course, if someone comes up with a workable quantum computer, such searches could be many orders of magnitude faster. (Theoretically O(1).) And if the NSA wants to break into your computer, they probably will; they can throw ridiculous amounts of resources at such problems. But if you need to protect yourself from the NSA, you're probably not going to consider Ostiary anyway.
Moral: Choose good passwords. Don't use words from the dictionary, your mother's maiden name, etc. Change your passphrases frequently.
If an attacker discovers your passwords (by whatever means; beating them out of you with a rubber hose, tricking them out of you, watching over your shoulder, stealing your Palm Pilot where you've written them down, etc.) you're, well, hosed. Best thing to do is send the kill signal if you realize your passwords are compromized.
Moral: Keep your passwords secure. Try not to write them down, etc. If you must write them down, keep them in an encrypted 'wallet'. (E.g., "Strip" or "Cryptopad" on the Palm.)
There are a few ways. There's the good old 'file open race condition' problem, where someone can create a symbolic link from some file a command writes to, to, say, /etc/passwd, and fill it with garbage. I haven't audited the config-file-parsing routines as religiously for buffer overflows. If someone has local access, they could possibly replace the command scripts that ostiaryd runs with trojans. I'm sure there are other methods.
But I'm not too worried about this; for one thing, your filesystem permissions should be set up to block this kind of nastiness. If a malicious user has a local account, there are plenty of ways besides ostiaryd for them to work michief. Ostiary is designed to keep bad guys out, not to fight them once they are already inside. For that you want tripwire, etc.
Hooray for you. You did send them... but ostiaryd didn't read them. Because we're dealing with extremely small amounts of data (32 bytes is smaller than an IP header plus TCP header, even compressed) the OS is usually happy to accept more than that and hold it in a buffer for the application to read. How much you can send depends on the OS, but no matter how much gets cached, ostiaryd only reads the first 32 bytes of it at most.
HMAC is designed for pretty much what I'm using it for - authenticating that a particular message came from a valid source. The cryptography behind it is well-respected, it's easy to implement, and uses only slightly more resources than the old algorithm.
For details about how HMAC works, check out The HMAC Papers.
MD5 has been baked into the Palm ROMs since OS 2.0, but there isn't a built-in SHA implementation. I didn't want to bloat the program without need, and I definitely wanted a Palm client. This drove the use of HMAC-MD5 in earlier versions of Ostiary.
Recent results indicate that there may well be a practical algorithm for generating arbitrary collisions with MD5. It looks like SHA1, too, is starting to show its age. There does not seem to be a good way to exploit collision attacks against HMAC (see here for the technical reasons), but since there are no (widely known) similar attacks against SHA256, it seemed prudent to move to a different hashing function. The Palm client's only a few KB bigger now. If that's a problem, like I said, version 2.31 or so is probably secure.
If it really bugs you, I tried to localize all the hashing to two files: ost_hash.h and ost_hash.c. It should be fairly trivial to set it up to use a different hash function. Have fun, but note that standard clients won't work with your new server...
Yes, in some configurations. The script that I use to enable ssh only enables it for the IP address that sent the command. If an attacker gets between me and my Ostiary, intercepts my communication, and presents my command hash as if it were their own, they would unlock ssh for them, while being able to block me from connecting to it.
Of course, then they have to find a way to get through ssh, either with a stolen password or a bug in ssh. And I will probably notice that even though I sent the command, I can't seem to connect via ssh, unless they get really clever and try a man-in-the- middle against ssh...
At this point we're back to depending on ssh to protect us, which is no worse than the case before Ostiary, and at least makes an attacker jump through more hoops.
Another possibility is to have the script just enable ssh for everybody when the command comes in (for a few minutes, anyway). This way, the attacker doesn't gain any special advantage by being in the middle, but for the window it's open, ssh can be attacked by any and all comers. There are pros and cons to both approaches. For my purposes, the odds of a 'MITM' attack are low enough that I prefer to shield ssh from the general 'net.
Both devices try to return cryptographically secure, unpredictable random values. The difference is that /dev/random won't return more data than it thinks it can safely generate, while /dev/urandom will return as much randomness as is currently avalable, then supplement it with output from a pseudo-random number generator.
I chose to use /dev/urandom because /dev/random could block. It's possible to handle blocking, but what would I do in that case? Refuse to generate a salt hash, and leave the client high and dry? The only reasonable choice would be to fall back on a PRNG, which is what urandom does anyway, and probably better than I could do myself.
The security of the algorithm doesn't depend critically on the salt being unpredictable anyway. The salt is there to prevent replay attacks, so as long as it doesn't repeat it's serving its purpose.
There is a slim chance that the same hash value will repeat at some point. That is to say, there is a chance that:
hash(time(x)+rand(x)) = hash(time(y)+rand(y))
for two times x and y. An attacker would have to sniff out both a salt value and the resulting hash the client sent, and then be able to predict, down to the second, when that salt value would repeat. For both /dev/random and /dev/urandom, this is highly unlikely. But what if ostiaryd is running on a platform where these are not available, and it's fallen back to plain old random()?
In this case, it's theoretically possible for an attacker to figure out the state that random() is in, and predict what the next values will be out of it. (They'd have to hit the Ostiary port, record the hash, and figure out what the time on the server was, and what output of random() was at that time. Then they'd have to try some guesses as to how it was seeded, but they could check their guesses with a few more hits on the port.)
Given the ability to predict what the output of random() will be, a theoretical replay attack becomes possible, though far from practical. If an attacker recorded all the traffic, matching salts to command hashes, they could try to figure when a salt hash might repeat again (by running through future times and random() outputs, seeing if any of them hash to the same value as one of the salts they've captured).
On average, if you've got 2^N possible values, and the chances of any of them appearing are truly equal, you'll have to wait 2^(N/2) tries before you see a repeat. For SHA-1, that means collecting 2^80 hashes before you'd expect to get a repeat. At one hash per second, on average you're going to have to wait 38 million billion years. And if the key changes during that time, you have to start all over. Maybe an attacker would get really lucky, but I'm not too worried.
(Note, it's not proven to my knowledge that all possible hash values are equally probable; that may be an unwarranted assumption. However, no one's been able to show such a problem with either MD5 or SHA256.)
Of course, I could just use the time value by itself, unhashed. That's guaranteed not to repeat until at least 2038 or so. I figure the marginal theoretical benefit of making the salt difficult to predict is worth the very slight risk of a salt repeating. If you disagree, feel free to comment out the relevant code in Make_Seed_Hash() in ost_main.c.
One important thing to note about random number seeds. There's an option for ostiaryd ("-s") that allows you to supply your own seed in the case that /dev/urandom is unavailable or ignored. If you use this option, you had better not use the same seed every time. You should find a way to provide a new seed every time ostiaryd is started. Use the current time, or the bash shell's $RANDOM variable, or some other source that'll produce a new number each time. Otherwise, you run the significant risk of having repeated challenges being sent out to clients, which is bad, because an attacker can just replay the corresponding response they captured before.
In terms of overall resource usage, it's quite nice. It's not designed to run commands any faster than once a second, but it can do that with minimal CPU or network usage.
However, when it's checking to see if a hash matches any of the commands it's set up with, it does a simple linear search. (You can't use a hash table because the hash changes each time.) The more commands you set up, the more searching it will have to do on each command. For the default limit (eight commands) this is negligible, but if you want to bastardize things and try to do "TCP over Ostiary" or something, be aware that behavior is likely to be rather suboptimal.
I suppose I could set it up to use some kind of 'MRU' search, but the best strategy really depends on what kind of usage patterns there would be. For a large number of commands, you might be able to set up a "command cache" using the same code as the current "bad IP cache", but I have no plans to do so. If anyone does, let me know!
This is stuff I might fix someday but just don't have time for now:
su - -c "/etc/init.d/sshd start"