OneOS protocols#

OneOS is an operating system built by Oliver Cooper (oeed) around their programs, which are mostly built around the Bedrock GUI framework.

The protocols are not security-aware, for the most part. Apart from the major security issues described below, message validation is poorly handled and very sensitive to any packet error. They should only be used for compatibility with existing OneOS / Bedrock ecosystems.

Message formats#

The modem usage on OneOS is mainly thought for wireless modems, as the module implementing functions to handle this format is called Wireless and some mechanisms are an attempt at distinguishing between packets sent on a given channel.

All messages in the protocols used by OneOS are tables that are serialized using textutils.serialize (which means modem messages are actually strings with OneOS), containing the following members:

  • content: the serialized content using textutils.serialize.

  • senderID: the computer numerical identifier, obtained through os.getComputerID.

  • senderName: the computer label, obtained through os.getComputerLabel.

  • channel: the channel on which the message is present.

  • replyChannel: an indication to the received about which channel to reply on; should be equal to the “reply channel” sent along the line.

  • messageID: the message numerical identifier, either picked at pseudorandom between 0 and 10000 or chosen by the protocol implementation.

  • destinationID: the destination computer identifier, which is based on its identifier returned by os.getComputerID once again. This member is nil when broadcasting.

The message identifier is used for ignoring a sent message when receiving, and the destinationID serves for ignoring messages for other computers on the given channel.

Notice that reply channels are always the request channel plus one. This is actually what the Wireless code does in OneOS if you don’t explicitely define a reply channel (see SendMessage: reply = reply or channel + 1).

Using the OneOS Wireless module, more specifically the synchronous function to receive messages RecieveMessage (the typo on “receive” is everywhere in OneOS…), the default timeout when unspecified is 1 second.

OneOS Ping Protocol#

This protocol uses two modem channels:

  • 4201 (a.k.a Ping) for sending ping “requests”.

  • 4202 (a.k.a PingReply) for sending ping replies.

These two channels are used for exchanging OneOS messages (see Message formats).

Ping “requests” are supposed to have the string “Ping!” set as the content, and ping replies are supposed to have the string “Pong!” set as the content. Any ping requests containing something else than “Ping!” shall not be answered.

OneOS Turtle Protocol#

The protocol uses two modem channels:

  • 4202 (a.k.a TurtleRemote) for sending turtle requests.

  • 4203 (a.k.a TurtleRemoteReply) for replying to turtle requests.

These channels are used for exchanging OneOS messages (see Message formats), in a request/response manner: requests are emitted on the TurtleRemote channel, and replies are emitted on the TurtleRemoteReply.

If the sent message on the TurtleRemote is the “Ping!” string, then the turtle responds with the “Pong!” string on the TurtleRemoteReply channel. Otherwise, requests’ and replies’ contents are supposed to be tables, where requests always define the action key as a string to determine the action type.

The device requesting a turtle to execute an action waits for a timeframe depending on which command it sent and the accompanying data.

Known requests are the following:

"move"

The turtle is asked to move a certain distance in a certain direction, using functions such as turtle.forward.

The request also defines the following fields:

  • direction: the direction in which to move, amongst the following: "forward", "back", "up", "down".

  • distance: the distance in blocks for the turtle to move.

The device requesting the application waits for 1 + distance / 2 seconds.

The turtle answers with the following fields:

  • success: whether the turtle has succeeded to move as much as required (true) or not (false); in case the turtle has moved for less than the requested distance, false will be returned.

  • distance: the traveled distance, in blocks.

  • reason: the reason, as a string (if success is false), or nil.

"turn"

The turtle is asked to turn in a certain direction, using functions such as turtle.turnLeft.

The request also defines the following fields:

  • left: a boolean indicating if the turn should be towards the left of the turtle (true) or to the right of the turtle (right).

  • turns: the number of turns to make.

The device requesting the application waits for 1 + turns / 2 seconds.

The turtle answers with the following fields:

  • success: whether the turtle has succeeded to turn as much as required (true) or not (false); in case the turtle has turned for less than the requested number of turns, false will be returned.

  • turns: the number of turns performed.

