Robot Not a Dev? Pre-order Now
Not a Dev?

URL injection for fun and profit


#1

URL injection for fun and profit

Summary

My goal is to allow someone to access a Misty robot remotely from the Misty API Explorer, and to provide some authorization. Currently the Misty API Explorer is served over HTTP, so the authorization presented here is weak but sufficient until HTTPS is finally available for the Misty API Explorer.

URL injection

Open a terminal, and start netcat to listen for TCP connections on some available port number above 1024; e.g.,

nc -l 127.0.0.1 8080

Depending on your OS, you might need to try ncat or netcat instead of nc as above.

Now open your Web browser, and go to http://api-explorer.mistyrobotics.com/. In the “Robot IP Address”, enter

127.0.0.1:8080/

Then, in the terminal in which you started netcat, there will be an HTTP request like

GET /:80/api/info/device HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://api-explorer.mistyrobotics.com/
Origin: http://api-explorer.mistyrobotics.com
DNT: 1
Connection: keep-alive

Notice the path: /:80/api/info/device. The JavaScript code of the Misty API Explorer takes the given “address” and appends :80 to it, which is supposed to cause 80 to be the destination port for the HTTP GET request. However, we subvert it by adding :8080/, which causes 8080 to be the destination port and moves :80 to be part of the GET path.

This observation can be generalized to any port number and any path prefix.

Authorization via hard-to-guess tokens

Now that we know how to inject a prefix into the AJAX request by the Misty API Explorer, we can use it to send a token to a reverse proxy server. This token allows the reverse proxy to decide whether or not to forward the incoming connection to the (remote) Misty robot. If the token is hard to guess, then it provides weak authorization. (It is weak because it is susceptible to interception.)

I provide a small proof-of-concept here using nginx. Consider the following nginx configuration file (also available at GitHub)

# Copyright (C) 2018 rerobots, Inc.
# by SCL
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# generated random SHA256 hash, acts as auth token:
# e.g., create one in Python
#
#     import hashlib
#     import os
#     print(hashlib.sha256(os.urandom(128)).hexdigest())
#
# an example output from which is
#
#     545ad0dc06640ff88c1fc7311d53c964185798f179d48072d2a39999cca175ab

worker_processes 4;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream app_server {
    server 127.0.0.1:8000 fail_timeout=0;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location /545ad0dc06640ff88c1fc7311d53c964185798f179d48072d2a39999cca175ab:80 {
	rewrite /545ad0dc06640ff88c1fc7311d53c964185798f179d48072d2a39999cca175ab:80(.*)$ $1 break;

	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	proxy_set_header Host $http_host;
	proxy_redirect off;
	proxy_buffering off;
	proxy_pass http://app_server;

	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection $connection_upgrade;
    }

    location / {
	return 404;
    }

}
}

The basic idea is to check for a SHA256 hash at the start of the request path. If it is found, then strip it and :80, and forward the incoming connection to port 8000 at the host address 127.0.0.1. If no match is found, the server returns 404 (Not Found). In the comments near the top of the file, Python code is given for generating cryptographically strong random hashes.

Assuming that you run nginx from a public server, the IP address of your Misty robot will not be directly accessible to the nginx process. Thus, we use 127.0.0.1 in the configuration file to facilitate ssh tunneling from 8000 on the public server to port 80 of your Misty robot. This can be done with a command like

ssh -T -N -R 127.0.0.1:8000:<misty-robot-address>:80 you@<public-ip-address>

where <misty-robot-address> is the local IP address of your Misty robot, <public-ip-address> is the server IP address, and you is the username on the server host. Change these values according to your environment.

With the above nginx and ssh processes running, go to the Misty API Explorer from anywhere on the Internet, and in the “Robot IP Address” enter a string like

<public-ip-address>/545ad0dc06640ff88c1fc7311d53c964185798f179d48072d2a39999cca175ab

where the first part is the server IP address, and the second is the SHA256 hash in your nginx configuration. The Misty API Explorer will be fully operational, including HTTP requests and WebSockets.

In my implementation of this proof-of-concept, I used my Misty I Developers’ Edition robot and changed the face expression and the front LED color, and I streamed measurements from the front panel of time-of-flight sensors.


#2

@Ben What is the plan for serving the API Explorer over HTTPS?


#3

It is more complicated than it sounds. I am trying to dig around for why exactly but I believe it is because we would have to have all connections secured - including those on the robot. I don’t think this is insurmountable but is a ways off.


#4

Indeed, the difficulty arises because of the need to serve HTTPS from the robot; otherwise, the browser prevents the connection because it is so-called mixed content (cf. Mixed content | MDN).

In a place outside this forum post, I am happy to discuss ideas for realizing HTTPS sooner rather than later. I understand that it might not be the highest priority for the Misty company.


#5

This is exactly correct @slivingston. Let’s plan on chatting soon on what your needs might be, I’m sure we can figured something out together!


#6

I implemented this for a general case, shown in the video at demo: proxy to Misty API Explorer on Vimeo and the tutorial at documentation - rerobots


#7

Proxy to a Misty I robot through HTTPS is now available. E.g., this makes it possible to hack on the official sample code from within CodePen

Read more at documentation - rerobots