Browse Source

Initial commit

Jose Ortiz 8 years ago
commit
6b23255e40
4 changed files with 531 additions and 0 deletions
  1. 52
    0
      InjectionLexTest.py
  2. 315
    0
      README.md
  3. 39
    0
      SQLInjection.py
  4. 125
    0
      SQLLexer.py

+ 52
- 0
InjectionLexTest.py View File

@@ -0,0 +1,52 @@
1
+from SQLInjection import *
2
+
3
+if __name__ == '__main__':
4
+
5
+	sqlI = SQLInjection()
6
+
7
+	# Test 1
8
+	sample = """select cat from dog where casa=1 ;"""
9
+	u_input =  """select cat from dog where casa=2 ;"""
10
+
11
+	print "Test 1"
12
+	print "Sample:  ", sample
13
+	print "User In: ", u_input
14
+	print "Is Valid?: ", sqlI.validateLex(sample, u_input)
15
+	print
16
+
17
+	# Test 2
18
+
19
+	u_input = """select cat from dog where casa=1 and cat="miau" ;"""
20
+	print "Test 1"
21
+	print "Sample:  ", sample
22
+	print "User In: ", u_input
23
+	print "Is Valid?: ", sqlI.validateLex(sample, u_input)
24
+	print
25
+
26
+	# Interactive Example with user input
27
+
28
+	print "Follow the instruction and then try to inject SQL."
29
+
30
+	while True:
31
+		try: 
32
+			s = raw_input("Input a number> ")
33
+		except EOFError:
34
+			break
35
+
36
+		u_input = """select cat from dog where casa=%s ;""" %s
37
+		print "User query: %s" % u_input
38
+
39
+		try:
40
+			print "Is Valid?: ", sqlI.validateLex(sample, u_input)
41
+			s_counter, u_counter = sqlI.getLastTokCounters()
42
+			print "Token NUMBER  sample: %s\t user:%s" % (s_counter["NUMBER"], u_counter["NUMBER"])
43
+			print "Token ID      sample: %s\t user:%s" % (s_counter["ID"], u_counter["ID"])
44
+			print "Token LITERAL sample: %s\t user:%s" % (s_counter["LITERAL"], u_counter["LITERAL"])
45
+			print "Token AND+OR  sample: %s\t user:%s" % (s_counter["AND"] + s_counter["OR"], u_counter["AND"] + u_counter["OR"])
46
+			print 
47
+		except:
48
+			print "False"
49
+
50
+
51
+
52
+

+ 315
- 0
README.md View File