"place"

The turtle is asked to place a block in a given direction, using functions such as turtle.place.

The request also defines the following fields:

  • slot: the slot in which to take the item to place.

  • direction: the direction in which to place, which can be up for placing up, down for placing down, or any other value for placing in the direction the turtle is currently facing.

The device requesting the action waits for 1.4 seconds.

The turtle answers with the following fields:

  • success: whether the block has been placed or not.

"dig"

The turtle is asked to dig in a certain direction, using functions such as turtle.digUp.

The request also defines the following field:

  • direction: the direction in which to dig, which can be up for digging up, down for digging down, or any other value for digging in the direction the turtle is currently facing.

The device requesting the application waits for 1.4 seconds.

The turtle answers with the following fields:

  • success: whether a block was broken (true) or not (false).

"attack"

The turtle is asked to attack, using a function such as turtle.attack.

The request also defines the following fields:

  • direction: the direction in which to attack, which can be up for attacking up, down for attacking down, or any other value for attacking in the direction the turtle is currently facing.

  • times: the number of times to attack, without delay, as a number.

If the number of times to attack is equal to zero, then the turtle is supposed to attack once, and check if an entity is present. If that’s the case, then it sleeps for .4 seconds and attacks an other time. Success is determined by whether an entity was attacked the first time.

Otherwise, if the number of times to attack is more than zero, then the turtle attacks the given number of times; the success is determined by whether an entity was attacked on the last attack.

The device requesting the application waits for 9 seconds.

The turtle answers with the following fields:

  • success: whether success has been achieved (true), or not (false); see the definition of it depending on the given number of times above.

"detect"

The turtle is asked to detect whether a solid block is in front of it or not, using a function such as turtle.detect.

The request also defines the following field:

  • direction: the direction in which to detect, which can be up for detecting up, down for detecting down, or any other value for detecting in the direction the turtle is currently facing.

The device requesting the application waits for 1.05 seconds.

The turtle answers with the following fields:

  • success: whether a solid block is in front of the turtle in the given direction (true) or not (false).

"drop"

The turtle is asked to drop an item in the given direction, using a function such as turtle.drop.

The request also defines the following fields:

  • slot: the slot in which to take the item to drop.

  • amount: the amount of items to drop from the selected stack.

  • direction: the direction in which to drop, which can be up for dropping up, down for dropping down, or any other value for dropping in the direction the turtle is currently facing.

The device requesting the action waits for 1.05 seconds.

The turtle answers with the following fields:

  • success: whether at least one item has been dropped (true) or not (false).

"suck"

The turtle is asked to suck an item from the given direction, using a function such as turtle.suck.

The request also defines the following fields:

  • slot: the slot in which to suck the item to.

  • direction: the direction from which to suck, which can be up for sucking from top, down for sucking from bottom, or any other value for sucking from the direction the turtle is currently facing.

The device requesting the action waits for 1.05 seconds.

The turtle answers with the following fields:

  • success: whether at least one item has been sucked (true) or not (false).

"compare"

The turtle is asked to compare an item in a given slot with a block in front of the turtle in a given direction, using a function such as turtle.compare.

The request also defines the following fields:

  • slot: the slot in which to find the item to compare the block to.

  • direction: the direction in which to look for the block to compare the item to, which can be up for looking up, down for looking down, or any other value for looking in the direction the turtle is currently facing.

The device requesting the application waits for 1.05 seconds.

The turtle answers with the following fields:

  • success: whether the item in the slot and the block looked at in the given direction are the same (true) or not (false).

"compareTo"

The turtle is asked to compare an item in a given slot with an item in another slot, using a function such as turtle.compareTo.

The request also defines the following fields:

  • slot: the slot in which to find the first item.

  • otherSlot: the slot in which to find the second item.

The device requesting the application waits for 1.05 seconds.

The turtle answers with the following fields:

  • success: whether the item in the two slots in the given direction are the same (true) or not (false).

"getState"

The turtle is asked to return its state, as a string. By default, this string is "Idle", but it can be get and set over the protocol and used by a local application.

