diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 92b3100..aadf5ba 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -26,5 +26,6 @@ jobs: - name: Run tests run: | cd tests + python vulunserver.py & python -m unittest *.py diff --git a/tests/test_cracker.py b/tests/test_cracker.py index e5c317c..3b92a29 100644 --- a/tests/test_cracker.py +++ b/tests/test_cracker.py @@ -3,39 +3,31 @@ import sys +from fenjing.form import get_form +from fenjing.requester import Requester + sys.path.append("..") import unittest import fenjing from typing import Union from fenjing.cracker import Cracker -from fenjing.submitter import BaseSubmitter, HTTPResponse +from fenjing.submitter import FormSubmitter, HTTPResponse from fenjing import const -import jinja2 import logging - -class FakeSubmitter(BaseSubmitter): - def __init__(self, waf, callback=None): - super().__init__(callback) - self.waf = waf - - def submit_raw(self, raw_payload: str) -> Union[HTTPResponse, None]: - if not self.waf(raw_payload): - return HTTPResponse(200, "Nope") - try: - result = jinja2.Template(source=raw_payload).render() - return HTTPResponse(200, result) - except Exception: - return HTTPResponse(500, "Internal Server Error") +VULUN_SERVER_ADDR = "http://127.0.0.1:5000/" class CrackerTestBase(unittest.TestCase): def setUp(self): super().setUp() self.blacklist = ["."] - self.subm = FakeSubmitter( - lambda x: all(w not in x for w in self.blacklist) + self.subm = FormSubmitter( + url=VULUN_SERVER_ADDR, + form=get_form(action="/", inputs=["name"], method="GET"), + target_field="name", + requester=Requester(interval=0.01), ) def test_waf(self): @@ -44,11 +36,12 @@ def test_waf(self): self.assertIsNotNone(full_payload_gen) payload, _ = full_payload_gen.generate( const.OS_POPEN_READ, - "echo 'cracked! @m wr!tIng s()th' " + self.__class__.__name__, + "echo 'cracked! @m wr!tI1111ng s()th' " + self.__class__.__name__, ) self.assertIsNotNone(payload) resp = self.subm.submit(payload) - self.assertIn("cracked! @m wr!tIng s()th", resp.text) + assert resp is not None + self.assertIn("cracked! @m wr!tI1111ng s()th", resp.text) class CrackerTestHard(CrackerTestBase): @@ -79,9 +72,7 @@ def setUp(self): "~", "{{", ] - self.subm = FakeSubmitter( - lambda x: all(w not in x for w in self.blacklist) - ) + self.subm = FakeSubmitter(lambda x: all(w not in x for w in self.blacklist)) class CrackerTestWeird(CrackerTestBase): diff --git a/tests/test_full_payload_gen.py b/tests/test_full_payload_gen.py index 0e9a777..ec20c96 100644 --- a/tests/test_full_payload_gen.py +++ b/tests/test_full_payload_gen.py @@ -1,12 +1,18 @@ -import sys # noqa +import sys +from fenjing.form import get_form +from fenjing.requester import Requester + +from fenjing.submitter import FormSubmitter # noqa sys.path.append("..") # noqa import fenjing import logging +from flask import render_template_string + + from fenjing import FullPayloadGen, const import unittest -import jinja2 import random fenjing.full_payload_gen.logger.setLevel(logging.ERROR) @@ -22,6 +28,7 @@ def get_full_payload_gen( detect_mode=detect_mode, ) +VULUN_SERVER_ADDR = "http://127.0.0.1:5000/" class FullPayloadGenTestCaseSimple(unittest.TestCase): def setUp(self) -> None: @@ -31,7 +38,12 @@ def setUp(self) -> None: "]", ] self.full_payload_gen = get_full_payload_gen(self.blacklist) - + self.subm = FormSubmitter( + url=VULUN_SERVER_ADDR, + form=get_form(action="/", inputs=["name"], method="GET"), + target_field="name", + requester=Requester(interval=0.01), + ) def test_string(self): strings = [ "123", @@ -45,8 +57,9 @@ def test_string(self): assert payload is not None # why? # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn(string, result) + resp = self.subm.submit(payload) + assert resp is not None + self.assertIn(string, resp.text) for word in self.blacklist: self.assertNotIn(word, payload) @@ -58,13 +71,14 @@ def test_os_popen_read(self): assert payload is not None # why? # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn("fen jing", result) + resp = self.subm.submit(payload) + assert resp is not None + self.assertIn("fen jing", resp.text) for word in self.blacklist: self.assertNotIn(word, payload) -class FullPayloadGenTestCaseHard(unittest.TestCase): +class FullPayloadGenTestCaseHard(FullPayloadGenTestCaseSimple): def setUp(self) -> None: super().setUp() self.blacklist = [ @@ -108,39 +122,8 @@ def setUp(self) -> None: ] self.full_payload_gen = get_full_payload_gen(self.blacklist) - def test_string(self): - strings = [ - "123", - "asdf", - "__dunder__", - "__import__('os').popen('echo test_command/$(ls / | base64 -w)').read()", - ] - for string in strings: - payload, _ = self.full_payload_gen.generate(const.STRING, string) - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn(string, result) - for word in self.blacklist: - self.assertNotIn(word, payload) - - def test_os_popen_read(self): - payload, _ = self.full_payload_gen.generate( - const.OS_POPEN_READ, "echo fen jing;" - ) - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn("fen jing", result) - for word in self.blacklist: - self.assertNotIn(word, payload) - -class FullPayloadGenTestCaseHard2(unittest.TestCase): +class FullPayloadGenTestCaseHard2(FullPayloadGenTestCaseSimple): def setUp(self) -> None: super().setUp() self.blacklist = [ @@ -183,40 +166,8 @@ def setUp(self) -> None: ] self.full_payload_gen = get_full_payload_gen(self.blacklist) - def test_string(self): - strings = [ - "123", - "asdf", - "__dunder__", - "__import__('os').popen('echo test_command/$(ls / | base64 -w)').read()", - ] - for string in strings: - payload, _ = self.full_payload_gen.generate(const.STRING, string) - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn(string, result) - for word in self.blacklist: - self.assertNotIn(word, payload) - - def test_os_popen_read(self): - payload, _ = self.full_payload_gen.generate( - const.OS_POPEN_READ, "echo fen jing;" - ) - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn("fen jing", result) - for word in self.blacklist: - self.assertNotIn(word, payload) - - -class FullPayloadGenTestCaseRandom(unittest.TestCase): +class FullPayloadGenTestCaseRandom(FullPayloadGenTestCaseSimple): def setUp(self) -> None: super().setUp() self.blacklists = [ @@ -277,33 +228,3 @@ def setUp(self) -> None: for blacklist in self.blacklists ] - def test_string(self): - strings = [ - "123", - "asdf", - "__dunder__", - "__import__('os').popen('echo test_command/$(ls / | base64 -w)').read()", - ] - for g, blacklist in zip(self.full_payload_gens, self.blacklists): - for string in strings: - payload, _ = g.generate(const.STRING, string) - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn(string, result) - for word in blacklist: - self.assertNotIn(word, payload) - - def test_os_popen_read(self): - for g, blacklist in zip(self.full_payload_gens, self.blacklists): - payload, _ = g.generate(const.OS_POPEN_READ, "echo fen jing;") - # self.assertIsNotNone(payload) - assert payload is not None - # why? - # cause the stupid type checker thinks the 'payload' below would still be None - result = jinja2.Template(payload).render() - self.assertIn("fen jing", result) - for word in blacklist: - self.assertNotIn(word, payload) diff --git a/tests/test_payload_gen.py b/tests/test_payload_gen.py index 38134e5..eac2bb9 100644 --- a/tests/test_payload_gen.py +++ b/tests/test_payload_gen.py @@ -4,6 +4,9 @@ import unittest import fenjing +from flask import render_template_string + + from fenjing.payload_gen import PayloadGenerator from fenjing import const import logging diff --git a/tests/vulunserver.py b/tests/vulunserver.py new file mode 100644 index 0000000..a15a165 --- /dev/null +++ b/tests/vulunserver.py @@ -0,0 +1,22 @@ +"""一个可以被SSTI的服务器 +""" + +from flask import Flask +from flask import request +from flask import render_template_string + +app = Flask(__name__) +@app.route('/',methods=['GET', 'POST']) +def test(): + name = request.args.get("name", "world") + template = ''' + +
+

Hello %s!
Welcome To My Blog

+
+ ''' %(name) + + return render_template_string(template) + +if __name__ == '__main__': + app.run(host="0.0.0.0",port=5000)