Snippet n°1 – Free deflect

require "roda"

class App < Roda
  route do |r|
    r.root do
      r.redirect '/acceis'
    end
    r.get 'acceis' do
      @base_url = 'https://www.acceis.fr'
      if r.params['redirect_url'].nil?
        r.redirect '/logout'
      elsif /^#{@base_url}\/.*$/i.match?(r.params['redirect_url'])
        r.redirect r.params['redirect_url']
      else
        r.redirect 'https://www.acceis.fr/rejoignez-nous/'
      end
    end
    r.get 'logout' do
      'You are disconnected!'
    end
  end
end
Solution
Open-Redirect
Fixed code:
require "roda"

class App < Roda
  route do |r|
    r.root do
      r.redirect '/acceis'
    end
    r.get 'acceis' do
      @base_url = 'https://www.acceis.fr'
      if r.params['redirect_url'].nil?
        r.redirect '/logout'
      elsif /\A#{Regexp.escape(@base_url)}\/.*\Z/.match?(r.params['redirect_url'])
        r.redirect r.params['redirect_url']
      else
        r.redirect 'https://www.acceis.fr/rejoignez-nous/'
      end
    end
    r.get 'logout' do
      'You are disconnected!'
    end
  end
end
open-redirect regexp unicode line-feed ruby roda
Language: ruby Source

Snippet n°2 – Blog à blagues

require 'roda'
require 'cgi'
require 'json'