The device requesting the application waits for 1.05 seconds.

The turtle answers with the following fields:

  • value: the current state, as a string.

"setState"

The turtle is asked to set its state to the given value, as a string. By default, this string is "Idle", but it can be set over the protocol and used by a local application.

The request also defines the following fields:

  • value: the new state of the turtle, as a string.

The device requesting the application waits for 1.05 seconds.

The turtle answers with the following field:

  • success: whether the state has been set successfully (true) or not (false).

"fuelLevel"

The turtle is asked to return its current fuel level, using a function such as turtle.getFuelLevel.

The device requesting the application waits for 1 second.

The turtle answers with the following field:

  • value: the current fuel level of the turtle.

"refuel"

The turtle is asked to refuel, using a function such as turtle.refuel.

The request also defines the following field:

  • amount: the amount of items to take (if nil, the default value is 64).

The turtle then attempts to find a slot in its inventory with at least one item with fuel, and consumes at most the given amount.

The device requesting the application waits for 1.8 seconds (1 + .05 * 16).

The turtle answers with the following fields:

  • success: whether at least one fuel item has been consumed to refuel the turtle (true) or not (false).

  • slot: the slot number at which the fuel item has been found and removed, nil if no fuel item was consumed.

"itemCount"

The turtle is asked to return its item count, using a function such as turtle.getItemCount.

The request also defines the following field:

  • slot: the number of the slot to check.

The device requesting the application waits for 1 second.

The turtle answers with the following field:

  • value: the value?

"itemSpace"

The turtle is asked to return its item space, using a function such as turtle.getItemSpace.

The request also defines the following field:

  • slot: the number of the slot to check.

The device requesting the application waits for 1 second.

The turtle answers with the following fields:

  • value: the space left in the given slot, as a number.

Note that in the OneOS source, the protocol is only implemented in a Turtle API and not used by a system application; I could not find any application online using this API…

OneOS Transmit Protocol#

This protocol is used by the Transmit application in OneOS. The idea behind this application is to send files (“sending mode”), or wait for other devices to send files to the current device (“receiving mode”).

The protocol uses the following channels:

  • 4204 (a.k.a TransmitDiscovery) for probing devices in receiving mode.

  • 4205 (a.k.a TransmitDiscoveryReply) for sending replies to the probes described above.

  • 4206 (a.k.a TransmitRequest) for asking a device to send a file to it.

  • 4207 (a.k.a TransmitRequestReply) for replying to the request described above by a yes or a no.

  • 4208 (a.k.a TransmitSent) for sending the file data in one go.

These five channels are used for exchanging OneOS messages (see Message formats).

Discovery is used by devices in sending mode for the device selection menu, when the user wants to select to which devices it will try to transfer. Such probes are done every second; it must use the “Discovery” string as the message content on the TransmitDiscovery channel, while requesting an answer to the TransmitDiscoveryReply channel. To these probes, the devices in receiving modes must answer with a TransmitDiscoveryReply with a table as its payload, containing the following members:

Once the user has chosen the file and the device to which to send it to, the device sends a message to the receiving device on the TransmitRequest channel, as a table with the following members:

  • senderName: the name of the device sending the transmit request, as obtained through os.getComputerLabel.

  • fileName: the name of the file to be sent.

Then the receiving device can display a pop-up to the user to choose if the file is accepted or not. When the user has replied, the receiving device answers the request on the TransmitRequestReply channel as requested, with a table with the following members as content:

  • accept: a boolean indicating if yes or no the file has been accepted by the user.

If the file has been accepted, the sending device then sends a message on the TransmitSend channel targetted at the same receiving device, with a table with the following members as the content:

  • data: the data of the file, as the string.

  • fileName: the name of the file to be sent, should be the same as the one advertised in the message on TransmitRequest earlier.

And the process ends here (there is no acknowledgment from the receiving device, it is supposed that the file arrived successfully).

