Programming high performance network security tools Using non-blocking I/O (node.js) Jaime Peñalba Estébanez @NighterMan
Que es node.js? node.js es un conjunto de bindings para la VM de javascript v8 (google chrome). Permite crear programas que realizan operaciones de I/O utilizando javascript. Esta orientado al alto rendimiento
Que es node.js? node.js no es un lenguaje de programación, el lenguaje es Javascript. node.js es a la vez un API y el runtime/interprete
Ryan Dahl Creador de Node.JS
Un ejemplo rápido 2000 requests 1000 clientes concurrentes 1 megabyte de respuesta nginx 3000 req/sec node 2800 req/sec apache 100 req/sec
Un ejemplo rápido: El código 1 var http = require('http'); 2 var fs = require('fs'); 3 4 var data = fs.readfilesync('file.rand'); 5 6 http.createserver(function (req, res) { 7 res.writehead(200); 8 res.end(data); 9 }).listen(8282);
Un ejemplo rápido: Consideraciones No todo es lo que parece... nginx con opciones de cache desactivadas nginx ha consumido ~4mb de ram node ha consumido ~60mb de ram
I/O en la actualidad Es normal encontrar código como este 1 result = query('select * from T'); 2 //use result Qué está haciendo el programa mientras espera la respuesta de la base de datos?
I/O en la actualidad Es normal encontrar código como este 1 result = query('select * from T'); 2 //use result Normalmente ESPERAR
Latencia: Ordenadores actuales Cache L1: 3 ciclos Cache L2: 14 ciclos RAM: 250 ciclos Disco: 41.000.000 ciclos Red: 240.000.000 ciclos
Latencia: Ordenadores actuales Cache L1: 3 ciclos Cache L2: 14 ciclos RAM: 250 ciclos No Blocante Disco: 41.000.000 ciclos Red: 240.000.000 ciclos Blocante
I/O en la actualidad El mejor software es el software multi tarea. Otros hilos pueden seguir ejecutándose mientras se espera a que otras tareas terminen.
I/O en la actualidad De verdad esperar y usar hilos es la mejor solución? Veamos el caso de Apache y NGINX
Apache vs NGINX reqs/sec
Apache vs NGINX memory 1
Apache vs NGINX diferencias Apache utiliza un hilo por conexión NGINX no utiliza hilos. Utiliza un event loop
I/O en la actualidad Cambiar de contexto no es gratis para el procesador El stack de cada hilo consume memoria Para concurrencia masiva, utilizar un hilo de sistema por cada conexión no es una buena opción. El modelo de concurrencia por hilos esta obsoleto.
I/O en la actualidad: Ejemplo Código como este 1 result = query('select * from T'); 2 //use result Bloquea el proceso o implica el uso de hilos y por lo tanto varios stacks de ejecución
I/O en la actualidad: Ejemplo Async 1 query('select ', function (res) { 2 //use result 3 }); De esta forma se permite continuar con la ejecución del programa de forma inmediata y solo se llamara al código encargado de manejar el resultado cuando este este listo. No hacen falta ni ningún otro tipo de trucos.
Lo que nos han enseñado 1 puts("enter your name: "); 2 var name = gets(); 3 puts("name: " + name); Nos han enseñado a pedir datos y a no hacer nada hasta que obtenemos el resultado.
Lo que nos han enseñado 1 puts("enter yout name: "); 2 gets(function (name)) { 3 puts("name: " + name); 4 }); 4 // Hacer otras cosas Algo así nos parece demasiado complicado.
Como funciona node: Arquitectura (old) Node.js Core Library (JavaScript) Node.js Bindings (C++) V8 (C++) Thread Pool (libeio) (C) Event loop (libev) (C)
Como funciona node: Arquitectura Node.js Core Library (JavaScript) Node.js Bindings (C++) Google V8 (C++) Libuv (C)
Libuv: Unicorn Velociraptor Librería enfocada a async I/O Desarrollada para node.js pero usada también en Rust, Luvit, Julia, pyuv y otros. Event loop utilizando epoll, kqueue, IOCP y event ports Soporte de sockets tcp/udp Operaciones de manejo de ficheros asíncronas Thread pool
Como funciona node: Event Loop http://www.aaronstannard.com/post/2011/12/14/intro-to-nodejs-for-net-developers.aspx
Como funciona node: Event Loop Cola de Eventos Evento 1 Event Loop Evento 2 Evento 3... Callback Código JavaScript Async call Código Async del OS epoll, kqueue, threads, timers, I/O completion ports, etc...
Como funciona node: Diseño Todo se presenta como un Stream para evitar almacenar datos Las funciones no deben de realizar I/O de forma directa, siempre debe de usarse un callback. La capa de JavaScript utiliza un solo hilo La capa de C/C++ es multihilo
Por qué no usa todo el mundo event loops? Los event loops mono hilo necesitan que todas las operaciones de I/O sean no blocantes. Si una operación es blocante, se bloquea el event loop. La mayoría de las librerías actuales son blocantes.
Identificar operaciones que realizan I/O NO hay I/O 1 b = a(); SI hay I/O 1 a(function (b) { 2 // handle result 3 });
Módulos: Core Modules Assertion Buffer Child Processes Cluster Console Crypto Debugger DNS Path Process Punycode Query Strings Readline REPL Stream String Decoder DNS Domain Events File System HTTP HTTPS Modules Net OS Timers TLS/SSL TTY UDP/Datagram URL Utilities VM ZLIB
NPM
NPM: Node Package Manager Gestor de paquetes por defecto Incluido con node desde la versión 0.6.3 Permite instalar programas o módulos disponibles Permite publica módulos
NPM: Inicializacion
NPM: package.json 1 { 2 "name": "taller", 3 "version": "0.0.1", 4 "description": "Programa de prueba", 5 "main": "index.js", 6 "scripts": { 7 "test": "echo \"Error: no test specified\" && exit 1" 8 }, 9 "author": "Jaime Peñalba", 10 "license": "GPLv3" 11 }
NPM: Instalar modulos npm install express npm install express -g npm install express --save
NPM: Instalar modulos
NPM: Instalar modulos 1 { 2 "name": "taller",... 6 "scripts": { 7 "test": "echo \"Error: no test \" && exit 1" 8 }, 9 "author": "Jaime Peñalba", 10 "license": "GPLv3", 11 "dependencies": { 12 "express": "^4.9.5" 13 } 14 }
NPM: Instalar dependencias $ npm install
NPM: Node Package Manager Muchas más funcionalidades Actualizar módulos Listar, eliminar módulos Scripts de post instalación Arrancar/parar programa
Basics
Funciones en javascript 1 function foo(a) { 2 console.log("hola"); 3 } 4 settimeout(foo, 2000); Función Función anónima 7 settimeout(function (a) { 8 console.log("hola"); 9 });
Funciones en javascript: como argumento 1 function foo(a) { 2 console.log("hola"); 3 } 4 settimeout(foo, 2000); 7 settimeout(function (a) { 8 console.log("hola"); 9 });
Funciones en javascript: como argumento Estamos pasando la función como argumento settimeout(foo, 2000); Estamos pasando el resultado de la ejecución de la función como argumento settimeout(foo(), 2000);
Callbacks Un callback es una función que se pasa como argumento de otra función y que se ejecuta en un momento determinado. No es un tipo de dato ni una propiedad especifica del lenguaje, sino una técnica de programación. Se puede realizar en otros lenguajes como en C.
Callbacks: Ejemplos 1 var http = require('http'); 2 3 http.get('http://..', function (response) { 4 console.log("la respuesta es: " + 5 response.statuscode); 6 });
Callbacks: Ejemplos 1 var http = require('http'); 2 3 function handleresponse(response) { 4 console.log("la respuesta es: " + 5 response.statuscode); 6 } 7 8 http.get('http://...', handleresponse);
Callbacks: Ejemplos 1 var child = require('child_process');; 2 3 child.exec('ls -al /', function (err, stdout) { 4 console.log(stdout); 5 }); 6 7 console.log("hello");
Módulos: Importar var http = require( http ); var http = require(./mymodule );
Módulos: Crear 1 function foo(a) { 2 console.log("arg is " + a); 3 } 4 5 exports.foo = foo; 1 var test = require('./module'); 2 test.foo();
API Básico
Módulo: http http.request(options, [callback]) http.get(options, [callback]) http.createserver([requestlistener])
Módulo: http (options) var options = { hostname: 'www.google.com', port: 80, path: '/upload', method: 'POST', headers: { 'User-Agent': 'morzilla', 'Cookie': 'USERID: algo' } };
Módulo: http (events) on('data', function () {}) Hay nuevos datos disponibles para leer en la respuesta del servidor o en el request del cliente (dependiendo de si somos cliente o servidor) on('end', function () {}) Se ha terminado el request del cliente o la respuesta del servidor on('error', function () {}) Ha ocurrido un error
Módulo: net net.createconnection(port, [host], [connectlistener]) net.connect(path, [connectlistener]) net.connect(options, [connectionlistener]) net.createconnection(path, [connectlistener])
Módulo: net (options) For TCP sockets: port: Port the client should connect to (Required). host: Host the client should connect to. localaddress: Local interface to bind to for connections. For UNIX domain sockets: path: Path the client should connect to (Required).
Módulo: net (events) on('data', function () {}) Hay datos nuevos disponibles para leer on('close', function () {}) Se ha terminado la conexion on('connection', function () {}) Se ha conectado un nuevo cliente o se ha conectado con el servidor on('error', function () {}) Ha ocurrido un error
Prácticas
Prácticas: Chat 1 var net = require('net'); 2 3 var sockets = []; 4 5 var s = net.server(function(socket) { 6 sockets.push(socket); 7 socket.write("welcome to the chat\n"); 8 9 socket.on('data', function(d) { 10 for (var i = 0; i < sockets.length; i++) { 11 if (sockets[i]!== socket) 12 sockets[i].write(d); 13 }; 14 }); 15 16 socket.on('close', function() { 17 var i = sockets.indexof(socket); 18 sockets.splice(i, 1); 19 }); 20 21 }); 22 23 s.listen(6667);
Prácticas: Escaner de puertos 1 var net = require('net'); 2 3 var host = process.argv[2]; 4 5 function checkport(port) { 6 var socket = net.connect(port, host); 7 8 socket.on('connect', function(){ 9 console.log(port + "/tcp is Open"); 10 socket.end(); 11 }); 12 13 socket.on('error', function(err){ 14 socket.destroy(); 15 }); 16 } 17 18 for (var i = 0; i < 1000; i++) 19 checkport(i);
Prácticas: Bruteforce web 1 var http = require('http'); 2 var fs = require('fs'); 3 4 var file = fs.readfilesync(process.argv[3]).tostring(); 5 var folders = file.split('\n'); 6 7 http.globalagent.maxsockets = 300; 8 9 folders.foreach(function (folder) { 10 http.get(process.argv[2] + folder, function (res) { 11 if (res.statuscode!== 404) { 12 console.log("/" + folder + ": " + res.statuscode); 13 } 14 res.socket.end(); 15 }); 16 });
Prácticas: Server web 1 var http = require('http'); 2 var fs = require('fs'); 3 4 5 http.createserver(function (req, res) { 6 res.writehead(200, {'Content-Type': 'text/html'}); 7 res.end("mi color favorito es el jamon"); 8 }).listen(8282);
Prácticas: r2pipe 1 var r2pipe = require('./lib/r2pipe'); 2 3 4 r2pipe.pipe('/bin/ls', function (r2) { 5 r2.cmd('pdj 20 @ entry0', function (res) { 6 var results = JSON.parse(res); 7 8 for (var i = 0; i < results.length; i++) { 9 var addr = results[i].offset.tostring(16); 10 console.log("0x" + addr + ": " + results[i].opcode); 11 }; 12 r2.quit(); 13 }); 14 });
Misc
Modulos utiles SoupSelect htmlparser Async Express r2pipe
Algunas ideas de herramientas Bruteforce logins, dirs o fuzzer Generador de informes Parser de herramientas automáticas Mass scanner Buscador de virtual hosts Helpers de explotación utilizando radare Downloaders de reddit, 4chan, etc... Registros automaticos Simuladores de clicks / visores de publicidad
Recursos interesantes http://www.nodejs.org/api/ http://www.nodebeginner.com http://www.strongloop.org Ryan Dahl: Cinco de node https://www.youtube.com/watch?v=m-sc73y-zqa Ryan Dahl: Intruction to node.js https://www.youtube.com/watch?v=jo_b4lthi3i