@@ -0,0 +1,315 @@
1
+[English](#markdown-header-regular-expressions-sql-injection) | [Español](#markdown-header-expresiones-regulares-inyeccion-de-sql)
2
+
3
+# Expresiones Regulares - Inyección de SQL
4
+##Objetivos
5
+
6
+1. Practicar la implementación de expresiones regulares para construir un analizador lexicografico de una versión simple de una pregunta de selección de SQL.
7
+ 
8
+## Pre-Modulo:
9
+
10
+### Bajar PLY
11
+Obten PLY desde: http://www.dabeaz.com/ply/
12
+
13
+###Instalar PLY
14
+
15
+Descomprime el archivo:
16
+
17
+```
18
+tar xzvf ply-X.X.tar.gz
19
+```
20
+
21
+Entra al directorio creado:
22
+
23
+```
24
+cd ply-X.X
25
+```
26
+
27
+Construye los modulos de PLY:
28
+
29
+```
30
+python setup.py build
31
+```
32
+
33
+Instala PLY en el sistema como administrador:
34
+```
35
+sudo python setup.py install
36
+```
37
+
38
+### Pre-Modulo
39
+
40
+Antes de trabajar en este módulo el estudiante debe
41
+
42
+1. Haber repasado la construcción de expresiones regulares.
43
+2. Haber repasado la implementación de expresiones regulares en python.
44
+3. Haber leido sobre el constructor de analizadores lexicográficos lex.
45
+
46
+## Prevenir Inyección de SQL usando un analizador lexicográfico
47
+
48
+La validación de entradas es escencial en el desarrollo de aplicaciones seguras ya que es la primera línea de defensa de todas las aplicaciones.  La gran mayoría de las vulnerabilidades en aplicaciones es debido a pobre validación de las entradas de las aplicaciones. Ejemplo de vulnerabilidades explotadas por pobre validación de entradas lo son:
49
+
50
+1. Desbordamiento del Búfer 
51
+2. Cross Site Scripting
52
+3. Inyección de SQL, XML u otros lenguajes
53
+4. Recorrido de directorios
54
+5. Acceso a archivos privados
55
+
56
+Los atacantes utilizan errores en la validación de entradas para acceder a información privilegiada, para acceder a los sistemas, o para causar negación de servicios.
57
+
58
+Utilizando un analizador lexicográfico se puede comparar las fichas generadas por una pregunta de SQL válida con el tipo de entradas esperadas versus las fichas generadas por preguntas de SQL con entradas de usuario y determinar por la diferencia en la cantidad de fichas generadas si la entrada del usuario es válida.
59
+
60
+Un ejemplo de una pregunta de SQL sencilla es:
61
+
62
+```
63
+select * from infoprivada where contrasena="valor aleatorio desconocido" ;
64
+```
65
+
66
+Esta pregunta de SQL revelaría la información "privada" de un campo de una tabla si se provee la contraseña correcta.  En la aplicación donde se utiliza esta pregunta la entrada del usuario consiste de insertar la contraseña.  Un posible ataque que revelaría la información privada de toda la tabla sería que usuario insertara como valor la cadena de caracteres `1" or 1=1` que generaría la pregunta: 
67
+
68
+```
69
+select * from infoprivada where contrasena="1" or 1=1;
70
+```
71
+
72
+En cuyo caso por la lógica de la pregunta la expresión del WHERE es siempre cierta, y por lo tanto revelaría toda la información de la tabla. Para aprender más sobre Inyección de SQL vaya a la referencia[1].
73
+
74
+Para propósito de este módulo, el analizador lexicográfico que se va a construir sirve para obtener las fichas de una versión simplificada de la pregunta de selección de SQL cuyas fichas son las que siguen:
75
+
76
+SELECT, FROM, WHERE, AND, OR, NUMBER, ID, LITERAL, LPAREN `(`, RPAREN `)`, EQ, NEQ, GT, LT, GTE, LTE, SEMI, COMMA, WILD `*`, COMMENT
77
+
78
+Por ejemplo:
79
+
80
+Para la pregunta de SQL `select * from table where field=%s`, donde %s sería la entrada del usuario; la posible entrada que requiere la aplicación donde se está utilizando puede ser un número insertado por el usuario. 
81
+
82
+Como la entrada esperada es un número, una pregunta que se conoce que va a ser válida es `select * from table where field=1`, la cual generaría las fichas:
83
+
84
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER
85
+
86
+Si un usuario entrara cualquier otro número lo cual es válido, también se generarían las fichas 
87
+
88
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER
89
+
90
+Pero si el usuario en ves de entrar un número ingresara la entrada `1 or 1=1` para que la pregunta siempre sea cierta `select * from table where field=1 or 1=1` las fichas que se generarían son:
91
+
92
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER, OR, NUMBER, EQ, NUMBER
93
+
94
+que como se puede observar son diferente a la cantidad de fichas generadas por una pregunta válida ya que hay 2 fichas NUMBER y una ficha EQ más que en el número de fichas en un query válido.
95
+
96
+Otro ejemplo de una entrada inválida sería poner algo diferente a un número como una cadena de caracteres como myfield `select * from table where field=myfield` donde las fichas generadas serían:
97
+
98
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, ID
99
+
100
+que como se puede observar la cantidad de fichas es la misma pero las fichas difieren en la última ficha.
101
+
102
+**Descargo de responsabilidad** Esto es un módulo cuyo proposito es enseñar expresiones regulares y construcción de analizadores lexicográficos utilizando una aplicación de seguridad.
103
+
104
+## Instrucciones generales
105
+
106
+En este módulo utilizaremos la librería de python PLY para construir un analizador lexicográfico de preguntas de selección de SQL simples.  El estudiante construirá expresiones regulares para completar el analizador lexicográfico y luego seguirá instrucciones para aprender como el analisis lexicográfico sirve para prevenir inyecciones de SQL.
107
+
108
+El archivo que el estudiante modificará para llevar a cabo este módulo es:
109
+`SQLLexer.py`
110
+
111
+
112
+## Ejercicio 1: Expresiones regulares para completar el analizador lexicográfico.
113
+
114
+Edite el archivo `SQLLexer.py` para completar las expresiones regulares en la sección que dice  # REGULAR EXPRESSION RULES FOR TOKENS
115
+
116
+1. Para `number` la expresión regular es uno o mas digitos.
117
+2. Para `identifier` la expresión regular empieza con un caracter del alfabeto o un subrayar (_), seguido por zero o mas caracteres del alfabeto, digitos o subrayares.
118
+3. Para `literal` la expresión regular comienza con comillas dobles, seguido por cualquier número de artículos que:
119
+    1. no son comillas dobles o barras invertida (\\)
120
+    2. o son una barra invertida seguida por cualquier caracter
121
+
122
+    y termina con comillas dobles.
123
+
124
+4. Para `comment` la expresión regular comienza con dos guiones, seguido por cualquier numero de caracteres.
125
+
126
+## Ejercicio 2: Correr el analizador lexicografico para contestar preguntas
127
+
128
+Corra el analizador lexicografico:
129
+```
130
+python  InjectionLexTest.py
131
+```
132
+Van a salir desplegadas dos pruebas donde se comparan una pregunta válida de ejemplo y otra donde las entradas del WHERE es distinta.
133
+
134
+1. Note que para la prueba 1, el resultado es válido.  Anote por qué?
135
+2. Note que para la prueba 2, el resultado es inválido.  Anote por qué?
136
+3. Inserte como entrada un número entero cualquiera. Es válido?  Por qué?
137
+4. Inserte como entrada `1 or 1=1`.  Es válido? Por qué?
138
+5. Inserte como entrada `"ccom" or casa=4`.  Es válido? Por qué?
139
+
140
+## Ejercicio 3: Un analizador léxico para preguntas de SQL para insertar
141
+
142
+Modifica el analizador léxico de el archivo `SQLLexer.py` para también analizar el léxico de preguntas de SQL simples como las siguientes: 
143
+
144
+insert into table (col1, col2, col3) value (id, 3, "literal") ;
145
+insert into table (col1, col2, col3) values (id, 3,"literal"), (id, 4, "otherliteral") ;
146
+
147
+**Nota** Crea una copia de el archivo `SQLLexer.py` del Ejercicio 1 porque necesitaras entregarlo al instructor con otra copia del `SQLLexer.py` para este ejercicio. 
148
+
149
+## Entregas
150
+
151
+Entregue al instructor una copia del archivo `SQLLexer.py` del Ejercicio 1 y el Ejercicio 3, y las contestaciones a las preguntas del Ejercicio 2.
152
+
153
+## References:
154
+[1] https://en.wikipedia.org/wiki/SQL_injection
155
+
156
+--- 
157
+
158
+[English](#markdown-header-regular-expressions-sql-injection) | [Español](#markdown-header-expresiones-regulares-inyeccion-de-sql)
159
+
160
+# Regular Expressions - SQL Injection
161
+
162
+##Objectives
163
+
164
+1. Practice the implementation of regular expressions to construct a lexical analyzer of a simple version of a SQL select query.
165
+ 
166
+## Pre-Module:
167
+
168
+### Download PLY
169
+Get PLY from: http://www.dabeaz.com/ply/
170
+
171
+###Install PLY
172
+
173
+Uncompress the file:
174
+
175
+```
176
+tar xzvf ply-X.X.tar.gz
177
+```
178
+
179
+Enter the directory created:
180
+
181
+```
182
+cd ply-X.X
183
+```
184
+
185
+Construct the PLY modules:
186
+
187
+```
188
+python setup.py build
189
+```
190
+
191
+Install PLY in the system as an administrator:
192
+
193
+```
194
+sudo python setup.py install
195
+```
196
+
197
+### Pre-Module
198
+
199
+Before working in this module the student must
200
+
201
+1. Have reviewed the construction of regular expressions.
202
+2. Have reviewed the implementation of regular expressions in python.
203
+3. Have read about the constructor of lexical analyzers lex.
204
+
205
+## Prevention SQL injection using a lexical analyzer
206
+
207
+Input validation is esential in the development of secure applications because it is the first line of defense of every application.  The vast majority of vulnerabilities in applications is due to poor input validation of the applications.  Example of vulnerabilities explited by poor input validation are:
208
+
209
+1. Buffer overflows
210
+2. Cross Site Scripting
211
+3. SQL, XML or other languages injection
212
+4. Directory Traversal
213
+5. Access to private files
214
+
215
+The attackers use errors in input validation to gain access to priviledged information, to gain access to systems, or to cause denial of services.
216
+
217
+Using a lexical analyzer we can compare the tokens generated by a valid SQL query with expected inputs types versus the tokens generated by a SQL query with user input and determine if the user input is valid by the differences in the amount of tokens. 
218
+
219
+An example of a simple SQL query is:
220
+
221
+```
222
+select * from infoprivada where contrasena="valor aleatorio desconocido" ;
223
+```
224
+
225
+This SQL query would reveal "private" information of a field of the a table if the right password is provided.  In the aplication where this query would be used the user input would consist of inserting the password.  A possible atack that would reveal the private information of all the table would be that the user inserts as a value the string `1" or 1=1` that would generate the query:
226
+
227
+```
228
+select * from infoprivada where contrasena="1" or 1=1;
229
+```
230
+
231
+In which case because of the logic of the query WHERE expression is always true, and therefore it would reveal all the information in the table.  To learn more about SQL injection go to reference [1].
232
+
233
+For the purpose of this module, the lexical analyzer that will be constructed will serve to obtain the tokens of a simplified version of the select SQL query which tokens are the following:
234
+
235
+SELECT, FROM, WHERE, AND, OR, NUMBER, ID, LITERAL, LPAREN `(`, RPAREN `)`, EQ, NEQ, GT, LT, GTE, LTE, SEMI, COMMA, WILD `*`, COMMENT
236
+
237
+For example:
238
+
239
+For the SQL query `select * from table where field=%s`, where %s would be the user input; the possible input requiered by the application where the query is being used could be a number inserted by the user.
240
+
241
+Because the expected input is a number, a query known to be valid is `select * from table where field=1`, which would generate the tokens:
242
+
243
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER
244
+
245
+If the user enters any other number, which is valid, also the following tokens would be generated:
246
+
247
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER
248
+
249
+But if the user instead of inserting a number inserts as input `1 or 1=1` to make the query always true `select * from table where field=1 or 1=1` the tokens that would be generated are:
250
+
251
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, NUMBER, OR, NUMBER, EQ, NUMBER
252
+
253
+that as can be observed are different to the number of tokens generated by a valid query because there are two additional NUMBER tokens and one additional EQ token than in the number of tokens in the valid query.
254
+
255
+Another example of an invalid input would be to insert something different to a number such a string like myfield `select * from table where field=myfield` where the generated tokens would be:
256
+
257
+SELECT, WILD, FROM, ID, WHERE, ID, EQ, ID
258
+
259
+that as can be observed the number of tokens is the same but the tokens differ in the last token.
260
+
261
+**Disclaimer** This is a module which purpose is to teach regular expressions and to construct lexical analyzers using security as an application.
262
+
263
+## General Instructions
264
+
265
+In this module we will use the python library PLY to construct a lexical analyzer of simple selection SQL queries.  The student will construct regular expressions to complete the lexical analizer and then will follow instructions to learn how the lexical analyisis serves to prevent SQL injections.
266
+
267
+The file that the student will modify to accomplish this module is: `SQLLexer.py`
268
+
269
+
270
+## Exercise 1: Regular expression to complete the lexical analyzer
271
+
272
+Edit the file  `SQLLexer.py` to complete the regular expression in the secctions that says: # REGULAR EXPRESSION RULES FOR TOKENS
273
+
274
+1. For `number` the regular expression is one or more digits.
275
+2. For `identifier` the regular expression starts with an alphabet character or an underscore followed by zero or more alphabet characters, digits or underscores.
276
+3. For `literal` the regular expression starts with a double quote, followed by any number of items which are:
277
+    1. either not a double-quote or a backslash
278
+    2. or are a backslash followed by any single character
279
+    
280
+    and then finishes with a closing double-quote.
281
+
282
+4. For `comment` the regular expression start with two dashes, followed by any number of characters.
283
+
284
+## Exercise 2: Run the lexical analyzer and answer the questions
285
+
286
+Run the lexical analyzer:
287
+
288
+```
289
+python  InjectionLexTest.py
290
+```
291
+The comparison between two tests, one with a sample valid query and other with a different WHERE expressions input is displayed.
292
+
293
+1. Note that for test 1, the result is valid.  Why?
294
+2. Note that for test 2, the result is invalid. Why?
295
+3. Insert as input any integer number.  Is valid? Why?
296
+4. Insert as input `1 or 1=1`. Is valid? Why?
297
+5. Insert as input `"ccom" or casa=4`. Is valid? Why?
298
+``
299
+
300
+## Exercise 3: A lexical analyzer for insert queries
301
+
302
+Modify the lexical analyzer of file `SQLLexer.py` to also analyze the lexicon of simple insert SQL queries like the following:
303
+
304
+insert into table (col1, col2, col3) value (id, 3, "literal") ;
305
+insert into table (col1, col2, col3) values (id, 3,"literal"), (id, 4, "otherliteral") ;
306
+
307
+**Note** make a copy of the file `SQLLexer.py` from Exercise 1 because you need to give it to the instructor with another `SQLLexer.py` for this exercise. 
308
+
309
+## Deliverables
310
+
311
+Give the instructor a copy of the file `SQLLexer.py` from Exercise 1, and Exercise 3; and the answers to the questions from Exercise 2.
312
+
313
+## References:
314
+[1] https://en.wikipedia.org/wiki/SQL_injection
315
+

+ 39
- 0
SQLInjection.py View File

@@ -0,0 +1,39 @@
1
+
2
+from SQLLexer import *
3
+
4
+class SQLInjection():
5
+
6
+	def __init__(self):
7
+			self.lexer = SQLLexer()
8
+			self.lexer.build()
9
+
10
+			self.u_tok_counter = None
11
+			self.s_tok_counter = None
12
+
13
+
14
+	def validateLex(self, sample_sql, user_sql):
15
+
16
+		self.s_tok_counter = self.lexer.getTokensHash()
17
+		self.u_tok_counter = self.lexer.getTokensHash()
18
+
19
+		for tok in self.lexer.tokenize(sample_sql):
20
+	 		self.s_tok_counter[tok.type] += 1
21
+
22
+	 	for tok in self.lexer.tokenize(user_sql):
23
+	 		self.u_tok_counter[tok.type] += 1
24
+
25
+	 	return self.s_tok_counter == self.u_tok_counter
26
+
27
+	def getLastTokCounters(self):
28
+		return self.s_tok_counter, self.u_tok_counter
29
+
30
+
31
+if __name__ == '__main__':
32
+
33
+	sqlI = SQLInjection()
34
+
35
+	# Test 1
36
+	print sqlI.validateLex("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 ;""")
37
+
38
+	# Test 2
39
+	print sqlI.validateLex("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 and cat="miau" ;""")

+ 125
- 0
SQLLexer.py View File

@@ -0,0 +1,125 @@
1
+import ply.lex as lex
2
+from ply.lex import TOKEN
3
+
4
+class SQLLexer(object):
5
+	# List of token names.  
6
+
7
+	reserved = {
8
+	
9
+		'SELECT' : 'SELECT',
10
+		'select' : 'SELECT',
11
+		'FROM' : 'FROM', 
12
+		'from' : 'FROM', 
13
+		'WHERE' : 'WHERE',
14
+		'where' : 'WHERE',
15
+		'AND' : 'AND',
16
+		'and' : 'AND',
17
+		'or' : 'OR',
18
+		'OR' : 'OR'
19
+	}
20
+
21
+	tokens = [
22
+			'COMMENT',
23
+			'NUMBER', 'ID', 'LITERAL',
24
+			'LPAREN', 'RPAREN', 
25
+			'EQ', 'NEQ', 'GT', 'LT', 'LTE', 'GTE', 'SEMI', 'COMMA', 'WILD'] + list(set(list(reserved.values())))
26
+
27
+
28
+	# REGULAR EXPRESSION RULES FOR TOKENS
29
+
30
+	number = r''
31
+	identifier = r''
32
+	literal = r''
33
+	comment = r''
34
+
35
+	t_LPAREN = r'\('
36
+	t_RPAREN = r'\)'
37
+	t_LTE = r'<='
38
+	t_GTE = r'>='
39
+	t_LT = r'<'
40
+	t_GT = r'>'
41
+	t_EQ = r'='
42
+	t_NEQ = r'!='
43
+	t_SEMI = r';'
44
+	t_COMMA =r','
45
+	t_WILD = r'\*'
46
+
47
+	# Ignore space characters
48
+	t_ignore = ' \t\n'
49
+
50
+	# Actions nexts
51
+
52
+	# Only integer numbers for this example
53
+
54
+	@TOKEN(number)
55
+	def t_NUMBER(self, t):
56
+		t.value = int(t.value)
57
+		return t 
58
+
59
+	# Extract literals content
60
+
61
+	@TOKEN(literal)
62
+	def t_LITERAL(self, t):
63
+		if t.value == "\"\"":
64
+			t.value = ""
65
+		else:
66
+			t.value = t.value[1:len(t.value)-1]
67
+		return t
68
+
69
+	# Identifiers
70
+	@TOKEN(identifier)
71
+	def t_ID(self, t):
72
+		t.type = self.reserved.get(t.value, 'ID')
73
+		return t
74
+
75
+	# Ignore comments
76
+	@TOKEN(comment)
77
+	def t_COMMENT(self, t):
78
+		pass
79
+
80
+	def t_error(self, t):
81
+		print("Illegal character '%s'" % t.value[0])
82
+		raise
83
+		#t.lexer.skip(1)
84
+
85
+
86
+	#Build the lexer
87
+	def build(self, **kwargs):
88
+		self.lexer = lex.lex(module=self, **kwargs)
89
+
90
+
91
+	def input(self, data):
92
+		self.lexer.input(data)
93
+
94
+	def token(self):
95
+		return self.lexer.token()
96
+
97
+	def tokenize(self, data):
98
+		tokens = []
99
+		self.lexer.input(data)
100
+		while True:
101
+			tok = self.lexer.token()
102
+			if not tok:
103
+				break
104
+			tokens.append(tok)
105
+		return tokens
106
+
107
+	def getTokensHash(self):
108
+		return dict((k,0) for k in self.tokens)
109
+
110
+
111
+
112
+if __name__ == '__main__':
113
+	sqllex = SQLLexer()
114
+	sqllex.build()
115
+	tok_counter = sqllex.getTokensHash()
116
+	while True:
117
+		try: 
118
+			s = raw_input("sql> ")
119
+		except EOFError:
120
+			break
121
+		for tok in sqllex.tokenize(s):
122
+	 		print tok
123
+	 		tok_counter[tok.type] += 1
124
+
125
+	 	print tok_counter