This utility has major security issues, however they are not particularly inherent to the protocol and they can be mitigated in any reimplementation. The issues are the following:

  • The fileName field in the message on the TransmitSend request can be different from the one advertised in the message on TransmitRequest; and for saving the file, the utility uses the one in the former. This can be very misleading, if not a security issue. This can be mitigated by checking, at the TransmitSend message reception if the file has already been approved.

  • The TransmitRequest/TransmitRequestReply step is actually completely optional, as the utility will gladly save any file you send to it. The mitigation quoted before should do the trick, as it requires the sender to get its request approved first.

  • In order to save the file, the final path is obtained by appending the given file name to “/Desktop/Documents/” without sanitizing the input. This allows for path traversal attacks by setting the filename in the TransmitSend step to “../../startup” for example. Combined with the previous security vulnerability, this gives you a wormable attack that can affect any OneOS device as soon as it is in receiving mode. This vulnerability can be corrected through file name sanitization at both the TransmitRequest and TransmitSend steps.

The only limit that the mitigations brings is, you can only receiving one file at a time with a given file name from a given sending device (because any transmit request doesn’t have an ID that’s repercuted in the message on the TransmitSend message). That’s a totally acceptable limit for most usages.

Ultimate Door Lock Protocol#

Ultimate Door Lock, also known as Door Lock on OneOS, is a program for locking doors using dedicated Pocket Computers (referred to as PDA in the code), which will (only) serve as “keys”, like NFC-enabled devices in the real world.

The application runs on both the computer dedicated to the door lock (and interacting with it through redstone), and the pocket computer, which must be dedicated to being the key as the door lock application is run at startup and must be running for the process to work.

The concept of the application is:

  • You register PDAs by putting them in a disk drive connected to the computer managing the door lock; a fingerprint will automatically be generated on the Pocket Computer, and this fingerprint will be added to a whitelist.

  • The door lock pings on a dedicated channel every .5 seconds.

  • If a PDA receives the ping on the given channel, it sends its fingerprint to a second channel dedicated to requests.

  • The door lock receives the request. If the received fingerprint is included in the whitelist and the request comes from within the distance in the settings (between 5 and 15 blocks), then the door opens.

  • If the door is open and no request has been successful in the last .6 seconds, the door closes.

The application is obviously security sensitive, and takes that fact into account as in case of error, the door stays locked. However, there are major security issues to this system:

  • The PDA registration is enabled at all time; this means you can put a disk drive next to the computer from the outside, and have your PDA registered; a mitigation would be to only allow registration at certain times.

  • The secret between the PDA and the door lock, called “fingerprint”, can be extracted from any PDA in wireless range by emitting a ping and gathering fingerprints from nearby devices, and try them out in surrounding doors; this is actually a real world issue as well, with attackers abusing credit card NFC in public spaces. A mitigation could be not to reveal the fingerprint but answer to unique challenges with hashes and salts.

  • The random finger print is 256 bytes between 32 and 126, which if the generator was truly random should be \(95 ^ {256}\) possibilities. However, the current program uses math.random() without setting the seed using math.randomseed(). This means if you know in which ten minutes the door is likely to have been generated, and there is only one PDA registered, you only have to test \(6e{10}\) possibilities, which is significantly less. However the previous method of sniffing fingerprints stays significantly quicker and easier. See Randomness and entropy for more information.

  • There is no timeout for a given computer if bruteforcing is attempted.

This is why this protocol is not recommended for new programs. However, this description is here to understand existing communications between Ultimate Door Lock enabled devices, and possibly attack those installations in PvP environment.

The protocol uses three modem channels:

  • 4210 (a.k.a UltimateDoorlockPing) for regular pings from the door lock.

  • 4211 (a.k.a UltimateDoorlockRequest) for door lock requests from the PDA.

  • 4212 (a.k.a UltimateDoorlockRequestReply) for door lock replies.

These three channels are used for exchanging OneOS messages (see Message formats).

On the doorlock ping channel, the content is the “Ping!” string. On the doorlock request channel, the content is the fingerprint as a string, and on the doorlock reply channel, the content is a boolean which equals either true if the door has been unlocked (whitelisted fingerprint and pocket computer within range), false otherwise.