You're in the room with a TV, an IOT Hub, and a smart wall clock. However, the TV's remote is not in your hand but you find the TV to be noisy, and want to turn it off.
You found out that the IOT Hub is connected to the guest WiFi and its API endpoint is exposed. Also, you found a simple client for the wall clock through the API.
Find a way to turn the TV off for the flag.
Another Epic Guessing Challenge.
We can access to an endpoint
/SendIRCommand of an IOT hub. and their IR protocol supports several commands:
CMD_PING = 0x01 CMD_GETTEMP = 0x10 CMD_GETHUMIDITY = 0x11 CMD_GETCO2 = 0x12 CMD_GETSMOKEDETECTOR = 0x13 CMD_SETTIME = 0x20 CMD_SYSVER = 0x30 CMD_REPLY = 0x80
We only have the client to communicate with the server. And we know nothing about the server.
Also, we know nothing about the TV, our target.
- Guess 🤔
- Guess again 🤔
- Guess once again 🤔
- Send a NEC IR message using that IOT hub.
Feel free to skip to the last section if you're not interested in how I came up with the guess.
Guessing about the server
Without any knowledge of the server, I start from poking the endpoint, trying to figure out what the device is.
Similar to services in other challenges, it is hosted on GCP (europe-west1). And the service is hidden behind Google Front End (GFE). I didn't find anything else, it seems to be a fake IOT hub.
Guessing about the service
We can send a IR packet in hex format to the endpoint, and it will give us the result it received from the smart clock.
According to the client, the IR packet looks like this:
u8 magic byte (0x55) u8 length of the packet including header u8 command u8 x n arguments u8 CRC8-CCITT
I tested all 256 commands, but I didn't found anything interesting other than those listed in the client.
CMD_PING replies our argument xor
0x12 + i, and commands larger than 0x80 simply replies what we send. Other commands reply their predefined strings.
I also put a packet inside a packet or inside the reply, hoping it trigger something. But nothing happened. Also the packet will be invalid after padding.
Great, I'm out of ideas.
Guessing about the client
In the client, there are five comments. two of them are interesting:
def crc8(data): # Expect a list of integer # LFSR calculation of CRC8-CCITT crc = 0 " ... " # Send the request packet to the Hub to be transmitted to the smart clock # through 940nm IR at 1786bps req = requests.post('https://%s/%s'%(host, HUB_ENDPOINT), data=packet_hex)
CRC8-CCITT is used in 3DS' IR protocol, but it seems to be unrelated. 940nm is typical wavelength of IR light, nothing interesting.
How about 1786bps? I couldn't find any protocol works in 1786bps. It's a pretty strange bitrate. It's too high for a control protocol, which is typically around 4 ~ 120 bps, and it's too low for a transmission protocol, which is typically around 10kbps ~ 1Gbps.
To test its behavior, I spawn a GCP instance at europe-west1, and send a 255 bytes ping packet to it. It should takes at least
255 * 8 * 2 / 1786 = 2.3 seconds to transmit.
However, the server replies in 0.3 second.
Ahhh, it's fake, well play. But why they write a fake comment?
Guessing about the protocol
When searching about IR protocols online, I notice that they mention about the length of burst.
For example, Philips RC-5 Protocol use a 889us burst following a 889us pause to represent bit 0.
I start wondering what is the length of the protocol in this task:
1bit / 1786bps = 560us
Googling with the keyword
560us again, and I found something promising: The NEC protocol.
Its bit symbol looks like this:
The protocol looks like:
- A 9ms leading burst
- A 4.5ms pause
- The address
- The inverted address
- The message
- The inverted message
- A 560us burst at the end
I trying to believe that our IOT hub sends raw IR burst so that we can forge a NEC packet.
Send a NEC packet
First, here's a function to build the bit symbols:
def buildNECSig(cmd): ret =  for s in range(8): if (cmd >> s) & 1: ret +=  +  * round(2250 / 560 - 1) else: ret +=  +  return ret
Next, I build the packet according to the protocol
def buildNEC(addr, cmd): sig =  sig +=  * round(9000 / 560) # leading burst sig +=  * round(4500 / 560) # leading pause sig += buildNECSig(addr) sig += buildNECSig(addr ^ 0xff) sig += buildNECSig(cmd) sig += buildNECSig(cmd ^ 0xff) sig +=  # Final burst assert len(sig) == 121 sz = (len(sig) + 7) // 8 # Find the correct bit order # sig = int(''.join(map(str, sig)), 2).to_bytes(sz, 'big') # sig = int(''.join(map(str, sig)), 2).to_bytes(sz, 'little') # sig = int(''.join(map(str, sig[::-1])), 2).to_bytes(sz, 'big') sig = int(''.join(map(str, sig[::-1])), 2).to_bytes(sz, 'little') return sig
I don't know how our IOT hub convert bytes to bits, so I test all four possible ones.
To get the flag, just send a NEC packet, the value of address and command doesn't matter.