diff options
| -rw-r--r-- | ychat/src/chat/user.cpp | 15 | ||||
| -rw-r--r-- | ychat/src/reqp.cpp | 10 | ||||
| -rw-r--r-- | ychat/src/sock/sock.cpp | 10 | ||||
| -rw-r--r-- | ychat/src/tool/tool.cpp | 31 | ||||
| -rw-r--r-- | ychat/src/tool/tool.h | 1 |
5 files changed, 67 insertions, 0 deletions
diff --git a/ychat/src/chat/user.cpp b/ychat/src/chat/user.cpp index 1308cb0..5a35371 100644 --- a/ychat/src/chat/user.cpp +++ b/ychat/src/chat/user.cpp @@ -391,6 +391,21 @@ user::command( string &s_command ) string s_mod( wrap::CONF->get_elem("httpd.modules.commandsdir") + "yc_" ); string s_command2 = s_command.substr(0, pos2-1); + + // Security: s_command2 is attacker-controlled (the first token of a chat + // message starting with '/') and is concatenated into the command-module + // .so path then dlopen()'d. Reject non-alphanumeric names so ".."/"/" can't + // traverse out of the commands dir and load an arbitrary shared object. + if ( ! tool::is_alpha_numeric(s_command2) ) + { + wrap::system_message("Chat: blocked command-name traversal: " + s_command2); + string s_msg = "<font color=\"" + wrap::CONF->get_elem("chat.html.errorcolor") + "\"" + + wrap::CONF->get_elem( "chat.msgs.err.findingcommand" ) + + "</font>\n"; + msg_post( &s_msg ); + return; + } + s_mod.append( s_command2 ).append( ".so" ); dynmod *mod = wrap::MODL->get_module( s_mod, get_name() ); diff --git a/ychat/src/reqp.cpp b/ychat/src/reqp.cpp index b68bed7..907161d 100644 --- a/ychat/src/reqp.cpp +++ b/ychat/src/reqp.cpp @@ -171,6 +171,16 @@ reqp::parse(context *p_context) void reqp::run_html_mod( string s_event, map<string,string> &map_params, user* p_user ) { + // Security: s_event is attacker-controlled (a query param) and is + // concatenated into the html-module .so path then dlopen()'d. Reject + // non-alphanumeric names so ".."/"/" can't traverse out of the modules dir + // and load an arbitrary shared object (RCE). + if ( ! tool::is_alpha_numeric(s_event) ) + { + wrap::system_message("Reqp: blocked module-name traversal: " + s_event); + return; + } + container *c = new container; c->elem[0] = (void*) wrap::WRAP; diff --git a/ychat/src/sock/sock.cpp b/ychat/src/sock/sock.cpp index 327b5ef..5fbcc93 100644 --- a/ychat/src/sock/sock.cpp +++ b/ychat/src/sock/sock.cpp @@ -474,6 +474,16 @@ sock::handle_client_read(int i_fd, short event, void *p_arg) if (s_request.empty()) s_request = wrap::CONF->get_elem("httpd.startsite"); + // Path-traversal guard (security): reject any request whose decoded path + // contains a "."/".." component — it would escape httpd.templatedir when + // html.cpp opens (templatedir + request). The raw-URL "/.." strip above + // is insufficient because it runs before url_decode (attacker uses %2e%2f). + if ( tool::path_has_traversal(s_request) ) + { + wrap::system_message("Sock: blocked path traversal: " + s_request); + s_request = wrap::CONF->get_elem("httpd.html.notfound"); + } + map_params["request"] = s_request; { diff --git a/ychat/src/tool/tool.cpp b/ychat/src/tool/tool.cpp index 6ef0217..fc9bf5c 100644 --- a/ychat/src/tool/tool.cpp +++ b/ychat/src/tool/tool.cpp @@ -286,6 +286,37 @@ tool::url_decode( string s_url ) return s_dest; } +// Returns true if the URL-decoded request path contains a "." or ".." +// path component (i.e. would escape the template directory when +// concatenated to httpd.templatedir and opened). The check must run on the +// DECODED path: the earlier raw-URL "/.." strip in sock.cpp misses +// %-encoded dots (e.g. %2e%2f) which decode to ".." afterwards. +bool +tool::path_has_traversal( const string &s_request ) +{ + // Reject embedded NULs outright: url_decode turns %00 into '\0', and a + // std::string can hold it while ifstream/c_str() truncate at it — a + // divergence that is never legitimate in a request path. + if ( s_request.find('\0') != string::npos ) + return true; + + size_t i_start = 0; + size_t i_len = s_request.size(); + + for ( size_t i = 0; i <= i_len; ++i ) + { + if ( i == i_len || s_request[i] == '/' ) + { + string s_seg = s_request.substr( i_start, i - i_start ); + if ( s_seg == ".." || s_seg == "." ) + return true; + i_start = i + 1; + } + } + + return false; +} + int tool::htoi(string &s_str) { diff --git a/ychat/src/tool/tool.h b/ychat/src/tool/tool.h index c1ad616..a9fbb80 100644 --- a/ychat/src/tool/tool.h +++ b/ychat/src/tool/tool.h @@ -51,6 +51,7 @@ public: static string shell_command( string s_command, method m_method ); static string ychat_version(); static string url_decode(string s_url); + static bool path_has_traversal(const string &s_request); static int htoi(string &s_str); }; |
