Skip to content

Commit

Permalink
Dynamic responses working
Browse files Browse the repository at this point in the history
  • Loading branch information
r00tdaemon committed May 2, 2020
1 parent 69838d0 commit 9bc13e3
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 15 deletions.
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Simserve
Simserve is a server simulator which can be used to simulate server interactions of a program (malware).
Simserve is a server simulator which can be used to simulate server interactions of a program (malware). It is highly configurable as users can configure it to produce valid http responses for given set of requests.

## Files
* `config_parse.py` - Parse the config.json file using the Config class. Also,
Expand All @@ -9,23 +9,41 @@ Simserve is a server simulator which can be used to simulate server interactions
request-response pair.
* `server.py` - Main file to start the server.

* `plugins.base` - The base plugin class used to for dynamic responses. For on this below.

## Installation and Usage
*This tool is written in python 3.6*
*This tool is written in **python 3.6***
To install dependencies run -
`pip install -r requirements.txt`

To configure the tool copy `config.json.example` to `config.json`.
To configure the tool copy `conf.json.example` to `conf.json`.
- `port` - Specifies the port on which to run the server.
- `routes` - List of paths for which server responds.
- `path` - Each route has a path string to define the URL path.
- `responses` - List of responses for each route for different methods. If no
response is defined for a method server returns 405 status.
- `headers` - Dictionary of headers to send with the response.
- `body` - The response body for given route.
- `methods` - Methods for which this response should be sent.
- `body` - The response body for given route. Used if `"response_type` is "static"
- `methods` - HTTP methods for which this response should be sent.
- `response_type` - How to generate the response. Can be "static" or "script"
- `script` - Name of python module from which to generate the response. Used if `"response_type` is "script"

To run - `python3 server.py`

### Plugins
Users can create their own plugins which will allow them to generate dynamic responses based on the requests received. All the plugins reside in the `plugins` directory.

To run -
`python3 server.py`
To create a plugin you need to import the base plugin class and override its abstract method.
```python
from plugins.base import Plugin

## Future Improvements
class MyResponse(Plugin):
def response(self, request):
return f"Hello from plugin"
```
The `customresp.py` serves as a simple example of a plugin.

## Future Improvements
* Allow specifying file path for config file.
* Option to log request and responses to a file.
* Create a GUI for the tool.
2 changes: 1 addition & 1 deletion conf.json.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"port": 8888,
"port": 1234,
"routes":[
{
"path":"/example1",
Expand Down
6 changes: 3 additions & 3 deletions handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _get_resp_body(self, resp: Dict):
plug_module,
lambda x: inspect.isclass(x) and not inspect.isabstract(x) and issubclass(x, Plugin)
)[0]
return plug().response()
return plug().response(self.request)
elif resp.get("response_type") == "static":
return resp.get("body")

Expand All @@ -47,7 +47,7 @@ def _format_request(req: httputil.HTTPServerRequest) -> str:
@staticmethod
def _format_response(resp: Dict) -> str:
log = f"\n----- Response -----\n"
for k, v in sorted(resp.get("headers").items()):
for k, v in sorted(resp.get("headers", {}).items()):
log += f"{k}: {v}\n"
# TODO: Log for script responses.
log += f"\n{resp.get('body')}\n"
Expand All @@ -66,7 +66,7 @@ def _handle_req(self):
self.log_req_resp(self.request, None)
raise tornado.web.HTTPError(405)

for k, v in resp.get("headers").items():
for k, v in resp.get("headers", {}).items():
self.set_header(k, v)
self.log_req_resp(self.request, resp)
self.finish(self._get_resp_body(resp))
Expand Down
Empty file added plugins/__init__.py
Empty file.
3 changes: 2 additions & 1 deletion plugins/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import abc
from tornado import httputil


class Plugin(abc.ABC):
@abc.abstractmethod
def response(self):
def response(self, request: httputil.HTTPServerRequest) -> str:
pass
2 changes: 1 addition & 1 deletion plugins/customresp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@


class MyResponse(Plugin):
def response(self):
def response(self, request):
return f"Dynamic response {randint(0,10)}"
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
tornado == 6.0.4
PyQt5 == 5.14.2

0 comments on commit 9bc13e3

Please sign in to comment.