From 7d2b945790b5033d50af20c1c0e5610efa044f81 Mon Sep 17 00:00:00 2001 From: noraj Date: Wed, 18 Oct 2023 00:35:10 +0200 Subject: [PATCH] add urlencode_component & urldecode_component fix #145 --- Gemfile | 1 + Gemfile.lock | 2 ++ bin/ctf-party | 10 +++--- docs/CHANGELOG.md | 3 ++ lib/ctf_party/cgi.rb | 68 +++++++++++++++++++++++++++++------------ test/test_string_cgi.rb | 42 +++++++++++++++++++------ 6 files changed, 92 insertions(+), 34 deletions(-) diff --git a/Gemfile b/Gemfile index e8fe0ac..854739b 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ group :runtime, :cli do end group :runtime, :all do + gem 'cgi', '~> 0.3.3' # url decode / html escape gem 'uri', '~> 0.12.2' # for argument parsing end diff --git a/Gemfile.lock b/Gemfile.lock index e4e40ad..c54b1f3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,6 +10,7 @@ GEM specs: ast (2.4.2) base64 (0.1.1) + cgi (0.3.6) commonmarker (0.23.10) docopt (0.6.1) json (2.6.3) @@ -50,6 +51,7 @@ PLATFORMS DEPENDENCIES bundler (~> 2.1) + cgi (~> 0.3.3) commonmarker (~> 0.23) ctf-party! docopt (~> 0.6) diff --git a/bin/ctf-party b/bin/ctf-party index 10d2890..c929bb1 100755 --- a/bin/ctf-party +++ b/bin/ctf-party @@ -58,10 +58,12 @@ cmd_whitelist = { to_hex: 'Encode a string into hexadecimal', to_hexip: 'Encode a dotted decimal IPv4 into a hexadecimal one', to_hexipv4: 'Encode a dotted decimal IPv4 into a hexadecimal one', - urldecode: 'URL-decode the string', - urldecode_component: 'URL-decode the URL component string', - urlencode: 'URL-encode the string', - urlencode_component: 'URL-encode the URL component string', + urldecode: 'URL-decode the string (RFC 2396)', + urldecode_component: 'URL-decode the URL component string (RFC 3986)', + urldecode_data: 'URL-decode the form data (application/x-www-form-urlencoded) string', + urlencode: 'URL-encode the string (RFC 2396)', + urlencode_component: 'URL-encode the URL component string (RFC 3986)', + urlencode_data: 'URL-encode form data (application/x-www-form-urlencoded) string', # native string commands bytesize: 'https://rubyapi.org/3.2/o/string#method-i-bytesize', capitalize: 'https://rubyapi.org/3.2/o/string#method-i-capitalize', diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 71bc2d4..64bc9eb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,12 +5,15 @@ - **Breaking changes** - **Drop Ruby 2.7 support** because there is no `URI:WS` and `URI:WSS` used for `defang_uri` and anyway [official support has ended too](https://www.ruby-lang.org/en/downloads/branches/) - `hex2bin` now returns even number of chars (pad with `0`) by default and add an option to disable it. + - `urlencode_component` & `urldecode_component` were renamed to `urlencode_data` & `urldecode_data`, `urlencode_component` & `urldecode_component` are now new functions - Changes: - Renamed `from_hexip` to `from_hexipv4` and created an aliases `from_hexip` for `from_hexipv4`. Same for `to_hexipv4` and bang methods. - add options support for all decimal methods and aliases - New methods: - `from_hexipv6` and `from_hexipv6!` - `bin2dec` and `dec2bin` + - `urlencode_component` (see **breaking change**, the old `urlencode_component` was renamed `urlencode_data`) + - `urldecode_component` (see **breaking change**, the old `urldecode_component` was renamed `urldecode_data`) - Fix: - bin2hex: fix odd byte cropping issue diff --git a/lib/ctf_party/cgi.rb b/lib/ctf_party/cgi.rb index 2b66688..ae7964d 100644 --- a/lib/ctf_party/cgi.rb +++ b/lib/ctf_party/cgi.rb @@ -5,11 +5,11 @@ require 'uri' class String - # URL-encode the URL string (RFC2396) + # URL-encode the URL string (RFC 2396) # @return [String] the URL-encoded string # @example # 'http://vulnerable.site/search.aspx?txt=">'.urlencode # => "http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E" - # "'Stop!' said Fred" # => "'Stop!'%20said%20Fred" + # "'Stop!' said Fred".urlencode # => "'Stop!'%20said%20Fred" def urlencode URI::Parser.new.escape self end @@ -19,21 +19,21 @@ def urlencode! replace(urlencode) end - # URL-encode the URL component string - # @return [String] the URL-encoded string + # URL-encode form data (`application/x-www-form-urlencoded`) string + # @return [String] the URL-encoded data # @example - # "'Stop!' said Fred".urlencode_component # => "%27Stop%21%27+said+Fred" - # 'http://vulnerable.site/search.aspx?txt=">'.urlencode_component # => "http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E" - def urlencode_component + # "'Stop!' said Fred".urlencode_data # => "%27Stop%21%27+said+Fred" + # 'http://vulnerable.site/search.aspx?txt=">'.urlencode_data # => "http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E" + def urlencode_data CGI.escape self end - # URL-encode the string in place as described for {String#urlencode_component}. - def urlencode_component! - replace(urlencode_component) + # URL-encode the data in place as described for {String#urlencode_data}. + def urlencode_data! + replace(urlencode_data) end - # URL-decode the URL string (RFC2396) + # URL-decode the URL string (RFC 2396) # @return [String] the URL-decoded string # @example # 'http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E'.urldecode # => "http://vulnerable.site/search.aspx?txt=\">" @@ -49,20 +49,20 @@ def urldecode! replace(urldecode) end - # URL-decode the URL component string + # URL-decode the form data (`application/x-www-form-urlencoded`) string # @return [String] the URL-decoded string # @example - # 'http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E'.urldecode_component # => "http://vulnerable.site/search.aspx?txt=\">" - # 'http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E'.urldecode_component # => "http://vulnerable.site/search.aspx?txt=\">" - # "'Stop!'%20said%20Fred".urldecode_component => "'Stop!' said Fred" - # '%27Stop%21%27+said+Fred'.urldecode_component # => "'Stop!' said Fred" - def urldecode_component + # 'http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E'.urldecode_data # => "http://vulnerable.site/search.aspx?txt=\">" + # 'http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E'.urldecode_data # => "http://vulnerable.site/search.aspx?txt=\">" + # "'Stop!'%20said%20Fred".urldecode_data => "'Stop!' said Fred" + # '%27Stop%21%27+said+Fred'.urldecode_data # => "'Stop!' said Fred" + def urldecode_data CGI.unescape self end - # URL-decode the string in place as described for {String#urldecode_component}. - def urldecode_component! - replace(urldecode_component) + # URL-decode the string in place as described for {String#urldecode_data}. + def urldecode_data! + replace(urldecode_data) end # HTML escape the string @@ -90,4 +90,32 @@ def htmlunescape def htmlunescape! replace(htmlunescape) end + + # URL-encode the URL component string (RFC 3986) + # @return [String] URL-encoded component string + # @example + # 'http://vulnerable.site/search.aspx?txt=">'.urlencode_component # => "http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E" + # "'Stop!' said Fred".urlencode_component # => "%27Stop%21%27%20said%20Fred" + def urlencode_component + CGI.escapeURIComponent self + end + + # URL-encode the URL component string (RFC 3986) as described for {String#urlencode_component}. + def urlencode_component! + replace(urlencode_component) + end + + # URL-decode the URL component string (RFC 3986) + # @return [String] URL-decoded component string + # @example + # 'http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E'.urldecode_component # => "http://vulnerable.site/search.aspx?txt=\">" + # '%27Stop%21%27%20said%20Fred'.urldecode_component # => "'Stop!' said Fred" + def urldecode_component + CGI.unescapeURIComponent self + end + + # URL-decode the URL component string (RFC 3986) as described for {String#urldecode_component}. + def urldecode_component! + replace(urldecode_component) + end end diff --git a/test/test_string_cgi.rb b/test/test_string_cgi.rb index cd78169..05f01bb 100644 --- a/test/test_string_cgi.rb +++ b/test/test_string_cgi.rb @@ -16,14 +16,14 @@ def test_cgi_urlencode # skip end - def test_cgi_urlencode_component + def test_cgi_urlencode_data my_url = 'http://vulnerable.site/search.aspx?txt=">' my_url_component = "'Stop!' said Fred" - assert_equal("http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E", my_url.urlencode_component) - assert_equal("%27Stop%21%27+said+Fred", my_url_component.urlencode_component) + assert_equal("http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E", my_url.urlencode_data) + assert_equal("%27Stop%21%27+said+Fred", my_url_component.urlencode_data) end - skip def test_cgi_urlencode_component! + skip def test_cgi_urlencode_data! # skip end @@ -42,18 +42,18 @@ def test_cgi_urldecode # skip end - def test_cgi_urldecode_component + def test_cgi_urldecode_data my_url = 'http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E' my_url2 = 'http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E' my_url_component = "'Stop!'%20said%20Fred" my_url_component2 = '%27Stop%21%27+said+Fred' - assert_equal('http://vulnerable.site/search.aspx?txt=">', my_url.urldecode_component) - assert_equal('http://vulnerable.site/search.aspx?txt=">', my_url2.urldecode_component) - assert_equal("'Stop!' said Fred", my_url_component.urldecode_component) - assert_equal("'Stop!' said Fred", my_url_component2.urldecode_component) + assert_equal('http://vulnerable.site/search.aspx?txt=">', my_url.urldecode_data) + assert_equal('http://vulnerable.site/search.aspx?txt=">', my_url2.urldecode_data) + assert_equal("'Stop!' said Fred", my_url_component.urldecode_data) + assert_equal("'Stop!' said Fred", my_url_component2.urldecode_data) end - skip def test_cgi_urldecode_component! + skip def test_cgi_urldecode_data! # skip end @@ -74,4 +74,26 @@ def test_cgi_htmlunescape skip def test_cgi_htmlunescape! # skip end + + def test_cgi_urlencode_component + my_url = 'http://vulnerable.site/search.aspx?txt=">' + my_data = "'Stop!' said Fred" + assert_equal('http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E', my_url.urlencode_component) + assert_equal('%27Stop%21%27%20said%20Fred', my_data.urlencode_component) + end + + skip def test_cgi_urlencode_component! + # skip + end + + def test_cgi_urldecode_component + my_url = 'http%3A%2F%2Fvulnerable.site%2Fsearch.aspx%3Ftxt%3D%22%3E%3Cscript%3Ealert%28%2FRubyfu%2F.source%29%3C%2Fscript%3E' + my_data = '%27Stop%21%27%20said%20Fred' + assert_equal('http://vulnerable.site/search.aspx?txt=">', my_url.urldecode_component) + assert_equal("'Stop!' said Fred", my_data.urldecode_component) + end + + skip def test_cgi_urldecode_component! + # skip + end end