1 '''
2 Wrapper to use untidy in Peach.
3
4 Portions of this code (the untidy stuff) are GPL
5
6 @author: Jon McClintock
7 '''
8
9 import sys
10 from Peach.generator import Generator
11
13 '''
14 This generator creates variations on a given XML blob, using the
15 untidy package. See:
16
17 http://untidy.sourceforge.net/
18 '''
19
20 _xmlString = None
21 _repetitions = [3, 30, 60]
22 _fuzzer = None
23 _iterator = None
24 _currentValue = None
25
26 - def __init__(self, xml, repetitions = None):
36
37
43
44
47
48
52
53
55 input = "<xml><field value=\"blah\">boo</field></xml>"
56 g = XmlFuzzer(input)
57 g.next()
58
59
60 unittest = staticmethod(unittest)
61
62 '''
63 fuzzingFunctions.py
64
65 Copyright 2006 Andres Riancho
66
67 This file is part of untidy, untidy.sourceforge.net .
68
69 untidy is free software; you can redistribute it and/or modify
70 it under the terms of the GNU General Public License as published by
71 the Free Software Foundation version 2 of the License.
72
73 w3af is distributed in the hope that it will be useful,
74 but WITHOUT ANY WARRANTY; without even the implied warranty of
75 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
76 GNU General Public License for more details.
77
78 You should have received a copy of the GNU General Public License
79 along with w3af; if not, write to the Free Software
80 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
81
82 '''
83 import re
84
86 '''
87 This class has a collection of fuzzing funcions for xml tags, text and attrs.
88
89 @author: Andres Riancho ( andres.riancho@gmail.com )
90 '''
92 self._ffTestList = [ self.ff0 ]
93
95 '''
96 @return: A list of fuzzing functions for testing.
97 '''
98 return self._ffTestList
99
101 '''
102 @return: A list of fuzzing functions.
103 '''
104 res = []
105 i = 0
106 try:
107 while True:
108
109 res.append( getattr( self, 'ff'+str(i) ) )
110 i += 1
111 except:
112
113 pass
114
115 return res
116
117
118
119
120
121
122
123 - def ff0( self, xmlItem, repetitions=[] ):
124 '''
125 Return the item without changes
126 '''
127 return [xmlItem,]
128
129
130
131
132
133
134
135 - def ff1( self, xmlItem, repetitions=[] ):
136 '''
137 Matches the opening <, replace with '>'*repetitions
138 '''
139 result = []
140 p = re.compile('^<')
141 for rep in repetitions:
142 if p.match( xmlItem ):
143 fuzzedItem = p.sub('>'*rep , xmlItem )
144 result.append( fuzzedItem )
145 return result
146
147 - def ff2( self, xmlItem, repetitions=[] ):
148 '''
149 If repetitions=2 and xmlItem='<foo>'
150 this ff returns '<foo><<>>'
151 '''
152 result = []
153 for rep in repetitions:
154 fuzzedItem = xmlItem
155 for i in range( rep ):
156 fuzzedItem += '<'
157 for i in range( rep ):
158 fuzzedItem += '>'
159 result.append( fuzzedItem )
160 return result
161
162 - def ff3( self, xmlItem, repetitions=0 ):
163 result = []
164 for rep in repetitions:
165 fuzzedItem = xmlItem
166 fuzzedItem += 'A'*rep
167 result.append( fuzzedItem )
168 return result
169
170 - def ff4( self, xmlItem, repetitions=[] ):
171 result = []
172 for rep in repetitions:
173 result.append(xmlItem*rep)
174 return result
175
176 - def ff5( self, xmlItem, repetitions=0 ):
178
179
180
181
182
183
184
185
187 if charA.isalpha() and charB.isalpha():
188 return True
189 elif charA.isdigit() and charB.isdigit():
190 return True
191 else:
192 return False
193
194 - def ff6( self, xmlItem, repetitions=[] ):
195 '''
196 Lots of fuzzing going on here! :)
197 Some of this fuzzed XML's will be valid, some not.
198 '''
199 result = []
200 last = ''
201 pointer = 0
202 for char in xmlItem:
203 if not self._sameType( last, char ):
204 for rep in repetitions:
205 fuzzedItem = xmlItem[ : pointer ]
206
207
208 if char.isalpha():
209 fuzzedItem += 'A'* rep
210 elif char.isdigit():
211 fuzzedItem += '1'* rep
212 else:
213 fuzzedItem += char* rep
214
215 fuzzedItem += xmlItem[ pointer : ]
216 result.append( fuzzedItem )
217 pointer += 1
218 last = char
219
220 return result
221
222
223
224
225
226 '''
227 xmlFuzzer.py
228
229 Copyright 2006 Andres Riancho
230
231 This file is part of untidy, untidy.sourceforge.net .
232
233 untidy is free software; you can redistribute it and/or modify
234 it under the terms of the GNU General Public License as published by
235 the Free Software Foundation version 2 of the License.
236
237 w3af is distributed in the hope that it will be useful,
238 but WITHOUT ANY WARRANTY; without even the implied warranty of
239 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
240 GNU General Public License for more details.
241
242 You should have received a copy of the GNU General Public License
243 along with w3af; if not, write to the Free Software
244 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
245
246 '''
247
248 DEBUG = False
249
250
251 import re
252
254 '''
255 This class fuzzes an xml string.
256
257 @author: Andres Riancho ( andres.riancho@gmail.com )
258 '''
260 self._stopfuzz = False
261 self._output = ''
262 self._current = 0
263 self._startFrom = []
264
265
266
267
268 self._repetitions = [ 30, 34 ]
269
270 self._ff = fuzzingFunctions()
271
274
276 '''
277 This method separates an xml in a "line by line" form.
278
279 Example:
280 xmlString: "<xml a="b">f00</xml>"
281 result: [ '<xml a="b">' , 'f00', '</xml>']
282
283 REX/Python
284
285 Based on Robert D. Cameron's REX/Perl 1.0.=20
286
287 Original copyright notice follows:
288
289 REX/Perl 1.0
290
291 Robert D. Cameron "REX: XML Shallow Parsing with Regular Expressions",
292 Technical Report TR 1998-17, School of Computing Science, Simon Fraser=20
293 University, November, 1998.
294 Copyright (c) 1998, Robert D. Cameron.=20
295 The following code may be freely used and distributed provided that
296 this copyright and citation notice remains intact and that modifications
297 or additions are clearly identified.
298
299 @parameter xmlString: A string representation of the xml
300 @return: A list of strings.
301 '''
302 TextSE = "[^<]+"
303 UntilHyphen = "[^-]*-"
304 Until2Hyphens = UntilHyphen + "(?:[^-]" + UntilHyphen + ")*-"
305 CommentCE = Until2Hyphens + ">?"
306 UntilRSBs = "[^\\]]*](?:[^\\]]+])*]+"
307 CDATA_CE = UntilRSBs + "(?:[^\\]>]" + UntilRSBs + ")*>"
308 S = "[ \\n\\t\\r]+"
309 NameStrt = "[A-Za-z_:]|[^\\x00-\\x7F]"
310 NameChar = "[A-Za-z0-9_:.-]|[^\\x00-\\x7F]"
311 Name = "(?:" + NameStrt + ")(?:" + NameChar + ")*"
312 QuoteSE = "\"[^\"]*\"|'[^']*'"
313 DT_IdentSE = S + Name + "(?:" + S + "(?:" + Name + "|" + QuoteSE +"))*"
314 MarkupDeclCE = "(?:[^\\]\"'><]+|" + QuoteSE + ")*>"
315 S1 = "[\\n\\r\\t ]"
316 UntilQMs = "[^?]*\\?+"
317 PI_Tail = "\\?>|" + S1 + UntilQMs + "(?:[^>?]" + UntilQMs + ")*>"
318 DT_ItemSE = "<(?:!(?:--" + Until2Hyphens + ">|[^-]" + MarkupDeclCE + ")|\\?" + Name + "(?:" + PI_Tail + "))|%" + Name + ";|" + S
319 DocTypeCE = DT_IdentSE + "(?:" + S + ")?(?:\\[(?:" + DT_ItemSE + ")*](?:" + S + ")?)?>?"
320 DeclCE = "--(?:" + CommentCE + ")?|\\[CDATA\\[(?:" + CDATA_CE + ")?|DOCTYPE(?:" + DocTypeCE + ")?"
321 PI_CE = Name + "(?:" + PI_Tail + ")?"
322 EndTagCE = Name + "(?:" + S + ")?>?"
323 AttValSE = "\"[^<\"]*\"|'[^<']*'"
324 ElemTagCE = Name + "(?:" + S + Name + "(?:" + S + ")?=(?:" + S + ")?(?:" + AttValSE + "))*(?:" + S + ")?/?>?"
325 MarkupSPE = "<(?:!(?:" + DeclCE + ")?|\\?(?:" + PI_CE + ")?|/(?:" + EndTagCE + ")?|(?:" + ElemTagCE + ")?)"
326 XML_SPE = TextSE + "|" + MarkupSPE
327
328 res = re.findall(XML_SPE, xmlString)
329 res = [ x for x in res if x !='\n' ]
330
331 return res
332
342
344 '''
345 @return: This method generates and iterator object that returns a string
346 representation of a fuzzed xml on every call to next()
347 '''
348 class iterator:
349 def __init__( self, fuzzedItems ):
350 self._fi = fuzzedItems
351
352
353 self._counter = []
354 for f00 in range( len(self._fi) ):
355 self._counter.append( 1 )
356
357
358 self._counter[ len( self._fi ) -1 ] = 0
359
360 self._stopIteration = False
361
362 def next( self ):
363 if self._stopIteration:
364 raise StopIteration
365 else:
366
367 self._incrementCounter()
368
369
370 itemList = self._fetchData()
371
372
373 xmlStr = ''.join( itemList )
374 return xmlStr
375
376 def _fetchData( self ):
377 itemList = []
378
379 for pos in range( len( self._counter ) ):
380 itemList.append( self._fi[ pos ][ self._counter[pos] -1 ] )
381
382
383 return itemList
384
385 def _incrementCounter( self ):
386
387
388 self._counter[ len(self._counter) -1 ] += 1
389
390 rpositions = range( len(self._counter) )
391 rpositions.reverse()
392 for position in rpositions:
393 if self._counter[ position ] > len( self._fi[ position ] ):
394
395 self._counter[ position ] = 1
396 self._counter[ position - 1 ] += 1
397
398
399
400 eq = True
401 for c in range( len( self._counter ) ):
402 if self._counter[ c ] != len( self._fi[ c ] ):
403 eq = False
404
405 if eq:
406 self._stopIteration = True
407
408 def __iter__( self ):
409
410 return self
411
412 it = iterator( fuzzedItems )
413 return it
414
415 - def fuzz( self, xmlString ):
416 '''
417 This method does all the work
418
419 @parameter xmlString: A string representation of the xml to fuzz
420 @return: An iterator object that returns a fuzzed xml on every call to next()
421 '''
422 xmlList = self._toList( xmlString )
423
424 fuzzedXmlItems = []
425 for xmlItem in xmlList:
426 fuzzedItems = []
427 for f in self._getFuzzFunctions():
428
429
430 fuzzedItem = apply( f, (xmlItem, self._repetitions ) )
431 if fuzzedItem != None:
432 fuzzedItems.extend( fuzzedItem )
433
434 fuzzedXmlItems.append ( fuzzedItems )
435
436 iter = self._generateIterator( fuzzedXmlItems )
437 return iter
438