Tickling a Sun Ray 2 - It Speaks!
This is the second post on a series I’m working on, regarding the black-box reverse-engineering of a Sun Microsystems Ray 2 Desktop Unit. The first post can be found here: Tickling a Sun Ray 2 - DHCP Discovery.
New Findings
In the last post, I mentioned that I had omitted a few DHCPACK and DHCPINFORM
packets, assuming they weren’t particularly important. My DHCP knowledge is a
bit rusty, and I naïvely thought: “DHCPINFORM? The client already has an IP. No big deal.”
That assumption was completely wrong.
What was actually happening is that the Ray was requesting someone to inform it about some knobs so it could finish bootstrapping and start the boot sequence! That should have been obvious, especially given that it was retrying the same packet over and over since no one was answering it, but yes, I missed that. Oops!
Now, back on track. The packet clearly indicated what it was looking for:
- Option 53 (DHCP Message Type): DHCPINFORM (
0x08) - Option 55: Parameter Request List
- Interface MTU (
0x1A) - X Window System Display Manager (
0x31) - TFTP Server Name (
0x42) - Vendor-Specific Information (
0x2B)
- Interface MTU (
The choices there are interesting indeed! MTU makes sense, but, XDM? Odd at least.
At this point, we know that:
- The Sun Ray successfully acquires an IP
- It explicitly requests vendor-specific parameters
- It stalls indefinitely without them
Looking for some information on the Web I could find some piece of documentation
that Oracle haven’t burned yet, and oh my, it’s telling. For pure archival
purposes, I’m mirroring it here: Sun Ray Client Initialization Requirements Using DHCP,
but what’s interesting there is the Alternate Vendor-Specific DHCP Options
section, listing exactly what we need. The response for the DHCPINFORM SHOULD
carry a bit of extra information in the Vendor-Specific Information TLV, but
the Ray could piggyback on “Standard” DHCP Options, if your DHCP server didn’t
support them. Clever, very Sun-ish.
What’s relevant for us is this: We could add a Vendor-Specific Information to
the DHCPACK of the DHCPINFORM packet, and the Ray would just use it. And we
also have A LOT of knobs that can be turned. Below is a quick summary:
-
AuthSrvr, Type:IP- Mandatory. Single Sun Ray server IP addresses. -
AuthPort, Type:NUMBER- Sun Ray server port. -
NewTVer, Type:ASCII- Desired firmware version. -
LogHost, Type:IP- Syslog server IP address. -
LogKern, Type:NUMBER- Log level for kernel. -
LogNet, Type:NUMBER- Log level for network. -
LogUSB, Type:NUMBER- Log level for USB. -
LogVid, Type:NUMBER- Log level for video. -
LogAppl, Type:NUMBER- Log level for firmware application. -
NewTBW, Type:NUMBER- Bandwidth cap, value is bits per second. -
FWSrvr, Type:IP- Firmware TFTP server IP address. -
NewTDispIndx, Type:NUMBER- Obsolete. Do not use. -
Intf, Type:ASCII- Sun Ray server interface name -
NewTFlags, Type:NUMBER- Obsolete. Do not use. -
AltAuth, Type:IP- List of Sun Ray server IP addresses. -
BarrierLevel, Type:NUMBER- Mandatory. Firmware Download: barrier level.
Lots of interesting stuff, but let’s begin with what can really help us: Logging. It seems the Ray is capable of emitting log data to a Syslog server, and we can set the level for several facilities, including firmware application, network, and kernel! That’s just awesome! Thanks, Sun!
Now, given it wants a Syslog server, one can assume that the log levels are also
standardised to the levels Syslog supports, and one would be absolutely right.
From RFC 3164 - The BSD syslog Protocol,
page 9 contains Table 2. syslog Message Severities, listing from 0
(Emergency) up to 7 (Debug). I think the box may use 6 (Informational) as
default, so setting it to 7 may make it be more chatty!
Encoding Vendor-Specific Information
The archived page also explains with less-than-desired details the encoding format, but it’s nothing more, nothing less than a plain old TLV structure. Adorable!
The structure is composed of repeated TLVs: one byte for the tag, one byte for
the length, followed by n bytes of value. The only important thing to keep in
mind is that any IP type is a simple representation of the IP octets, meaning
that 192.168.100.1 becomes 0xC0, 0xA8, 0x64, 0x01.
Now let’s make the Ray talk to us through syslog, and make it chatty. The DHCP Option payload for Vendor-Specific Information would then be:
- Option
0x2B:-
0x18,0x04,0xC0,0xA8,0x64,0x01. (LogHost, 4 bytes,192.168.100.1) -
0x25,0x01,0x07. (LogKern, 1 byte, value 7) -
0x26,0x01,0x07. (LogNet, 1 byte, value 7) -
0x27,0x01,0x07. (LogUSB, 1 byte, value 7) -
0x28,0x01,0x07. (LogVid, 1 byte, value 7) -
0x29,0x01,0x07. (LogAppl, 1 byte, value 7)
-
I don’t want to bring up a full syslog daemon, so listening to UDP port 514
is enough.
And surely, we got the little box to talk with us:
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Network: soff 0 n 1
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Network: GetNextAltAuth 0 0 0
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Application: Stop tokens 8009df20
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Application: sessionMonitor TcpOpen timeout to 0.0.0.0:7009
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Network: soff 0 n 1
[syslog from 192.168.100.50:514] <15> 0x0.0x48b 0:14:4f:85:f2:b Network: GetNextAltAuth 0 0 0
[syslog from 192.168.100.50:514] <15> 0x0.0x873 0:14:4f:85:f2:b Application: getNextServer: no message - timeout
Gorgeous! It can talk, and the log levels do map cleanly to syslog severities.
That <15> is a PRI value in the syslog protocol, obtained by
(facility * 8) + severity, as defined by the spec. Working backwards:
15 / 8 = 1 remainder 7
Meaning:
- Facility: 1 (aka user-level messages)
- Severity: 7 (aka Debug)
The Authentication Server
It seems to be trying to do something, but failing. That’s expected, since the
essential value is still missing from the Vendor TLV: AuthSrvr. It’s even
marked as mandatory! One funny detail about that table is that, although the
BarrierLevel is also marked as mandatory, the client continues the process
normally. More on that later.
Now let’s set the AuthSrvr value. It’s the same as LogHost, but with a
different tag:
-
0x15,0x04,0xC0,0xA8,0x64,0x01. (AuthSrv, 4 bytes,192.168.100.1)
After updating what’s mimicking the DHCP server the Ray expects and adding the potentially last flag it wants, we have new stuff on the syslog!
[syslog from 192.168.100.50:514] <15> 0x0.0x1e9 0:14:4f:85:f2:b Network: GetNextAltAuth 0 c0a86401 0
[syslog from 192.168.100.50:514] <15> 0x0.0x1e9 0:14:4f:85:f2:b Application: Stop tokens 8009df20
[syslog from 192.168.100.50:514] <15> 0x0.0x1e9 0:14:4f:85:f2:b Application: sessionMonitor TcpOpen timeout to 192.168.100.1:7009
[syslog from 192.168.100.50:514] <15> 0x0.0x1e9 0:14:4f:85:f2:b Network: GetNextAltAuth 0 c0a86401 0
[syslog from 192.168.100.50:514] <15> 0x0.0x1fd 0:14:4f:85:f2:b Network: GetNewtBandwidth: status down speed 100000000 mode full
There’s one little thing that may catch your eye: Application: sessionMonitor TcpOpen timeout to 192.168.100.1:7009.
TCPOpen? To the IP of the AuthSrvr? Port 7009? That’s oddly familiar! We
had a mysterious UDP packet going to that port in the last post!
The Mysterious serverQ
To be honest, serverQ was a dead-end. I couldn’t find any references on what
it means, how it should respond, and why that happens. But hey! Now we have the
Ray trying to open a full-fledged TCP connection with us! I mean, it’s trying,
and that’s progress!
I’m calling this out explicitly because the previous post ended on a bit of a
cliffhanger, and I hate those as much as you probably do. For now, serverQ
remains unexplained, but it no longer blocks progress.
Moving on!
A Sun Ray walks into a bar…
Since the Ray wants to speak, let it speak! Let’s spin a TCP server listening
on port 7009 and dump whatever we get there. Here’s a few bytes of output from
the hacky TCP server:
DummyAuthSrvr: 696e666f526571204d54553d31353...
For the well-trained eye, that hex looks an awful lot like an ASCII blob. And guess what? It is! Here’s the ASCII version of it:
infoReq MTU=1500 _=1 barrierLevel=310 cause=insert clientRand=nrlifRNCnn4xe.F5Be/DA7gkwFF9cOXQOmxxzu8SM3e ddcconfig=1 event=insert firstServer=c0a86401 fw=MfgPkg_4.15_2006.07.20.16.57 hw=SunRayP8 id=00144f85f20b initState=1 namespace=IEEE802 pn=53622 realIP=c0a86432 sn=00144f85f20b startRes=1400x1050:1400x1050 state=disconnected tokenSeq=1 type=pseudo
And that’s A LOT! Holy guacamole!
Let’s try to break it up:
-
infoReq: Possibly an operation identifier? -
MTU: The MTU we already provided to it. -
_: No idea. Maybe a placeholder, maybe something deprecated. Who knows! -
barrierLevel: Possibly theBarrierLevelwe didn’t provide it. Something related to firmware download, but let’s leave that for another time. -
cause: Perhaps what made the Ray request that operation? -
clientRand: A Base64! Sweet! Given the entropy, it’s really random! Perhaps for some kind of negotiation? I think we will eventually figure that out. -
ddcconfig: No idea. -
event: The same value ofcause? We will need some other packages to figure this one out as well. -
firstServer: That’s the server’s IP in hexadecimal. Nothing interesting so far. -
fw: A firmware version! Seems like this one is from 2006. Cute! -
hw: A hardware identifier! It is aSunRayP8. Duly noted. -
id: I don’t know, but this value is static across all messages it sent. (And resent.) -
initState: Not a clue! -
namespace:IEEE802is a fancy name for Ethernet. Maybe this thing (or other revisions) could support other connection methods? -
pn: This value changes in a non-monotonic fashion. Packet number? We will figure out. -
realIP: This is the Ray’s IP in hexadecimal. -
sn: Also static across all packets. Possibly a serial number. -
startRes: It’s initial resolution? -
state: Disconnected? Maybe because authentication hasn’t completed? -
tokenSeq: I’m sure we will eventually figure out as well. -
type: pseudo is an interesting choice. I’m interested, but not a clue.
Then, it immediately sends another packet:
DummyAuthSrvr: 6b656570416c697665526571205f3...
Again, more ASCII:
keepAliveReq _=1 fw=MfgPkg_4.15_2006.07.20.16.57 hw=SunRayP8 namespace=IEEE802 pn=53622 sn=00144f85f20b state=disconnected
And we have more details now!
- Seems like we have another operation:
keepAliveReq -
pnis the same across both messages. Possibly not a packet number! - Everything else stays the same.
Since we’re not answering after that point it just eventually times out and retries:
[syslog from 192.168.100.50:514] <15> 0x0.0x16ca1 0:14:4f:85:f2:b Network: TcpReceive: FIN received 805bdd78 1
[syslog from 192.168.100.50:514] <15> 0x0.0x16ca1 0:14:4f:85:f2:b Application: restartAuth
[syslog from 192.168.100.50:514] <15> 0x0.0x16ca1 0:14:4f:85:f2:b Network: TcpClose: 805bdd78
[syslog from 192.168.100.50:514] <15> 0x0.0x16ca1 0:14:4f:85:f2:b Application: TcpClose1 failed
[syslog from 192.168.100.50:514] <14> 0x0.0x16ca1 0:14:4f:85:f2:b Network: FreeTcb:Freeing Tcb before the connection is closed
It’s not rebooting, though. Only restarting the authentication phase. It may be
worth noticing that whenever it retries, even without rebooting both clientRand
and pn changes, but everything else stays the same. There’s one small detail,
though! Here’s a new detail in syslog:
[syslog from 192.168.100.50:514] <13> 0x0.0x544 0:14:4f:85:f2:b USB: rdd: unable to contact device manager on 192.168.100.1:7011 retrying
It’s ALSO trying to establish a connection to port 7011! Looking at my packet
dumps, it tried to open a TCP connection to that port. Let’s also see what
happens if we let it! It seems to be something called a Device Manager, so
let’s do the same we did with the AuthSrvr:
DummyDeviceManager: 636f6e6e6563740a
[syslog from 192.168.100.50:514] <15> 0x0.0x1f270 0:14:4f:85:f2:b Network: TcpClose: 8059c538
[syslog from 192.168.100.50:514] <15> 0x0.0x1f270 0:14:4f:85:f2:b Network: TcpReceive: FIN received 8059c538 8
[syslog from 192.168.100.50:514] <14> 0x0.0x1f2d8 0:14:4f:85:f2:b Network: TCBDaemon: packet receive error
[syslog from 192.168.100.50:514] <14> 0x0.0x1f33c 0:14:4f:85:f2:b Network: TCBDaemon: packet receive error
[syslog from 192.168.100.50:514] <14> 0x0.0x1f3a0 0:14:4f:85:f2:b Network: TCBDaemon: packet receive error
...
Yeah, it’s not happy. Again, an ASCII payload. This time it only says connect,
followed by a newline.
Eventually it will close the connection and send another connect, while the
AuthSrvr continues receiving keepAliveReq.
For now, that’s it. I’ll eventually rewrite the C stuff in Golang just for the
sake of it, and keep it updated in a Git repository. This way we can isolate the
AuthSrvr, Device Manager, and the hacky DHCP server in a single place.
I said I hate cliffhangers, and I meant it, but unfortunately the Sun Ray just gave me a really good one.
In the next post, we’ll respond to its requests and see what happens when we get it wrong.