file: escape strings in HTML output
authorJo-Philipp Wich <jo@mein.io>
Wed, 4 Apr 2018 14:58:11 +0000 (16:58 +0200)
committerJo-Philipp Wich <jo@mein.io>
Wed, 4 Apr 2018 15:17:43 +0000 (17:17 +0200)
Escape untrusted input like the request URL or filesystem paths in HTML
outputs such as the directory listing or 404 error messages.

This fixes certain XSS vulnerabilities which can be leveraged to further
exploit the system.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
file.c
utils.c

diff --git a/file.c b/file.c
index b3702c8779759778e5fd3cd8c6b9eb98260dbafa..ae9119ea3e28f316d3630cc126793e841d7f4829 100644 (file)
--- a/file.c
+++ b/file.c
@@ -476,6 +476,7 @@ static void list_entries(struct client *cl, struct dirent **files, int count,
        const char *type = "directory";
        unsigned int mode = S_IXOTH;
        struct stat s;
+       char *escaped;
        char *file;
        char buf[128];
        int i;
@@ -501,16 +502,22 @@ static void list_entries(struct client *cl, struct dirent **files, int count,
                if (!(s.st_mode & mode))
                        goto next;
 
+               escaped = uh_htmlescape(name);
+
+               if (!escaped)
+                       goto next;
+
                uh_chunk_printf(cl,
                                "<li><strong><a href='%s%s%s'>%s</a>%s"
                                "</strong><br /><small>modified: %s"
                                "<br />%s - %.02f kbyte<br />"
                                "<br /></small></li>",
-                               path, name, suffix,
-                               name, suffix,
+                               path, escaped, suffix,
+                               escaped, suffix,
                                uh_file_unix2date(s.st_mtime, buf, sizeof(buf)),
                                type, s.st_size / 1024.0);
 
+               free(escaped);
                *file = 0;
 next:
                free(files[i]);
@@ -520,21 +527,29 @@ next:
 static void uh_file_dirlist(struct client *cl, struct path_info *pi)
 {
        struct dirent **files = NULL;
+       char *escaped_path = uh_htmlescape(pi->name);
        int count = 0;
 
+       if (!escaped_path)
+       {
+               uh_client_error(cl, 500, "Internal Server Error", "Out of memory");
+               return;
+       }
+
        uh_file_response_200(cl, NULL);
        ustream_printf(cl->us, "Content-Type: text/html\r\n\r\n");
 
        uh_chunk_printf(cl,
                "<html><head><title>Index of %s</title></head>"
                "<body><h1>Index of %s</h1><hr /><ol>",
-               pi->name, pi->name);
+               escaped_path, escaped_path);
 
        count = scandir(pi->phys, &files, NULL, dirent_cmp);
        if (count > 0) {
                strcpy(uh_buf, pi->phys);
-               list_entries(cl, files, count, pi->name, uh_buf);
+               list_entries(cl, files, count, escaped_path, uh_buf);
        }
+       free(escaped_path);
        free(files);
 
        uh_chunk_printf(cl, "</ol><hr /></body></html>");
@@ -613,7 +628,7 @@ static void uh_file_request(struct client *cl, const char *url,
 {
        int fd;
        struct http_request *req = &cl->request;
-       char *error_handler;
+       char *error_handler, *escaped_url;
 
        if (!(pi->stat.st_mode & S_IROTH))
                goto error;
@@ -649,9 +664,14 @@ error:
                        return;
        }
 
+       escaped_url = uh_htmlescape(url);
+
        uh_client_error(cl, 403, "Forbidden",
                        "You don't have permission to access %s on this server.",
-                       url);
+                       escaped_url ? escaped_url : "the url");
+
+       if (escaped_url)
+               free(escaped_url);
 }
 
 void uh_dispatch_add(struct dispatch_handler *d)
@@ -872,7 +892,7 @@ void uh_handle_request(struct client *cl)
        struct http_request *req = &cl->request;
        struct dispatch_handler *d;
        char *url = blobmsg_data(blob_data(cl->hdr.head));
-       char *error_handler;
+       char *error_handler, *escaped_url;
 
        blob_buf_init(&cl->hdr_response, 0);
        url = uh_handle_alias(url);
@@ -906,5 +926,11 @@ void uh_handle_request(struct client *cl)
                        return;
        }
 
-       uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.", url);
+       escaped_url = uh_htmlescape(url);
+
+       uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.",
+                       escaped_url ? escaped_url : "");
+
+       if (escaped_url)
+               free(escaped_url);
 }
diff --git a/utils.c b/utils.c
index 1c61c41fa2ceb9e36845570787787c9c1376e32e..d990d7dfd2bee38ac13db87ddffb452a14836e0f 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -269,11 +269,11 @@ static bool is_html_special_char(char c)
 
 char *uh_htmlescape(const char *str)
 {
-       size_t len;
+       size_t i, len;
        char *p, *copy;
 
-       for (p = str, len = 1; *p; p++)
-               if (is_html_special_char(*p))
+       for (i = 0, len = 1; str[i]; i++)
+               if (is_html_special_char(str[i]))
                        len += 6; /* &#x??; */
                else
                        len++;
@@ -283,11 +283,11 @@ char *uh_htmlescape(const char *str)
        if (!copy)
                return NULL;
 
-       for (p = copy; *str; str++)
-               if (is_html_special_char(*str))
-                       p += sprintf(p, "&#x%02x;", (unsigned int)*str);
+       for (i = 0, p = copy; str[i]; i++)
+               if (is_html_special_char(str[i]))
+                       p += sprintf(p, "&#x%02x;", (unsigned int)str[i]);
                else
-                       *p++ = *str;
+                       *p++ = str[i];
 
        return copy;
 }