ソースを参照

added fuzz inputs

Sharon Brizinov 5 年 前
コミット
c6c4a749f8

+ 14 - 4
README.md

@@ -130,6 +130,7 @@ command line in the opener main directory.
 
 Fuzzing
 --------------
+#### Intro
 Fuzzing is an automated testing method that directs varying input data to a program in 
 order to monitor output. It is a way to test for overall reliability as well as identify 
 potential security bugs.
@@ -140,6 +141,9 @@ The fuzzer we are using is AFL, a fuzzer that uses runtime guided techniques to
 - Feeds the fuzzed process with the test case through STDIN
 - Monitors the execution and registers which paths are reachable
 
+![Alt text](fuzz/imgs/fuzz.png "AFL Fuzzing")
+
+#### Compile
 To start fuzzing this project with AFL you'll need to compile it with AFL.
 First make sure you have AFL installed:
 ```
@@ -157,16 +161,22 @@ Then, compile OpENer with AFL:
 2. Compile OpENer with AFL ``./setup_posix_fuzz_afl.sh`` 
 3. Run ``make``
 
+#### Fuzz
 Finally, generate some test cases and start AFL:
 ```
 # Generate inputs
 mkdir inputs
-echo 630000000000000000000000000000000000000000000000 | xxd -r -p > ./inputs/req_list_identity
-# You can also use the inputs we prepared from ``fuzz/inputs``
+echo 630000000000000000000000000000000000000000000000 | xxd -r -p > ./inputs/enip_req_list_identity
+# You can also use the inputs we prepared from OpENer/fuzz/inputs
 # Finally, let's fuzz!
-afl-fuzz -i inputs -o findings ./src/ports/POSIX/OpENer eth1
+afl-fuzz -i inputs -o findings ./src/ports/POSIX/OpENer <interface_name>
 ```
-![Alt text](fuzz/fuzz.png "AFL Fuzzing")
+
+#### Reproduce a crash
+Usually to reproduce a crash it's enough to retransmit the testcase using ``cat testcase | nc IP_ADDR 44818``
+However, since CIP runs over the EtherNet/IP layer, it must first register a valid session. Therefore, we need to use a dedicated script:
+`python2 fuzz/scripts/send_testcase.py IP testcase_path`
+
 
 Porting OpENer:
 ---------------

+ 0 - 0
fuzz/fuzz.png → fuzz/imgs/fuzz.png


BIN
fuzz/inputs/cip_req_forward_open


BIN
fuzz/inputs/cip_req_list_identity_cip


BIN
fuzz/inputs/enip_req_list_identity


BIN
fuzz/inputs/enip_req_register_session


+ 38 - 0
fuzz/scripts/send_testcase.py

@@ -0,0 +1,38 @@
+import sys
+import socket
+import struct
+
+if len(sys.argv) != 3:
+	print("python {} IP TESTCASE_PATH".format(sys.argv[0]))
+	sys.exit(1)
+
+HOST_IP = sys.argv[1]
+HOST_PORT = 44818
+TESTCASE_PATH = sys.argv[2]
+
+ENIP_SESSION_CONTEXT = b"\x92\x83J\x0b=\x9e\x0cW"
+ENIP_INIT_SESSION_PACKET = b"e\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ENIP_SESSION_CONTEXT + b"\x00\x00\x00\x00\x01\x00\x00\x00"
+
+
+print("[-] Connecting to {}:{}".format(HOST_IP, HOST_PORT))
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((HOST_IP, HOST_PORT))
+
+print("[-] Init ENIP session")
+s.sendall(ENIP_INIT_SESSION_PACKET)
+enip_session = s.recv(1024)
+session_handle = enip_session[4:8]
+print("[-] Got ENIP Session Handle: {}".format(struct.unpack("<I", session_handle)[0]))
+print("[-] Reading testcase from: '{}'".format(TESTCASE_PATH))
+with open(TESTCASE_PATH, "rb") as f:
+	testcase_data = f.read()
+
+print("[-] Patching sender context and session handle")
+testcase = testcase_data[:4]		# command, len
+testcase += session_handle 		# session handle
+testcase += testcase_data[8:12] 	# status
+testcase += ENIP_SESSION_CONTEXT 	# session context
+testcase += testcase_data[20:]		# options and payload
+print("[-] Sending testcase of {} bytes".format(len(testcase)))
+s.send(testcase)
+s.close()