summaryrefslogtreecommitdiffstats
path: root/sploit/comm.py (follow)
AgeCommit message (Collapse)AuthorFilesLines
2024-01-13comm: Promote from module to packageMalfurious1-205/+0
This is done to help clean the top-level "sploit" package. Furthermore, there is some planned future work to refactor comm into multiple modules, so this lays some groundwork for that. Signed-off-by: Malfurious <m@lfurio.us>
2024-01-13log: Move to sploit.util packageMalfurious1-1/+1
Signed-off-by: Malfurious <m@lfurio.us>
2023-04-02comm: Fix bug where readline thinks pipe is brokenv0.4dusoleil1-2/+2
We should strip the newline from the data after checking if we got an empty string returned. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-03-01Add special cases for read(size <= 0)dusoleil1-4/+9
A read of 0 isn't particularly useful, but it is weird that it will cause a BrokenPipeError. Instead, it makes more sense to just return an empty string. A read of <0 would normally read until EOF, but we already have that feature in readall() and it wouldn't be particularly useful here. A similar functionality of reading the entire current contents of the buffer is useful, though. This is already implemented in readall_nonblock() and this would be a nice user-facing way of calling that. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-03-01Add io.last as the result of the last discrete readdusoleil1-1/+9
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18comm: Localize stdin nonblock to interact's readalldusoleil1-4/+6
In interact(), we set stdin to be nonblocking for the duration of the function. As an unexpected side-effect, this was setting stdout to be nonblocking as well. This has caused at least one crash in the past. Localizing the nonblock to just when we're reading from stdin should solve this. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18Use buffered read throughout Commdusoleil1-1/+1
We had originally decided to use the os.read() function instead of the actual buffered file object's read function. This was due to the blocking behavior or os.read() being closer to POSIX read than the other function. As it turns out, os.read() is an unbuffered read. Every other read call in this interface is buffered. This causes some undefined behavior in certain cases and leads to some really confusing bugs. After some discussion, we've decided that, in this application's domain, the blocking behavior of the buffered file object's read is actually often more useful anyways. Changing this call will deal with both issues. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18Read once at the beginning of interact mode.dusoleil1-0/+1
This behavior was accidentally removed in dcba5f2 interact mode works by polling for IO events, but it will miss any unread data already in the buffer when it is first entered. We can ensure this gets caught by just doing a read once at the beginning. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18comm: Strip \n character from readline()Malfurious1-1/+4
Line-oriented reads now strip the newline from the end of their returned string. Additionally, readall() strips the newline, but only from the string that gets logged to the user's terminal (goodbye to all the "\n" printed at the end of each line). Of course, these functions are called by other parts of the read API and have downstream effects. Consideration was given to the entire API with these rules in mind: - Raw reads (or non-line-oriented reads) will not filter ANY of their read content. They are logged to the screen as one "line" of log text with \n characters shown in-place (not actually resetting the terminal cursor). If reading binary, these bytes dont actually mean line termination anyway. functions: read, readall(_nonblock) *, readuntil - Line-oriented reads will strip the terminating \n, log the single line to the screen, and return it. functions: readline, readlineuntil ** * readall(_nonblock) functions turn out to be a special case. They will operate as raw reads, returning a blob of content. However, we generally want to run them on line-oriented input, so they log according to the line-oriented rules. ** Although content returned from readlineuntil will have \n's stripped, the lines are returned in an array, so we can still distinguish them. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18comm: Add default argument for writeline()Malfurious1-1/+1
The writeline function will now default to send an empty line when called without an argument. I don't believe any such default makes sense for the plain write function, as writing nothing should have no effect. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18comm: Enable logonread during interact()Malfurious1-0/+3
This is normally not an issue, since logonread defaults to True. However, if the user disables this setting, interact() becomes a lot less useful. logonread is now forced on during io.interact(), but respected through the rest of the API. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2023-02-18comm: Squelch BrokenPipeError during shutdown()Malfurious1-1/+4
Failure to close target stdout is not interesting. Furthermore, if sploit ever gets into this situation, the user script has likely already raised a more useful error/backtrace. Handling this exception typically results in a duplicate error. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-04-08sploit: Allow multiple reads in Comm.readall_nonblock()Malfurious1-2/+6
Due to line buffering, we may often trigger a burst of data to be sent by the target, but resolve the non-blocking read only after the first line is received. We would like to wait just a little longer to receive the entire burst instead. readall_nonblock() will now reset its timeout period whenever any data becomes readable and will not return until we go an entire period of silence. Under normal conditions, the full duration of readall_nonblock should barely be any longer than the defined period itself. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-04-08sploit: Fix units for Comm.timeoutMalfurious1-1/+1
select's poll.poll() function expects its timeout argument to be in milliseconds. This is an artifact from earlier developent where we were using the higher-level 'selectors' API, which never got merged. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-03-14sploit: Clean up function Comm.interact()Malfurious1-32/+26
The previous patches in this series have needed to utilize similar logic as Comm.interact() throughout other parts of the Comm class. This patch just revisits .interact() to clean up redundant code. Co-authored-by: dusoleil <howcansocksbereal@gmail.com> Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-03-14sploit: Add function popen()Malfurious1-0/+6
This is a free-function in the comm module, intended to help setup Sploit plumbing when working in the Python interactive interpreter. At the moment, the intended user experience in the interpreter is to err on the side of being interactive/responsive. As such, the Comm object returned from popen() is initialized with overridden IO settings to prefer 'readonwrite' by default. Addtionally, any early output from the target is also read, so that it may be immediately visible. A consequence of this configuration is that, until readonwrite is set False, most target output will be consumed before any .read* function has a chance to return it. While that would be a hard showstopper for any Sploit script, an interactive user can simply copy/paste any important data that is produced. Given that the interpreter workflow is likely going to be most useful for quick prototyping and recon with the proposed rev module, I consider this tradeoff appropriate at the moment, but will consider revisiting this if its usage is problematic. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-03-14sploit: Add Comm property 'readonwrite'Malfurious1-0/+2
If readonwrite is set to True (default False), Sploit will catch up and read all available stdin data from the target in a non-blocking fashion. If logonread is also set to True, this data will immediately be presented to the user whenever data is sent, but is otherwise lost (not returned). This mode is primarily intended for use in the interactive Python interpreter, where it can be cumbersome to keep alternating read and write calls when one does not care to actually record the read values. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2022-03-14sploit: Add function Comm.readall_nonblock()Malfurious1-0/+11
Function should consume all available incoming data from target and return it, however will return 'immediately' (according to a configurable timeout) if the pipe is empty. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Automatically shutdown outgoing comms after script executionMalfurious1-0/+3
A new function, Comm.shutdown(), is added. It will close only the stdout stream of the communications backend, potentially making the termination of the target program more fluid. The name 'shutdown' is chosen to emulate shutdown(2) from the low-level socket api, which is used to close just part of a full-duplex file descriptor. This is in contrast to 'close', which I would expect to completely terminate the given object IO. comm.shutdown() is now called by main.py, after the user script returns, to ensure that the subsequent readall() doesn't get stuck because our target is blocked reading its stdin. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Catch KeyboardInterrupt in Comm.readall()Malfurious1-3/+6
If execution is stuck inside readall() (for example, due to blocked IO), handling KeyboardInterrupt allows the user a way to get out, without exiting the active script early or losing the data read so far. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Ensure the logonread option is restored by Comm.readuntil()Malfurious1-5/+7
This function has a momentary side-effect of switching self.logonread to False. This patch ensures its original value is always restored, even if an exception is raised. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Add logonwrite option to commsMalfurious1-0/+2
If enabled, data sent to the target will be printed/logged as alt text, similar to data directly printed by the user. Feature is off by default. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Check logonread in function Comm.readall()Malfurious1-1/+1
This function will no longer mistakenly log data when logonread is set to False. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-12-17sploit: Rework loggerMalfurious1-16/+17
The log module is updated to support binary encodings, colors, and for improved compatibility with Python's print() builtin. Encoding semantics are switched up, since it seems like some of the more interesting encoding modes (from a CTF perspective) actually use bytes-like objects as their high-level form (that is, bytes are encoded to another form, such as hex, then decoded back to the original form). So the logged value is now passed to encode instead of decode, and only if the object is of type 'bytes', as unicode strings are now considered out-of-scope for this operation. Additionally, the bytes wrapper (b'') is no longer visible in the logged content. For readability, several standard colors have been defined for use within Sploit: - RED: Errors - YELLOW: Warnings - GREEN: Status messages / Startup messages - WHITE: Target output - GRAY: User output / Alt text Logging functions now support an optional color option to select the desired color, and have specific defaults based on who is invoking the log (see below...) Logging functions are now also fully compatible with the builtin print() function. This is because Sploit now replaces the standard print() with a logging function within the user's script (which is done to maintain additional consistency of messages displayed in the console). Function ilog (internal log) has default values tuned for the library's convenience: Text goes to stderr, and is presented as status messages (green). Function elog (external log) has default values tuned for the user: Text goes to stdout, and is presented as alt text to distinguish it from data read from the target. Within the user context, 'print' refers to this function. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-02Handle Process destr when Process constr throwsdusoleil1-0/+1
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-02Reuse read() and readline() in the until() APIdusoleil1-4/+5
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-02Add readall() which reads until EOFdusoleil1-0/+7
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-02Move comm toggles for consistencydusoleil1-3/+3
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Add Config Toggles for Read/Write Extra Behaviordusoleil1-5/+8
logonread can enable/disable logging the result of every read flushonwrite can enable/disable automatically flushing every write Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Rewrite interact() to be Single Threadeddusoleil1-36/+26
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Add Convenience Utility to readuntil()dusoleil1-2/+5
readuntil() and readlineuntil() will now automatically bind() a predicate and given arguments to produce the single function predicate required. The 'until' module will provide convenience utilities for use with readuntil() and readlineuntil(). For now, it contains functools.partial renamed as bind(), lastline() which can call a predicate with the last element of the array of lines given from readlineuntil(), and simplified versions of re.search and re.fullmatch renamed as contains and equals. These allow us to write powerful and legible statements like: comm.readlineuntil(lastline,contains,b'Enter') Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01readlineuntil() Operates on an Array of Linesdusoleil1-4/+9
Instead of only operating on and returning the last line read, readlineuntil() will now check the predicate against an array of all lines read and return that array when the predicate is true. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Correct read() Semanticsdusoleil1-1/+1
The BufferedReader's .read() doesn't behave as expected. It reads EXACTLY size bytes and will block until there are enough available to read. os.read() does what we expect. It will read UP TO size bytes and only block if there is nothing available to read. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Throw a BrokenPipeError on Broken Readdusoleil1-0/+4
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Formattingdusoleil1-10/+10
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Move "Read Rest of Output" Out of Destructordusoleil1-4/+0
With the "read rest of output" code in the Comm destructor, it would continue to read output even in situations where some error happened and we expect sploit to die or when the user presses Ctrl+C to end sploit. By moving it to the end of the script running code in main, it behaves more intuitively. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Better Shutdown Process for Pipesdusoleil1-4/+7
Handle all of the edge cases when shutting down in Pipes mode. e.g. If the pipes are broken (tried to write after the program died) If the fifos don't exist anymore (sometimes tempfile cleans them up before the destructor finishes when certain errors happen) If the object attributes for the streams and fifo paths aren't set (this can happen if the constructor didn't finish. e.g. the user cancels while waiting on a connection) Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Better Shutdown Process for Target Programdusoleil1-1/+8
If we need to wait on the target program to die, we don't want to just wait forever with no indication to the user. Instead, only call wait if the program is still alive, inform the user that we are doing this, and give them the ability to forcefully kill the target program with Ctrl+C. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-09-01Better Info Messagesdusoleil1-2/+2
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-08-31Use Entire Path When Given The Pipe Directorydusoleil1-1/+3
Previously, you could specify a directory which must exist under /tmp. Now, you can give the full path to a directory to be used by Pipes. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-08-31Add Interactive Mode to Commsdusoleil1-1/+49
comm.interact() will drop the user into an "interactive" mode where they can directly control what is sent. A SIGINT (Ctrl+C) will drop the script out of interactive mode and continue executing the rest of the script. If the output of the program (input into our script) goes into a broken state (such as when the target program exits), interactive mode will automatically exit. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-08-31Add readuntil() and readlineuntil() to Commsdusoleil1-0/+14
Both new functions check the input for a predicate and keep reading until the predicate is true. readuntil() will consume input byte by byte and use the entire string read to check the predicate. It will then return that entire string. readlineuntil() consumes input line by line and only uses the last line to check the predicate. The line that satisfies the predicate is all that is returned. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
2021-08-30Sploit Rework MVP Structure, Packaging, and Commsdusoleil1-0/+68
First part of the MVP for the larger Sploit rework effort. Add project structure, python packaging, basic comms, and "log" hook. From in or out of the sploit directory, you can run the "sploit.py" script, run python -m sploit, or import the sploit modules from the python3 shell. You can also pip install Sploit and from anywhere you can run the sploit command, run python -m sploit, or import the sploit modules from the python3 shell. Running as a standalone application, Sploit can run in a "target" mode, a "pipe" mode, and a "pipe daemon" mode. In "target" mode, Sploit will launch a target program as a subprocess and run an exploit script against its I/O. In "pipe" mode, Sploit will create named fifos and wait for a program to connect to them to run an exploit script against them. In "pipe daemon" mode, Sploit will run similar to the "pipe" mode, but automatically recreate the fifos with the same name after each execution. Basic comm operations of read, readline, write, and writeline are available to the exploit script. A "log" hook is executed whenever data is read in from the target program. This will just print the data out, but it can be configured to decode it with a specific encoding or you could replace the function for different behavior. Signed-off-by: dusoleil <howcansocksbereal@gmail.com>