├── Peach
├── Utilities
│ ├── __init__.py
│ ├── Gaia
│ │ ├── __init__.py
│ │ └── atoms
│ │ │ └── gaia_lock_screen.js
│ ├── JS
│ │ └── Loader
│ │ │ └── files.html
│ ├── network.py
│ └── common.py
├── Transformers
│ ├── __init__.py
│ ├── Encode
│ │ ├── __init__.py
│ │ ├── UTF8.py
│ │ ├── Hex.py
│ │ ├── Base64Decode.py
│ │ ├── Base64Encode.py
│ │ ├── IPv6StringToOctet.py
│ │ ├── HTMLDecode.py
│ │ ├── HTMLEncode.py
│ │ ├── UTF16.py
│ │ ├── IPv4StringToOctet.py
│ │ ├── URLEncode.py
│ │ ├── IPv4StringToNetworkOctet.py
│ │ ├── ASCII7bit.py
│ │ ├── SIDStringToBytes.py
│ │ ├── WideChar.py
│ │ ├── HTMLEncodeAggressive.py
│ │ ├── JSEncode.py
│ │ ├── UTF16LE.py
│ │ ├── UTF16BE.py
│ │ ├── PDU.py
│ │ ├── NetBiosDecode.py
│ │ ├── NetBiosEncode.py
│ │ └── HexString.py
│ ├── Type
│ │ ├── __init__.py
│ │ ├── StringToInt.py
│ │ ├── StringToFloat.py
│ │ ├── IntToHex.py
│ │ ├── Pack.py
│ │ ├── NumberToString.py
│ │ └── Integer.py
│ ├── Compression
│ │ ├── __init__.py
│ │ ├── Bzip2.py
│ │ ├── Gzip.py
│ │ └── Zlib.py
│ ├── Cryptography
│ │ ├── __init__.py
│ │ ├── SHA1.py
│ │ ├── MD5.py
│ │ ├── HMAC.py
│ │ ├── CVS.py
│ │ ├── ASN1.py
│ │ └── UnixMD5Crypt.py
│ ├── Null.py
│ └── Eval.py
├── Fixups
│ ├── __init__.py
│ ├── numbers.py
│ └── strings.py
├── Strategies
│ ├── __init__.py
│ ├── default.py
│ └── random.py
├── MutateStrategies
│ └── __init__.py
├── Engine
│ ├── __init__.py
│ └── defaults.xml
├── Mutators
│ ├── __init__.py
│ ├── Values.py
│ ├── path.py
│ └── datatree.py
├── Agent
│ ├── __init__.py
│ ├── linux.py
│ ├── util.py
│ ├── gui.py
│ └── socketmon.py
├── Publishers
│ ├── __init__.py
│ ├── remote.py
│ ├── irc.py
│ ├── sql.py
│ ├── httpserver.py
│ ├── icmp.py
│ ├── dll.py
│ ├── stdout.py
│ ├── sslayer.py
│ ├── com.py
│ ├── smtp.py
│ └── usb.py
├── Generators
│ ├── __init__.py
│ ├── null.py
│ ├── flipper.py
│ ├── unicode.py
│ └── repeater.py
├── __init__.py
├── Analyzers
│ ├── pit.py
│ ├── __init__.py
│ ├── stringtoken.py
│ └── asn1.py
├── fixup.py
├── analyzer.py
├── mutator.py
├── transformer.py
├── config.py
├── mutatestrategies.py
└── strategy.py
├── requirements.txt
├── AUTHORS.md
├── CODE_OF_CONDUCT.md
├── Pits
├── Files
│ ├── XML
│ │ └── xml.xml
│ └── blob.xml
└── Targets
│ ├── stdout.xml
│ ├── console.xml
│ └── Firefox
│ └── firefox.xml
├── .gitignore
└── README.md
/Peach/Utilities/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Transformers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Utilities/Gaia/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Transformers/Compression/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Twisted==14.0.0
2 | lxml==3.3.5
3 | psutil==2.1.1
4 | pyasn1==0.1.7
5 | tlslite==0.4.6
6 |
--------------------------------------------------------------------------------
/AUTHORS.md:
--------------------------------------------------------------------------------
1 | ## Credits
2 |
3 | #### Development Lead
4 |
5 | * Christoph Diehl cdiehl@mozilla.com
6 |
7 | #### Contributors
8 |
9 | * Michael Eddington
10 | * Jesse Schwarzentruber
11 | * Tyson Smith
12 | * Christian Holler
13 | * Mark Goodwin
14 | * Aditya Murray
15 |
--------------------------------------------------------------------------------
/Peach/Fixups/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import checksums
5 | import numbers
6 | import strings
7 |
8 | __all__ = ["checksums", "numbers", "strings"]
9 |
--------------------------------------------------------------------------------
/Peach/Strategies/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import default
5 | import random
6 |
7 | __all__ = [
8 | "default",
9 | "random"
10 | ]
11 |
--------------------------------------------------------------------------------
/Peach/MutateStrategies/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import rand
5 | import sequential
6 |
7 | __all__ = [
8 | "rand",
9 | "sequential"
10 | ]
11 |
--------------------------------------------------------------------------------
/Peach/Utilities/JS/Loader/files.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Peach WebSocket Publisher
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Peach/Engine/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | # Circular dependencies were caused by this file!
6 |
7 | #import engine, parser, incoming, dom, state, common
8 | #__all__ = [ "engine", "parser", "incoming", "dom", "state", "common" ]
9 |
10 | # end
11 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/StringToInt.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class StringToInt(Transformer):
8 | """Transform a string into an integer (atoi)."""
9 |
10 | def realEncode(self, data):
11 | return int(data)
12 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/StringToFloat.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class StringToFloat(Transformer):
8 | """Transform a string into an float (atof)."""
9 |
10 | def realEncode(self, data):
11 | return float(data)
12 |
--------------------------------------------------------------------------------
/Peach/Mutators/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import string
5 | import datatree
6 | import number
7 | import blob
8 | import array
9 | import size
10 | import path
11 |
12 | __all__ = [
13 | "string",
14 | "datatree",
15 | "number",
16 | "blob",
17 | "array",
18 | "size",
19 | "path"
20 | ]
21 |
--------------------------------------------------------------------------------
/Peach/Transformers/Null.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class Null(Transformer):
8 | """Null transformer. Returns that data unaltered."""
9 |
10 | def realEncode(self, data):
11 | return data
12 |
13 | def realDecode(self, data):
14 | return data
15 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/UTF8.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class Utf8(Transformer):
8 | """Encode string as UTF-8."""
9 |
10 | def realEncode(self, data):
11 | return data.encode("utf8")
12 |
13 | def realDecode(self, data):
14 | return str(data.decode("utf8"))
15 |
16 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/Hex.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from binascii import b2a_hex, a2b_hex
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Hex(Transformer):
10 | """Transform a data stream into Hex."""
11 |
12 | def realEncode(self, data):
13 | return b2a_hex(data)
14 |
15 | def realDecode(self, data):
16 | return a2b_hex(data)
17 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/Base64Decode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import base64
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Base64Decode(Transformer):
10 | """Base64 decode."""
11 |
12 | def realEncode(self, data):
13 | return base64.decodestring(data)
14 |
15 | def realDecode(self, data):
16 | return base64.encodestring(data).rstrip().replace('\n', '')
17 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/Base64Encode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import base64
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Base64Encode(Transformer):
10 | """Base64 encode."""
11 |
12 | def realEncode(self, data):
13 | return base64.encodestring(data).rstrip().replace('\n', '')
14 |
15 | def realDecode(self, data):
16 | return base64.decodestring(data)
17 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/IPv6StringToOctet.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Ipv6StringToOctet(Transformer):
10 | """Convert a collen notation IPv6 address into a 4 byte octet
11 | representation.
12 | """
13 |
14 | def realEncode(self, data):
15 | return pack('BBBBBBBBBBBBBBBB', data.split(':'))
16 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/HTMLDecode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import xml.sax.saxutils
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class HtmlDecode(Transformer):
10 | """Decode HTML encoded string."""
11 |
12 | def realEncode(self, data):
13 | return xml.sax.saxutils.unescape(data)
14 |
15 | def realEncode(self, data):
16 | return xml.sax.saxutils.escape(data)
17 |
--------------------------------------------------------------------------------
/Peach/Agent/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import network
5 | import debugger
6 | import process
7 | import test
8 | import memory
9 | import gui
10 | import socketmon
11 | import osx
12 | import util
13 |
14 | __all__ = [
15 | "network",
16 | "debugger",
17 | "process",
18 | "test",
19 | "memory",
20 | "socketmon",
21 | "gui",
22 | "osx",
23 | "util",
24 | "linux"
25 | ]
26 |
--------------------------------------------------------------------------------
/Peach/Strategies/default.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.strategy import *
5 |
6 |
7 | class StaticStrategy(Strategy):
8 |
9 | def __init__(self, stateMachine, routeDescriptor, params=None):
10 | Strategy.__init__(self, stateMachine, routeDescriptor)
11 |
12 | def _findRoute(self, start, destination):
13 | if destination is None:
14 | return [start]
15 | return [start, destination]
16 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/HTMLEncode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import xml.sax.saxutils
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class HtmlEncode(Transformer):
10 | """Perform standard HTML encoding of < > & and "."""
11 |
12 | def realEncode(self, data):
13 | return xml.sax.saxutils.quoteattr(data).strip('"')
14 |
15 | def realDecode(self, data):
16 | return xml.sax.saxutils.unescape(data)
17 |
--------------------------------------------------------------------------------
/Peach/Strategies/random.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.strategy import *
5 |
6 |
7 | class RandomStrategy(Strategy):
8 |
9 | def __init__(self, stateMachine, routeDescriptor, params=None):
10 | Strategy.__init__(self, stateMachine, routeDescriptor, params)
11 |
12 | def _findRoute(self, start, destination):
13 | """Todo"""
14 | print(int(self.params['maxsteps']))
15 | return [start, destination]
16 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/UTF16.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class Utf16(Transformer):
8 | """Encode string as UTF-16.
9 | String is prefixed with BOM. Supports surrogate pair encoding of values
10 | larger then 0xFFFF.
11 | """
12 |
13 | def realEncode(self, data):
14 | return data.encode("utf16")
15 |
16 | def realDecode(self, data):
17 | return str(data.decode("utf16"))
18 |
--------------------------------------------------------------------------------
/Peach/Publishers/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import file
5 | import sql
6 | import stdout
7 | import tcp
8 | import udp
9 | import com
10 | import process
11 | import http
12 | import icmp
13 | import raw
14 | import remote
15 | import dll
16 | import smtp
17 | import wifi
18 | import sslayer
19 |
20 |
21 | __all__ = ["file", "sql", "stdout",
22 | "tcp", "udp", "com", "process",
23 | "http", "icmp", "raw", "remote",
24 | "dll", "smtp", "wifi", "sslayer"]
25 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/IPv4StringToOctet.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Ipv4StringToOctet(Transformer):
10 | """Convert a dot notation IPv4 address into a 4 byte octet representation.
11 | """
12 |
13 | def realEncode(self, data):
14 | data = data.split('.')
15 | for i in range(len(data)):
16 | data[i] = int(data[i])
17 | return pack('BBBB', data[0], data[1], data[2], data[3])
18 |
--------------------------------------------------------------------------------
/Peach/Generators/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import block
5 | import dictionary
6 | import incrementor
7 | import repeater
8 | import static
9 | import null
10 | import flipper
11 | import data
12 | import xmlstuff
13 | import uri
14 |
15 | __all__ = ["block",
16 | "dictionary",
17 | "incrementor",
18 | "repeater",
19 | "static",
20 | "null",
21 | #"unicode",
22 | "flipper",
23 | "data",
24 | "xmlstuff",
25 | "uri"]
26 |
--------------------------------------------------------------------------------
/Peach/Transformers/Eval.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 | from Peach import transformer
6 |
7 |
8 | class Eval(Transformer):
9 | """Eval a statement.
10 | Utility transformer for when all else fails.
11 | """
12 |
13 | _eval = None
14 |
15 | def __init__(self, eval, anotherTransformer=None):
16 | transformer.Transformer.__init__(self, anotherTransformer)
17 | self._eval = eval
18 |
19 | def realEncode(self, data):
20 | return eval(self._eval % data)
21 |
--------------------------------------------------------------------------------
/Peach/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import publisher
5 | import transformer
6 | import Publishers
7 | import Transformers
8 | import Engine
9 | import agent
10 | import mutator
11 | import Mutators
12 | import mutatestrategies
13 | import MutateStrategies
14 | import logger
15 | import Fixups
16 | import fixup
17 | __all__ = [
18 | "publisher",
19 | "transformer",
20 | "Publishers",
21 | "Transformers",
22 | "Engine",
23 | "agent",
24 | "mutator",
25 | "Mutators",
26 | "fixup",
27 | "Fixups",
28 | "mutatestrategies",
29 | "MutateStrategies"
30 | ]
31 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Community Participation Guidelines
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines.
4 | For more details, please read the
5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
6 |
7 | ## How to Report
8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
9 |
10 |
16 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/URLEncode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import urllib
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class UrlEncode(Transformer):
10 | """URL encode without pluses."""
11 |
12 | def realEncode(self, data):
13 | return urllib.quote(data)
14 |
15 | def realDecode(self, data):
16 | return urllib.unquote(data)
17 |
18 |
19 | class UrlEncodePlus(Transformer):
20 | """URL encode with pluses."""
21 |
22 | def realEncode(self, data):
23 | return urllib.quote_plus(data)
24 |
25 | def realDecode(self, data):
26 | return urllib.unquote_plus(data)
27 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/SHA1.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import hashlib
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Sha1(Transformer):
10 | """SHA-1 transform (hex and binary)"""
11 |
12 | _asHex = 0
13 |
14 | def __init__(self, asHex=0):
15 | """
16 | @param asHex: 1 is hex, 0 is binary
17 | @type asHex: int
18 | """
19 | Transformer.__init__(self)
20 | self._asHex = asHex
21 |
22 | def realEncode(self, data):
23 | if self._asHex == 0:
24 | return hashlib.sha1(data).digest()
25 | return hashlib.sha1(data).hexdigest()
26 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/MD5.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import hashlib
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Md5(Transformer):
10 | """MD5 transform (hex and binary)"""
11 |
12 | _asHex = 0
13 |
14 | def __init__(self, asHex=0):
15 | """
16 | @param asHex: 1 is hex, 0 is binary
17 | @type asHex: int
18 | """
19 | Transformer.__init__(self)
20 | self._asHex = asHex
21 |
22 | def realEncode(self, data):
23 | m = hashlib.md5()
24 | m.update(data)
25 |
26 | if self._asHex == 0:
27 | return m.digest()
28 |
29 | return m.hexdigest()
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/IPv4StringToNetworkOctet.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Ipv4StringToNetworkOctet(Transformer):
10 | """Convert a dot notation IPv4 address into a 4 byte octet representation.
11 | """
12 |
13 | def realEncode(self, data):
14 | data = data.split('.')
15 | for i in range(len(data)):
16 | try:
17 | data[i] = int(data[i])
18 | except Exception as e:
19 | data[i] = 0
20 | for i in range(len(data), 4):
21 | data.append(0)
22 | return pack('!BBBB', data[0], data[1], data[2], data[3])
23 |
--------------------------------------------------------------------------------
/Pits/Files/XML/xml.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/ASCII7bit.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class Data7Bit(Transformer):
8 | def __init__(self):
9 | Transformer.__init__(self)
10 |
11 | def realEncode(self, data):
12 | result = []
13 | count = 0
14 | last = 0
15 | for c in data:
16 | this = ord(c) << (8 - count)
17 | if count:
18 | result.append('%02X' % ((last >> 8) | (this & 0xFF)))
19 | count = (count + 1) % 8
20 | last = this
21 | result.append('%02x' % (last >> 8))
22 | return ''.join(result)
23 |
24 | def realDecode(self, data):
25 | return data
26 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/SIDStringToBytes.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class SidStringToBytes(Transformer):
10 | """Transform a string representation of SID to a bytes.
11 | Format: S-1-5-21-2127521184-1604012920-1887927527-1712781
12 | """
13 |
14 | def realEncode(self, data):
15 | sid = data.split('-')
16 | if len(sid) < 3 or sid[0] != 'S':
17 | raise Exception("Invalid SID string: {!s}".format(data))
18 | ret = pack("BBBBBBBB", int(sid[1]), int(sid[2]), 0, 0, 0, 0, 0, 5)
19 | for i in range(int(sid[2])):
20 | ret += pack("I", int(sid[i + 3]))
21 | return ret
--------------------------------------------------------------------------------
/Peach/Analyzers/pit.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.Engine.parser import *
5 | from Peach.Engine.common import *
6 | from Peach.analyzer import *
7 |
8 |
9 | class PitXmlAnalyzer(Analyzer):
10 | """
11 | Analyzers produce data and state models. Examples of analyzers would be the parsing
12 | of PeachPit XML files, tokenizing a string, building a data model based on XML file, etc.
13 | """
14 |
15 | supportParser = True
16 |
17 | def __init__(self):
18 | self.configs = None
19 |
20 | def asParser(self, uri):
21 | """
22 | Called when Analyzer is used as default Pit parser.
23 | Should produce a Peach DOM.
24 | """
25 | return ParseTemplate(self.configs).parse(uri)
26 |
27 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/IntToHex.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from types import StringType
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class IntToHex(Transformer):
10 | """Transform an integer into hex."""
11 |
12 | def __init__(self, withPrefix=0):
13 | """Create IntToHex object.
14 | withPrefix flag indicates if 0x prefix should be tagged onto string.
15 | Default is no.
16 | """
17 | Transformer.__init__(self)
18 | self._withPrefix = withPrefix
19 |
20 | def realEncode(self, data):
21 | if type(data) == StringType:
22 | data = int(data)
23 | ret = hex(data)
24 | if self._withPrefix == 0:
25 | return ret[2:]
26 | return ret
--------------------------------------------------------------------------------
/Peach/Analyzers/__init__.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import pit
5 | import shark
6 | import stringtoken
7 | import xml
8 | import binary
9 | import asn1
10 |
11 | from xml import XmlAnalyzer
12 |
13 | # Alias default analyzers
14 | XmlAnalyzer = xml.XmlAnalyzer
15 | Asn1Analyzer = asn1.Asn1Analyzer
16 | BinaryAnalyzer = binary.Binary
17 | PitXmlAnalyzer = pit.PitXmlAnalyzer
18 | WireSharkAnalyzer = shark.WireSharkAnalyzer
19 | StringTokenAnalyzer = stringtoken.StringTokenAnalyzer
20 |
21 | __all__ = ["xml", "shark", "stringtoken", "pit", "binary", "asn1",
22 | "XmlAnalyzer",
23 | "Asn1Analyzer",
24 | "BinaryAnalyzer",
25 | "PitXmlAnalyzer",
26 | "WireSharkAnalyzer",
27 | "StringTokenAnalyzer"
28 | ]
29 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/WideChar.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class WideChar(Transformer):
8 | """Transform a normal string into a wchar string.
9 | Does not convert unicode into wchar strings or anything super fancy.
10 | """
11 |
12 | def realEncode(self, data):
13 | try:
14 | return data.encode("utf-16le")
15 | except Exception as e:
16 | pass
17 | ret = ""
18 | for c in data:
19 | ret += c + "\0"
20 | return ret
21 |
22 | def realDecode(self, data):
23 | try:
24 | return str(data.decode("utf-16le"))
25 | except Exception as e:
26 | pass
27 | ret = ""
28 | for i in range(0, len(data), 2):
29 | ret += data[i]
30 | return ret
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/HTMLEncodeAggressive.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class HtmlEncodeAggressive(Transformer):
8 | """Perform aggressive HTML encoding. Only alpha-nums will not be encoded.
9 | """
10 |
11 | def realEncode(self, data):
12 | # Allow: a-z A-Z 0-9 SP , .
13 | # Allow (dec): 97-122 65-90 48-57 32 44 46
14 | if data is None or len(data) == 0:
15 | return ""
16 | out = ""
17 | for char in data:
18 | c = ord(char)
19 | if ((97 <= c <= 122) or
20 | (65 <= c <= 90) or
21 | (48 <= c <= 57) or
22 | c == 32 or c == 44 or c == 46):
23 | out += char
24 | else:
25 | out += "{};".format(c)
26 | return out
27 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/JSEncode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class JsEncode(Transformer):
8 | """Perform JavaScript encoding of a string."""
9 |
10 | def realEncode(self, data):
11 | # Allow: a-z A-Z 0-9 SP , .
12 | # Allow (dec): 97-122 65-90 48-57 32 44 46
13 | if data is None or len(data) == 0:
14 | return ""
15 | out = ''
16 | for char in data:
17 | c = ord(char)
18 | if ((97 <= c <= 122) or
19 | (65 <= c <= 90) or
20 | (48 <= c <= 57) or
21 | c == 32 or c == 44 or c == 46):
22 | out += char
23 | elif c <= 127:
24 | out += "\\x%02X" % c
25 | else:
26 | out += "\\u%04X" % c
27 | return out
--------------------------------------------------------------------------------
/Peach/Utilities/Gaia/atoms/gaia_lock_screen.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | 'use strict';
6 |
7 | var GaiaLockScreen = {
8 |
9 | unlock: function() {
10 |
11 | let setlock = window.wrappedJSObject.SettingsListener.getSettingsLock();
12 | let obj = {'screen.timeout': 0};
13 | setlock.set(obj);
14 |
15 | window.wrappedJSObject.ScreenManager.turnScreenOn();
16 |
17 | waitFor(
18 | function() {
19 | window.wrappedJSObject.LockScreen.unlock();
20 | waitFor(
21 | function() {
22 | finish(window.wrappedJSObject.LockScreen.locked);
23 | },
24 | function() {
25 | return !window.wrappedJSObject.LockScreen.locked;
26 | }
27 | );
28 | },
29 | function() {
30 | return !!window.wrappedJSObject.LockScreen;
31 | }
32 | );
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/Peach/Transformers/Compression/Bzip2.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import bz2
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Compress(Transformer):
10 | """Bzip2 compression transform.
11 | Allows for compression level selection (default is 9).
12 | """
13 |
14 | def __init__(self, level=9):
15 | """
16 | @type level: int
17 | @param level: The compress level parameter, if given, must be a number
18 | between 1 and 9; the default is 9.
19 | """
20 | Transformer.__init__(self)
21 | self._level = level
22 |
23 | def realEncode(self, data):
24 | return bz2.compress(data, self._level)
25 |
26 | def realDecode(self, data):
27 | return bz2.decompress(data)
28 |
29 |
30 | class Bz2Decompress(Transformer):
31 | """Bzip2 decompression transform."""
32 |
33 | def realEncode(self, data):
34 | return bz2.decompress(data)
35 |
36 | def realDecode(self, data):
37 | return bz2.compress(data, 6)
38 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/UTF16LE.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Utf16Le(Transformer):
10 | """Encode string as UTF-16LE.
11 | Supports surrogate pair encoding of values larger then 0xFFFF.
12 | """
13 |
14 | def realEncode(self, data):
15 | ret = ''
16 | for c in data:
17 | if ord(c) <= 0xFF:
18 | ret += pack(">BB", ord(c), 0x00)
19 | elif ord(c) <= 0xFFFF:
20 | ret += pack(" 0xFFFF:
22 | # Perform surrogate pair encoding
23 | value = ord(c) # value
24 | value -= 0x10000
25 | valueHigh = value & 0xFFF # high bits
26 | valueLow = value & 0xFFF000 # low bits
27 | word1 = 0xD800
28 | word2 = 0xDC00
29 | word1 |= valueHigh
30 | word2 |= valueLow
31 | ret += pack("BB", 0x00, ord(c))
19 | elif ord(c) <= 0xFFFF:
20 | ret += pack(">H", ord(c))
21 | elif ord(c) > 0xFFFF:
22 | # Perform surrogate pair encoding
23 | value = ord(c) # value
24 | value -= 0x10000
25 | valueHigh = value & 0xFFF # high bits
26 | valueLow = value & 0xFFF000 # low bits
27 | word1 = 0xD800
28 | word2 = 0xDC00
29 | word1 |= valueHigh
30 | word2 |= valueLow
31 | ret += pack("
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/HMAC.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import hmac
5 | import hashlib
6 |
7 | from Peach.transformer import Transformer
8 |
9 |
10 | class Hmac(Transformer):
11 | """HMAC as described in RFC 2104."""
12 |
13 | _key = None
14 | _digestmod = None
15 | _asHex = None
16 |
17 | def __init__(self, key, digestmod=hashlib.md5, asHex=0):
18 | """
19 | Key is a generator for HMAC key, digestmod is hash to use (md5 or sha)
20 |
21 | :param key: HMAC key
22 | :type key: Generator
23 | :param digestmod: which digest to use
24 | :type digestmod: MD5 or SHA hashlib object
25 | :param asHex: 1 is hex, 0 is binary
26 | :type asHex: int
27 | """
28 | Transformer.__init__(self)
29 | self._key = key
30 | self._digestmod = digestmod
31 | self._asHex = asHex
32 |
33 | def realEncode(self, data):
34 | if self._asHex == 0:
35 | return hmac.new(self._key.getValue(), data, self._digestmod).digest()
36 | return hmac.new(self._key.getValue(), data, self._digestmod).hexdigest()
37 |
--------------------------------------------------------------------------------
/Pits/Targets/stdout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/PDU.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class PhoneNumber(Transformer):
8 | def __init__(self):
9 | Transformer.__init__(self)
10 |
11 | def realEncode(self, data):
12 | s = ""
13 |
14 | # Add padding F to make length of phone number even
15 | s = s + "F" if len(data) % 2 else s
16 |
17 | # Split string in 2 byte segments
18 | segments = [s[i:i + 2] for i in range(0, len(s), 2)]
19 |
20 | # Reverse and join back together
21 | segments = "".join([i[1] + i[0] for i in segments])
22 |
23 | return segments
24 |
25 | def realDecode(self, data):
26 | # NOTE: after Data.realDecode data is binary NOT semi decimal octets!
27 |
28 | # Split string in 2 bytes segments
29 | segments = [data[i] for i in range(0, len(data), 1)]
30 |
31 | segments = ["%02x" % ord(i) for i in segments]
32 |
33 | # Reverse and join back together
34 | segments = "".join([i[1] + i[0] for i in segments])
35 |
36 | # Strip potential padding F
37 | s = segments[:-1] if segments[-1].upper() == "F" else segments
38 |
39 | return s
40 |
--------------------------------------------------------------------------------
/Peach/fixup.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 |
6 | class Fixup(object):
7 | """
8 | Fixup of values in the data model. This is done by keeping an internal
9 | list of references that can be resolved during fixup(). Helper functions
10 | are provided in this base class for resolving elements in the data model.
11 | """
12 |
13 | def __init__(self):
14 | self.context = None
15 | self.is_in_self = False
16 |
17 | def do_fixup(self):
18 | """Wrapper around fixup() to prevent endless recursion."""
19 | if not self.is_in_self:
20 | try:
21 | self.is_in_self = True
22 | return self.fixup()
23 | finally:
24 | self.is_in_self = False
25 |
26 | def _getRef(self):
27 | """
28 | After incoming data is cracked some elements move around. Peach will
29 | auto update parameters called "ref" but you will need to re-fetch the
30 | value using this method.
31 | """
32 | for param in self.context.fixup:
33 | if param.name == "ref":
34 | return eval(param.defaultValue)
35 |
36 | def fixup(self):
37 | """Performs the required fixup."""
38 | raise Exception("Fixup not implemented yet!")
39 |
--------------------------------------------------------------------------------
/Peach/Utilities/network.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import socket
5 | import SocketServer
6 | import multiprocessing
7 | import SimpleHTTPServer
8 | import atexit
9 | import logging
10 |
11 | socketProcesses = []
12 | assignedPorts = set()
13 |
14 |
15 | @atexit.register
16 | def cleanupSocketProcesses():
17 | logging.debug("Killing socket processes.")
18 | for process in socketProcesses:
19 | process.join()
20 |
21 |
22 | def getUnboundPort(host="", minRange=30000, maxRange=35000, socket_type=1, assignOnlyOnce=False):
23 | for port in range(minRange, maxRange):
24 | if port in assignedPorts:
25 | continue
26 |
27 | sd = socket.socket(type=socket_type)
28 | try:
29 | sd.bind((host, port))
30 | except socket.error as err:
31 | if err.errno != 48:
32 | print(err.strerror)
33 | sd = None
34 | else:
35 | sd.close()
36 | if assignOnlyOnce:
37 | assignedPorts.add(port)
38 | return port
39 | return -1
40 |
41 |
42 | def runHTTPDThread():
43 | port = getUnboundPort(minRange=8000, maxRange=9000)
44 | httpd = SocketServer.TCPServer(("", port), SimpleHTTPServer.SimpleHTTPRequestHandler)
45 | p = multiprocessing.Process(target=httpd.serve_forever, args=())
46 | p.start()
47 | socketProcesses.append(p)
48 | return port
49 |
--------------------------------------------------------------------------------
/Peach/Fixups/numbers.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import random
5 |
6 | from Peach.fixup import Fixup
7 |
8 |
9 | class EvenNumber(Fixup):
10 |
11 | def __init__(self, ref):
12 | Fixup.__init__(self)
13 | self.ref = ref
14 |
15 | def fixup(self):
16 | ref = self.context.findDataElementByName(self.ref)
17 | stuff = ref.getValue()
18 | if stuff is None:
19 | raise Exception("Error: EvenNumberFixup was unable to locate "
20 | "[{}]".format(self.ref))
21 | return stuff if stuff % 2 == 0 else stuff + 1
22 |
23 |
24 | class SequenceIncrementFixup(Fixup):
25 | """
26 | Allows a field to emit a sequential value without adding additional test
27 | cases. This is useful for sequence numbers common in network protocols.
28 | """
29 |
30 | num = 1
31 |
32 | def __init__(self):
33 | Fixup.__init__(self)
34 |
35 | def fixup(self):
36 | SequenceIncrementFixup.num += 1
37 | return SequenceIncrementFixup.num
38 |
39 |
40 | class SequenceRandomFixup(Fixup):
41 | """
42 | Allows a field to emit a random value without adding additional test cases.
43 | This is useful for sequence numbers common in network protocols.
44 | """
45 |
46 | def __init__(self):
47 | random.seed()
48 | Fixup.__init__(self)
49 |
50 | def fixup(self):
51 | return random.randint(0, (1 << self.context.size) - 1)
52 |
--------------------------------------------------------------------------------
/Peach/Publishers/remote.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.Engine.engine import Engine
5 | from Peach.publisher import *
6 |
7 |
8 | class RemotePublisher(Publisher):
9 | """
10 | Load a publisher on a remote Agent
11 | """
12 |
13 | def __init__(self, agentName, name, cls, *args):
14 | Publisher.__init__(self)
15 | self._agentName = agentName
16 | self._name = name
17 | self._cls = cls
18 | self._args = args
19 | self._initialized = False
20 |
21 | def start(self):
22 | if not self._initialized:
23 | self._agent = Engine.context.agent[self._agentName]
24 | self._agent.PublisherInitialize(self._name, self._cls, self._args)
25 | self._initialized = True
26 |
27 | self._agent.PublisherStart(self._name)
28 |
29 | def stop(self):
30 | self._agent.PublisherStop(self._name)
31 |
32 | def send(self, data):
33 | self._agent.PublisherSend(self._name, data)
34 |
35 | def receive(self, size=None):
36 | self._agent.PublisherReceive(self._name, size)
37 |
38 | def call(self, method, args):
39 | self._agent.PublisherCall(self._name, method, args)
40 |
41 | def property(self, property, value=None):
42 | self._agent.PublisherProperty(self._name, value)
43 |
44 | def connect(self):
45 | self._agent.PublisherConnect(self._name)
46 |
47 | def accept(self):
48 | self._agent.PublisherAccept(self._name)
49 |
50 | def close(self):
51 | self._agent.PublisherClose(self._name)
52 |
--------------------------------------------------------------------------------
/Peach/analyzer.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 |
6 | class Analyzer(object):
7 | """
8 | Analyzers produce data and state models. Examples of analyzers would be the parsing of Peach Pit XML files,
9 | tokenizing a string, building a data model based on XML file, etc.
10 | """
11 | supportParser = False
12 | supportDataElement = False
13 | supportCommandLine = False
14 | supportTopLevel = False
15 |
16 | def asParser(self, uri):
17 | """
18 | Called when Analyzer is used as default Pit parser. Produces a Peach DOM.
19 | """
20 | raise Exception("This analyzer cannot be used as parser.")
21 |
22 | def asDataElement(self, parent, args, dataBuffer):
23 | """
24 | Called when Analyzer is used in a data model. Returns a DataElement.
25 | """
26 | raise Exception("This analyzer does not support being attached to a data element.")
27 |
28 | def asCommandLine(self, args):
29 | """
30 | Called when Analyzer is used from command line. Produces a Peach Pit XML.
31 | """
32 | raise Exception("This analyzer does not support being run from the command line.")
33 |
34 | def asTopLevel(self, peach, args):
35 | """
36 | Called when Analyzer is used from top level.
37 | From the top level producing zero or more data models and state models is possible.
38 |
39 | :param args: arguments from elements.
40 | :type args: dict
41 | """
42 | raise Exception("This analyzer does not support being used as a top level element.")
43 |
--------------------------------------------------------------------------------
/Peach/Mutators/Values.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | # Default: Big Endian
6 | # Schema: (name, byteorder): [ values, ... ]
7 |
8 | CustomValues = \
9 | {
10 | ("8bit", True): [
11 | "\xff",
12 | "\xfe",
13 | "\x01",
14 | "\x80",
15 | "\x7f",
16 | ],
17 | ("16bit", True): [
18 | "\xff\xff",
19 | "\xff\xfe",
20 | "\x7f\xff",
21 | "\x7f\xfe",
22 | "\x80\x00",
23 | "\x20\x00", # short << 2
24 | "\x40\x00", # ushort << 2
25 | "\x00\x01",
26 | "\x00\x00",
27 | ],
28 | ("32bit", True): [
29 | "\xff\xff\xff\xff",
30 | "\xff\xff\xff\xfe",
31 | "\x7f\xff\xff\xff",
32 | "\x7f\xff\xff\xfe",
33 | "\x80\x00\x00\x00",
34 | "\x00\x00\x00\x01", # unsigned short z = x - y
35 | "\xff\xc4\x40\x0f", # width * height * 4
36 | "\x00\x00\x00\x00", # n/0, array length
37 | ],
38 | ("bytestr", False): [
39 | "\x41\x41\x41\x41",
40 | "\x41\x41\x41\x41\x41\x41\x41"
41 | ],
42 | ("str", False): [
43 | "1",
44 | "10",
45 | "100"
46 | ]
47 | # https://developer.apple.com/fonts/ttrefman/RM05/Chap5.html
48 | # 0-255 8bit value as syscall + instruction bytes
49 | # ...
50 | }
51 |
52 | # UTF-7 2B 2F 76 (38 | 39 | 2B | 2F)
53 | # UTF-8 EF BB BF
54 | # UTF-16 LE FE FF
55 | # UTF-16 BE FF FE
56 | # UTF-32 BE 00 00 FE FF
57 | # UTF-32 LE FF FE 00 00
--------------------------------------------------------------------------------
/Peach/Transformers/Type/Pack.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Pack(Transformer):
10 | """Simple pack transform.
11 | Only a single piece of data can be used. Useful to generate binary data
12 | from a generator.
13 |
14 | Format C Type Python
15 | x pad byte no value
16 | c char string of length 1
17 | b signed char integer
18 | B unsigned char integer
19 | h short integer
20 | H unsigned short integer
21 | i int integer
22 | I unsigned int long
23 | l long integer
24 | L unsigned long long
25 | q long long long
26 | Q unsigned long long long
27 | f float float
28 | d double float
29 | s char[] string
30 | p char[] string
31 | P void* integer
32 | """
33 |
34 | def __init__(self, packFormat):
35 | """Create a Pack transformer.
36 | packFormat is a standard pack format string. Format string should only
37 | contain a single data place holder.
38 | """
39 | Transformer.__init__(self)
40 | self._packFormat = packFormat
41 |
42 | def realEncode(self, data):
43 | return pack(self._packFormat, data)
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/NetBiosDecode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class NetBiosDecode(Transformer):
8 | """NetBiosName decode.
9 | @author: Blake Frantz
10 | """
11 |
12 | def __init__(self, anotherTransformer=None):
13 | """Create Transformer object.
14 |
15 | :param anotherTransformer: a transformer to run next
16 | :type anotherTransformer: Transformer
17 | """
18 | Transformer.__init__(self, anotherTransformer)
19 |
20 | def realEncode(self, data):
21 | if data % 2 != 0:
22 | raise Exception("Invalid NetBiosEncoding, length must be "
23 | "divisible by two.")
24 | decoded = ""
25 | data = data.upper()
26 | for cnt in range(0, len(data), 2):
27 | c1 = ord(data[cnt])
28 | c2 = ord(data[cnt + 1])
29 | part1 = (c1 - 0x41) * 16
30 | part2 = (c2 - 0x41)
31 | decoded += chr(part1 + part2)
32 | return decoded
33 |
34 | def realDecode(self, data):
35 | encoded = ""
36 | data = data.upper()
37 | if self._pad:
38 | while len(data) < 16:
39 | data += " "
40 | data = data[:16]
41 | for c in data:
42 | ascii = ord(c)
43 | encoded += chr((ascii / 16) + 0x41)
44 | encoded += chr((ascii - (ascii / 16 * 16) + 0x41))
45 | # The 16th byte is the name scope id, set it to 'host' and
46 | # null term it.
47 | if self._pad:
48 | encoded = encoded[:30]
49 | encoded += '\x41'
50 | encoded += '\x41'
51 | return encoded
52 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/NetBiosEncode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class NetBiosEncode(Transformer):
8 | """NetBiosName encode.
9 | @author: Blake Frantz
10 | """
11 |
12 | def __init__(self, anotherTransformer=None, pad=True):
13 | """Create Transformer object.
14 |
15 | :param anotherTransformer: A transformer to run next
16 | :type anotherTransformer: Transformer
17 | :param pad: will pad / trim encoded name to 32 bytes
18 | :type pad: bool
19 | """
20 | Transformer.__init__(self, anotherTransformer)
21 | self._pad = pad
22 |
23 | def realEncode(self, data):
24 | encoded = ""
25 | data = data.upper()
26 | if self._pad:
27 | while len(data) < 16:
28 | data += " "
29 | data = data[:16]
30 | for c in data:
31 | ascii = ord(c)
32 | encoded += chr((ascii / 16) + 0x41)
33 | encoded += chr((ascii - (ascii / 16 * 16) + 0x41))
34 | # The 16th byte is the name scope id, set it to 'host' and
35 | # null term it.
36 | if self._pad:
37 | encoded = encoded[:30]
38 | encoded += '\x41'
39 | encoded += '\x41'
40 | return encoded
41 |
42 | def realDecode(self, data):
43 | if data % 2 != 0:
44 | raise Exception("Invalid NetBiosEncoding, length must be "
45 | "divisible by two.")
46 | decoded = ""
47 | data = data.upper()
48 | for cnt in range(0, len(data), 2):
49 | c1 = ord(data[cnt])
50 | c2 = ord(data[cnt + 1])
51 | part1 = (c1 - 0x41) * 16
52 | part2 = (c2 - 0x41)
53 | decoded += chr(part1 + part2)
54 | return decoded
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/CVS.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class CvsScramble(Transformer):
8 | """CVS pserver password scramble."""
9 |
10 | _shifts = [
11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
12 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
13 | 114, 120, 53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87,
14 | 111, 52, 75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105,
15 | 41, 57, 83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35,
16 | 125, 55, 54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56,
17 | 36, 121, 117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
18 | 58, 113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223,
19 | 225, 216, 187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
20 | 199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203, 226, 193,
21 | 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238, 161, 179, 160, 212,
22 | 207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130, 135, 133, 143, 246,
23 | 192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147, 186, 214, 176,
24 | 227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177, 134, 127,
25 | 182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195,
26 | 243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152
27 | ]
28 |
29 | def realEncode(self, data):
30 | s = []
31 | for i in range(len(data)):
32 | s.append(data[i])
33 | s.append(None)
34 | for i in range(len(data)):
35 | s[i + 1] = "%c" % self._shifts[ord(data[i])]
36 | out = ""
37 | for i in range(len(s)):
38 | out += s[i]
39 | return out
40 |
--------------------------------------------------------------------------------
/Peach/Generators/null.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 |
6 | from Peach import generator
7 |
8 |
9 | class PrintStdout(generator.Generator):
10 | """
11 | Logical value of empty string, but will display a value to stdout when
12 | called. Useful for displaying status messages.
13 | """
14 |
15 | _msg = None
16 | _generator = None
17 |
18 | def __init__(self, msg, generator=None):
19 | """
20 | @type msg: string
21 | @param msg: Value to output
22 | @type generator: Generator
23 | @param generator: Generator to wrap
24 | """
25 | self._msg = msg
26 | self._generator = generator
27 |
28 | def getRawValue(self):
29 | print(self._msg)
30 | if self._generator:
31 | return self._generator.getRawValue()
32 | return ""
33 |
34 | def next(self):
35 | if self._generator:
36 | self._generator.next()
37 | else:
38 | raise generator.GeneratorCompleted("PrintStdout")
39 |
40 |
41 | class PrintStderr(generator.Generator):
42 | """
43 | Logical value of empty string, but will display a value to stderr when
44 | called. Useful for displaying status messages.
45 | """
46 |
47 | _msg = None
48 | _generator = None
49 |
50 | def __init__(self, msg, generator=None):
51 | """
52 | @type msg: string
53 | @param msg: Value to output
54 | @type generator: Generator
55 | @param generator: Generator to wrap
56 | """
57 | self._msg = msg
58 | self._generator = generator
59 |
60 | def getRawValue(self):
61 | sys.stderr.write(self._msg + "\n")
62 | if self._generator:
63 | return self._generator.getRawValue()
64 | return ""
65 |
66 | def next(self):
67 | if self._generator:
68 | self._generator.next()
69 | else:
70 | raise generator.GeneratorCompleted("PrintStderr")
71 |
--------------------------------------------------------------------------------
/Peach/Utilities/common.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import logging
6 |
7 | from Peach.Engine.common import highlight
8 |
9 |
10 | def isSupportedOS(platforms):
11 | return filter(lambda x: x == sys.platform, platforms)
12 |
13 |
14 | def isPosix():
15 | return 'posix' in sys.builtin_module_names
16 |
17 |
18 | def isLinux():
19 | return sys.platform == "linux2"
20 |
21 |
22 | def isMacOS():
23 | return sys.platform == "darwin"
24 |
25 |
26 | def isWindows():
27 | return sys.platform == "win32"
28 |
29 |
30 | def printHex(src):
31 | FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
32 | N = 0
33 | result = ''
34 | length = 16
35 | while src:
36 | s, src = src[:length], src[length:]
37 | hexa = ' '.join(["%02X" % ord(x) for x in s])
38 | s = s.translate(FILTER)
39 | result += "%04X %-*s %s\n" % (N, length * 3, hexa, s)
40 | N += length
41 | print(result)
42 |
43 |
44 | def getBooleanAttribute(args, name):
45 | val = args.get(name, '0').lower().replace("'''", "")
46 | result = val in ('true', 'yes', '1')
47 | if not result:
48 | assert val in ('false', 'no', '0')
49 | return result
50 |
51 |
52 | def getFloatAttribute(args, name, default=""):
53 | return float(args.get(name, default).replace("'''", ""))
54 |
55 |
56 | def getStringAttribute(args, name, default=""):
57 | return args.get(name, default).replace("'''", "")
58 |
59 |
60 | def setAttributesFromParams(node):
61 | if node is not None and node.get('params') is not None:
62 | for kv in node.get('params').split(','):
63 | try:
64 | k, v = [s.strip() for s in kv.split('=', 1)]
65 | except ValueError:
66 | logging.error(highlight.error("The macro %s has no value." % kv))
67 | sys.exit(-1)
68 | k = k[0].lower() + k[1:] # CamelCase -> camelCase
69 | node.set(k, v)
70 |
--------------------------------------------------------------------------------
/Peach/Agent/linux.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import time
6 | import os
7 |
8 | from Peach.agent import Monitor
9 |
10 |
11 | class LinuxApport(Monitor):
12 |
13 | def __init__(self, args):
14 | if 'Executable' in args:
15 | self.programPath = str(args['ProgramPath']).replace("'''", "")
16 | self.processName = os.path.basename(self.programPath)
17 | else:
18 | self.processName = None
19 |
20 | if 'LogFolder' in args:
21 | self.logFolder = str(args['LogFolder']).replace("'''", "")
22 | else:
23 | self.logFolder = "/var/crash/"
24 |
25 | if 'Apport' in args:
26 | self.Apport = self.logFolder = str(args['Apport']).replace("'''", "")
27 | else:
28 | self.Apport = "/usr/share/apport/apport"
29 |
30 | self._name = "LinuxApport"
31 |
32 | self.data = None
33 | self.startingFiles = None
34 |
35 | def OnTestStarting(self):
36 | self.startingFiles = os.listdir(self.logFolder)
37 |
38 | def GetMonitorData(self):
39 | if not self.data:
40 | return None
41 | return {"LinuxApport.txt": self.data}
42 |
43 | def DetectedFault(self):
44 | try:
45 | time.sleep(0.25)
46 | time.sleep(0.25)
47 | self.data = None
48 | for f in os.listdir(self.logFolder):
49 | if f not in self.startingFiles and f.endswith(".crash") and \
50 | (self.processName is None or f.find(self.processName) > -1):
51 | fd = open(os.path.join(self.logFolder, f), "rb")
52 | self.data = fd.read()
53 | fd.close()
54 | os.unlink(os.path.join(self.logFolder, f))
55 | return True
56 | return False
57 | except:
58 | print(sys.exc_info())
59 | return False
60 |
61 | def StopRun(self):
62 | return False
63 |
--------------------------------------------------------------------------------
/Peach/Transformers/Compression/Gzip.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import zlib
5 |
6 | from Peach.transformer import Transformer
7 |
8 |
9 | class Compress(Transformer):
10 | """Gzip compression transformer.
11 | Allows compression level selection (default is 6).
12 | """
13 |
14 | def __init__(self, level=6):
15 | """
16 | :param level: an integer from 1 to 9 controlling the level of
17 | compression; 1 is fastest and produces the least
18 | compression, 9 is slowest and produces the most.
19 | The default value is 6.
20 | :type level: int
21 | """
22 | Transformer.__init__(self)
23 | self._level = level
24 | self._wbits = 15
25 |
26 | def realEncode(self, data):
27 | return zlib.compress(data, self._level)
28 |
29 | def realDecode(self, data):
30 | return zlib.decompress(data, self._wbits)
31 |
32 |
33 | class Decompress(Transformer):
34 | """Gzip decompression transform."""
35 |
36 | def __init__(self, wbits=15):
37 | """
38 | :type wbits: int
39 | :param wbits: The absolute value of wbits is the base two logarithm
40 | of the size of the history buffer (the 'window size')
41 | used when compressing data. Its absolute value should be
42 | between 8 and 15 for the most recent versions of the
43 | zlib library, larger values resulting in better
44 | compression at the expense of greater memory usage.
45 | The default value is 15. When wbits is negative, the
46 | standard gzip header is suppressed; this is an
47 | undocumented feature of the zlib library, used for
48 | compatibility with unzip's compression file format.
49 | """
50 | Transformer.__init__(self)
51 | self._wbits = wbits
52 | self._level = 6
53 |
54 | def realEncode(self, data):
55 | return zlib.decompress(data, self._wbits)
56 |
57 | def realDecode(self, data):
58 | return zlib.compress(data, self._level)
59 |
--------------------------------------------------------------------------------
/Peach/Publishers/irc.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import time
6 | import socket
7 |
8 | from Peach.Engine.common import PeachException
9 | from Peach.Publishers.tcp import TcpListener
10 |
11 |
12 | #
13 | # CAP REQ :multi-prefix
14 | # NICK cdiehl
15 | # USER cdiehl 0 * :Christoph Diehl
16 | #
17 |
18 |
19 | class FakeServer(TcpListener):
20 | def __init__(self, host, port):
21 | TcpListener.__init__(self, host, port)
22 | self._accepted = False
23 |
24 | def start(self):
25 | if self._listen is None:
26 | for i in range(3):
27 | try:
28 | self._listen = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
29 | self._listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
30 | self._listen.bind((self._host, self._port))
31 | self._listen.listen(1)
32 | exception = None
33 | break
34 | except:
35 | self._listen = None
36 | exception = sys.exc_info()
37 | time.sleep(0.5)
38 | if self._listen is None:
39 | value = ""
40 | try:
41 | value = str(exception[1])
42 | except:
43 | pass
44 | raise PeachException("TCP bind attempt failed: %s" % value)
45 | self.buff = ""
46 | self.pos = 0
47 |
48 | def accept(self):
49 | if not self._accepted:
50 | self.buff = ""
51 | self.pos = 0
52 |
53 | conn, addr = self._listen.accept()
54 | self._socket = conn
55 | self._clientAddr = addr
56 | self._accepted = True
57 |
58 | def close(self):
59 | pass
60 |
61 | def stop(self):
62 | pass
63 |
64 |
65 |
66 | # connectedUsers = []
67 | #
68 | # request = client.recv(1024)
69 | # initialized = True
70 | #
71 | # request = request.split("\r\n")
72 | # for line in request:
73 | # if line.startswith("NICK"):
74 | # connectedUsers.append(line.split("NICK ")[1])
75 | #
76 | # hostname = client.gethostname()
77 | # client.send(":%s %s")
78 |
--------------------------------------------------------------------------------
/Peach/Transformers/Encode/HexString.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.transformer import Transformer
5 |
6 |
7 | class HexString(Transformer):
8 | """Transform a string of bytes into the specified hex format.
9 |
10 | Example:
11 |
12 | gen = Static("AAAABBBB").setTransformer(HexString())
13 | print gen.getValue()
14 | 41 41 41 41 42 42 42 42
15 |
16 | gen = Static("AAAABBBB").setTransformer(HexString(None, 4, "0x"))
17 | print gen.getValue()
18 | 0x414141410x42424242
19 |
20 | gen = Static("AAAABBBB").setTransformer(HexString(None, 1, " \\x"))
21 | print gen.getValue()
22 | \x41 \x41 \x41 \x41 \x42 \x42 \x42 \x42
23 | """
24 |
25 | def __init__(self, anotherTransformer=None, resolution=None, prefix=None):
26 | """Create Transformer object.
27 |
28 | :param anotherTransformer: a transformer to run next
29 | :type anotherTransformer: Transformer
30 | :param resolution: number of nibbles between separator
31 | (must be a positive even integer)
32 | :type resolution: int
33 | :param prefix: a value to prepend each chunk with (defaults to ' ')
34 | :type prefix: str
35 | """
36 |
37 | Transformer.__init__(self, anotherTransformer)
38 | self._resolution = resolution
39 | self._prefix = prefix
40 |
41 | def realEncode(self, data):
42 | ret = ''
43 | if self._resolution is None:
44 | self._resolution = 1
45 | # Try to detect if user passed in an odd numbered value
46 | if self._resolution % 2 and self._resolution != 1:
47 | raise Exception("Resolution must be 1 or a multiple of two.")
48 | if len(data) % self._resolution != 0:
49 | raise Exception("Data length must be divisible by resolution.")
50 | if self._prefix is None:
51 | self._prefix = " "
52 | tmp = ''
53 | for c in data:
54 | h = hex(ord(c))[2:]
55 | if len(h) == 2:
56 | tmp += h
57 | else:
58 | tmp += "0%s" % h
59 | if len(tmp) / 2 == self._resolution:
60 | ret += self._prefix + tmp
61 | tmp = ''
62 | ret = ret.strip()
63 | return ret
--------------------------------------------------------------------------------
/Peach/mutator.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.Engine.dom import *
5 |
6 |
7 | class MutatorCompleted(Exception):
8 | """
9 | At end of available mutations.
10 | """
11 | pass
12 |
13 |
14 | class MutatorError(Exception):
15 | pass
16 |
17 |
18 | class Mutator(object):
19 | """
20 | A Mutator implements a method of mutating data/state for a Peach fuzzer.
21 | For example a mutator might change the state flow defined by a Peach
22 | fuzzer. Another mutator might mutate data based on known relationships.
23 | Another mutator might perform numerical type tests against fields.
24 | """
25 | elementType = "mutator"
26 | dataTypes = [
27 | 'template',
28 | 'string',
29 | 'number',
30 | 'flags',
31 | 'choice',
32 | 'sequence',
33 | 'blob',
34 | 'block'
35 | ]
36 | _random = random.Random()
37 | weight = 1
38 |
39 | def __init__(self):
40 | self.name = "Mutator"
41 | self._count = None
42 | self.changedName = "N/A"
43 | self.isFinite = False
44 |
45 | @staticmethod
46 | def supportedDataElement(node):
47 | """
48 | Returns true if element is supported by this mutator.
49 | """
50 | return isinstance(node, DataElement) and node.isMutable
51 |
52 | def next(self):
53 | """
54 | Go to next mutation. When this is called the state machine is updated
55 | as needed.
56 | """
57 | pass
58 |
59 | def getCount(self):
60 | """
61 | If mutator is finite than the total test count can be calculated.
62 | This calculation cannot occur until after the state machine has been
63 | run the first time. Once the state machine has been run the count can
64 | be calculated. This typically occurs in a separate thread as it can
65 | take some time to calculate. This method will return None until a the
66 | correct value has been calculated.
67 | """
68 | return self._count
69 |
70 | def sequentialMutation(self, node):
71 | """
72 | Perform a sequential mutation.
73 | """
74 | pass
75 |
76 | def randomMutation(self, node):
77 | """
78 | Perform a random mutation.
79 | """
80 | pass
81 |
--------------------------------------------------------------------------------
/Peach/Publishers/sql.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from types import *
5 |
6 | import sys
7 |
8 | try:
9 | import dbi, odbc
10 | except ImportError:
11 | pass
12 |
13 | from Peach.publisher import Publisher
14 |
15 | #__all__ = ["Odbc"]
16 |
17 |
18 | class Odbc(Publisher):
19 | """
20 | Publisher for ODBC connections. Generated data sent as a SQL query via
21 | execute. Calling receave will return a string of all row data concatenated
22 | together with \t as field separator.
23 |
24 | Currently this Publisher makes use of the odbc package which is some what
25 | broken in that you must create an actual ODBC DSN via the ODBC manager.
26 | Check out mxODBC which is not open source for another alterative.
27 |
28 | Note: Each call to start/stop will create and close the SQL connection and
29 | cursor.
30 | """
31 |
32 | def __init__(self, dsn):
33 | """
34 | @type dsn: string
35 | @param dsn: DSN must be in format of "dsn/user/password" where DSN is a DSN name.
36 | """
37 | Publisher.__init__(self)
38 | self._dsn = dsn
39 | self._sql = None
40 | self._cursor = None
41 | self._sql = None
42 |
43 | def start(self):
44 | """
45 | Create connection to server.
46 | """
47 | self._sql = odbc.odbc(self._dsn)
48 |
49 | def stop(self):
50 | """
51 | Close any open cursors, and close connection to server.
52 | """
53 | self._cursor.close()
54 | self._cursor = None
55 | self._sql.close()
56 | self._sql = None
57 |
58 | def call(self, method, args):
59 | """
60 | Create cursor and execute data.
61 | """
62 | self._cursor = self._sql.cursor()
63 |
64 | try:
65 | self._cursor.execute(method, args)
66 | except:
67 | print("Warning: execute failed: %s" % sys.exc_info())
68 | pass
69 |
70 | ret = ''
71 | try:
72 | row = self._cursor.fetchone()
73 | for i in range(len(row)):
74 | retType = type(row[i])
75 | if retType is IntType:
76 | ret += "\t%d" % row[i]
77 | elif retType is FloatType:
78 | ret += "\t%f" % row[i]
79 | elif retType is LongType:
80 | ret += "\t%d" % row[i]
81 | elif retType is StringType:
82 | ret += "\t%s" % row[i]
83 |
84 | except:
85 | pass
86 |
87 | return ret
88 |
--------------------------------------------------------------------------------
/Peach/Publishers/httpserver.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
5 | import urllib
6 | import urlparse
7 | import base64
8 | import threading
9 |
10 | from Peach.Publishers.websocket import WebSocketFilePublisher
11 | from Peach.Utilities.network import getUnboundPort
12 |
13 |
14 | class HeaderReflectHandler(BaseHTTPRequestHandler):
15 | """
16 | A handler that reflects data in requests params to response headers
17 | """
18 | def _write_header(self, header, value):
19 | self.send_header(header, value)
20 |
21 | def do_GET(self):
22 | """
23 | Handle a GET request. Takes 2 request params; 'header' (the response
24 | header to write) and 'data' (base64 encoded data to be written to that
25 | header)
26 | """
27 | qs = {}
28 | path = self.path
29 | self.send_response(200)
30 | if '?' in path:
31 | path, tmp = path.split('?', 1)
32 | qs = urlparse.parse_qs(tmp)
33 | if qs['data'] and qs['header']:
34 | self._write_header(qs['header'][0], base64.b64decode(qs['data'][0]))
35 | self.end_headers()
36 | self.wfile.write('Wow! Such fuzz!')
37 | return
38 |
39 |
40 | class HttpHeaderPublisher(WebSocketFilePublisher):
41 | """
42 | A publisher for serving headers from an HTTP server.
43 | """
44 | def initialize(self):
45 | WebSocketFilePublisher.initialize(self)
46 | # find a port we can bind for our webserver
47 | self.webserver_port = getUnboundPort()
48 | self.debug("attempting to bind %i for webserver" % self.webserver_port)
49 | server = HTTPServer(('0.0.0.0', self.webserver_port), HeaderReflectHandler)
50 | self._running = True
51 |
52 | # TODO: start the webserver
53 | def run_server():
54 | while self._running:
55 | server.handle_request()
56 |
57 | t = threading.Thread(target=run_server)
58 | t.daemon = True
59 | t.start()
60 |
61 | def _prepareBrowserTemplate(self, data):
62 | if self._publish == "base64":
63 | data = self._contentTemplate.substitute(FILE=base64.b64encode(data), PORT=self.webserver_port)
64 | else:
65 | data = self._contentTemplate.substitute(FILE=urlparse.urljoin(self._publish, self._storagePath), PORT=self.webserver_port)
66 | data = urllib.quote(data)
67 | return data
68 |
69 | def finalize(self):
70 | WebSocketFilePublisher.finalize(self)
71 | self._running = False
72 |
--------------------------------------------------------------------------------
/Peach/Publishers/icmp.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import socket
5 | import time
6 |
7 | from Peach.publisher import Publisher
8 |
9 |
10 | class Icmp(Publisher):
11 | """
12 | A simple ICMP publisher.
13 | """
14 |
15 | _host = None
16 | _socket = None
17 |
18 | def __init__(self, host, timeout=0.1):
19 | """
20 | @type host: string
21 | @param host: Remote host
22 | @type timeout: number
23 | @param timeout: How long to wait for response
24 | """
25 | Publisher.__init__(self)
26 | self._host = host
27 | self._timeout = float(timeout)
28 |
29 | def stop(self):
30 | """
31 | Close connection if open.
32 | """
33 | self.close()
34 |
35 | def connect(self):
36 | if self._socket is not None:
37 | # Close out old socket first
38 | self._socket.close()
39 | self._socket = socket.socket(socket.AF_INET,
40 | socket.SOCK_RAW,
41 | socket.getprotobyname('icmp'))
42 | self._socket.connect((self._host, 22))
43 |
44 | def close(self):
45 | if self._socket is not None:
46 | self._socket.close()
47 | self._socket = None
48 |
49 | def send(self, data):
50 | """
51 | Send data via sendall.
52 |
53 | @type data: string
54 | @param data: Data to send
55 | """
56 | self._socket.sendall(data)
57 |
58 | def receive(self, size=None):
59 | """
60 | Receive up to 10000 bytes of data.
61 |
62 | @rtype: string
63 | @return: received data.
64 | """
65 | if size is not None:
66 | return self._socket.recv(size)
67 | else:
68 | self._socket.setblocking(0)
69 | timeout = self._timeout
70 | beginTime = time.time()
71 | ret = ""
72 | try:
73 | while True:
74 | if len(ret) > 0 or time.time() - beginTime > timeout:
75 | break
76 | try:
77 | ret += self._socket.recv(10000)
78 | except socket.error as e:
79 | if str(e).find("The socket operation could not "
80 | "complete without blocking") == -1:
81 | raise
82 | except socket.error as e:
83 | print("Tcp::Receive(): Caught socket.error [{}]".format(e))
84 | self._socket.setblocking(1)
85 | return ret
86 |
--------------------------------------------------------------------------------
/Peach/transformer.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 |
6 | class Transformer(object):
7 | """
8 | Transformers encode or decode some form of input e.g. Base64 string.
9 | Chained transformer run after this transformer.
10 | """
11 |
12 | def __init__(self, anotherTransformer=None):
13 | """Create a Transformer object.
14 |
15 | :param anotherTransformer: a transformer to run next
16 | :type anotherTransformer: Transformer
17 | """
18 | self._anotherTransformer = anotherTransformer
19 |
20 | def changesSize(self):
21 | return True
22 |
23 | def encode(self, data):
24 | """Transform data and call next transformer in chain if provided.
25 |
26 | :param data: data to transform
27 | :type data: str
28 | :returns: transformed data
29 | :rtype: str
30 | """
31 | ret = self.realEncode(data)
32 | if self._anotherTransformer is not None:
33 | return self._anotherTransformer.encode(ret)
34 | return ret
35 |
36 | def decode(self, data):
37 | """Perform reverse transformation if possible.
38 |
39 | :param data: data to transform
40 | :type data: str
41 | :returns: transformed data
42 | :rtype: str
43 | """
44 | if self._anotherTransformer is not None:
45 | data = self._anotherTransformer.decode(data)
46 | return self.realDecode(data)
47 |
48 | def getTransformer(self):
49 | """Gets the next transformer in the chain.
50 |
51 | :returns: next transformer in chain or None
52 | :rtype: Transformer
53 | """
54 | return self._anotherTransformer
55 |
56 | def addTransformer(self, transformer):
57 | """Set the next transformer in the chain.
58 |
59 | :param transformer: transformer to set
60 | :type transformer: Transformer
61 | :returns: this transformer
62 | :rtype: Transformer
63 | """
64 | self._anotherTransformer = transformer
65 | return self
66 |
67 | def realEncode(self, data):
68 | """Override this method to implement your transform.
69 |
70 | :param data: data to transform
71 | :type data: str
72 | :returns: transformed data
73 | :rtype: str
74 | """
75 | raise Exception("realEncode(): Transformer does not work both ways.")
76 |
77 | def realDecode(self, data):
78 | """Override this method to implement your transform.
79 |
80 | :param data: data to transform
81 | :type data: str
82 | :returns: transformed data
83 | :rtype: str
84 | """
85 | raise Exception("realDecode(): Transformer does not work both ways.")
86 |
--------------------------------------------------------------------------------
/Peach/config.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import os
5 | import uuid
6 | import subprocess
7 | import re
8 | import atexit
9 | import shutil
10 | import logging
11 | import shlex
12 |
13 | from Peach.Utilities import network
14 |
15 |
16 | def getInstanceProvider(name):
17 | if name not in globals():
18 | raise Exception("Invalid instance provider in configuration: " + name)
19 | return globals()[name]
20 |
21 |
22 | class FirefoxProfile(object):
23 | instances = {}
24 |
25 | def __init__(self, identifier, config):
26 | firefox_binary = shlex.split(config["DefaultBrowser"])
27 | self.profile_name = identifier + "-" + str(uuid.uuid4())
28 | output = subprocess.check_output(firefox_binary +
29 | ['-no-remote',
30 | '-CreateProfile', self.profile_name],
31 | stderr=subprocess.STDOUT)
32 | output = output.strip()
33 | if "Success: created profile" not in output:
34 | raise Exception("Unexpected output while creating Firefox profile: %s" % output)
35 | self.profile_prefs_path = re.findall("'.+?'", output)[1].strip("'")
36 | pref_path = config['DefaultFirefoxPrefs']
37 | logging.debug(self.profile_prefs_path)
38 | shutil.copyfile(pref_path, self.profile_prefs_path)
39 | logging.debug("Successfully created temporary Firefox profile at %s" % self.profile_prefs_path)
40 |
41 | @staticmethod
42 | def getInstanceById(identifier, config):
43 | if identifier not in FirefoxProfile.instances:
44 | FirefoxProfile.instances[identifier] = FirefoxProfile(identifier, config)
45 | return FirefoxProfile.instances[identifier]
46 |
47 | @staticmethod
48 | @atexit.register
49 | def cleanInstances():
50 | for instance in FirefoxProfile.instances.values():
51 | instance.destroy()
52 |
53 | def __repr__(self):
54 | return self.profile_name
55 |
56 | def destroy(self):
57 | if self.profile_prefs_path.endswith('/prefs.js'):
58 | profile_path = os.path.dirname(self.profile_prefs_path)
59 | logging.debug("Deleting temporary Firefox profile at %s" % profile_path)
60 | shutil.rmtree(profile_path)
61 |
62 |
63 | class TCPPort(object):
64 | instances = {}
65 |
66 | def __init__(self, identifier, config):
67 | self.port_number = network.getUnboundPort(assignOnlyOnce=True)
68 | if self.port_number < 0:
69 | raise Exception("Unable to allocate free TCP port")
70 |
71 | @staticmethod
72 | def getInstanceById(identifier, config):
73 | if identifier not in TCPPort.instances:
74 | TCPPort.instances[identifier] = TCPPort(identifier, config)
75 | return TCPPort.instances[identifier]
76 |
77 | def __repr__(self):
78 | return str(self.port_number)
79 |
--------------------------------------------------------------------------------
/Peach/Mutators/path.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.mutator import *
5 | from Peach.mutatestrategies import *
6 |
7 |
8 | class NullMutator(Mutator):
9 | """
10 | Does not make any changes to data tree. This is usually the first mutator
11 | applied to a fuzzing run so the generated data can be verified.
12 | """
13 |
14 | def __init__(self, peach):
15 | Mutator.__init__(self)
16 | self.name = "NullMutator"
17 |
18 | def isFinite(self):
19 | """
20 | Some mutators could continue forever, this should indicate.
21 | """
22 | return True
23 |
24 | def next(self):
25 | """
26 | Go to next mutation. When this is called the state machine is updated
27 | as needed.
28 | """
29 | raise MutatorCompleted()
30 |
31 | def getState(self):
32 | """
33 | Return a binary string that contains any information about current
34 | state of Mutator. This state information should be enough to let the
35 | same mutator "restart" and continue when setState() is called.
36 | """
37 | return ""
38 |
39 | def setState(self, state):
40 | """
41 | Set the state of this object. Should put us back in the same place as
42 | when we said "getState()".
43 | """
44 | pass
45 |
46 | def getCount(self):
47 | return 1
48 |
49 | def getActionValue(self, action):
50 | if action.template.modelHasOffsetRelation:
51 | stringBuffer = StreamBuffer()
52 | action.template.getValue(stringBuffer)
53 | stringBuffer.setValue("")
54 | stringBuffer.seekFromStart(0)
55 | action.template.getValue(stringBuffer)
56 | return stringBuffer.getValue()
57 | return action.template.getValue()
58 |
59 | def getActionParamValue(self, action):
60 | if action.template.modelHasOffsetRelation:
61 | stringBuffer = StreamBuffer()
62 | action.template.getValue(stringBuffer)
63 | stringBuffer.setValue("")
64 | stringBuffer.seekFromStart(0)
65 | action.template.getValue(stringBuffer)
66 | return stringBuffer.getValue()
67 | return action.template.getValue()
68 |
69 | def getActionChangeStateValue(self, action, value):
70 | return value
71 |
72 |
73 | class PathValidationMutator(NullMutator, MutationStrategy):
74 | """
75 | This mutator is just used to trace path of each test for path validation
76 | purposes so this is not an actual Mutator that is used on fuzzing.
77 | """
78 |
79 | def __init__(self):
80 | Mutator.__init__(self)
81 | self.states = []
82 | self.name = "PathValidationMutator"
83 |
84 | def onStateStarting(self, stateMachine, state):
85 | self.states.append(state.name)
86 |
87 | def onStateMachineStarting(self, engine):
88 | pass
89 |
90 | def onStateMachineComplete(self, engine):
91 | engine.pathFinder.reset()
92 |
--------------------------------------------------------------------------------
/Peach/Publishers/dll.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import ctypes
5 | from Peach.publisher import Publisher
6 |
7 |
8 | class Dll(Publisher):
9 | """
10 | Shared library publisher using ctypes
11 | """
12 |
13 | def __init__(self, library):
14 | Publisher.__init__(self)
15 | self.library = library
16 | self.dll = None
17 | self.withNode = True
18 |
19 | def start(self):
20 | try:
21 | self.dll = ctypes.cdll.LoadLibrary(self.library)
22 |
23 | except:
24 | print("Caught exception loading library [%s]" % self.library)
25 | raise
26 |
27 | def stop(self):
28 | self.dll = None
29 |
30 | def callWithNode(self, method, args, argNodes):
31 | """
32 | Call method on COM object.
33 |
34 | @type method: string
35 | @param method: Name of method to invoke
36 | @type args: array of objects
37 | @param args: Arguments to pass
38 | """
39 |
40 | self._lastReturn = None
41 |
42 | #ct = argNodes[0].asCType()
43 | #print ct
44 | #print ct.contents
45 | #print ct._fields_
46 | #print ct.Named_37
47 | #print ct.Named_38
48 |
49 | try:
50 | ret = None
51 | callStr = "self.dll.%s(" % str(method)
52 |
53 | if len(args) > 0:
54 | for i in range(0, len(args)):
55 | callStr += "argNodes[%d].asCType()," % i
56 |
57 | callStr = callStr[:len(callStr) - 1] + ")"
58 |
59 | else:
60 | callStr += ")"
61 |
62 | ret = eval(callStr)
63 | return ret
64 |
65 | except:
66 | print("dll.Dll(): Caught unknown exception making call to %s" % method)
67 | raise
68 |
69 | #def property(self, property, value = None):
70 | # '''
71 | # Get or set property
72 | #
73 | # @type property: string
74 | # @param property: Name of method to invoke
75 | # @type value: object
76 | # @param value: Value to set. If None, return property instead
77 | # '''
78 | #
79 | # try:
80 | # if value == None:
81 | # ret = None
82 | # callStr = "ret = self._object.%s" % str(property)
83 | #
84 | # #print "Call string:", callStr
85 | # exec callStr
86 | # return ret
87 | #
88 | # ret = None
89 | # callStr = "self._object.%s = value" % str(property)
90 | #
91 | # #print "Call string:", callStr
92 | # exec callStr
93 | # return None
94 | #
95 | # except pywintypes.com_error, e:
96 | # print "Caught pywintypes.com_error on property [%s]" % e
97 | # #raise
98 | #
99 | # except NameError, e:
100 | # print "Caught NameError on property [%s]" % e
101 | # #raise
102 | #
103 | # except:
104 | # print "Com::property(): Caught unknown exception"
105 | # raise
106 | #
107 | # return None
108 |
--------------------------------------------------------------------------------
/Peach/Engine/defaults.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Peach/Publishers/stdout.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import logging
5 |
6 | from Peach.publisher import Publisher
7 | from Peach.Utilities.common import printHex
8 |
9 |
10 | class Stdout(Publisher):
11 | """
12 | Basic stdout publisher. All data is written to stdout.
13 | No input is available from this publisher.
14 | """
15 |
16 | def accept(self):
17 | pass
18 |
19 | def connect(self):
20 | pass
21 |
22 | def close(self):
23 | pass
24 |
25 | def send(self, data):
26 | print(data)
27 |
28 | def receive(self, size=None):
29 | return ""
30 |
31 | def call(self, method, args):
32 | str = ""
33 | for a in args:
34 | str += "%s, " % repr(a)
35 | print("%s(%s)" % (method, str[:-2]))
36 | return ""
37 |
38 |
39 | class StdoutHex(Publisher):
40 | """
41 | Basic stdout publisher that emits a hex dump. All data is written to stdout.
42 | No input is available from this publisher.
43 | """
44 |
45 | def accept(self):
46 | pass
47 |
48 | def connect(self):
49 | pass
50 |
51 | def close(self):
52 | pass
53 |
54 | def send(self, src):
55 | # http://code.activestate.com/recipes/142812/
56 | FILTER = "".join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
57 | N = 0
58 | result = ""
59 | length = 16
60 | while src:
61 | s, src = src[:length], src[length:]
62 | hexa = " ".join(["%02X" % ord(x) for x in s])
63 | s = s.translate(FILTER)
64 | result += "%04X %-*s %s\n" % (N, length * 3, hexa, s)
65 | N += length
66 | logging.debug("Hexdump of mutated data:\n%s" % result)
67 |
68 | def receive(self, size=None):
69 | return ""
70 |
71 | def call(self, method, args):
72 | str = ""
73 | for a in args:
74 | str += "%s, " % repr(a)
75 | print("%s(%s)" % (method, str[:-2]))
76 | return ""
77 |
78 |
79 | class Null(Publisher):
80 | """
81 | Basic stdout publisher. All data is written to stdout.
82 | No input is available from this publisher.
83 | """
84 |
85 | def accept(self):
86 | pass
87 |
88 | def connect(self):
89 | pass
90 |
91 | def close(self):
92 | pass
93 |
94 | def send(self, data):
95 | pass
96 |
97 | def receive(self):
98 | return ""
99 |
100 | def call(self, method, args):
101 | return ""
102 |
103 |
104 | class StdoutCtypes(Publisher):
105 | withNode = True
106 |
107 | def connect(self):
108 | pass
109 |
110 | def sendWithNode(self, data, dataNode):
111 | """
112 | Publish some data
113 |
114 | @type data: string
115 | @param data: Data to publish
116 | @type dataNode: DataElement
117 | @param dataNode: Root of data model that produced data
118 | """
119 | value = dataNode.asCType()
120 | print(value)
121 | print(dir(value))
122 |
123 | def close(self):
124 | """
125 | Close current stream/connection.
126 | """
127 | pass
128 |
--------------------------------------------------------------------------------
/Pits/Targets/console.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Peach/Agent/util.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import os
5 | import shutil
6 |
7 | from Peach.agent import Monitor
8 |
9 |
10 | class CleanupFolder(Monitor):
11 | """
12 | This monitor will remove any files created in a folder during a fuzzing
13 | iteration. Create for removing stale temp files, etc.
14 | """
15 |
16 | def __init__(self, args):
17 | """
18 | Constructor. Arguments are supplied via the Peach XML file.
19 |
20 | @type args: Dictionary
21 | @param args: Dictionary of parameters
22 | """
23 | self._name = None
24 | self._folder = args['Folder'].replace("'''", "")
25 | self._folderListing = os.listdir(self._folder)
26 |
27 | def OnTestStarting(self):
28 | """
29 | Called right after a test case or variation.
30 | """
31 | listing = os.listdir(self._folder)
32 | for item in listing:
33 | if item not in self._folderListing:
34 | realName = os.path.join(self._folder, item)
35 | print("CleanupFolder: Removing '{}'".format(realName))
36 | try:
37 | os.unlink(realName)
38 | except:
39 | pass
40 | try:
41 | shutil.rmtree(realName)
42 | except:
43 | pass
44 |
45 |
46 | try:
47 | import win32api, win32con
48 | except:
49 | pass
50 |
51 |
52 | class CleanupRegistry(Monitor):
53 | """
54 | This monitor will remove any sub-keys for a specified registry key before
55 | each run. This is useful for removing document recovery keys for fuzzing
56 | Office.
57 | """
58 |
59 | def __init__(self, args):
60 | """
61 | Constructor. Arguments are supplied via the Peach XML file.
62 |
63 | @type args: Dictionary
64 | @param args: Dictionary of parameters
65 | """
66 | self._name = None
67 | self._key = args['Key'].replace("'''", "")
68 |
69 | if self._key.startswith("HKCU\\"):
70 | self._root = win32con.HKEY_CURRENT_USER
71 | elif self._key.startswith("HKCC\\"):
72 | self._root = win32con.HKEY_CURRENT_CONFIG
73 | elif self._key.startswith("HKLM\\"):
74 | self._root = win32con.HKEY_LOCAL_MACHINE
75 | elif self._key.startswith("HKPD\\"):
76 | self._root = win32con.HKEY_PERFORMANCE_DATA
77 | elif self._key.startswith("HKU\\"):
78 | self._root = win32con.HKEY_USERS
79 | else:
80 | print("CleanupRegistry: Error, key must be prefixed with: "
81 | "HKCU, HKCC, HKLM, HKPD, or HKU.")
82 | raise Exception("CleanupRegistry: Error, key must be prefixed "
83 | "with: HKCU, HKCC, HKLM, HKPD, or HKU.")
84 | self._key = self._key[self._key.find("\\") + 1:]
85 |
86 | def OnTestStarting(self):
87 | self.deleteKey(self._root, self._key)
88 |
89 | def deleteKey(self, hKey, subKey):
90 | """
91 | Recursively remove registry keys.
92 | """
93 | try:
94 | hKey = win32api.RegOpenKeyEx(hKey, subKey, 0,
95 | win32con.KEY_ALL_ACCESS)
96 | try:
97 | while True:
98 | s = win32api.RegEnumKey(hKey, 0)
99 | self.deleteKey(hKey, s)
100 | print("CleanupRegistry: Removing sub-key '{}'".format(s))
101 | win32api.RegDeleteKey(hKey, s)
102 | except win32api.error:
103 | pass
104 | finally:
105 | win32api.RegCloseKey(hKey)
106 | except:
107 | print("Warning: Unable to open registry key!")
108 | pass
109 |
--------------------------------------------------------------------------------
/Peach/Publishers/sslayer.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import ssl
6 | import socket
7 | import tlslite.api
8 |
9 | from Peach.Publishers.tcp import TcpListener
10 | from Peach.publisher import PublisherSoftException
11 | from Peach.Engine.common import PeachException
12 |
13 |
14 | class SSL(TcpListener):
15 | def __init__(self, host, port, cert, pkey, timeout=0.25):
16 | TcpListener.__init__(self, host, port, timeout)
17 |
18 | self.cert = cert
19 | self.pkey = pkey
20 |
21 | def accept(self):
22 | print("[*] Waiting for incoming connection")
23 | client, addr = self._listen.accept()
24 | print("[*] Client:", addr[0], addr[1])
25 |
26 | print("[*] Wrapping socket to TLS/SSL")
27 | try:
28 | self._socket = ssl.wrap_socket(client,
29 | server_side=True,
30 | certfile=self.cert,
31 | keyfile=self.pkey,
32 | do_handshake_on_connect=False)
33 | except ssl.SSLError as e:
34 | raise PeachException(str(e))
35 |
36 | print("[*] Performing TLS/SSL handshake")
37 | try:
38 | self._socket.do_handshake()
39 | except ssl.SSLError as e:
40 | raise PeachException(str(e))
41 |
42 | def close(self):
43 | try:
44 | if self._socket is not None:
45 | self._socket.shutdown(socket.SHUT_RDWR)
46 | self._socket.close()
47 | except:
48 | pass
49 | finally:
50 | self._socket = None
51 |
52 |
53 | class TLSLiteServer(TcpListener):
54 | def __init__(self, host, port, version, cert, pkey, timeout=0.25):
55 | TcpListener.__init__(self, host, port, timeout)
56 |
57 | self.cert = cert
58 | self.pkey = pkey
59 | self.version = version
60 |
61 | try:
62 | with open(self.cert) as fd:
63 | cert_content = fd.read()
64 | except IOError:
65 | raise PeachException("Unable to open %s" % self.cert)
66 |
67 | x509 = tlslite.api.X509()
68 | x509.parse(cert_content)
69 | self.certChain = tlslite.api.X509CertChain([x509])
70 |
71 | try:
72 | with open(self.pkey) as fd:
73 | pkey_content = fd.read()
74 | except IOError:
75 | raise PeachException("Unable to open %s" % self.pkey)
76 |
77 | self.privateKey = tlslite.api.parsePEMKey(pkey_content, private=True)
78 |
79 | def accept(self):
80 | print("[*] Waiting for incoming connection")
81 | sys.stdout.flush()
82 | client, addr = self._listen.accept()
83 |
84 | print("[*] Connected by %s:%s" % (addr[0], str(addr[1])))
85 | print("[*] Wrapping socket to TLS/SSL")
86 | try:
87 | self._socket = tlslite.api.TLSConnection(client)
88 | except:
89 | client.close()
90 | value = sys.exc_info()[1]
91 | msg = "[!] Wrapping socket failed, reason: %s" % value
92 | raise PublisherSoftException(msg)
93 |
94 | print("[*] Performing TLS/SSL handshake)")
95 | try:
96 | self._socket.handshakeServer(certChain=self.certChain,
97 | privateKey=self.privateKey,
98 | #reqCert=True,
99 | nextProtos=[self.version])
100 | except:
101 | self.close()
102 | value = sys.exc_info()[1]
103 | msg = "[!] Performing TLS/SSL handshake failed, reason: %s" % value
104 | raise PublisherSoftException(msg)
105 | print("done!")
106 |
107 |
108 | class SPDYPublisher(TLSLiteServer):
109 | def __init__(self, host, port, cert, pkey, timeout=0.25):
110 | TLSLiteServer.__init__(self, host, port, cert, pkey, timeout)
111 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/ASN1.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | try:
5 | from struct import *
6 | from Peach.transformer import Transformer
7 | from pyasn1.type import univ
8 | from pyasn1.codec import der, ber, cer
9 |
10 | class DerEncodeOctetString(Transformer):
11 | """DER encode an octect string ASN.1 style."""
12 |
13 | def realEncode(self, data):
14 | return der.encoder.encode(univ.OctetString(data))
15 |
16 | class DerEncodeBitString(Transformer):
17 | """DER encode a bit string ASN.1 style."""
18 |
19 | def realEncode(self, data):
20 | return der.encoder.encode(univ.BitString(data))
21 |
22 | class DerEncodeInteger(Transformer):
23 | """DER encode an integer ASN.1 style."""
24 |
25 | def realEncode(self, data):
26 | return der.encoder.encode(univ.Integer(int(data)))
27 |
28 | class DerEncodeBoolean(Transformer):
29 | """DER encode a boolean ASN.1 style. Expects 0 or 1."""
30 |
31 | def realEncode(self, data):
32 | data = int(data)
33 | if data != 0 and data != 1:
34 | raise Exception("DerEncodeBoolean transformer expects 0 or 1")
35 | return der.encoder.encode(univ.Boolean(data))
36 |
37 | class DerEncodeObjectIdentifier(Transformer):
38 | """DER encode an object identifierASN.1 style."""
39 |
40 | def realEncode(self, data):
41 | return der.encoder.encode(univ.ObjectIdentifier(data))
42 |
43 | class BerEncodeOctetString(Transformer):
44 | """BER encode a string ASN.1 style."""
45 |
46 | def realEncode(self, data):
47 | return ber.encoder.encode(univ.OctetString(data))
48 |
49 | class BerEncodeBitString(Transformer):
50 | """BER encode a bit string ASN.1 style."""
51 |
52 | def realEncode(self, data):
53 | return ber.encoder.encode(univ.BitString(data))
54 |
55 | class BerEncodeInteger(Transformer):
56 | """BER encode an integer ASN.1 style."""
57 |
58 | def realEncode(self, data):
59 | return ber.encoder.encode(univ.Integer(int(data)))
60 |
61 | class BerEncodeBoolean(Transformer):
62 | """BER encode a boolean ASN.1 style. Expects 0 or 1."""
63 |
64 | def realEncode(self, data):
65 | data = int(data)
66 | if data != 0 and data != 1:
67 | raise Exception("BerEncodeBoolean transformer expects 0 or 1")
68 | return ber.encoder.encode(univ.Boolean(data))
69 |
70 | class BerEncodeObjectIdentifier(Transformer):
71 | """BER encode an object identifierASN.1 style."""
72 |
73 | def realEncode(self, data):
74 | return ber.encoder.encode(univ.ObjectIdentifier(data))
75 |
76 | class CerEncodeOctetString(Transformer):
77 | """CER encode a string ASN.1 style."""
78 |
79 | def realEncode(self, data):
80 | return cer.encoder.encode(univ.OctetString(data))
81 |
82 | class CerEncodeBitString(Transformer):
83 | """CER encode a bit string ASN.1 style."""
84 |
85 | def realEncode(self, data):
86 | return cer.encoder.encode(univ.BitString(data))
87 |
88 | class CerEncodeInteger(Transformer):
89 | """CER encode an integer ASN.1 style."""
90 |
91 | def realEncode(self, data):
92 | return cer.encoder.encode(univ.Integer(int(data)))
93 |
94 | class CerEncodeBoolean(Transformer):
95 | """CER encode a boolean ASN.1 style. Expects 0 or 1."""
96 |
97 | def realEncode(self, data):
98 | data = int(data)
99 | if data != 0 and data != 1:
100 | raise Exception("CerEncodeBoolean transformer expects 0 or 1")
101 | return cer.encoder.encode(univ.Boolean(data))
102 |
103 | class CerEncodeObjectIdentifier(Transformer):
104 | """CER encode an object identifierASN.1 style."""
105 |
106 | def realEncode(self, data):
107 | return cer.encoder.encode(univ.ObjectIdentifier(data))
108 | except Exception as e:
109 | pass
110 |
--------------------------------------------------------------------------------
/Peach/Generators/flipper.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import random
5 | import struct
6 | from Peach.generator import *
7 |
8 |
9 | class SequentialFlipper(SimpleGenerator):
10 | """
11 | Sequentially flips bits in a data blob. This is for "random" fuzzing.
12 | Useful brute forcing, codecs, etc.
13 | """
14 |
15 | _data = None
16 | _position = 0
17 |
18 | def __init__(self, group, data):
19 | """
20 | @type data: string
21 | @param data: Binary static blob to flip
22 | """
23 | SimpleGenerator.__init__(self, group)
24 | self._data = str(data) # Could be a unicode string.
25 | self._len = len(data)
26 | self._position = 0
27 | self._bit = 0
28 |
29 | def next(self):
30 | if self._bit == 7:
31 | self._position += 1
32 | self._bit = 0
33 | if self._position >= self._len:
34 | self._position -= 1
35 | self._bit = 7
36 | raise GeneratorCompleted("all done here")
37 | else:
38 | self._bit += 1
39 |
40 | def reset(self):
41 | self._bit = 0
42 | self._position = 0
43 |
44 | def getRawValue(self):
45 | if self._position >= self._len:
46 | return ""
47 | data = self._data
48 | byte = struct.unpack('B', data[self._position])[0]
49 | mask = 1 << self._bit
50 | if (byte & mask) >> self._bit == 1:
51 | mask = 0
52 | for i in range(8 - self._bit):
53 | mask |= 1 << i
54 | mask <<= 1
55 | if self._bit > 1:
56 | mask <<= self._bit
57 | for i in range(self._bit):
58 | mask |= 1 << i
59 | byte &= mask
60 | else:
61 | byte |= mask
62 | packedup = struct.pack("B", byte)
63 | data = data[:self._position] + packedup + data[self._position + 1:]
64 | return data
65 |
66 |
67 | class PartialFlipper(SimpleGenerator):
68 | """
69 | Performs flips of 20% of bits in data.
70 | """
71 |
72 | _data = None
73 | _position = 0
74 |
75 | def __init__(self, group, data, maxRounds=None):
76 | """
77 | @type data: string
78 | @param data: Binary static blob to flip
79 | @type maxRounds: int
80 | @param maxRounds: optional, override 0.2% with a fixed number
81 | """
82 | SimpleGenerator.__init__(self, group)
83 |
84 | self._data = str(data) # Could be a unicode string.
85 | self._len = len(data)
86 | self._position = 0
87 | self._bit = 0
88 | self._maxRounds = int((len(data) * 8) * 0.20)
89 | self._round = 0
90 |
91 | if maxRounds is not None:
92 | self._maxRounds = maxRounds
93 |
94 | def next(self):
95 | self._round += 1
96 | # Exit if we are completed with rounds, or have no data
97 | # to flip
98 | if self._round > self._maxRounds or (len(self._data) - 1) < 1:
99 | raise GeneratorCompleted("all done here")
100 | self._position = random.randint(0, len(self._data) - 1)
101 | self._bit = random.randint(0, 7)
102 |
103 | def reset(self):
104 | self._bit = 0
105 | self._position = 0
106 | self._round = 0
107 |
108 | def getRawValue(self):
109 | if self._position >= self._len:
110 | return ""
111 | data = self._data
112 | byte = struct.unpack('B', data[self._position])[0]
113 | mask = 1 << self._bit
114 | if (byte & mask) >> self._bit == 1:
115 | mask = 0
116 | for i in range(8 - self._bit):
117 | mask |= 1 << i
118 | mask <<= 1
119 | if self._bit > 1:
120 | mask <<= self._bit
121 | for i in range(self._bit):
122 | mask |= 1 << i
123 | byte &= mask
124 | else:
125 | byte |= mask
126 | packedup = struct.pack("B", byte)
127 | data = data[:self._position] + packedup + data[self._position + 1:]
128 | return data
129 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/NumberToString.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 | from struct import unpack
6 | from types import IntType
7 | from types import FloatType
8 | from types import LongType
9 |
10 | from Peach.transformer import Transformer
11 |
12 |
13 | class NumberToString(Transformer):
14 | """Transforms any type of number (int, long, float) to string."""
15 |
16 | def __init__(self, formatString=None):
17 | """Create NumberToString Instance.
18 | |formatString| is a standard Python string formatter (optional).
19 | """
20 | Transformer.__init__(self)
21 | self._formatString = formatString
22 |
23 | def realEncode(self, data):
24 | """Convert number to string.
25 | If no formatString was specified in class constructor data type is
26 | dynamically determined and converted using a default formatString of
27 | "%d", "%f", or "%d" for Int, Float, and Long respectively.
28 | """
29 |
30 | if self._formatString is None:
31 | retType = type(data)
32 | if retType is IntType:
33 | return "%d" % data
34 | elif retType is FloatType:
35 | return "%f" % data
36 | elif retType is LongType:
37 | return "%d" % data
38 | else:
39 | return data
40 |
41 | return self._formatString % data
42 |
43 |
44 | class SignedNumberToString(Transformer):
45 | """Transforms unsigned numbers to strings."""
46 |
47 | def __init__(self):
48 | Transformer.__init__(self)
49 | self._size = None
50 |
51 | def realEncode(self, data):
52 | self._size = size = len(data) * 8
53 |
54 | if size == 0:
55 | return ""
56 | elif size == 8:
57 | return str(unpack("b", data)[0])
58 | elif size == 16:
59 | return str(unpack("h", data)[0])
60 | elif size == 24:
61 | raise Exception("24 bit numbers not supported")
62 | elif size == 32:
63 | return str(unpack("i", data)[0])
64 | elif size == 64:
65 | return str(unpack("q", data)[0])
66 |
67 | raise Exception("Unknown numerical size")
68 |
69 | def realDecode(self, data):
70 | size = self._size
71 |
72 | if size is None:
73 | return ""
74 |
75 | if size == 8:
76 | return pack("b", data)
77 | elif size == 16:
78 | return pack("h", data)
79 | elif size == 24:
80 | raise Exception("24 bit numbers not supported")
81 | elif size == 32:
82 | return pack("i", data)
83 | elif size == 64:
84 | return pack("q", data)
85 |
86 | raise Exception("Unknown numerical size")
87 |
88 |
89 | class UnsignedNumberToString(Transformer):
90 | """Transforms unsigned numbers to strings."""
91 |
92 | def __init__(self):
93 | Transformer.__init__(self)
94 | self._size = None
95 |
96 | def realEncode(self, data):
97 | self._size = size = len(data) * 8
98 |
99 | if size == 0:
100 | return ""
101 | elif size == 8:
102 | return str(unpack("B", data)[0])
103 | elif size == 16:
104 | return str(unpack("H", data)[0])
105 | elif size == 24:
106 | raise Exception("24 bit numbers not supported")
107 | elif size == 32:
108 | return str(unpack("I", data)[0])
109 | elif size == 64:
110 | return str(unpack("Q", data)[0])
111 |
112 | raise Exception("Unknown numerical size:")
113 |
114 | def realDecode(self, data):
115 | size = self._size
116 |
117 | if size is None:
118 | return ""
119 |
120 | if size == 8:
121 | return pack("B", data)
122 | elif size == 16:
123 | return pack("H", data)
124 | elif size == 24:
125 | raise Exception("24 bit numbers not supported")
126 | elif size == 32:
127 | return pack("I", data)
128 | elif size == 64:
129 | return pack("Q", data)
130 |
131 | raise Exception("Unknown numerical size")
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | MozPeach is a fork of [Peach v2.7](http://www.peachfuzzer.com) by Mozilla Security. With support from our community and partnerships our goal is to continue to deliver Peach as an open source product with Python compatibility and new features.
4 |
5 | Our focus is on usability, speed and fewer dependencies. We have also begun work on Python 3 support, replaced deprecated Python dependencies, switched the XML back-end, added a new configuration system, simplified code and much more.
6 |
7 | ### Setup
8 |
9 | ##### Prerequisites for Ubuntu
10 | ```
11 | sudo apt-get --yes --quiet install libxml2-dev libxslt1-dev lib32z1-dev
12 | ```
13 |
14 | ##### General
15 | ```bash
16 | pip install virtualenv
17 | pip install virtualenvwrapper
18 |
19 | git clone --depth 1 https://github.com/mozillasecurity/peach
20 |
21 | cd peach
22 | git clone --depth 1 https://github.com/mozillasecurity/fuzzdata
23 |
24 | mkvirtualenv -r requirements.txt peach
25 | ```
26 |
27 | or
28 |
29 | ```bash
30 | workon peach
31 | ```
32 |
33 | ### Fundamentals
34 | Peach uses XML based "pits" as configuration files. There are two types of pits which we will briefly describe here.
35 |
36 | **Pit: Data Model**
37 |
38 | A data-model pit is an XML description of a specification and is required to parse any kind of input into an in-memory XML tree. Peach then uses that tree to generate fuzzed output.
39 |
40 | **Pit: Target**
41 |
42 | The target pit is used to define how the target process will get fuzzed, how it will be monitored for suspicious behavior and how to deal with results.
43 |
44 | It is optional whether you place everything into one pit however not doing so will simplify working with multiple targets, different hosts and reusing pits. Following the data model/target pit practice will allow the reuse of data model pits across projects.
45 |
46 |
47 | ### Examples
48 |
49 | ##### Run
50 | ```bash
51 | ./peach.py -pit Pits///.xml -target Pits/Targets/firefox.xml -run Browser
52 | ```
53 |
54 | **HINT**: You can set related configuration values for both pits from the command-line by using the -macros switch.
55 |
56 | ##### Debug
57 | ```bash
58 | ./peach.py -pit Pits///.xml -1 -debug | less -R
59 | ```
60 |
61 | **NOTE**: This will show a very verbose output of the parsing process. To see only the results of the parsing process for each element you can add: "| grep Rating | less -R"
62 |
63 |
64 | ### Help Menu
65 | ```
66 | % ./peach.py -h
67 | usage: peach.py [-h] [-pit path] [-run name]
68 | [-analyzer ANALYZER [ANALYZER ...]] [-parser PARSER]
69 | [-target TARGET] [-macros MACROS [MACROS ...]] [-seed #]
70 | [-debug] [-new] [-1] [-range # #] [-test] [-count] [-skipto #]
71 | [-parallel # #] [-agent # #] [-logging #]
72 | [-check model samples] [-verbose] [-clean] [-version]
73 |
74 | Peach Runtime
75 |
76 | optional arguments:
77 | -h, --help show this help message and exit
78 | -pit path pit file
79 | -run name run name
80 | -analyzer ANALYZER [ANALYZER ...]
81 | load analyzer.
82 | -parser PARSER use specific parser.
83 | -target TARGET select a target pit.
84 | -macros MACROS [MACROS ...]
85 | override configuration macros
86 | -seed # seed
87 | -debug turn on debugging. (default: False)
88 | -new use new relations.
89 | -1 run single test case.
90 | -range # # run range of test cases.
91 | -test validate pit file.
92 | -count count test cases for deterministic strategies.
93 | -skipto # skip to a test case number.
94 | -parallel # # use parallelism.
95 | -agent # # start agent.
96 | -logging # verbosity level of logging
97 | -check model samples validate a data model against a set of samples.
98 | -verbose turn verbosity on. (default: False)
99 | -clean remove python object files.
100 | -version show program's version number and exit
101 | ```
102 |
103 | ### Resources
104 |
105 | Resources which aid in building a pit based on the grammar of a file format:
106 |
107 | * http://www.sweetscape.com/010editor/templates/
108 | * http://www.synalysis.net/formats.xml
109 |
--------------------------------------------------------------------------------
/Peach/Mutators/datatree.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.mutator import *
5 | from Peach.Engine.common import *
6 |
7 |
8 | class DataTreeRemoveMutator(Mutator):
9 | """
10 | Remove nodes from data tree.
11 | """
12 |
13 | def __init__(self, peach, node):
14 | Mutator.__init__(self)
15 | self.isFinite = True
16 | self.name = "DataTreeRemoveMutator"
17 | self._peach = peach
18 |
19 | def next(self):
20 | raise MutatorCompleted()
21 |
22 | def getCount(self):
23 | return 1
24 |
25 | @staticmethod
26 | def supportedDataElement(e):
27 | if isinstance(e, DataElement) and e.isMutable:
28 | return True
29 | return False
30 |
31 | def sequentialMutation(self, node):
32 | self.changedName = node.getFullnameInDataModel()
33 | node.setValue("")
34 |
35 | def randomMutation(self, node, rand):
36 | self.changedName = node.getFullnameInDataModel()
37 | node.setValue("")
38 |
39 |
40 | class DataTreeDuplicateMutator(Mutator):
41 | """
42 | Duplicate a node's value starting at 2x through 50x.
43 | """
44 |
45 | def __init__(self, peach, node):
46 | Mutator.__init__(self)
47 | self.isFinite = True
48 | self.name = "DataTreeDuplicateMutator"
49 | self._peach = peach
50 | self._cnt = 2
51 | self._maxCount = 50
52 |
53 | def next(self):
54 | self._cnt += 1
55 | if self._cnt > self._maxCount:
56 | raise MutatorCompleted()
57 |
58 | def getCount(self):
59 | return self._maxCount
60 |
61 | @staticmethod
62 | def supportedDataElement(e):
63 | if isinstance(e, DataElement) and e.isMutable:
64 | return True
65 | return False
66 |
67 | def sequentialMutation(self, node):
68 | self.changedName = node.getFullnameInDataModel()
69 | node.setValue(node.getValue() * self._cnt)
70 |
71 | def randomMutation(self, node, rand):
72 | self.changedName = node.getFullnameInDataModel()
73 | count = rand.randint(0, self._cnt)
74 | node.setValue(node.getValue() * count)
75 |
76 |
77 | class DataTreeSwapNearNodesMutator(Mutator):
78 | """
79 | Swap two nodes in the data model that are near each other.
80 | TODO: Actually move the nodes instead of just the data.
81 | """
82 |
83 | def __init__(self, peach, node):
84 | Mutator.__init__(self)
85 | self.isFinite = True
86 | self.name = "DataTreeSwapNearNodesMutator"
87 | self._peach = peach
88 |
89 | def next(self):
90 | raise MutatorCompleted()
91 |
92 | def getCount(self):
93 | return 1
94 |
95 | def _moveNext(self, currentNode):
96 | # Check if we are top dogM
97 | if currentNode.parent is None or \
98 | not isinstance(currentNode.parent, DataElement):
99 | return None
100 | # Get sibling
101 | foundCurrent = False
102 | for node in currentNode.parent:
103 | if node == currentNode:
104 | foundCurrent = True
105 | continue
106 | if foundCurrent and isinstance(node, DataElement):
107 | return node
108 | # Get sibling of parentM
109 | return self._moveNext(currentNode.parent)
110 |
111 | def _nextNode(self, node):
112 | nextNode = None
113 | # Walk down node tree
114 | for child in node._children:
115 | if isinstance(child, DataElement):
116 | nextNode = child
117 | break
118 | # Walk over or up if we can
119 | if nextNode is None:
120 | nextNode = self._moveNext(node)
121 | return nextNode
122 |
123 | @staticmethod
124 | def supportedDataElement(e):
125 | if isinstance(e, DataElement) and e.isMutable:
126 | return True
127 | return False
128 |
129 | def sequentialMutation(self, node):
130 | self.changedName = node.getFullnameInDataModel()
131 | nextNode = self._nextNode(node)
132 | if nextNode is not None:
133 | v1 = node.getValue()
134 | v2 = nextNode.getValue()
135 | node.setValue(v2)
136 | nextNode.setValue(v1)
137 |
138 | def randomMutation(self, node, rand):
139 | self.changedName = node.getFullnameInDataModel()
140 | self.sequentialMutation(node)
141 |
--------------------------------------------------------------------------------
/Peach/Publishers/com.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import os
5 | import sys
6 | import time
7 | import signal
8 | try:
9 | import pywintypes
10 | import win32com.client
11 | import win32com.client.gencache
12 | except:
13 | pass
14 |
15 | from Peach.publisher import Publisher
16 |
17 |
18 | class Com(Publisher):
19 | """
20 | Very simple Com publisher that allows for a single method call. The
21 | method call is fed in as a string which is evaled. This allows for
22 | calling any method with any number of parameters.
23 | """
24 |
25 | _clsid = None
26 | _methodFormat = None
27 | _lastReturn = None
28 | _object = None
29 |
30 | def __init__(self, clsid):
31 | """
32 | Create Com Object. clsid = '{...}'
33 |
34 | @type clsid: string
35 | @param clsid: CLSID of COM object in {...} format
36 | """
37 | Publisher.__init__(self)
38 | self._clsid = clsid
39 | self.withNode = True
40 |
41 | def start(self):
42 | try:
43 | self._object = None
44 | self._object = win32com.client.DispatchEx(self._clsid)
45 |
46 | except pywintypes.com_error as e:
47 | print("Caught pywintypes.com_error creating ActiveX control [%s]" % e)
48 | raise
49 |
50 | except:
51 | print("Caught unkown exception creating ActiveX control [%s]" % sys.exc_info()[0])
52 | raise
53 |
54 | def stop(self):
55 | self._object = None
56 |
57 | # def call(self, method, args):
58 | def callWithNode(self, method, args, argNodes):
59 | """
60 | Call method on COM object.
61 |
62 | @type method: string
63 | @param method: Name of method to invoke
64 | @type args: array of objects
65 | @param args: Arguments to pass
66 | """
67 |
68 | self._lastReturn = None
69 |
70 | realArgNodes = []
71 | for arg in argNodes:
72 | if len(arg) == 1:
73 | realArgNodes.append(arg[0])
74 | else:
75 | realArgNodes.append(arg)
76 |
77 | for arg in realArgNodes:
78 | print("Type", type(arg.getInternalValue()))
79 | print("Value", repr(arg.getInternalValue()))
80 |
81 | try:
82 | ret = None
83 | callStr = "ret = self._object.%s(" % str(method)
84 |
85 | if len(realArgNodes) > 0:
86 | for i in range(0, len(argNodes)):
87 | callStr += "realArgNodes[%d].getInternalValue()," % i
88 |
89 | callStr = callStr[:len(callStr) - 1] + ")"
90 |
91 | else:
92 | callStr += ")"
93 |
94 | print("Call:", callStr)
95 |
96 | exec(callStr)
97 | return ret
98 |
99 | except pywintypes.com_error as e:
100 | print("Caught pywintypes.com_error on call [%s]" % e)
101 | raise
102 |
103 | except NameError as e:
104 | print("Caught NameError on call [%s]" % e)
105 | raise
106 |
107 | except:
108 | print("Com::Call(): Caught unknown exception")
109 | raise
110 |
111 | # def property(self, property, value = None):
112 | def propertyWithNode(self, property, value, valueNode):
113 | """
114 | Get or set property
115 |
116 | @type property: string
117 | @param property: Name of method to invoke
118 | @type value: object
119 | @param value: Value to set. If None, return property instead
120 | """
121 |
122 | try:
123 | if value is None:
124 | ret = None
125 | callStr = "ret = self._object.%s" % str(property)
126 |
127 | #print "Call string:", callStr
128 | exec(callStr)
129 | return ret
130 |
131 | ret = None
132 | callStr = "self._object.%s = valueNode.getInternalValue()" % str(property)
133 |
134 | #print "Call string:", callStr
135 | exec(callStr)
136 | return None
137 |
138 | except pywintypes.com_error as e:
139 | print("Caught pywintypes.com_error on property [%s]" % e)
140 | #raise
141 |
142 | except NameError as e:
143 | print("Caught NameError on property [%s]" % e)
144 | #raise
145 |
146 | except:
147 | print("Com::property(): Caught unknown exception")
148 | raise
149 |
150 | return None
151 |
--------------------------------------------------------------------------------
/Peach/Generators/unicode.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach import generator
5 | from Peach.Generators.dictionary import *
6 | from Peach.Generators.static import *
7 |
8 |
9 | class GoodUnicode(generator.SimpleGenerator):
10 |
11 | _generator = Static('PLACE HOLDER')
12 |
13 | def __init__(self, group, generator):
14 | """
15 | @type group: Group
16 | @param group: Group to use
17 | """
18 |
19 | Generator.__init__(self)
20 | self.setGroup(group)
21 |
22 |
23 | class BadUnicode(generator.SimpleGenerator):
24 |
25 | _generator = Static('PLACE HOLDER')
26 |
27 | def __init__(self, group, generator):
28 | """
29 | @type group: Group
30 | @param group: Group to use
31 | """
32 |
33 | Generator.__init__(self)
34 | self.setGroup(group)
35 |
36 |
37 | class OverLongUtf8(generator.Generator):
38 | """
39 | This generator creates overlong UTF-8 encodings. First output is correct
40 | notation, then on each generation we perform a longer encoding of each
41 | character until we can do no more.
42 |
43 | NOTE: Only supports ascii chars under 127 right now :/
44 | """
45 |
46 | _data = None
47 | _size = 1
48 | _maxSize = 6
49 | _emptyByte = 0x80
50 | _start2 = 0xC0
51 | _start3 = 0xE0
52 | _start4 = 0xF0
53 | _start5 = 0xF8
54 | _start6 = 0xFC
55 | _firstMask = 0xC0
56 | _lastMask = 0x80
57 |
58 | def __init__(self, group, data):
59 | """
60 | @type group: Group
61 | @param group: Group to use
62 | @type data: Generator
63 | @param data: Data to perform UTF-8 encoding on
64 | """
65 | self.setGroup(group)
66 | self._data = data
67 |
68 | def next(self):
69 | self._size += 1
70 | if self._size > self._maxSize:
71 | raise generator.GeneratorCompleted("OverLongUtf8")
72 |
73 | def getRawValue(self):
74 | data = self._data.getValue()
75 | ret = ''
76 |
77 | if self._size == 1:
78 | return data
79 |
80 | elif self._size == 2:
81 | for c in data:
82 | ret += "%c%c" % (self._start2, self._lastMask | ord(c))
83 |
84 | elif self._size == 3:
85 | for c in data:
86 | ret += "%c%c%c" % (self._start3, self._emptyByte,
87 | self._lastMask | ord(c))
88 |
89 | elif self._size == 4:
90 | for c in data:
91 | ret += "%c%c%c%c" % (self._start4, self._emptyByte,
92 | self._emptyByte, self._lastMask | ord(c))
93 |
94 | elif self._size == 5:
95 | for c in data:
96 | ret += "%c%c%c%c%c" % (self._start5, self._emptyByte,
97 | self._emptyByte, self._emptyByte,
98 | self._lastMask | ord(c))
99 |
100 | elif self._size == 6:
101 | for c in data:
102 | ret += "%c%c%c%c%c%c" % (self._start6, self._emptyByte,
103 | self._emptyByte, self._emptyByte,
104 | self._emptyByte, self._lastMask | ord(c))
105 |
106 | return ret
107 |
108 | def reset(self):
109 | self._size = 1
110 |
111 | @staticmethod
112 | def unittest():
113 | expected1 = "%c" % 0x0A
114 | expected2 = "%c%c" % (0xC0, 0x8A)
115 | expected3 = "%c%c%c" % (0xE0, 0x80, 0x8A)
116 | expected4 = "%c%c%c%c" % (0xF0, 0x80, 0x80, 0x8A)
117 | expected5 = "%c%c%c%c%c" % (0xF8, 0x80, 0x80, 0x80, 0x8A)
118 | expected6 = "%c%c%c%c%c%c" % (0xFC, 0x80, 0x80, 0x80, 0x80, 0x8A)
119 |
120 | g = OverLongUtf8(None, Static("%c" % 0x0A))
121 |
122 | if g.getRawValue() != expected1:
123 | print("OverLongUtf8 unittest failure 1")
124 | g.next()
125 | if g.getRawValue() != expected2:
126 | print("OverLongUtf8 unittest failure 2")
127 | g.next()
128 | if g.getRawValue() != expected3:
129 | print("OverLongUtf8 unittest failure 3")
130 | g.next()
131 | if g.getRawValue() != expected4:
132 | print("OverLongUtf8 unittest failure 4")
133 | g.next()
134 | if g.getRawValue() != expected5:
135 | print("OverLongUtf8 unittest failure 5")
136 | g.next()
137 | if g.getRawValue() != expected6:
138 | print("OverLongUtf8 unittest failure 6")
139 | print("Done with OverLongUtf8 unittests")
140 |
--------------------------------------------------------------------------------
/Peach/Transformers/Type/Integer.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from struct import pack
5 | from struct import unpack
6 |
7 | from Peach.transformer import Transformer
8 |
9 |
10 | class _AsNumber(Transformer):
11 | """Base class to transform a number to a specific size."""
12 |
13 | def __init__(self, isSigned=1, isLittleEndian=1):
14 | """
15 | :param isSigned: 1 for signed, 0 for unsigned
16 | :type isSigned: number
17 | :param isLittleEndian: 1 for signed, 0 for unsigned
18 | :type isLittleEndian: number
19 | """
20 |
21 | Transformer.__init__(self)
22 | self._isSigned = isSigned
23 | self._isLittleEndian = isLittleEndian
24 |
25 | def _unfuglyNumber(self, data):
26 | """
27 | Will attempt to figure out if the incoming data is a byte stream that
28 | must be converted to get our number we will then cast, due to
29 | StaticBinary issues.
30 | """
31 | try:
32 | if int(data) == int(str(data)):
33 | return int(data)
34 | except Exception as e:
35 | pass
36 | hexString = ""
37 | for c in data:
38 | h = hex(ord(c))[2:]
39 | if len(h) < 2:
40 | h = "0" + h
41 | hexString += h
42 | return int(hexString, 16)
43 |
44 | def realEncode(self, data):
45 | data = self._unfuglyNumber(data)
46 | packStr = ''
47 | if self._isLittleEndian == 1:
48 | packStr = '<'
49 | else:
50 | packStr = '>'
51 | if self._isSigned == 1:
52 | packStr += self._packFormat.lower()
53 | else:
54 | packStr += self._packFormat.upper()
55 | # Prevent silly deprecation warnings from Python
56 | if packStr[1] == 'b' and data > 0xfe:
57 | data = 0
58 | elif packStr[1] == 'B' and (data > 0xff or data < 0):
59 | data = 0
60 | elif packStr[1] == 'h' and data > 0xfffe:
61 | data = 0
62 | elif packStr[1] == 'H' and (data > 0xffff or data < 0):
63 | data = 0
64 | elif packStr[1] == 'i' and data > 0xfffffffe:
65 | data = 0
66 | elif packStr[1] == 'L' and (data > 0xffffffff or data < 0):
67 | data = 0
68 | elif packStr[1] == 'q' and data > 0xfffffffffffffffe:
69 | data = 0
70 | elif packStr[1] == 'Q' and (data > 0xffffffffffffffff or data < 0):
71 | data = 0
72 | try:
73 | return pack(packStr, int(data))
74 | except Exception as e:
75 | return pack(packStr, 0)
76 |
77 | def realDecode(self, data):
78 | packStr = ''
79 | if self._isLittleEndian == 1:
80 | packStr = '<'
81 | else:
82 | packStr = '>'
83 | if self._isSigned == 1:
84 | packStr += self._packFormat.lower()
85 | else:
86 | packStr += self._packFormat.upper()
87 | try:
88 | return unpack(packStr, data)[0]
89 | except Exception as e:
90 | return 0
91 |
92 |
93 | class AsInt8(_AsNumber):
94 | """Transform an number to an Int8 or UInt8."""
95 | _packFormat = 'b'
96 |
97 |
98 | class AsInt16(_AsNumber):
99 | """Transform an number to an Int16 or UInt16."""
100 | _packFormat = 'h'
101 |
102 |
103 | class AsInt24(Transformer):
104 | """Transform an number to a UInt24."""
105 |
106 | def __init__(self, isSigned=1, isLittleEndian=1):
107 | """
108 | @param isSigned: 1 for signed, 0 for unsigned (we ignore this)
109 | @type isSigned: number
110 | @param isLittleEndian: 1 for signed, 0 for unsigned
111 | @type isLittleEndian: number
112 | """
113 | Transformer.__init__(self)
114 | self._isLittleEndian = isLittleEndian
115 |
116 | def realEncode(self, data):
117 | try:
118 | data = int(data)
119 | packStr = ''
120 | if self._isLittleEndian == 1:
121 | packStr = '<'
122 | else:
123 | packStr = '>'
124 | packStr += 'L'
125 | v = pack(packStr, data)
126 | if self._isLittleEndian == 1:
127 | return v[:3]
128 | else:
129 | return v[1:]
130 | except Exception as e:
131 | return '0'
132 |
133 |
134 | class AsInt32(_AsNumber):
135 | """Transform an number to an Int32 or UInt32."""
136 | _packFormat = 'l'
137 |
138 |
139 | class AsInt64(_AsNumber):
140 | """Transform an number to an Int64 or UInt64."""
141 | _packFormat = 'q'
142 |
--------------------------------------------------------------------------------
/Pits/Targets/Firefox/firefox.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Peach/Publishers/smtp.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import time
5 | import sys
6 | import smtplib
7 |
8 | from email.mime.multipart import MIMEMultipart
9 | from email.mime.base import MIMEBase
10 | from email.mime.text import MIMEText
11 | from email import Encoders
12 | from email.Utils import COMMASPACE, formatdate
13 | from Peach.publisher import Publisher
14 | from Peach.Engine.common import PeachException
15 |
16 |
17 | class SMTPPublisher(Publisher):
18 | def __init__(self, host="localhost",
19 | port=smtplib.SMTP_PORT,
20 | debugLevel=0,
21 | mailFrom="localhost@localhost",
22 | mailTo="localhost@localhost",
23 | username="",
24 | password=""):
25 | Publisher.__init__(self)
26 | self.host = host
27 | try:
28 | self.port = int(port)
29 | except:
30 | raise PeachException("The SMTP publisher parameter for port is not a valid number.")
31 | self.debugLevel = int(debugLevel)
32 | self.mailFrom = mailFrom
33 | self.mailTo = mailTo
34 | self.username = username
35 | self.password = password
36 | self.loadBalance = 0
37 | self._connected = None
38 |
39 | def start(self):
40 | if self._connected:
41 | return
42 |
43 | print("[*] Connecting to %s:%d ..." % (self.host, self.port))
44 | try:
45 | self.smtp = smtplib.SMTP(self.host, self.port)
46 | except:
47 | raise PeachException("Peer %s:%d is down or connection settings are wrong." % (self.host, self.port))
48 | self._connected = True
49 | self.smtp.set_debuglevel(self.debugLevel)
50 |
51 | def connect(self):
52 | pass
53 |
54 | def send(self, data):
55 | if not self.loadBalance % 500 and self.loadBalance != 0:
56 | print("[*] Pause ...")
57 | time.sleep(10)
58 | for i in range(3):
59 | try:
60 | self.smtp.sendmail(self.mailFrom, self.mailTo, data)
61 | exception = None
62 | break
63 | except:
64 | exception = sys.exc_info()
65 | time.sleep(5)
66 | if exception:
67 | reason = ""
68 | try:
69 | reason = str(exception[1])
70 | except:
71 | reason = "unknown reason."
72 | message = "SMTP send mail to %s:%d failed: %s" % (self.host, self.port, reason)
73 | if message.find("5.4.0") > -1:
74 | print(message)
75 | else:
76 | self.smtp.close()
77 | raise PeachException(message)
78 | self.loadBalance += 1
79 |
80 | def close(self):
81 | pass
82 |
83 | def stop(self):
84 | pass
85 |
86 |
87 | class EmailAttachment(Publisher):
88 | """
89 | Send fuzzed data as email attachment.
90 | """
91 |
92 | def __init__(self, server, fileName, msgTo, msgFrom="peach@peach.org",
93 | msgSubject="Fuzzing Test",
94 | msgText="Message generated by Peach Fuzzing Platform.\n\nhttp://peachfuzzer.com\n\n - Peach\n"):
95 | Publisher.__init__(self)
96 | self.server = server
97 | self.fileName = fileName
98 | self.msgFrom = msgFrom
99 | self.msgTo = msgTo
100 | self.msgSubject = msgSubject
101 | self.msgText = msgText
102 |
103 | def send(self, data):
104 | """
105 | Publish some data
106 |
107 | @type data: string
108 | @param data: Data to publish
109 | """
110 | # Build Message Body
111 | msg = MIMEMultipart()
112 | msg['From'] = self.msgFrom
113 | msg['To'] = self.msgTo
114 | msg['Date'] = formatdate(localtime=True)
115 | msg['Subject'] = self.msgSubject
116 | msg.attach(MIMEText(self.msgText))
117 | # Attach file
118 | part = MIMEBase('application', 'pdf')
119 | part.set_payload(data)
120 | Encoders.encode_base64(part)
121 | part.add_header('Content-Disposition', 'attachment; filename="%s"' % self.fileName)
122 | msg.attach(part)
123 | # Send email
124 | smtp = smtplib.SMTP(self.server)
125 | smtp.sendmail(self.msgFrom, self.msgTo, msg.as_string())
126 | smtp.close()
127 |
128 | def connect(self):
129 | """
130 | Called to connect or open a connection/file.
131 | """
132 | pass
133 |
134 | def close(self):
135 | """
136 | Close current stream/connection.
137 | """
138 | pass
139 |
140 |
141 | class _OleStorage(object):
142 | """
143 | This class wraps OLE Storage APIs
144 | """
145 | pass
146 |
--------------------------------------------------------------------------------
/Peach/Agent/gui.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | try:
5 |
6 | import win32gui, win32con
7 | import sys, time, os, signal
8 | from threading import *
9 | from Peach.agent import *
10 |
11 | class _WindowWatcher(Thread):
12 | """
13 | Look one child deep on each top level window to try
14 | and locate dialog boxen.
15 | """
16 |
17 | def __init__(self):
18 | Thread.__init__(self)
19 |
20 | self.CloseWindows = False
21 | self.FoundWindowEvent = None # Will be Event()
22 | self.WindowNames = None # Will be []
23 | self.StopEvent = None # Will be Event()
24 |
25 | @staticmethod
26 | def enumCallback(hwnd, self):
27 | title = win32gui.GetWindowText(hwnd)
28 |
29 | for name in self.WindowNames:
30 | if title.find(name) > -1:
31 | try:
32 | self.FoundWindowEvent.set()
33 |
34 | if self.CloseWindows:
35 | win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
36 | except:
37 | pass
38 | else:
39 | try:
40 | win32gui.EnumChildWindows(hwnd, _WindowWatcher.enumChildCallback, self)
41 | except:
42 | pass
43 |
44 | return True
45 |
46 | @staticmethod
47 | def enumChildCallback(hwnd, self):
48 | title = win32gui.GetWindowText(hwnd)
49 |
50 | for name in self.WindowNames:
51 | if title.find(name) > -1:
52 | try:
53 | self.FoundWindowEvent.set()
54 |
55 | if self.CloseWindows:
56 | win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
57 | except:
58 | pass
59 |
60 | return True
61 |
62 | def run(self):
63 | while not self.StopEvent.isSet():
64 | win32gui.EnumWindows(_WindowWatcher.enumCallback, self)
65 | time.sleep(.2)
66 |
67 |
68 | class PopupWatcher(Monitor):
69 | """
70 | Will watch for specific dialogs and optionally kill
71 | or log a fault when detected.
72 | """
73 |
74 | def __init__(self, args):
75 | """
76 | Constructor. Arguments are supplied via the Peach XML
77 | file.
78 |
79 | @type args: Dictionary
80 | @param args: Dictionary of parameters
81 | """
82 |
83 | # Our name for this monitor
84 | self._name = "PopupWatcher"
85 | self._closeWindows = False
86 | self._triggerFaults = False
87 |
88 | if args.has_key("CloseWindows"):
89 | if args["CloseWindows"].replace("'''", "").lower() in ["yes", "true", "1"]:
90 | self._closeWindows = True
91 |
92 | if args.has_key("TriggerFaults"):
93 | if args["TriggerFaults"].replace("'''", "").lower() in ["yes", "true", "1"]:
94 | self._triggerFaults = True
95 |
96 | if not args.has_key("WindowNames"):
97 | raise Exception("PopupWatcher requires a parameter named WindowNames.")
98 |
99 | self._names = args["WindowNames"].replace("'''", "").split(',')
100 |
101 | def OnTestStarting(self):
102 | """
103 | Called right before start of test case or variation
104 | """
105 |
106 | self._thread = _WindowWatcher()
107 |
108 | self._thread.CloseWindows = self._closeWindows
109 | self._thread.FoundWindowEvent = Event()
110 | self._thread.WindowNames = self._names
111 | self._thread.StopEvent = Event()
112 |
113 | self._thread.start()
114 |
115 | def OnTestFinished(self):
116 | """
117 | Called right after a test case or variation
118 | """
119 | self._thread.StopEvent.set()
120 | time.sleep(.6)
121 |
122 | def DetectedFault(self):
123 | """
124 | Check if a fault was detected.
125 | """
126 | if self._triggerFaults:
127 | return self._thread.FoundWindowEvent.isSet()
128 |
129 | return False
130 |
131 | def OnShutdown(self):
132 | """
133 | Called when Agent is shutting down, typically at end
134 | of a test run or when a Stop-Run occurs
135 | """
136 | try:
137 | self._thread.StopEvent.set()
138 | time.sleep(.6)
139 | except:
140 | pass
141 |
142 | except:
143 | pass
144 |
--------------------------------------------------------------------------------
/Peach/Publishers/usb.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import os
5 | import sys
6 | import struct
7 |
8 | from Peach.publisher import Publisher
9 | from Peach.Engine.common import PeachException
10 |
11 |
12 | try:
13 | import usb.core
14 | import usb.util
15 | except ImportError:
16 | print("Please install the following dependencies:")
17 |
18 | if sys.platform == "darwin":
19 | sys.exit("sudo port install libusb py27-pyusb-devel")
20 |
21 | if sys.platform == "linux2":
22 | sys.exit("sudo apt-get install libusb-1.0-0 python-usb")
23 |
24 | if sys.platform == "nt":
25 | sys.exit("PyUSB: http://sourceforge.net/apps/trac/pyusb/\r\n"
26 | "LibUSB: http://sourceforge.net/apps/trac/libusb-win32/")
27 |
28 |
29 | def getDevice(idVendor, idProduct):
30 | busses = usb.busses()
31 | for i, dev in enumerate(busses[0].devices):
32 | if dev.idProduct == idProduct and dev.idVendor == idVendor:
33 | return busses[0].devices[i]
34 |
35 |
36 | def getDeviceName(handle):
37 | name = []
38 | for index in range(1, 3):
39 | name.append(handle.getString(index, 32))
40 | return " ".join(name)
41 |
42 |
43 | def getConfiguration(dev):
44 | return dev.configurations[0]
45 |
46 |
47 | def getInterface(dev):
48 | conf = getConfiguration(dev)
49 | return conf.interfaces[0][0]
50 |
51 |
52 | def getEndpoints(dev):
53 | endpoints = []
54 | for endpoint in getInterface(dev):
55 | endpoints.append(endpoint)
56 | return endpoints
57 |
58 |
59 | def send_controlMsg(handle, command):
60 | try:
61 | #handle.controlMsg(0xA1, 1, 0)
62 | handle.controlMsg(usb.TYPE_CLASS, 0x9, [0x01, command, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], 0x200, 2000)
63 | except usb.USBError as e:
64 | print(e)
65 |
66 |
67 | def send_interruptWrite(handle):
68 | try:
69 | handle.interruptWrite(0x81, [0] * 64, 1000)
70 | except:
71 | pass
72 |
73 |
74 | def send_ctrl_transfer():
75 | dev = usb.core.find(idVendor=0x05ac, idProduct=0x12a0)
76 | dev.set_configuration()
77 | dev.ctrl_transfer(0x80, 6, 0x100, 1, [0x40, 1, 3], 1000)
78 |
79 |
80 | class USBCtrlTransfer(Publisher):
81 | def __init__(self, idVendor, idProduct):
82 | Publisher.__init__(self)
83 |
84 | self.idVendor = int(idVendor, 16)
85 | self.idProduct = int(idProduct, 16)
86 |
87 | self.dev = None
88 |
89 | def start(self):
90 | #if self.dev:
91 | # return
92 |
93 | self.dev = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
94 |
95 | if not self.dev:
96 | raise PeachException("Device not found.")
97 |
98 | try:
99 | self.dev.set_configuration()
100 | except usb.core.USBError as e:
101 | raise PeachException(str(e))
102 |
103 | def send(self, data):
104 | try:
105 | bmRequestType, bRequest, wValue, wIndex, wLength = struct.unpack(">BBHHH", data)
106 | except:
107 | pass
108 | else:
109 | try:
110 | self.dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, timeout=1000)
111 | except:
112 | pass
113 |
114 | def connect(self):
115 | pass
116 |
117 | def close(self):
118 | pass
119 |
120 |
121 | if __name__ == "__main__":
122 |
123 | if sys.platform == "darwin":
124 | os.system("system_profiler SPUSBDataType")
125 |
126 | dev = getDevice(0x05ac, 0x12a0)
127 | usbConfiguration = getConfiguration(dev)
128 | usbInterface = getInterface(dev)
129 |
130 | handle = dev.open()
131 |
132 | print("\nDevice Name: {}\n".format(getDeviceName(handle)))
133 |
134 | try:
135 | prin("Unload current driver from interface ...")
136 | handle.detachKernelDriver(usbInterface.interfaceNumber)
137 | print("Done.")
138 | except usb.USBError as e:
139 | print("Failed!")
140 |
141 | try:
142 | print("Loading USB config ...")
143 | handle.setConfiguration(usbConfiguration.value)
144 | print("Done.")
145 | except usb.USBError as e:
146 | print("Failed!")
147 |
148 | try:
149 | print("Claiming interface ...")
150 | handle.claimInterface(usbInterface.interfaceNumber)
151 | print("Done.")
152 | except usb.USBError as e:
153 | print("Failed!")
154 |
155 | try:
156 | print("Set alternative interface ...")
157 | handle.setAltInterface(usbInterface.interfaceNumber)
158 | print("Done.")
159 | except usb.USBError as e:
160 | print("Failed!")
161 |
162 | try:
163 | print("Resetting device ...")
164 | handle.reset()
165 | print("Done.")
166 | except usb.USBError as e:
167 | print("Failed!")
168 |
169 | # ...
170 |
171 | try:
172 | print("Releasing interface ...")
173 | handle.releaseInterface()
174 | print("done.")
175 | except usb.USBError as e:
176 | print("failed!")
--------------------------------------------------------------------------------
/Peach/Generators/repeater.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import static
5 | from Peach import generator, group
6 | from Peach.generator import Generator
7 |
8 |
9 | class Repeater(generator.Generator):
10 | """
11 | Will repeat a value (generated by a Generator) by round count. Can be used
12 | for basic buffer overflow testing.
13 |
14 | Example:
15 |
16 | gen = Repeater(None, String("A"), 3)
17 | gen.getValue()
18 | A
19 | gen.next()
20 | gen.getValue()
21 | AA
22 | gen.next()
23 | gen.getValue()
24 | AAA
25 |
26 | Example:
27 |
28 | gen = Repeater(None, Static("Peach "), 5, 3)
29 | gen.getValue()
30 | Peach
31 | gen.next()
32 | gen.getValue()
33 | Peach Peach Peach Peach Peach
34 | gen.next()
35 | gen.getValue()
36 | Peach Peach Peach Peach Peach Peach Peach Peach Peach Peach
37 | """
38 |
39 | def __init__(self, group, generator, incrementor=1, maxSteps=-1,
40 | startStep=None):
41 | """
42 | @type group: Group
43 | @param group: Group this generator belongs to
44 | @type generator: Generator
45 | @param generator: Generator to repeat
46 | @type incrementor: number
47 | @param incrementor: Multiplier against round count
48 | @type maxSteps: number
49 | @param maxSteps: Maximum repeats
50 | @type startSteps: number
51 | @param startSteps: Start at this step
52 | """
53 | Generator.__init__(self)
54 | self._incrementor = None
55 | self._roundCount = 1
56 | self._generator = None
57 | self._maxSteps = -1
58 | self._generator = generator
59 | self._incrementor = incrementor
60 | self.setGroup(group)
61 | self._maxSteps = maxSteps
62 | self._startStep = startStep
63 | if self._startStep is not None:
64 | self._roundCount = self._startStep
65 |
66 | def next(self):
67 | self._roundCount += 1
68 | if self._maxSteps != -1 and self._roundCount > self._maxSteps:
69 | self._roundCount -= 1
70 | raise generator.GeneratorCompleted("Peach.repeater.Repeater")
71 |
72 | def getRawValue(self):
73 | return str(self._generator.getValue()) * \
74 | (self._roundCount * self._incrementor)
75 |
76 | def getGenerator(self):
77 | """
78 | Get Generator who's value we will repeat.
79 |
80 | @rtype: Generator
81 | @return: Generator we are repeating
82 | """
83 | return self._generator
84 |
85 | def setGenerator(self, generator):
86 | """
87 | Set Generator who's value we will repeat.
88 |
89 | @type generator: Generator
90 | @param generator: Generator to repeate
91 | """
92 | self._generator = generator
93 |
94 | def reset(self):
95 | self._roundCount = 1
96 | if self._startStep is not None:
97 | self._roundCount = self._startStep
98 | self._generator.reset()
99 |
100 | @staticmethod
101 | def unittest():
102 | g = group.Group()
103 | r = Repeater(g, static.Static('A'), 1, 10)
104 | try:
105 | while g.next():
106 | print(r.getValue())
107 | except group.GroupCompleted:
108 | pass
109 |
110 |
111 | class RepeaterGI(generator.Generator):
112 | """
113 | Will repeat a value (generated by a Generator) by multiplier (generator).
114 |
115 | Example:
116 |
117 | Repeater(None, String("A"), BadUnsignedNumbers(None))
118 |
119 | Would produce a string of A's the length of each number returned by
120 | BadUnsignedNumbers.
121 | """
122 |
123 | def __init__(self, group, generator, incrementor):
124 | """
125 | @type group: Group
126 | @param group: Group this generator belongs to
127 | @type generator: Generator
128 | @param generator: Generator to repeat
129 | @type incrementor: Generator
130 | @param incrementor: Multiplier against round count
131 | """
132 | Generator.__init__(self)
133 | self._incrementor = None
134 | self._roundCount = 1
135 | self._generator = None
136 | self._generator = generator
137 | self._incrementor = incrementor
138 | self.setGroup(group)
139 |
140 | def next(self):
141 | self._roundCount += 1
142 | self._incrementor.next()
143 |
144 | def getRawValue(self):
145 | try:
146 | ret = str(self._generator.getValue()) * \
147 | int(self._incrementor.getValue())
148 | except OverflowError:
149 | # Integer overflow exception. Oh well, we tried!
150 | ret = self._generator.getValue()
151 | except MemoryError:
152 | ret = self._generator.getValue()
153 | #print "RepeaterGI: MemoryError! Value is %d long multiplier is %d." % (
154 | # len(str(ret)), int(self._incrementor.getValue()))
155 | return ret
156 |
157 | def reset(self):
158 | self._roundCount = 1
159 | self._incrementor.reset()
160 | self._generator.reset()
161 |
--------------------------------------------------------------------------------
/Peach/Analyzers/stringtoken.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.Engine.dom import *
5 | from Peach.Engine.common import *
6 | from Peach.analyzer import *
7 |
8 |
9 | class StringTokenAnalyzer(Analyzer):
10 | """
11 | Produces a tree of strings based on token precidence.
12 | """
13 |
14 | supportCommandLine = False
15 | supportDataElement = True
16 | supportTopLevel = True
17 |
18 | # Tokens that group parts of a string
19 | pairs = [
20 | [u'{', u'}'],
21 | [u'(', u')'],
22 | [u'[', u']'],
23 | [u'<', u'>'],
24 | ]
25 |
26 | # Tokens in order of precidence
27 | tokens = [
28 | u'\0', u'\n', u'\r', u'<', u'>', u'?', u';', u',', u'|', u'@', u':', u'(', u')',
29 | u'{', u'}', u'[', u']', u'/', u'\\', u'&', u'=', u' ', u'-', u'+', u'.'
30 | ]
31 |
32 | def asCommandLine(self, args):
33 | """
34 | Called when Analyzer is used from command line.
35 | Analyzer should produce PeachPit XML as output.
36 | """
37 | raise NotImplementedError("StringTokenAnalyzer asCommandLine is not supported.")
38 |
39 | def asTopLevel(self, peach, args):
40 | """
41 | Called when Analyzer is used from top level.
42 | From the top level producing zero or more data models and state models is possible.
43 | """
44 | raise NotImplementedError("StringTokenAnalyzer asTopLevel is not supported.")
45 |
46 | def asDataElement(self, parent, args, data):
47 | """
48 | Called when Analyzer is used in a data model. Should return a DataElement.
49 | """
50 | if not isinstance(parent, String):
51 | raise PeachException("StringTokenAnalyzer can only be attached to elements.")
52 | self.stringType = parent.type
53 | dom = self.tokenize_string(data, None)
54 | if parent.nullTerminated:
55 | blob = Blob(None, None)
56 | blob.defaultValue = "\x00" if parent.type is "wchar" else "\x00\x00"
57 | dom.append(blob)
58 | # replace parent with new dom
59 | dom.name = parent.name
60 | i = parent.parent.index(parent)
61 | del parent.parent[parent.name]
62 | parent.parent.insert(i, dom)
63 |
64 | def _new_string_element(self, value, parent=None):
65 | s = String(None, parent)
66 | s.type = self.stringType
67 | s.defaultValue = value
68 | try:
69 | _ = int(s.defaultValue)
70 | hint = Hint("NumericalString", s)
71 | hint.value = "true"
72 | s.hints.append(hint)
73 | except ValueError:
74 | pass
75 | return s
76 |
77 | def _split(self, string, token):
78 | """
79 | A version of split that also returns the tokens.
80 | """
81 | # return re.split('(\%c)' % token, string)
82 | pos = string.find(token)
83 | parts = []
84 | if pos is -1:
85 | return parts
86 | while pos > -1:
87 | parts.append(string[:pos])
88 | parts.append(string[pos:pos + 1])
89 | string = string[pos + 1:]
90 | pos = string.find(token)
91 | parts.append(string)
92 | return parts
93 |
94 | def tokenize_string(self, string, tokens=None):
95 | if string is None:
96 | return None
97 | tokens = self.tokens if tokens is None else tokens
98 | parent = Block(None, None)
99 | parent.append(self._new_string_element(string))
100 | for p in self.pairs:
101 | self._pair_tree(p, parent)
102 | for t in tokens:
103 | self._token_tree(t, parent)
104 | return parent
105 |
106 | def _pair_tree(self, p, node):
107 | if isinstance(node, Block):
108 | for c in node:
109 | self._pair_tree(p, c)
110 | return
111 | string = node.defaultValue
112 | index1 = string.find(p[0])
113 | if index1 is -1:
114 | return
115 | index2 = string[index1:].find(p[1])
116 | if index2 is -1:
117 | return
118 | index2 += index1
119 | block = Block(None, None)
120 | pre = string[:index1]
121 | token_start = string[index1]
122 | middle = string[index1 + 1:index2]
123 | token_end = string[index2]
124 | after = string[index2 + 1:]
125 | if len(pre):
126 | block.append(self._new_string_element(pre))
127 | block.append(self._new_string_element(token_start))
128 | block.append(self._new_string_element(middle))
129 | block.append(self._new_string_element(token_end))
130 | if len(after):
131 | block.append(self._new_string_element(after))
132 | block.name = node.name
133 | node.parent[node.name] = block
134 |
135 | def _token_tree(self, token, node):
136 | if isinstance(node, Block):
137 | for c in node:
138 | self._token_tree(token, c)
139 | return
140 | if len(node.defaultValue) < 2:
141 | return
142 | stuff = self._split(node.defaultValue, token)
143 | if not len(stuff):
144 | return
145 | block = Block(node.name, None)
146 | for s in stuff:
147 | block.append(self._new_string_element(s))
148 | node.parent[node.name] = block
149 |
--------------------------------------------------------------------------------
/Peach/Fixups/strings.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import random
5 | import string
6 | import re
7 | import time
8 |
9 | from Peach.fixup import Fixup
10 |
11 |
12 | class Uppercase(Fixup):
13 |
14 | def __init__(self, ref):
15 | Fixup.__init__(self)
16 | self.ref = ref
17 |
18 | def fixup(self):
19 | stuff = self.context.findDataElementByName(self.ref).getValue()
20 | if stuff is None:
21 | raise Exception("Error: Uppercase was unable to locate "
22 | "[{}].".format(self.ref))
23 | return stuff.upper()
24 |
25 |
26 | class Escape(Fixup):
27 |
28 | def __init__(self, ref):
29 | Fixup.__init__(self)
30 | self.ref = ref
31 |
32 | def fixup(self):
33 | stuff = self.context.findDataElementByName(self.ref).getValue()
34 | if stuff is None:
35 | raise Exception("Error: Escape was unable to locate "
36 | "[{}].".format(self.ref))
37 | return '"' + stuff + '"'
38 |
39 |
40 | class LogicalField(Fixup):
41 |
42 | def __init__(self, values, listsep="; ", unitsep=","):
43 | Fixup.__init__(self)
44 | self.values = values
45 | self.listsep = listsep
46 | self.unitsep = unitsep
47 |
48 | def fixup(self):
49 | values = string.split(self.values, self.listsep)
50 | if values is None:
51 | raise Exception("Error: LogicalField was unable to locate its "
52 | "values.")
53 | rndIndex = random.randint(0, len(values) - 1)
54 | rndValue = string.split(values[rndIndex], self.unitsep)
55 | if rndValue[0] == "String":
56 | return TranslateHexDigits(rndValue[1])
57 | if rndValue[0] == "Number":
58 | return int(rndValue[1], 16)
59 | return TranslateHexDigits(rndValue[1])
60 |
61 |
62 | class RandomField(Fixup):
63 |
64 | def __init__(self, minlen, maxlen, fieldtype):
65 | Fixup.__init__(self)
66 | self.minlen = minlen
67 | self.maxlen = maxlen
68 | self.fieldtype = fieldtype
69 |
70 | def fixup(self):
71 | minlen = self.minlen
72 | maxlen = self.maxlen
73 | if minlen is None:
74 | raise Exception("Error: RandomField was unable to locate minlen.")
75 | if maxlen is None:
76 | raise Exception("Error: RandomField was unable to locate maxlen.")
77 | if int(minlen) > int(maxlen):
78 | raise Exception("Error: minlen ({}) > maxlen ({})."
79 | .format(minlen, maxlen))
80 | value = ""
81 | length = random.randint(int(minlen), int(maxlen))
82 | if self.fieldtype == "String":
83 | for _ in range(length):
84 | value += random.choice(string.printable)
85 | elif self.fieldtype == "Number":
86 | for _ in range(length):
87 | value += random.choice(string.digits)
88 | else:
89 | for _ in range(length):
90 | value += random.choice(string.hexdigits)
91 | value = TranslateHexDigits(value)
92 | return value
93 |
94 |
95 | class Padding(Fixup):
96 |
97 | def __init__(self, ref):
98 | Fixup.__init__(self)
99 | self.ref = ref
100 |
101 | def fixup(self):
102 | ref = self.context.findDataElementByName(self.ref)
103 | stuff = ref.getValue()
104 | if stuff is None:
105 | raise Exception("Error: PaddingFixup was unable to locate "
106 | "[{}]".format(self.ref))
107 | x = len(stuff) % 4
108 | return x * "\x00" if x == 0 else (4 - x) * "\x00"
109 |
110 |
111 | def TranslateHexDigits(value):
112 | regsHex = (
113 | re.compile(r"^([,\s]*\\x([a-zA-Z0-9]{2})[,\s]*)"),
114 | re.compile(r"^([,\s]*%([a-zA-Z0-9]{2})[,\s]*)"),
115 | re.compile(r"^([,\s]*0x([a-zA-Z0-9]{2})[,\s]*)"),
116 | re.compile(r"^([,\s]*x([a-zA-Z0-9]{2})[,\s]*)"),
117 | re.compile(r"^([,\s]*([a-zA-Z0-9]{2})[,\s]*)")
118 | )
119 | ret = ""
120 | valueLen = len(value) + 1
121 | while valueLen > len(value):
122 | valueLen = len(value)
123 | for i in range(len(regsHex)):
124 | match = regsHex[i].search(value)
125 | if match is not None:
126 | while match is not None:
127 | ret += chr(int(match.group(2), 16))
128 | value = regsHex[i].sub('', value)
129 | match = regsHex[i].search(value)
130 | break
131 | return ret
132 |
133 |
134 | class Timestamp(Fixup):
135 |
136 | def __init__(self, ref):
137 | Fixup.__init__(self)
138 | self.ref = ref
139 |
140 | def fixup(self):
141 | ref = self.context.findDataElementByName(self.ref)
142 | stuff = ref.getValue()
143 | if stuff is None:
144 | raise Exception("Error: Timestamp Fixup was unable to locate "
145 | "[{}]" % self.ref)
146 | values = time.strftime("%y %m %d %H %M %S %z",
147 | time.gmtime()).split(" ")
148 | x = 0
149 | if values[-1][0] == "+":
150 | x = 0
151 | if values[-1][0] == "-":
152 | x = 1
153 | values[-1] = "%02d" % (int(values[-1][1:3]) | x << 3)
154 | s = ""
155 | for i in values:
156 | s += i[::-1]
157 | s = s.decode("hex")
158 | return s
159 |
--------------------------------------------------------------------------------
/Peach/Transformers/Cryptography/UnixMD5Crypt.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import string
5 | import hashlib
6 |
7 | from Peach.transformer import Transformer
8 |
9 |
10 | MAGIC = '$1$'
11 | ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
12 |
13 |
14 | def to64(v, n):
15 | ret = ''
16 | while n - 1 >= 0:
17 | n -= 1
18 | ret += ITOA64[v & 0x3f]
19 | v >>= 6
20 | return ret
21 |
22 |
23 | def apache_md5_crypt(pw, salt):
24 | # change the Magic string to match the one used by Apache
25 | return unix_md5_crypt(pw, salt, '$apr1$')
26 |
27 |
28 | def unix_md5_crypt(pw, salt, magic=None):
29 | if magic is None:
30 | magic = MAGIC
31 |
32 | # Take care of the magic string if present
33 | if salt[:len(magic)] == magic:
34 | salt = salt[len(magic):]
35 |
36 | # salt can have up to 8 characters:
37 | salt = string.split(salt, '$', 1)[0]
38 | salt = salt[:8]
39 |
40 | ctx = pw + magic + salt
41 | final = hashlib.md5(pw + salt + pw).digest()
42 |
43 | for pl in range(len(pw), 0, -16):
44 | if pl > 16:
45 | ctx = ctx + final[:16]
46 | else:
47 | ctx = ctx + final[:pl]
48 |
49 | # Now the 'weird' xform (??)
50 | i = len(pw)
51 | while i:
52 | if i & 1:
53 | ctx += chr(0)
54 | else:
55 | ctx = ctx + pw[0]
56 | i >>= 1
57 |
58 | final = hashlib.md5(ctx).digest()
59 |
60 | # The following is supposed to make things run slower.
61 | for i in range(1000):
62 | ctx1 = ''
63 | if i & 1:
64 | ctx1 = ctx1 + pw
65 | else:
66 | ctx1 = ctx1 + final[:16]
67 | if i % 3:
68 | ctx1 = ctx1 + salt
69 | if i % 7:
70 | ctx1 = ctx1 + pw
71 | if i & 1:
72 | ctx1 = ctx1 + final[:16]
73 | else:
74 | ctx1 = ctx1 + pw
75 | final = hashlib.md5(ctx1).digest()
76 |
77 | # Final xform
78 | passwd = ''
79 | passwd += to64((int(ord(final[0])) << 16)
80 | | (int(ord(final[6])) << 8)
81 | | (int(ord(final[12]))), 4)
82 | passwd += to64((int(ord(final[1])) << 16)
83 | | (int(ord(final[7])) << 8)
84 | | (int(ord(final[13]))), 4)
85 | passwd += to64((int(ord(final[2])) << 16)
86 | | (int(ord(final[8])) << 8)
87 | | (int(ord(final[14]))), 4)
88 | passwd += to64((int(ord(final[3])) << 16)
89 | | (int(ord(final[9])) << 8)
90 | | (int(ord(final[15]))), 4)
91 | passwd += to64((int(ord(final[4])) << 16)
92 | | (int(ord(final[10])) << 8)
93 | | (int(ord(final[5]))), 4)
94 | passwd += to64((int(ord(final[11]))), 2)
95 | return magic + salt + '$' + passwd
96 |
97 |
98 | class ApacheMd5Crypt(Transformer):
99 | """Apache style MD5 crypt.
100 | If no salt is specified will use first two chars of data, ala pwd style.
101 |
102 | Uses '$apr1$' as magic.
103 |
104 | From underlying docs:
105 | I{apache_md5_crypt() provides a function compatible with Apache's
106 | .htpasswd files. This was contributed by Bryan Hart .}
107 |
108 | I{"THE BEER-WARE LICENSE" (Revision 42):
109 | wrote this file. As long as you retain this notice
110 | you can do whatever you want with this stuff. If we meet some day, and you
111 | think this stuff is worth it, you can buy me a beer in return.
112 | Poul-Henning Kamp}
113 | """
114 |
115 | _salt = None
116 |
117 | def __init__(self, salt=None):
118 | """
119 | :param salt: salt for crypt (optional)
120 | :type salt: str
121 | """
122 | Transformer.__init__(self)
123 | self._salt = salt
124 |
125 | def realEncode(self, data):
126 | if self._salt is None:
127 | return apache_md5_crypt(data, data[:2])
128 | return apache_md5_crypt(data, self._salt)
129 |
130 |
131 | class UnixMd5Crypt(Transformer):
132 | """UNIX style MD5 crypt.
133 | If no salt is specified will use first two chars of data, ala pwd style.
134 |
135 | From underlying docs:
136 |
137 | I{unix_md5_crypt() provides a crypt()-compatible interface to the
138 | rather new MD5-based crypt() function found in modern operating systems.
139 | It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
140 | contains the following license in it:}
141 |
142 | I{"THE BEER-WARE LICENSE" (Revision 42):
143 | wrote this file. As long as you retain this notice
144 | you can do whatever you want with this stuff. If we meet some day, and you
145 | think this stuff is worth it, you can buy me a beer in return.
146 | Poul-Henning Kamp}
147 | """
148 |
149 | _salt = None
150 | _magic = None
151 |
152 | def __init__(self, salt=None, magic=None):
153 | """
154 | :type salt: str
155 | :param salt: salt for crypt (optional)
156 | :type magic: str
157 | :param magic: magic, usually $1$ on unix (optional)
158 | """
159 | Transformer.__init__(self)
160 | self._salt = salt
161 | self._magic = magic
162 |
163 | def realEncode(self, data):
164 | if self._salt is None:
165 | return unix_md5_crypt(data, data[:2], self._magic)
166 | return unix_md5_crypt(data, self._salt, self._magic)
167 |
--------------------------------------------------------------------------------
/Peach/mutatestrategies.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Peach.mutator import MutatorCompleted
5 | from Peach.Utilities.common import setAttributesFromParams
6 |
7 |
8 | class MutationStrategy(object):
9 | """
10 | Mutation strategies control how fuzzing occurs, weather that be changing
11 | each field sequentially or randomly selecting 5 fields to change each test
12 | case.
13 | Mutation strategies are implemented by overriding event handlers and using
14 | them to affect the state machine, data models, etc.
15 | """
16 |
17 | DefaultStrategy = None
18 |
19 | def __init__(self, node, parent):
20 | self.parent = parent # This will be a object
21 | setAttributesFromParams(node)
22 |
23 | def isFinite(self):
24 | """
25 | Will this mutation strategy ever end?
26 | """
27 | return True
28 |
29 | def getCount(self):
30 | """
31 | Return the number of test cases.
32 | """
33 | return None
34 |
35 | def next(self):
36 | """
37 | Go to next test sequence. Throws MutatorCompleted when we are done.
38 | """
39 | raise MutatorCompleted()
40 |
41 | def currentMutator(self):
42 | """
43 | Return the current Mutator in use.
44 | """
45 | pass
46 |
47 | def onTestCaseStarting(self, test, count, stateEngine):
48 | """
49 | Called as we start a test case.
50 |
51 | :param test: current Test being run
52 | :type test: Test
53 | :param count: current test number
54 | :type count: int
55 | :param stateEngine: StateEngine instance in use
56 | :type stateEngine: StateEngine
57 | """
58 | pass
59 |
60 | def onTestCaseFinished(self, test, count, stateEngine):
61 | """
62 | Called as we exit a test case
63 |
64 | :param test: current Test being run
65 | :type test: Test
66 | :param count: current test number
67 | :type count: int
68 | :param stateEngine: StateEngine instance in use
69 | :type stateEngine: StateEngine
70 | """
71 | pass
72 |
73 | def onFaultDetected(self, test, count, stateEngine, results, actionValues):
74 | """
75 | Called if a fault was detected during our current test case.
76 |
77 | :param test: current Test being run
78 | :type test: Test
79 | :param count: current test number
80 | :type count: int
81 | :param stateEngine: StateEngine instance in use
82 | :type stateEngine: StateEngine
83 | :param results: monitor results
84 | :type results: dict
85 | :param actionValues: values used to perform test
86 | :type actionValues: dict
87 | """
88 | pass
89 |
90 | def onStateMachineStarting(self, stateEngine):
91 | """
92 | Called as we enter the state machine.
93 |
94 | :param stateEngine: StateEngine instance in use
95 | :type stateEngine: StateEngine
96 | """
97 | pass
98 |
99 | def onStateMachineFinished(self, stateEngine):
100 | """
101 | Called as we exit the state machine.
102 |
103 | :param stateEngine: StateEngine instance in use
104 | :type stateEngine: StateEngine
105 | """
106 | pass
107 |
108 | def onStateStarting(self, stateEngine, state):
109 | """
110 | Called as we enter a new state.
111 |
112 | :param stateEngine: StateEngine instance in use
113 | :type stateEngine: StateEngine
114 | :param state: State
115 | :type state: current state
116 | """
117 | pass
118 |
119 | def onStateFinished(self, stateEngine, state):
120 | """
121 | Called as we exit a state.
122 |
123 | :param stateEngine: StateEngine instance in use
124 | :type stateEngine: StateEngine
125 | :param state: State
126 | :type state: current state
127 | """
128 | pass
129 |
130 | def onStateChange(self, currentState, newState):
131 | """
132 | Called before state is changed. If result if non-None we can select
133 | a different state to change to.
134 |
135 | :param currentState: current state
136 | :type currentState: State
137 | :param newState: new state we are moving to
138 | :type newState: State
139 | :returns: None or a different state instance
140 | :rtype: State
141 | """
142 | return None
143 |
144 | def onActionStarting(self, state, action):
145 | """
146 | Called as we enter an action.
147 |
148 | :param state: current state
149 | :type state: State
150 | :param action: action we are starting
151 | :type action: Action
152 | """
153 | pass
154 |
155 | def onActionFinished(self, state, action):
156 | """
157 | Called as we exit an action.
158 |
159 | :param state: current state
160 | :type state: State
161 | :param action: action we are starting
162 | :type action: Action
163 | """
164 | pass
165 |
166 | def onDataModelGetValue(self, action, dataModel):
167 | """
168 | Called before getting a value from a data model.
169 |
170 | :param action: action we are starting
171 | :type action: Action
172 | :param dataModel: data model we are using
173 | :type dataModel: DataModel
174 | """
175 | pass
176 |
--------------------------------------------------------------------------------
/Peach/Analyzers/asn1.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import os
5 | import sys
6 |
7 | from Peach.analyzer import *
8 | from Peach.Engine.dom import *
9 | from Peach.Engine.common import *
10 |
11 | try:
12 | from pyasn1.type import univ
13 | import pyasn1.codec.ber.decoder
14 | import pyasn1.codec.cer.decoder
15 | import pyasn1.codec.der.decoder
16 |
17 | import pyasn1.codec.ber.encoder
18 | import pyasn1.codec.cer.encoder
19 | import pyasn1.codec.der.encoder
20 |
21 | except:
22 | #raise PeachException("Error loading pyasn1 library. This library\ncan be installed from the dependencies folder.\n\n")
23 | pass
24 |
25 |
26 | class Asn1Analyzer(Analyzer):
27 | """
28 | Produces data models or peach pits from XML documents.
29 | """
30 |
31 | #: Does analyzer support asDataElement()
32 | supportDataElement = True
33 | #: Does analyzer support asCommandLine()
34 | supportCommandLine = False
35 | #: Does analyzer support asTopLevel()
36 | supportTopLevel = True
37 |
38 | def __init__(self):
39 | pass
40 |
41 | def analyzeAsn1(self, codec, data):
42 |
43 | decoder = eval("pyasn1.codec.%s.decoder" % codec)
44 |
45 | asn1Obj = decoder.decode(data)[0]
46 | return self.Asn12Peach(codec, asn1Obj)
47 |
48 | def Asn12Peach(self, codec, asn1Obj):
49 |
50 | obj = Asn1Type(None, None)
51 | obj.asn1Type = asn1Obj.__class__.__name__
52 | obj.encodeType = codec
53 | obj.asnTagSet = None #asn1Obj._tagSet
54 | obj.asn1Spec = None # asn1Obj._asn1Spec
55 |
56 | if hasattr(asn1Obj, "_value"):
57 | value = asn1Obj._value
58 | obj.objType = type(value)
59 |
60 | if type(value) == long or type(value) == int:
61 | n = Number(None, None)
62 | n.defaultValue = str(value)
63 | n.size = 32
64 |
65 | obj.append(n)
66 |
67 | elif type(value) == str:
68 | # Could be blob or string...hmmm
69 |
70 | # Sometimes we have ASN.1 inside of ASN.1
71 | # most common for OctetString type
72 | if asn1Obj.__class__.__name__ == 'OctetString':
73 | try:
74 | decoder = eval("pyasn1.codec.%s.decoder" % codec)
75 | subAsn1 = decoder.decode(asn1Obj._value)[0]
76 |
77 | child = self.Asn12Peach(codec, subAsn1)
78 | b = Block(None, None)
79 | b.append(child)
80 |
81 | except:
82 | b = Blob(None, None)
83 | b.defaultValue = value
84 |
85 | else:
86 | b = Blob(None, None)
87 | b.defaultValue = value
88 |
89 | obj.append(b)
90 |
91 | elif type(value) == tuple:
92 | # Probably and ObjectIdentifier!
93 |
94 | if asn1Obj.__class__.__name__ == 'ObjectIdentifier':
95 | oid = []
96 | for i in value:
97 | oid.append(str(i))
98 |
99 | b = String(None, None)
100 | b.defaultValue = ".".join(oid)
101 |
102 | obj.append(b)
103 |
104 | elif asn1Obj.__class__.__name__ == 'BitString':
105 | # Make this a blob
106 | b = Blob(None, None)
107 |
108 | encoder = eval("pyasn1.codec.%s.encoder" % codec)
109 | b.defaultValue = encoder.encode(asn1Obj)[4:]
110 |
111 | obj.append(b)
112 |
113 | else:
114 | print("UNKNOWN TUPLE TYPE")
115 | print(asn1Obj.__class__.__name__)
116 | print(value)
117 | raise Exception("foo")
118 |
119 | if hasattr(asn1Obj, "_componentValues"):
120 | for c in asn1Obj._componentValues:
121 | child = self.Asn12Peach(codec, c)
122 | obj.append(child)
123 |
124 | return obj
125 |
126 | def asDataElement(self, parent, args, dataBuffer):
127 | """
128 | Called when Analyzer is used in a data model.
129 |
130 | Should return a DataElement such as Block, Number or String.
131 | """
132 |
133 | dom = self.analyzeAsn1("der", dataBuffer)
134 |
135 | # Replace parent with new dom
136 |
137 | dom.name = parent.name
138 | parentOfParent = parent.parent
139 |
140 | indx = parentOfParent.index(parent)
141 | del parentOfParent[parent.name]
142 | parentOfParent.insert(indx, dom)
143 |
144 | # now just cross our fingers :)
145 |
146 | def asCommandLine(self, args):
147 | """
148 | Called when Analyzer is used from command line. Analyzer
149 | should produce Peach PIT XML as output.
150 | """
151 |
152 | raise Exception("asCommandLine not supported (yet)")
153 |
154 | #try:
155 | # inFile = args["xmlfile"]
156 | # outFile = args["out"]
157 | #except:
158 | # raise PeachException("XmlAnalyzer requires two parameters, xmlfile and out.")
159 | #
160 | #xml = _Xml2Peach().xml2Peach("file:"+inFile)
161 | #
162 | #fd = open(outfile, "wb+")
163 | #fd.write(xml)
164 | #fd.close()
165 |
166 | def asTopLevel(self, peach, args):
167 | """
168 | Called when Analyzer is used from top level.
169 |
170 | From the top level producing zero or more data models and
171 | state models is possible.
172 | """
173 | raise Exception("asTopLevel not supported")
174 |
--------------------------------------------------------------------------------
/Peach/Agent/socketmon.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import time
6 | import socket
7 |
8 | sys.path.append("..")
9 | sys.path.append("../..")
10 |
11 | from Peach.agent import Monitor
12 | from twisted.internet import reactor, protocol
13 | from threading import Thread
14 |
15 | g_socketData = None
16 | g_faultDetected = False
17 | g_stopReactor = False
18 |
19 | # TODO: Port this over to use ThreadedSelectReactor
20 | # http://twistedmatrix.com/documents/current/api/twisted.internet._threadedselect.ThreadedSelectReactor.html
21 |
22 | class SocketMonitor(Monitor):
23 |
24 | def __init__(self, args):
25 | """
26 | Constructor. Arguments are supplied via the Peach XML file.
27 |
28 | @type args: Dictionary
29 | @param args: Dictionary of parameters
30 | """
31 | try:
32 | self._name = "Socket Monitor"
33 | self._ip = ""
34 | self._port = ""
35 | self._protocol = ""
36 | self._thread = None
37 | self._internalError = False
38 | self._stopOnFault = False
39 | # Report an error if no MemoryLimit and/or neither pid nor
40 | # processName is defined.
41 | while 1:
42 | if args.has_key('IP'):
43 | self._ip = str(args["IP"]).replace("'''", "")
44 | else:
45 | print("Socket Monitor: No IP specified, using using "
46 | "127.0.0.1")
47 | self._ip = "127.0.0.1"
48 | if args.has_key('Port'):
49 | self._port = int(args['Port'].replace("'''", ""))
50 | print("Socket Monitor: Listening on Port: {}"
51 | .format(self._port))
52 | else:
53 | print("Socket Monitor: No Port specified, using 80")
54 | self._port = "8002"
55 | if args.has_key('Protocol'):
56 | self._protocol = args['Protocol'].replace("'''", "")
57 | print("Socket Monitor: Protocol = %s" % self._protocol)
58 | else:
59 | print("Socket Monitor: No Protocol specified, using TCP")
60 | self._protocol = "tcp"
61 | break
62 | except:
63 | self._internalError = True
64 | print("Socket Monitor: Caught Exception Parsing Arguments")
65 | raise
66 |
67 | def OnTestStarting(self):
68 | """
69 | Called right before start of test case or variation.
70 | """
71 | global g_faultDetected
72 | global g_socketData
73 | # fire up twisted if it hasn't been already
74 | if self._thread is None:
75 | self._thread = ReactorThread(self._ip, self._port, self._protocol)
76 | self._thread.start()
77 | g_socketData = None
78 | g_faultDetected = False
79 | return
80 |
81 | def GetMonitorData(self):
82 | """
83 | Get any monitored data from a test case.
84 | """
85 | global g_socketData
86 | return {'SocketData.txt': str(g_socketData)}
87 |
88 | def DetectedFault(self):
89 | """
90 | Check if a fault was detected.
91 | """
92 | global g_faultDetected
93 | return g_faultDetected
94 |
95 | def OnShutdown(self):
96 | """
97 | Called when Agent is shutting down, typically at end of a test run or
98 | when a Stop-Run occurs.
99 | """
100 | global g_stopReactor
101 | g_stopReactor = True
102 | s = None
103 | # hack to stop reactor
104 | if self._protocol.lower() == "tcp":
105 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106 | else:
107 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
108 | s.connect((self._ip, int(self._port)))
109 | s.close()
110 |
111 | def StopRun(self):
112 | """
113 | Return True to force test run to fail. This should return True if an
114 | unrecoverable error occurs.
115 | """
116 | return self._internalError
117 |
118 |
119 | class ReactorThread(Thread):
120 | def __init__(self, ip, port, protocol):
121 | Thread.__init__(self)
122 | self._ip = ip
123 | self._port = port
124 | self._protocol = protocol
125 | self._factory = None
126 |
127 | def run(self):
128 | self._factory = protocol.ServerFactory()
129 | self._factory.protocol = Listener
130 | if self._protocol.lower() == "tcp":
131 | reactor.listenTCP(int(self._port), self._factory)
132 | else:
133 | reactor.listenUDP(int(self._port), self._factory)
134 | reactor.run(installSignalHandlers=0)
135 |
136 |
137 | class Listener(protocol.Protocol):
138 | def connectionMade(self):
139 | global g_stopReactor
140 | global g_socketData
141 | # hack until ThreadedSelectReactor is implemented
142 | if g_stopReactor:
143 | print("Socket Monitor: Stopping Reactor")
144 | reactor.stop()
145 | else:
146 | host = self.transport.getHost()
147 | g_socketData = host.host + ":" + str(host.port)
148 | global g_faultDetected
149 | g_faultDetected = True
150 |
151 |
152 | if __name__ == "__main__":
153 | d = {
154 | "IP": "127.0.0.1",
155 | "Port": "8002",
156 | "Protocol": "tcp"
157 | }
158 | a = SocketMonitor(d)
159 | a.OnTestStarting()
160 | while True:
161 | a.OnTestStarting()
162 | time.sleep(2)
163 | print(a.DetectedFault())
164 | a.OnTestFinished()
165 | a.OnShutdown()
166 |
--------------------------------------------------------------------------------
/Peach/strategy.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | from Engine.common import *
5 |
6 |
7 | class StrategyError(PeachException):
8 | """Strategy Exception"""
9 | pass
10 |
11 |
12 | class RouteDescriptor(object):
13 | """Used to describe the route."""
14 |
15 | def __init__(self):
16 | self.paths = []
17 | self.index = 0
18 |
19 | def addPath(self, path):
20 | if path is None:
21 | raise PeachException("Argument 'path' cannot be None!")
22 | self.paths.append(path)
23 |
24 | def reset(self):
25 | self.index = 0
26 |
27 | def clear(self):
28 | del self.paths[:]
29 |
30 | def hasNextSingle(self):
31 | """
32 | This method is handy when there is no pair but only a path.
33 | """
34 | return len(self.paths) == 1 and self.index < len(self.paths)
35 |
36 | def nextSingle(self):
37 | """
38 | To get the current path.
39 | """
40 | if not self.hasNextSingle():
41 | raise PeachException("End of the route description is reached!")
42 | self.index += 1
43 | return self.paths[self.index - 1]
44 |
45 | def hasNextPair(self):
46 | """
47 | Returns True when it has two paths more to visit otherwise False.
48 | """
49 | return self.index < len(self.paths) - 1
50 |
51 | def nextPair(self):
52 | """
53 | Returns the next pair which strategy will find a proper path in
54 | between. If hasNextPair() returns False then this method completes
55 | the pair to be returned with a None and yields the result.
56 | """
57 | if not self.hasNextPair():
58 | raise PeachException("End of the route-pairs is reached!")
59 | self.index += 1
60 | return [self.paths[self.index - 1], self.paths[self.index]]
61 |
62 |
63 | class Strategy(object):
64 | """
65 | Strategy is an abstract class that is used to define a method to find out
66 | a route between start and destination paths.
67 | """
68 |
69 | def __init__(self, stateMachine, routeDescriptor, params=None):
70 | self.params = {}
71 | if params is not None:
72 | self.params = params
73 | if routeDescriptor is None:
74 | raise PeachException("Argument 'routeDescriptor' cannot be None!")
75 | self.routeDesc = routeDescriptor
76 | self.stateMachine = stateMachine
77 |
78 | def getRoute(self):
79 | """
80 | This method invokes abstract _findRoute method for each pair taken
81 | from routeDescriptor to obtain a proper route and finally returns
82 | the route.
83 | """
84 | route = []
85 | while self.routeDesc.hasNextPair():
86 | start, destination = self.routeDesc.nextPair()
87 | partialRoute = self._findRoute(start, destination)
88 | if len(route) == 0:
89 | route.extend(partialRoute)
90 | else:
91 | route.extend(partialRoute[1:])
92 | if self.routeDesc.hasNextSingle():
93 | lastPath = self.routeDesc.nextSingle()
94 | route.extend(self._findRoute(lastPath, None))
95 | return route
96 |
97 | def _findRoute(self, start, destination):
98 | """
99 | This method is used to explore a route(list of paths) in between
100 | start and destination paths.
101 |
102 | :param start: start path
103 | :param destination: destination path
104 | :returns: a list of paths starting with parameter start and ending
105 | with parameter destination.
106 | """
107 | pass
108 |
109 | def _reset(self):
110 | """
111 | Used to reset a strategy especially when re-discovering the route.
112 | """
113 | self.routeDesc.reset()
114 |
115 |
116 | class StrategyCollection(Strategy):
117 | """
118 | This class behaves like a proxy between strategies defined in XML file,
119 | which is used to run all the strategies respectively to produce a
120 | resultant route.
121 | """
122 |
123 | def __init__(self):
124 | Strategy.__init__(self, None, RouteDescriptor())
125 | self.strategies = []
126 | self.route = None
127 |
128 | def addStrategy(self, strategy):
129 | self.strategies.append(strategy)
130 | # Re-explore the route as we have a new strategy.
131 | self._reset()
132 |
133 | def getRoute(self):
134 | if self.route is not None:
135 | return self.route
136 | self.route = []
137 | for strategy in self.strategies:
138 | self.route.extend(strategy.getRoute())
139 | return self.route
140 |
141 | def _reset(self):
142 | self.route = None
143 | for strategy in self.strategies:
144 | strategy._reset()
145 |
146 |
147 | class StrategyFactory(object):
148 | """
149 | To centralize the creation of strategies.
150 | """
151 |
152 | def __init__(self, defStrategy="default.StaticStrategy"):
153 | self.defStrategy = defStrategy
154 |
155 | def createStrategy(self, stateMachine, routeDescriptor,
156 | clazz=None, params=None):
157 | if clazz is None:
158 | clazz = self.defStrategy
159 | try:
160 | code = clazz + "(stateMachine, routeDescriptor, params)"
161 | print("StrategyFactory.createStrategy: {}".format(code))
162 | strategy = eval(code)
163 | if strategy is None:
164 | raise PeachException("Unable to create Strategy [{}]"
165 | .format(clazz))
166 | return strategy
167 | except:
168 | raise PeachException("Unable to create Strategy [{}]"
169 | .format(clazz))
170 |
--------------------------------------------------------------------------------
/Peach/Transformers/Compression/Zlib.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | import sys
5 | import ctypes as C
6 | import os
7 | from ctypes import util
8 |
9 | from Peach.transformer import Transformer
10 |
11 |
12 | if sys.platform == "linux2" or sys.platform == "darwin":
13 | _zlib = C.cdll.LoadLibrary(util.find_library('libz'))
14 | elif sys.platform == "win32":
15 | _zlib = C.cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), "zlib1.dll"))
16 | else:
17 | raise NotImplementedError
18 |
19 |
20 | class _z_stream(C.Structure):
21 | _fields_ = [
22 | ("next_in", C.POINTER(C.c_ubyte)),
23 | ("avail_in", C.c_uint),
24 | ("total_in", C.c_ulong),
25 | ("next_out", C.POINTER(C.c_ubyte)),
26 | ("avail_out", C.c_uint),
27 | ("total_out", C.c_ulong),
28 | ("msg", C.c_char_p),
29 | ("state", C.c_void_p),
30 | ("zalloc", C.c_void_p),
31 | ("zfree", C.c_void_p),
32 | ("opaque", C.c_void_p),
33 | ("data_type", C.c_int),
34 | ("adler", C.c_ulong),
35 | ("reserved", C.c_ulong),
36 | ]
37 |
38 |
39 | ZLIB_VERSION = C.c_char_p("1.2.3")
40 | Z_NULL = 0x00
41 | Z_OK = 0x00
42 | Z_STREAM_END = 0x01
43 | Z_NEED_DICT = 0x02
44 | Z_BUF_ERR = -0x05
45 | Z_NO_FLUSH = 0x00
46 | Z_SYNC_FLUSH = 0x02
47 | Z_FINISH = 0x04
48 | CHUNK = 1024 * 128
49 |
50 |
51 | class Compressor(object):
52 | def __init__(self, level=-1, dictionary=None):
53 | self.level = level
54 | self.st = _z_stream()
55 | err = _zlib.deflateInit_(C.byref(self.st), self.level, ZLIB_VERSION,
56 | C.sizeof(self.st))
57 | assert err == Z_OK, err
58 | if dictionary:
59 | err = _zlib.deflateSetDictionary(
60 | C.byref(self.st),
61 | C.cast(C.c_char_p(dictionary), C.POINTER(C.c_ubyte)),
62 | len(dictionary)
63 | )
64 | assert err == Z_OK, err
65 |
66 | def __call__(self, input):
67 | outbuf = C.create_string_buffer(CHUNK)
68 | self.st.avail_in = len(input)
69 | self.st.next_in = C.cast(C.c_char_p(input), C.POINTER(C.c_ubyte))
70 | self.st.next_out = C.cast(outbuf, C.POINTER(C.c_ubyte))
71 | self.st.avail_out = CHUNK
72 | err = _zlib.deflate(C.byref(self.st), Z_SYNC_FLUSH)
73 | if err in [Z_OK, Z_STREAM_END]:
74 | x = outbuf[:CHUNK - self.st.avail_out]
75 | return x
76 | else:
77 | raise AssertionError(err)
78 |
79 | def __del__(self):
80 | err = _zlib.deflateEnd(C.byref(self.st))
81 |
82 |
83 | class Decompressor(object):
84 | def __init__(self, dictionary=None):
85 | self.dictionary = dictionary
86 | self.st = _z_stream()
87 | err = _zlib.inflateInit2_(C.byref(self.st), 15, ZLIB_VERSION,
88 | C.sizeof(self.st))
89 | assert err == Z_OK, err
90 |
91 | def __call__(self, input):
92 | outbuf = C.create_string_buffer(CHUNK)
93 | self.st.avail_in = len(input)
94 | self.st.next_in = C.cast(C.c_char_p(input), C.POINTER(C.c_ubyte))
95 | self.st.avail_out = CHUNK
96 | self.st.next_out = C.cast(outbuf, C.POINTER(C.c_ubyte))
97 | err = _zlib.inflate(C.byref(self.st), Z_SYNC_FLUSH)
98 | if err == Z_NEED_DICT:
99 | assert self.dictionary, "no dictionary provided"
100 | dict_id = _zlib.adler32(0,
101 | C.cast(C.c_char_p(self.dictionary), C.POINTER(C.c_ubyte)),
102 | len(self.dictionary))
103 | err = _zlib.inflateSetDictionary(
104 | C.byref(self.st),
105 | C.cast(C.c_char_p(self.dictionary), C.POINTER(C.c_ubyte)),
106 | len(self.dictionary)
107 | )
108 | assert err == Z_OK, err
109 | err = _zlib.inflate(C.byref(self.st), Z_SYNC_FLUSH)
110 | if err in [Z_OK, Z_STREAM_END]:
111 | return outbuf[:CHUNK - self.st.avail_out]
112 | else:
113 | raise AssertionError(err)
114 |
115 | def __del__(self):
116 | err = _zlib.inflateEnd(C.byref(self.st))
117 |
118 |
119 | class DictZLib(Transformer):
120 | def __init__(self, level=-1, dicto=None):
121 | Transformer.__init__(self)
122 | self._level = level
123 | self._dicto = dicto
124 |
125 | def realEncode(self, data):
126 | _compress = Compressor(self._level, self._dicto)
127 | return _compress(data)
128 |
129 | def realDecode(self, data):
130 | _decompress = Decompressor(self._dicto)
131 | return _decompress(data)
132 |
133 |
134 | class SPDYZLib(DictZLib):
135 | def __init__(self):
136 | _dicto = \
137 | "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encod"\
138 | "ingaccept-languageauthorizationexpectfromhostif-modified-sinceif"\
139 | "-matchif-none-matchif-rangeif-unmodifiedsincemax-forwardsproxy-a"\
140 | "uthorizationrangerefererteuser-agent1001012002012022032042052063"\
141 | "0030130230330430530630740040140240340440540640740840941041141241"\
142 | "3414415416417500501502503504505accept-rangesageetaglocationproxy"\
143 | "-authenticatepublicretry-afterservervarywarningwww-authenticatea"\
144 | "llowcontent-basecontent-encodingcache-controlconnectiondatetrail"\
145 | "ertransfer-encodingupgradeviawarningcontent-languagecontent-leng"\
146 | "thcontent-locationcontent-md5content-rangecontent-typeetagexpire"\
147 | "slast-modifiedset-cookieMondayTuesdayWednesdayThursdayFridaySatu"\
148 | "rdaySundayJanFebMarAprMayJunJulAugSepOctNovDecchunkedtext/htmlim"\
149 | "age/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/pl"\
150 | "ainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1.1status"\
151 | "versionurl\0"
152 | DictZLib.__init__(self, dicto=_dicto)
153 |
154 |
155 | if __name__ == "__main__":
156 | print(SPDYZLib().realDecode(DictZLib().realEncode('big ba\x45ng')))
157 |
--------------------------------------------------------------------------------