Skip to content

Commit

Permalink
Merge branch 'release-0.27'
Browse files Browse the repository at this point in the history
  • Loading branch information
k0retux committed Jan 17, 2019
2 parents ed17a0b + 8eade0a commit 90de14e
Show file tree
Hide file tree
Showing 71 changed files with 6,498 additions and 2,656 deletions.
3 changes: 0 additions & 3 deletions TODO
Expand Up @@ -9,9 +9,6 @@

[ENHANCEMENT]

- Add support for absorption of nodes leveraging the 'Bitfield collapse'
customization (i.e., absorption of bit-oriented nodes without a byte boundary).
(Counter-part of the generation supported feature.)
- Add support for absorption of nodes whose existence has not been resolved yet.
(Counter-part of the generation supported feature.)
- Clean up test/test_integration.py
4 changes: 2 additions & 2 deletions data_models/.gitignore
Expand Up @@ -4,6 +4,6 @@
!file_formats/*.py
!protocols
!protocols/*.py
!example*.py
!tuto*.py
!tutorial
!tutorial/*.py
!__init__.py
4 changes: 2 additions & 2 deletions data_models/file_formats/jpg.py
Expand Up @@ -49,7 +49,7 @@ class JPG_DataModel(DataModel):
file_extension = 'jpg'
name = 'jpg'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):
nm = 'jpg_{:0>2d}'.format(idx)
jpg = self.jpg.get_clone(nm, new_env=True)
jpg.set_current_conf('ABS', recursive=True)
Expand All @@ -68,7 +68,7 @@ def absorb(self, data, idx):
print("--> Create {:s} from provided JPG sample [x:{:d}, y:{:d}].".format(nm, x, y))
return jpg
else:
return Node(nm, values=['JPG ABSORBSION FAILED'])
return None

def build_data_model(self):

Expand Down
4 changes: 2 additions & 2 deletions data_models/file_formats/pdf.py
Expand Up @@ -164,8 +164,8 @@ def get_number(name, int_m=0, int_M=2**40, dec_m=0, dec_M=2**20, enforce_unsigne
e = Node(name)
e.set_subnodes_with_csts([
2, ['u>', [sign, 0, 1], [int_part, 0, 1], [end, 1]],
3, ['u>', [sign, 0, 1], [int_part, 1], [end, 0, 1]],
1, ['u>', [sign, 0, 1], [int_part, 1], [dot, 1]]
3, ['u>', [sign, 0, 1], [int_part.get_clone('int_part_s2'), 1], [end.get_clone('float_part_s2'), 0, 1]],
1, ['u>', [sign, 0, 1], [int_part.get_clone('int_part_s3'), 1], [dot, 1]]
])

e.set_semantics(NodeSemantics(['PDF_number', 'basic_type']))
Expand Down
2 changes: 1 addition & 1 deletion data_models/file_formats/png.py
Expand Up @@ -31,7 +31,7 @@ class PNG_DataModel(DataModel):
file_extension = 'png'
name = 'png'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):
nm = 'PNG_{:0>2d}'.format(idx)
png = self.png.get_clone(nm, new_env=True)
status, off, size, name = png.absorb(data, constraints=AbsNoCsts(size=True))
Expand Down
2 changes: 1 addition & 1 deletion data_models/file_formats/zip.py
Expand Up @@ -33,7 +33,7 @@ class ZIP_DataModel(DataModel):
file_extension = 'zip'
name = 'zip'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):

nm = 'ZIP_{:0>2d}'.format(idx)
pkzip = self.pkzip.get_clone(nm, new_env=True)
Expand Down
2 changes: 1 addition & 1 deletion data_models/protocols/http.py
Expand Up @@ -29,7 +29,7 @@
class HTTPModel(DataModel):
name = 'HTTP'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):
pass

def build_data_model(self):
Expand Down
4 changes: 2 additions & 2 deletions data_models/protocols/pppoe.py
Expand Up @@ -29,7 +29,7 @@ class PPPOE_DataModel(DataModel):

file_extension = 'bin'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):
pass

def build_data_model(self):
Expand Down Expand Up @@ -266,7 +266,7 @@ def cycle_tags(tag):

pado = pppoe_msg.get_clone('pado')
pado['.*/code'].set_values(value_type=UINT8(values=[0x7]))
pado['.*/code'].clear_attr(MH.Attr.Mutable)
# pado['.*/code'].clear_attr(MH.Attr.Mutable)