class App < Roda
  route do |r|
    r.root do
      r.redirect '/articles'
    end
    r.get 'articles' do
      if r.params['search']
        data = JSON.load_file('data.json')
        sanitized_query = CGI.escapeHTML r.params['search']
        normalized_query = sanitized_query.unicode_normalize(:nfkc)
        matches = data['articles'].select {|x| x['title'].include?(normalized_query)}
        links = matches.map {|x| "
  • #{x['title']}
  • "} res = "

    Articles matching #{normalized_query}

    " res += "
      #{links.join}
    " response.write res else response.write 'Use /articles?search=word to search in article title' end end end end
    Solution
    XSS
    Fixed code:
    require 'roda'
    require 'cgi'
    require 'json'
    
    class App < Roda
      route do |r|
        r.root do
          r.redirect '/articles'
        end
        r.get 'articles' do
          if r.params['search']
            data = JSON.load_file('data.json')
            normalized_query = r.params['search'].unicode_normalize(:nfkc)
            sanitized_query = CGI.escapeHTML normalized_query
            matches = data['articles'].select {|x| x['title'].include?(sanitized_query)}
            links = matches.map {|x| "
  • #{x['title']}
  • "} res = "

    Articles matching #{sanitized_query}

    " res += "
      #{links.join}
    " response.write res else response.write 'Use /articles?search=word to search in article title' end end end end
    case-transformation collision unicode xss normalization ruby roda
    Language: ruby Source

    Snippet n°3 – Secret manager

    const express = require('express');
    const app = express();
    const auth = require('./authentication');
    const secret = require('./secret');
    
    app.use((req, res, next) => {
      if (req.url.startsWith('/secret')) {
        const authorized = auth.verify(req);
        if (!authorized) {
          return res.status(401).send('Incorrect authentication token!');
        }
      }
      next();
    });
    
    app.use('/secret', secret);
    
    app.listen(4242);
    
    Solution
    Broken Access Control
    Fixed code:
    const express = require('express');
    const app = express();
    const auth = require('./authentication');
    const secret = require('./secret');
    
    // cf. https://expressjs.com/en/api.html#app.settings.table
    app.set('case sensitive routing', true);
    
    app.use((req, res, next) => {
      // or case insensitive check: /^\/secret/i.test(req.url)
      // Regexp is probably not the right way to handle it
      if (req.url.startsWith('/secret')) {
        const authorized = auth.verify(req);
        if (!authorized) {
          return res.status(401).send('Incorrect authentication token!');
        }
      }
      next();
    });
    
    app.use('/secret', secret);
    
    app.listen(4242);
    
    access-control express node.js route
    Language: javascript Source

    Snippet n°4 – Admin area

    require 'roda'
    require 'uri'
    require 'net/http'
    
    class App < Roda
      route do |r|
        r.get 'local' do
          addr = r.get_header('HTTP_X_FORWARDED_FOR') ? r.get_header('HTTP_X_FORWARDED_FOR') : r.get_header('REMOTE_ADDR')
          addr = "http://#{addr}" # add protocol
          if URI.parse(addr).host == '127.0.0.1' # authorize admin access from local host only
            safe_addr = URI.parse(URI::Parser.new.escape(addr))
            safe_addr.path = '/login'
            data = {user: 'admin', pass: 'AJMMbzLckY37'}
            begin
              Net::HTTP.post_form(safe_addr, data)
            rescue Errno::ECONNREFUSED => e
              puts e.message
            ensure
              response.status = 200
              response.write 'Service proceeded'
            end
          else
            response.status = 403
            response.write "Not authorized from your address: #{addr}"
          end
        end
      end
    end
    
    Solution
    SSRF
    Fixed code:
    require 'roda'
    require 'uri'
    require 'net/http'
    
    class App < Roda
      route do |r|
        r.get 'local' do
          addr = r.ip # safer than parsing HTTP headers
          addr = "http://#{addr}"
          parsed_addr = URI.parse(addr)
          if parsed_addr.host == '127.0.0.1'
            safe_addr = parsed_addr.dup # always use the same method to process the data that was used in the security check
            safe_addr.path = '/login'
            data = {user: 'admin', pass: 'AJMMbzLckY37'}
            begin
              Net::HTTP.post_form(safe_addr, data)
            rescue Errno::ECONNREFUSED => e
              puts e.message
            ensure
              response.status = 200
              response.write 'Service proceeded'
            end
          else
            response.status = 403
            response.write "Not authorized from your address: #{addr}"
          end
        end
      end
    end
    
    inconsistent-values ssrf url-parsing bypass ruby roda
    Language: ruby Source

    Snippet n°5 – My little proxy

    require 'roda'
    require 'resolv'
    require 'httpx'
    
    def trusted?(host)
      # whitelist to only allow requests on our internal website
      authorized_ips = ['10.10.0.200', '10.10.0.201']
      authorized_ips.include?(Resolv.getaddress(host))
    end
    
    # configure http client
    def http
      HTTPX.with(resolver_class: :system)
        .with(timeout: { connect_timeout: 10 })
        .plugin(:follow_redirects)
        .plugin(:cookies)
        .plugin(:compression)
        .plugin(:h2c)
    end
    
    class App < Roda
      route do |r|
        r.on 'admin' do
          r.get 'proxy' do
            url = URI(r.params['url'])
            host = url.host
            if trusted?(host)
              res = http.get(url)
              res.error ? "Connection failed" : res.to_s
            else
              "Unauthorized target"
            end
          end
        end
      end
    end
    
    Solution
    SSRF
    Fixed code:
    require 'roda'
    require 'resolv'
    require 'httpx'
    
    RESOLVER_CONFIG = {
      :nameserver => ['127.0.0.153'],
      :nameserver_port => [['127.0.0.153', 5353]]
    }
    
    def trusted?(host)
      # whitelist to only allow requests on our internal website
      authorized_ips = ['10.10.0.200', '10.10.0.201']
      r = Resolv::DNS.new(RESOLVER_CONFIG)
      authorized_ips.include?(r.getaddress(host).to_s)
    end
    
    # configure http client
    def http
      HTTPX.with(resolver_class: :native, :resolver_options => RESOLVER_CONFIG)
        .with(timeout: { connect_timeout: 10 })
        .plugin(:follow_redirects)
        .plugin(:cookies)
        .plugin(:compression)
        .plugin(:h2c)
    end
    
    class App < Roda
      route do |r|
        r.on 'admin' do
          r.get 'proxy' do
            url = URI(r.params['url'])
            host = url.host
            if trusted?(host)
              res = http.get(url)
              res.error ? "Connection failed" : res.to_s
            else
              "Unauthorized target"
            end
          end
        end
      end
    end
    
    dns ssrf rebinding bypass ruby roda
    Language: ruby Source

    Snippet n°6 – Pigments

     array(
        'user' => 'admin',
        'password' => '$argon2id$v=19$m=65536,t=4,p=1$TXdqdy9iNGRhdkkuRWZuYQ$fTjXCFlKDPB8yfJGxLcpHybAaur7XeTCAbFyJkeERj4'
      ),
      'messages' => array(
        'access_denied' => 'Access denied!',
        'welcome' => "

    Hello {$_SERVER['PHP_AUTH_USER']}!

    ", 'recipe' => "

    Here is the secret recipe:" ), 'secrets' => array( 'french_crepe_recipe' => '1 cup flour, 2 eggs, ½ cup milk, ½ cup water, ¼ teaspoon salt, 2 tablespoons butter' ), 'color' => 'red' ); extract($config); if (isset($_REQUEST['color']['color'])) extract($_REQUEST['color']); function login($user, $pass) { if ($user !== $_SERVER['PHP_AUTH_USER'] || !password_verify($_SERVER['PHP_AUTH_PW'], $pass)) { header('WWW-Authenticate: Basic realm="AVCS 6"'); header("HTTP/1.0 401 Unauthorized"); exit($access_denied); } } if (!empty($credentials)) { login($credentials['user'], $credentials['password']); } $color = urlencode($color); echo "

    {$messages['welcome']}
    "; echo $messages['recipe'] . " {$secrets['french_crepe_recipe']}"; echo "

    Log out

    "; ?>
    Solution
    Resource Injection
    Fixed code:
     array(
        'user' => 'admin',
        'password' => '$argon2id$v=19$m=65536,t=4,p=1$TXdqdy9iNGRhdkkuRWZuYQ$fTjXCFlKDPB8yfJGxLcpHybAaur7XeTCAbFyJkeERj4'
      ),
      'messages' => array(
        'access_denied' => 'Access denied!',
        'welcome' => "

    Hello {$_SERVER['PHP_AUTH_USER']}!

    ", 'recipe' => "

    Here is the secret recipe:" ), 'secrets' => array( 'french_crepe_recipe' => '1 cup flour, 2 eggs, ½ cup milk, ½ cup water, ¼ teaspoon salt, 2 tablespoons butter' ), 'color' => 'red' ); extract($config); if (isset($_REQUEST['color'])) $color = $_REQUEST['color']; function login($user, $pass) { if ($user !== $_SERVER['PHP_AUTH_USER'] || !password_verify($_SERVER['PHP_AUTH_PW'], $pass)) { header('WWW-Authenticate: Basic realm="AVCS 6"'); header("HTTP/1.0 401 Unauthorized"); exit($access_denied); } } if (!empty($credentials)) { login($credentials['user'], $credentials['password']); } $color = urlencode($color); echo "

    {$messages['welcome']}
    "; echo $messages['recipe'] . " {$secrets['french_crepe_recipe']}"; echo "

    Log out

    "; ?>
    resource-injection authentication bypass php backdoor
    Language: php Source

    Snippet n°7 – Polygons

    use actix_files::NamedFile;
    use actix_web::{get, HttpRequest, HttpResponse, Responder, Result};
    use std::path::PathBuf;
    
    #[get("/")]
    async fn index() -> impl Responder {
      let html = "

    Polygons!

    "; HttpResponse::Ok().body(html) } async fn r#static(req: HttpRequest) -> Result { let path: PathBuf = req.match_info().query("filename").parse().unwrap(); Ok(NamedFile::open(path)?) } #[actix_web::main] async fn main() -> std::io::Result<()> { use actix_web::{web, App, HttpServer}; HttpServer::new(|| App::new() .service(index) .route("/public/{filename:.*}", web::get().to(r#static)) ) .bind(("127.0.0.1", 8888))? .run() .await }
    Solution
    Local file disclosure
    Fixed code:
    use actix_files as fs;
    use actix_web::{get, HttpResponse, Responder};
    
    #[get("/")]
    async fn index() -> impl Responder {
      let html = "

    Polygons!

    "; HttpResponse::Ok().body(html) } #[actix_web::main] async fn main() -> std::io::Result<()> { use actix_web::{App, HttpServer}; HttpServer::new(|| App::new() .service(index) .service(fs::Files::new("/public", "../static")) ) .bind(("127.0.0.1", 8888))? .run() .await }
    local-file-disclosure path-traversal improper-limitation rust permissive-regexp
    Language: rust Source