Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Update from master #88

Open
wants to merge 26 commits into
base: sulley_refactor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
afcd647
Implement a quick fix to network_monitor.py
Fitblip Jun 19, 2014
fead662
Added return value on function start_target so sulley doesn't close o…
Oct 27, 2014
0e8a0ef
Merge pull request #65 from mathieulavoie/master
Fitblip Oct 28, 2014
8952ad0
Allow the loopback interface to be used.
edevil Nov 10, 2014
2edc1ca
Fix issue #66
Fitblip Nov 11, 2014
cb5e62c
Merge pull request #67 from edevil/master
Fitblip Nov 15, 2014
829758b
Add "offset" parameter to the s_size funtion.
SteveJM Jun 10, 2015
266f861
Merge pull request #75 from SteveJM/SteveJM_add_field_to_size
Fitblip Jun 10, 2015
68dc3f5
Add support for binary prinitives to be defined with a list / tuple o…
SteveJM Jun 14, 2015
7bc54f2
Fixed primitives "off-by-one" error. This was preventing "full_range"…
SteveJM Jun 16, 2015
1cefd1f
Merge pull request #76 from SteveJM/SteveJM_max_num_value_excluded
Fitblip Jun 16, 2015
6342b77
Merge pull request #1 from OpenRCE/master
SteveJM Jun 16, 2015
24f2eb0
Merge branch 'master' into SteveJM_add_support_for_typles_of_values
SteveJM Jun 16, 2015
b742a0d
Mutate primitives defined with lists / tuples through the values.
SteveJM Jun 17, 2015
007edd3
Adding workaround since singal.pause() is missing in Windows.
jtpereyda Jun 19, 2015
ac0c625
Corrected exception thrown when the wrong value is used.
SteveJM Jun 19, 2015
8928ce8
process_monitor.py now accepts relative filenames in -c argument.
jtpereyda Jun 19, 2015
b842560
network_monitor.py now runs its server in a thread, making Ctrl+C wor…
jtpereyda Jun 22, 2015
62c405b
Merge pull request #77 from SteveJM/SteveJM_add_support_for_typles_of…
Fitblip Jun 24, 2015
cf74c68
OpenRCE/sulley Issue #79 Changing code and usage string to indicate t…
jtpereyda Jun 24, 2015
54bdbbe
Update sessions.py
jtpereyda Jun 25, 2015
c83f26d
Merge pull request #80 from jtpereyda/vmcontrol-required-argument-fix
Fitblip Jun 30, 2015
bfa6978
Merge pull request #81 from jtpereyda/network-monitor-thread
Fitblip Jun 30, 2015
958a424
Merge pull request #78 from jtpereyda/windows-signal-pause-fix
Fitblip Aug 13, 2015
3bce87a
Merge pull request #85 from jtpereyda/process-monitor-relative-filenames
Fitblip Aug 13, 2015
b78c235
Merge branch 'master' into refactor-update-from-master. Messy merge. …
jtpereyda Aug 14, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.pyc
archived_fuzzies/*.session
docs/
.idea/
testing/
testing/
23 changes: 18 additions & 5 deletions network_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pcapy
import impacket
import impacket.ImpactDecoder
import signal
from sulley import pedrpc


Expand All @@ -27,9 +28,10 @@ def create_usage():
[-l|--log_level LEVEL] log level (default 1), increase for more verbosity
[--port PORT] TCP port to bind this agent to

Network Device List:"""
Network Device List:
"""
for index, pcapy_device in enumerate(pcapy.findalldevs()):
IFS.append(device)
IFS.append(pcapy_device)
# if we are on windows, try and resolve the device UUID into an IP address.
if sys.platform.startswith("win"):
import _winreg
Expand Down Expand Up @@ -64,7 +66,7 @@ def __init__(self, network_monitor, pcap, pcap_save_path):
self.data_bytes = 0

# register the appropriate decoder.
if pcap.datalink() == pcapy.DLT_EN10MB:
if pcap.datalink() == pcapy.DLT_EN10MB or pcap.datalink() == pcapy.DLT_NULL:
self.decoder = impacket.ImpactDecoder.EthDecoder()
elif pcap.datalink() == pcapy.DLT_LINUX_SLL:
self.decoder = impacket.ImpactDecoder.LinuxSLLDecoder()
Expand Down Expand Up @@ -218,9 +220,9 @@ def set_log_path(self, new_log_path):


if __name__ == "__main__":
IFS = []
usage_message = create_usage()
rpc_port = 26001
IFS = []
opts = None

# parse command line options.
Expand Down Expand Up @@ -251,6 +253,17 @@ def set_log_path(self, new_log_path):

try:
servlet = NetworkMonitorPedrpcServer("0.0.0.0", rpc_port, device, pcap_filter, log_path, log_level)
servlet.serve_forever()
t = threading.Thread(target=servlet.serve_forever)
t.daemon = True
t.start()
# Now wait in a way that will not block signals like SIGINT
try:
while True:
signal.pause()
except AttributeError:
# signal.pause() is missing for Windows; wait 1ms and loop instead
while True:
time.sleep(.001)

except:
pass
3 changes: 2 additions & 1 deletion process_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def __init__(self, host, port, crash_filename, proc=None, pid_to_ignore=None, le
# initialize the PED-RPC server.
pedrpc.Server.__init__(self, host, port)

self.crash_filename = crash_filename
self.crash_filename = os.path.abspath(crash_filename)
self.proc_name = proc
self.ignore_pid = pid_to_ignore
self.log_level = level
Expand Down Expand Up @@ -299,6 +299,7 @@ def start_target(self):

self.log("done. target up and running, giving it 5 seconds to settle in.")
time.sleep(5)
return True

def stop_target(self):
"""
Expand Down
8 changes: 5 additions & 3 deletions sulley/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ def s_repeat(block_name, min_reps=0, max_reps=None, step=1, variable=None, fuzza
blocks.CURRENT.push(repeat)


def s_size(block_name, length=4, endian=LITTLE_ENDIAN, output_format="binary", inclusive=False, signed=False, math=None,
fuzzable=False, name=None):
def s_size(block_name, offset=0, length=4, endian=LITTLE_ENDIAN, output_format="binary", inclusive=False, signed=False,
math=None, fuzzable=False, name=None):
"""
Create a sizer block bound to the block with the specified name. You *can not* create a sizer for any
currently open blocks.
Expand All @@ -211,6 +211,8 @@ def s_size(block_name, length=4, endian=LITTLE_ENDIAN, output_format="binary", i

@type block_name: str
@param block_name: Name of block to apply sizer to
@type offset: int
@param offset: (Optional, def=0) Offset to calculated size of block
@type length: int
@param length: (Optional, def=4) Length of sizer
@type endian: Character
Expand All @@ -234,7 +236,7 @@ def s_size(block_name, length=4, endian=LITTLE_ENDIAN, output_format="binary", i
raise sex.SullyRuntimeError("CAN NOT ADD A SIZE FOR A BLOCK CURRENTLY IN THE STACK")

size = blocks.Size(
block_name, blocks.CURRENT, length, endian, output_format, inclusive, signed, math, fuzzable, name
block_name, blocks.CURRENT, offset, length, endian, output_format, inclusive, signed, math, fuzzable, name
)
blocks.CURRENT.push(size)

Expand Down
13 changes: 8 additions & 5 deletions sulley/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,10 @@ def checksum(self, data):

if type(self.algorithm) is str:
if self.algorithm == "crc32":
return struct.pack(self.endian + "L", zlib.crc32(data))
return struct.pack(self.endian + "L", (zlib.crc32(data) & 0xFFFFFFFFL))

elif self.algorithm == "adler32":
return struct.pack(self.endian + "L", zlib.adler32(data))
return struct.pack(self.endian + "L", (zlib.adler32(data) & 0xFFFFFFFFL))

elif self.algorithm == "md5":
digest = hashlib.md5(data).digest()
Expand Down Expand Up @@ -684,8 +684,8 @@ class Size:
user does not need to be wary of this fact.
"""

def __init__(self, block_name, request, length=4, endian="<", output_format="binary", inclusive=False, signed=False,
math=None, fuzzable=False, name=None):
def __init__(self, block_name, request, offset=0, length=4, endian="<", output_format="binary", inclusive=False,
signed=False, math=None, fuzzable=False, name=None):
"""
Create a sizer block bound to the block with the specified name. You *can not* create a sizer for any
currently open blocks.
Expand All @@ -696,6 +696,8 @@ def __init__(self, block_name, request, length=4, endian="<", output_format="bin
@param request: Request this block belongs to
@type length: int
@param length: (Optional, def=4) Length of sizer
@type offset: int
@param offset: (Optional, def=0) Offset for calculated size value
@type endian: chr
@param endian: (Optional, def=LITTLE_ENDIAN) Endianess of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >)
@type output_format: str
Expand All @@ -714,6 +716,7 @@ def __init__(self, block_name, request, length=4, endian="<", output_format="bin

self.block_name = block_name
self.request = request
self.offset = offset
self.length = length
self.endian = endian
self.format = output_format
Expand Down Expand Up @@ -802,7 +805,7 @@ def render(self):
self_size = 0

block = self.request.closed_blocks[self.block_name]
self.bit_field.value = self.math(len(block.rendered) + self_size)
self.bit_field.value = self.math(len(block.rendered) + self_size + self.offset)
self.rendered = self.bit_field.render()

# otherwise, add this sizer block to the requests callback list.
Expand Down
2 changes: 1 addition & 1 deletion sulley/pgraph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,4 +609,4 @@ def sorted_nodes(self):
node_keys = self.nodes.keys()
node_keys.sort()

return [self.nodes[key] for key in node_keys]
return [self.nodes[key] for key in node_keys]
57 changes: 35 additions & 22 deletions sulley/primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ def __init__(self, value, width, max_num=None, endian=LITTLE_ENDIAN, output_form

super(BitField, self).__init__()

assert isinstance(value, (int, long)), "value must be an integer!"
assert isinstance(value, (int, long, list, tuple)), "value must be an integer, list, or tuple!"
assert isinstance(width, (int, long)), "width must be an integer!"

self.value = self.original_value = value
Expand All @@ -622,9 +622,10 @@ def __init__(self, value, width, max_num=None, endian=LITTLE_ENDIAN, output_form
self.full_range = full_range
self.fuzzable = fuzzable
self.name = name
self.cyclic_index = 0 # when cycling through non-mutating values

if not self.max_num:
self.max_num = self.to_decimal("1" * width)
self.max_num = self.to_decimal("1" + "0" * width)

assert isinstance(self.max_num, (int, long)), "max_num must be an integer!"

Expand All @@ -633,15 +634,20 @@ def __init__(self, value, width, max_num=None, endian=LITTLE_ENDIAN, output_form
for i in xrange(0, self.max_num):
self.fuzz_library.append(i)
else:
# try only "smart" values.
self.add_integer_boundaries(0)
self.add_integer_boundaries(self.max_num / 2)
self.add_integer_boundaries(self.max_num / 3)
self.add_integer_boundaries(self.max_num / 4)
self.add_integer_boundaries(self.max_num / 8)
self.add_integer_boundaries(self.max_num / 16)
self.add_integer_boundaries(self.max_num / 32)
self.add_integer_boundaries(self.max_num)
if type(value) in [list, tuple]:
# Use the supplied values as the fuzz library.
for val in value:
self.fuzz_library.append(val)
else:
# try only "smart" values.
self.add_integer_boundaries(0)
self.add_integer_boundaries(self.max_num / 2)
self.add_integer_boundaries(self.max_num / 3)
self.add_integer_boundaries(self.max_num / 4)
self.add_integer_boundaries(self.max_num / 8)
self.add_integer_boundaries(self.max_num / 16)
self.add_integer_boundaries(self.max_num / 32)
self.add_integer_boundaries(self.max_num)

# TODO: Add injectable arbitrary bit fields

Expand All @@ -655,7 +661,7 @@ def add_integer_boundaries(self, integer):
for i in xrange(-10, 10):
case = integer + i
# ensure the border case falls within the valid range for this field.
if 0 <= case <= self.max_num:
if 0 <= case < self.max_num:
if case not in self.fuzz_library:
self.fuzz_library.append(case)

Expand Down Expand Up @@ -693,15 +699,15 @@ def render(self):
# Otherwise we have ascii/something else
# if the sign flag is raised and we are dealing with a signed integer (first bit is 1).
if self.signed and self.to_binary()[0] == "1":
max_num = self.to_decimal("0" + "1" * (self.width - 1))
max_num = self.to_decimal("1" + "0" * (self.width - 1))
# chop off the sign bit.
val = self.value & max_num
val = self.value & self.to_decimal("1" * (self.width - 1))

# account for the fact that the negative scale works backwards.
val = max_num - val
val = max_num - val - 1

# toss in the negative sign.
self.rendered = "%d" % (-val - 1)
self.rendered = "%d" % ~val

# unsigned integer or positive signed integer.
else:
Expand All @@ -721,9 +727,16 @@ def to_binary(self, number=None, bit_count=None):
@rtype: str
@return: Bit string
"""

if not number:
number = self.value
if type(self.value) in [list, tuple]:
# We have been given a list to cycle through that is not being mutated...
if self.cyclic_index == len(self.value):
# Reset the index.
self.cyclic_index = 0
number = self.value[self.cyclic_index]
self.cyclic_index += 1
else:
number = self.value

if not bit_count:
bit_count = self.width
Expand Down Expand Up @@ -755,7 +768,7 @@ def __init__(self, value, *args, **kwargs):

self.s_type = "byte"

if type(self.value) not in [int, long]:
if type(self.value) not in [int, long, list, tuple]:
self.value = struct.unpack(self.endian + "B", self.value)[0]


Expand All @@ -769,7 +782,7 @@ def __init__(self, value, *args, **kwargs):

self.s_type = "word"

if type(self.value) not in [int, long]:
if type(self.value) not in [int, long, list, tuple]:
self.value = struct.unpack(self.endian + "H", self.value)[0]


Expand All @@ -783,7 +796,7 @@ def __init__(self, value, *args, **kwargs):

self.s_type = "dword"

if type(self.value) not in [int, long]:
if type(self.value) not in [int, long, list, tuple]:
self.value = struct.unpack(self.endian + "L", self.value)[0]


Expand All @@ -796,5 +809,5 @@ def __init__(self, value, *args, **kwargs):

self.s_type = "qword"

if type(self.value) not in [int, long]:
if type(self.value) not in [int, long, list, tuple]:
self.value = struct.unpack(self.endian + "Q", self.value)[0]
11 changes: 7 additions & 4 deletions sulley/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,13 @@ def error_handler(error, msg, error_target, error_sock=None):
# wait for a signal only if fuzzing is finished (this function is recursive)
# if fuzzing is not finished, web interface thread will catch it
if self.total_mutant_index == self.total_num_mutations:
while True:
signal.pause()
else:
raise Exception("No signal.pause() on windows. #Fixme!")
try:
while True:
signal.pause()
except AttributeError:
# signal.pause() is missing for Windows; wait 1ms and loop instead
while True:
time.sleep(0.001)

def import_file(self):
"""
Expand Down
5 changes: 4 additions & 1 deletion unit_tests/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,12 @@ def dependencies():
s_static("TWO" * 100)
s_block_end()

assert(s_num_mutations() == 2)
assert(s_mutate() == True)
assert (s_render().find("TWO") == -1)
s_mutate()
assert(s_mutate() == True)
assert (s_render().find("ONE") == -1)
assert(s_mutate() == False)


def repeaters():
Expand Down
10 changes: 5 additions & 5 deletions vmcontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
USAGE = "USAGE: vmcontrol.py" \
"\n <-x|--vmx FILENAME|NAME> path to VMX to control or name of VirtualBox image" \
"\n <-r|--vmrun FILENAME> path to vmrun.exe or VBoxManage" \
"\n [-s|--snapshot NAME> set the snapshot name" \
"\n <-s|--snapshot NAME> set the snapshot name" \
"\n [-l|--log_level LEVEL] log level (default 1), increase for more verbosity" \
"\n [-i|--interactive] Interactive mode, prompts for input values" \
"\n [--port PORT] TCP port to bind this agent to" \
Expand Down Expand Up @@ -83,7 +83,7 @@ def __init__(self, host, port, vmrun, vmx, snap_name=None, log_level=1, interact
print "[*] Using %s" % vmrun
break
except:
print "[!] Error while trying to find vmrun.exe. Try again without -I."
print "[!] Error while trying to find vmrun.exe. Try again without -i."
sys.exit(1)

# get vmx path
Expand Down Expand Up @@ -111,7 +111,7 @@ def __init__(self, host, port, vmrun, vmx, snap_name=None, log_level=1, interact
else:
print "[!] No .vmx file found in the selected folder, please try again"
except:
print "[!] Error while trying to find the .vmx file. Try again without -I."
print "[!] Error while trying to find the .vmx file. Try again without -i."
sys.exit(1)

# Grab snapshot name and log level if we're in interactive mode
Expand Down Expand Up @@ -512,7 +512,7 @@ def is_target_paused(self):
if __name__ == "__main__":
opts = None

vmrun_arg = r"C:\progra~1\vmware\vmware~1\vmrun.exe"
vmrun_arg = None
vmx_arg = None
snap_name_arg = None
log_level_arg = 1
Expand Down Expand Up @@ -558,7 +558,7 @@ def is_target_paused(self):
print "[!] Interactive mode currently only works on Windows operating systems."
ERR(USAGE)

if not vmx_arg and not interactive_arg:
if (not vmx_arg or not vmrun_arg or not snap_name_arg) and not interactive_arg:
ERR(USAGE)

if not virtualbox_arg:
Expand Down