padr = pppoe_msg.get_clone('padr')
padr['.*/code'].set_values(value_type=UINT8(values=[0x19]))
Expand Down
70 changes: 39 additions & 31 deletions data_models/protocols/pppoe_strategy.py
Expand Up @@ -38,30 +38,37 @@ def retrieve_X_from_feedback(env, current_step, next_step, feedback, x='padi', u

for source, status, timestamp, data in feedback:

msg_x = env.dm.get_atom(x)
msg_x.set_current_conf('ABS', recursive=True)
if x == 'padi':
mac_dst = b'\xff\xff\xff\xff\xff\xff'
elif x == 'padr':
if current_step.content is not None:
mac_src = current_step.content['.*/mac_src']
env.mac_src = mac_src
else:
mac_src = env.mac_src
if mac_src is not None:
mac_dst = mac_src.to_bytes()
print('\n*** Destination MAC will be set to: {!r}'.format(mac_dst))
else:
raise ValueError
if x == 'padi':
mac_dst = b'\xff\xff\xff\xff\xff\xff'
elif x == 'padr':
if current_step.content is not None:
mac_src = current_step.content['.*/mac_src']
env.mac_src = mac_src
else:
mac_src = env.mac_src
if mac_src is not None:
mac_dst = mac_src.to_bytes()
# print('\n*** Destination MAC will be set to: {!r}'.format(mac_dst))
else:
raise ValueError
else:
raise ValueError

if data is None:
continue

if data is None:
continue
off = data.find(mac_dst)
off = -1
while True:
off = data.find(mac_dst, off+1)
if off < 0:
break
data = data[off:]
msg_x = env.dm.get_atom(x)
msg_x.set_current_conf('ABS', recursive=True)
result = msg_x.absorb(data, constraints=AbsNoCsts(size=True, struct=True))
print('\n [ ABS result: {!s} ]'.format(result))
# print('\n [ ABS result: {!s} \n data: {!r} \n source: {!s} \ ts: {!s}]'
# .format(result, data, source, timestamp))

if result[0] == AbsorbStatus.FullyAbsorbed:
try:
service_name = msg_x['.*/value/v101'].to_bytes()
Expand Down Expand Up @@ -118,9 +125,10 @@ class t_fix_pppoe_msg_fields(Disruptor):

def disrupt_data(self, dm, target, prev_data):
n = prev_data.content
n.freeze()
error_msg = '\n*** The node has no path to: {:s}. Thus, ignore it.\n'\
' (probable reason: the node has been fuzzed in a way that makes the' \
'path unavailable)'
' path unavailable)'
if self.mac_src:
try:
n['.*/mac_dst'] = self.mac_src
Expand Down Expand Up @@ -171,14 +179,14 @@ def disrupt_data(self, dm, target, prev_data):
step_wait_padi = NoDataStep(fbk_timeout=10, fbk_mode=Target.FBK_WAIT_UNTIL_RECV,
step_desc='Wait PADI')

dp_pado = DataProcess(process=[('ALT', None, UI(conf='fuzz')),
('tTYPE', UI(init=1), UI(order=True, fuzz_mag=0.7)),
dp_pado = DataProcess(process=[('ALT', UI(conf='fuzz')),
('tTYPE', UI(init=1, order=True, fuzz_mag=0.7)),
'FIX_FIELDS#pado1'], seed='pado')
dp_pado.append_new_process([('ALT', None, UI(conf='fuzz')),
('tSTRUCT', UI(init=1), UI(deep=True)), 'FIX_FIELDS#pado2'])
dp_pado.append_new_process([('ALT', UI(conf='fuzz')),
('tSTRUCT', UI(init=1, deep=True)), 'FIX_FIELDS#pado2'])
step_send_pado = Step(dp_pado, fbk_timeout=0.1, fbk_mode=Target.FBK_WAIT_FULL_TIME)
# step_send_pado = Step('pado')
step_end = Step(DataProcess(process=[('FIX_FIELDS#pado3', None, UI(reevaluate_csts=True))],
step_end = Step(DataProcess(process=[('FIX_FIELDS#pado3', UI(reevaluate_csts=True))],
seed='padt'), fbk_timeout=0.1, fbk_mode=Target.FBK_WAIT_FULL_TIME)

step_wait_padi.connect_to(step_send_pado, cbk_after_fbk=retrieve_padi_from_feedback_and_update)
Expand All @@ -190,16 +198,16 @@ def disrupt_data(self, dm, target, prev_data):

### PADS fuzz scenario ###
step_wait_padi = NoDataStep(fbk_timeout=10, fbk_mode=Target.FBK_WAIT_UNTIL_RECV, step_desc='Wait PADI')
step_send_valid_pado = Step(DataProcess(process=[('FIX_FIELDS#pads1', None, UI(reevaluate_csts=True))],
step_send_valid_pado = Step(DataProcess(process=[('FIX_FIELDS#pads1', UI(reevaluate_csts=True))],
seed='pado'), fbk_timeout=0.1, fbk_mode=Target.FBK_WAIT_FULL_TIME)
step_send_padt = Step(DataProcess(process=[('FIX_FIELDS#pads2', None, UI(reevaluate_csts=True))],
step_send_padt = Step(DataProcess(process=[('FIX_FIELDS#pads2', UI(reevaluate_csts=True))],
seed='padt'), fbk_timeout=0.1, fbk_mode=Target.FBK_WAIT_FULL_TIME)

dp_pads = DataProcess(process=[('ALT', None, UI(conf='fuzz')),
('tTYPE#2', UI(init=1), UI(order=True, fuzz_mag=0.7)),
dp_pads = DataProcess(process=[('ALT', UI(conf='fuzz')),
('tTYPE#2', UI(init=1, order=True, fuzz_mag=0.7)),
'FIX_FIELDS#pads3'], seed='pads')
dp_pads.append_new_process([('ALT', None, UI(conf='fuzz')),
('tSTRUCT#2', UI(init=1), UI(deep=True)), 'FIX_FIELDS#pads4'])
dp_pads.append_new_process([('ALT', UI(conf='fuzz')),
('tSTRUCT#2', UI(init=1, deep=True)), 'FIX_FIELDS#pads4'])
step_send_fuzzed_pads = Step(dp_pads, fbk_timeout=0.1, fbk_mode=Target.FBK_WAIT_FULL_TIME)
step_wait_padr = NoDataStep(fbk_timeout=10, fbk_mode=Target.FBK_WAIT_UNTIL_RECV,
step_desc='Wait PADR/PADI')
Expand Down
14 changes: 7 additions & 7 deletions data_models/protocols/sms.py
Expand Up @@ -29,7 +29,7 @@ class SMS_DataModel(DataModel):

file_extension = 'sms'

def absorb(self, data, idx):
def create_node_from_raw_data(self, data, idx, filename):
pass

def build_data_model(self):
Expand Down Expand Up @@ -146,6 +146,12 @@ def build_data_model(self):
{'name': 'TP-DCS', # Data Coding Scheme (refer to GSM 03.38)
'custo_set': MH.Custo.NTerm.CollapsePadding,
'contents': [
{'name': 'msb',
'determinist': True,
'contents': BitField(subfield_sizes=[4], endian=VT.BigEndian,
subfield_values=[
[0b1111,0b1101,0b1100,0b0000]], # last coding group
) },
{'name': 'lsb1',
'determinist': True,
'exists_if': (BitFieldCondition(sf=0, val=[0b1111]), 'msb'),
Expand All @@ -170,12 +176,6 @@ def build_data_model(self):
[0b0000] # Default alphabet
]
) },
{'name': 'msb',
'determinist': True,
'contents': BitField(subfield_sizes=[4], endian=VT.BigEndian,
subfield_values=[
[0b1111,0b1101,0b1100,0b0000]], # last coding group
) },
]},
{'name': 'UDL',
'contents': LEN(vt=UINT8),
Expand Down
Empty file.
6 changes: 4 additions & 2 deletions data_models/example.py → data_models/tutorial/example.py
Expand Up @@ -176,12 +176,14 @@ def build_data_model(self):
prefix.make_determinist()

te3 = Node('EVT3')
te3.set_values(value_type=BitField(subfield_sizes=[4,4], subfield_values=[[0x5, 0x6], [0xF, 0xC]]))
te3.set_values(value_type=BitField(subfield_sizes=[4,4], endian=VT.LittleEndian,
subfield_values=[[0x5, 0x6], [0xF, 0xC]]))
te3.set_fuzz_weight(8)
# te3.make_determinist()

te4 = Node('EVT4')
te4.set_values(value_type=BitField(subfield_sizes=[4,4], subfield_val_extremums=[[4, 8], [3, 15]]))
te4.set_values(value_type=BitField(subfield_sizes=[4,4], endian=VT.LittleEndian,
subfield_val_extremums=[[4, 8], [3, 15]]))
te4.set_fuzz_weight(7)
# te4.make_determinist()

Expand Down
File renamed without changes.
131 changes: 131 additions & 0 deletions data_models/tutorial/myproto.py
@@ -0,0 +1,131 @@
################################################################################
#
# Copyright 2018 Eric Lacombe <eric.lacombe@security-labs.org>
#
################################################################################
#
# This file is part of fuddly.
#
# fuddly is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# fuddly is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with fuddly. If not, see <http://www.gnu.org/licenses/>
#
################################################################################

from framework.value_types import *
from framework.data_model import *
from framework.encoders import *

class MyProto_DataModel(DataModel):

name = 'myproto'

def build_data_model(self):

req_desc = \
{'name': 'req',
'contents': [
{'name': 'header',
'contents': BitField(subfield_sizes=[5,7,4], endian=VT.BigEndian,
subfield_values=[[0], [1,10,20], [1,2,3]],
subfield_descs=['reserved', 'cmd', 'version'])},

{'name': 'init',
'exists_if': (BitFieldCondition(sf=1, val=[1]), 'header'),
'contents': TIMESTAMP("%H:%M:%S"),
'absorb_csts': AbsFullCsts(contents=False)},

{'name': 'register',
'custo_clear': MH.Custo.NTerm.FrozenCopy,
'exists_if': (BitFieldCondition(sf=1, val=10), 'header'),
'contents': [
{'name': 'payload',
'contents': [
{'name': 'file_qty',
'contents': UINT16_be(min=2, max=8)},
{'name': 'file_entry',
'qty_from': 'file_qty',
'contents': [
{'name': 'filename',
'contents': Filename(min_sz=1, max_sz=15, alphabet='abcdef')},
{'name': 'len',
'contents': UINT32_be()},
{'name': 'content',
'sync_size_with': 'len',
'contents': String(min_sz=20, max_sz=50, alphabet='éùijklm:;!',
codec='latin-1')},
{'name': 'crc32',
'contents': CRC(vt=UINT32_be),
'node_args': ['filename', 'content']},
]}
]}
]},

{'name': 'zregister',
'exists_if/and': [(BitFieldCondition(sf=1, val=20), 'header'),
(BitFieldCondition(sf=2, val=3), 'header')],
'encoder': GZIP_Enc(6),
'contents': [
{'name': 'zpayload', 'clone': 'payload'}
]},

]}

req_atom = NodeBuilder(add_env=True).create_graph_from_desc(req_desc)

init_atom = req_atom.get_clone('init', ignore_frozen_state=True)
init_atom['.*/header'].set_subfield(idx=1, val=1)
init_atom.unfreeze(recursive=True)
register_atom = req_atom.get_clone('register', ignore_frozen_state=True)
register_atom['.*/header'].set_subfield(idx=1, val=10)
register_atom.unfreeze(recursive=True)
zregister_atom = req_atom.get_clone('zregister', ignore_frozen_state=True)
zregister_atom['.*/header'].set_subfield(idx=1, val=20)
zregister_atom['.*/header'].set_subfield(idx=2, val=3)
zregister_atom.unfreeze(recursive=True)

self.register(req_atom, init_atom, register_atom, zregister_atom)

def validation_tests(self):

data = [b'\x10 17:20:47',

b'!@\x00\x02dffdecbcaab\x00\x00\x00/i\xe9!mkikl!jilmm!\xe9ml\xe9:;;\xe9\xe9'
b'\xe9kki\xf9!j\xf9j\xf9k\xf9::ji!!m:j:!\xcc\xc0\xc4\xedfab\x00\x00\x00*mmk!i;j'
b'\xf9\xe9\xe9!il;;m;\xe9!!l;ijklikl!kmlk\xf9!:;;jmmB\x8d8\x11',

b'2\x80x\x9c%\x8e!\x0e\x02Q\x0cDW`\xf08\xd4\xc7\x82\xc1\xb6\xa7@\xa36\xbbl2\xd36{'
b',\x8e\xc0\x018\x03\x87\xa8D '
b'\xea\xf8\x04;y\xf3\xf2\x86\xcd\xbcL\xf3\xb8\x0c\xc3p\xa0jc\x1aQ\xae\xa5\rQ\x91'
b'\x91"&\x92\xcd\xa3\xcf\xfb;\xfd\xb6\x8c\x1d>1\x85\x9a%\xca\xa4\x1b\n\xe6\xc5,'
b'U\x83\xabe\x13z\x98"\xcd\x9d\xd7\xe7\x87\xcb\xd4_\xbb\xce\xfd\xd4\xaaR\x9a\x99N'
b'\xc0\xd7\xed\xeb\xfd\x97\x9e\xa1&*A\xba$M`\x16@R\n\xbd\xa3\xc1\xa2\x05\xe1\x110'
b'\x96[\xb5\xba<\xd6\xe3\x17\x1c\xe3Sj ']

ok = True

for d in data:
atom = self.get_atom('req')
status, off, size, name = atom.absorb(d, constraints=AbsFullCsts())
if status != AbsorbStatus.FullyAbsorbed:
ok = False

print('Absorb Status: {!r}, {:d}, {:d}, {:s}'.format(status, off, size, name))
print(' \_ length of original data: {:d}'.format(len(d)))
print(' \_ remaining: {!r}'.format(d[size:size+1000]))

atom.show()

return ok


data_model = MyProto_DataModel()

0 comments on commit 90de14e

Please sign in to comment.