State Machine with boofuzz #580
Replies: 9 comments 4 replies
-
Apologies for the long delay. Stateful fuzzing is not fully implemented in boofuzz but there are some helpers you could use. Inside of those callbacks you have access to all of the blocks and primitives of the node and their values, which you can also overwrite to construct a valid packet. Check #368 but I'm not sure about how manipulating values affects dynamic primitives like s_size and s_checksum. In case you only want to fuzz the payload there is a second option. You could write a dedicated connection class that handles all the stateful stuff. The SSLSocketConnection class basically does this, but it uses external libraries for it. Either way, it would be interesting to see the outcome if you find a solution. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the question @TwoBottomBuns ! @SR4ven hit the nail on the head. Right now the way to handle a state machine would be to use the various callback methods and modify your own state machine / object as necessary. If anybody has any ideas for how boofuzz could make this easier, feel free to share. The main challenge is that every protocol's state machine will be unique. s_size and s_checksum render pretty late and should accept any modified values. |
Beta Was this translation helpful? Give feedback.
-
Hi, def define_third_handshake_packet():
s_initialize('ThirdPacket')
with s_block('header'):
s_bytes(b'foo', name='bar')
with s_block('body'):
s_dword(0, name='secure channel id', fuzzable=False) # will be overwritten
s_dword(4, name='secure token id', fuzzable=False) # will be overwritten
s_dword(2, name='secure sequence number', fuzzable=False) # will be overwritten
s_dword(2, name='secure request id', fuzzable=False) # will be overwritten
[...]
def set_channel_parameters(target, fuzz_data_logger, session, node, *args, **kwargs): # pylint: disable=protected-access
'''
1. Unpacking values at [8:12], [12:16] and so on in the received packet.
1a. Using length fields to account for variable length header content
1b. Handling struct.error for empty response
2. Directly setting _value of next node. Replacing node breaks the fuzzing process
'''
try:
recv = session.last_recv # last received packet
# Lots of magic numbers, all are protocol specific
channel_id, policy_len = struct.unpack('ii', recv[8:16])
sequence_offset = 24 + policy_len
seq_num, req_id = struct.unpack('ii', recv[sequence_offset:sequence_offset + 8])
request_header_length = 24
token_offset = sequence_offset + 8 + request_header_length + 4
sec_channel_id, token_id = struct.unpack('ii', recv[token_offset:token_offset + 8])
except struct.error:
fuzz_data_logger.log_error('Could not unpack channel parameters for this test case')
else:
node.stack[1].stack[0]._value = sec_channel_id
node.stack[1].stack[1]._value = token_id
node.stack[1].stack[2]._value = seq_num + 1
node.stack[1].stack[3]._value = req_id + 1
[...]
def main():
[...]
session.connect(s_get('FirstPacket'))
session.connect(s_get('FirstPacket'), s_get('ThirdPacket'), callback=set_channel_parameters)
session.fuzz() |
Beta Was this translation helpful? Give feedback.
-
Thanks for sharing your solution @dorpvom! |
Beta Was this translation helpful? Give feedback.
-
Hi there, I would reopen this issue if it is possible and reasonable. I am trying to use a callback to read a value from a node and pass it to the next node (as @dorpvom suggested) The message that I want to modify:
So I want to overwrite the primitive The callback signature is The session graph is
The reading of the response is ok, I get a correct 'myVal' from response to prev msg. Notice that the following prints (inside the callback) gave me the right value of the field I want to overwrite:
The only write that works is: How I solved my problem: My question is: |
Beta Was this translation helpful? Give feedback.
-
I used to do the same thing in an ooolder version of Maybe you can have a try. I'm not sure if it changes in the latest version. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the answer @cq674350529. I tried the Also the type of The only thing to try after modifying the next node with Thanks again, all your suggestions are welcome. |
Beta Was this translation helpful? Give feedback.
-
@solimand I had a look at the
I may not understand what's your point. |
Beta Was this translation helpful? Give feedback.
-
@cq674350529 the only doubt is: could the overwriting of the Anyway, the solution provided by @dorpvom is not more valid, so I commented on this post with my experience in this regard. |
Beta Was this translation helpful? Give feedback.
-
Is it possible to manage nodes with a state machine? Suppose I want to fuzz a stateful network protocol (such as TLS), and would need to keep track of certain pieces for different parts of the protocol. I would need to keep track of things like the handshake hash, the negotiated ciphersuite, keys, etc. in order to send valid packets and handshake messages while fuzzing.
Based on examples and documentation, it seems like the default values defined when constructing blocks and primitives are intended to be used for all cases in which that block is not currently being fuzzed. But when building a block for a Finished message (in which the client sends an encrypted payload), it would be impossible to construct a valid payload without having the contents from the ClientHello and ServerHello.
Is there an efficient way to manage a state machine with boofuzz that can track all of these necessary parameters and then dynamically populate blocks with the values? I was looking at the s_update() function to do this, but I saw in another issue how it intentionally doesn't work with the s_string primitive. I also saw mention that s_update may be removed for the future.
Beta Was this translation helpful? Give feedback.
All reactions