diff --git a/.github/workflows/build_preview.yml b/.github/workflows/build_preview.yml index 586b57c..9414556 100644 --- a/.github/workflows/build_preview.yml +++ b/.github/workflows/build_preview.yml @@ -44,6 +44,18 @@ jobs: with: name: linux-arm path: packages/linux/arm64/* + - name: install test dependencies + run: | + sudo pip install impacket pytest + - name: copy beagle into path + run: | + sudo cp packages/linux/amd64/SMBeagle /bin/smbeagle + sudo chmod +x /bin/smbeagle + sudo mkdir /empty_dir + - name: run pytest + run: | + cd tests + sudo ROOTDIR=/ pytest -v -k "not on_windows" build_windows: runs-on: windows-2019 steps: @@ -71,27 +83,3 @@ jobs: with: name: windows-x64 path: packages\windows/x64\* - test_on_linux: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 5.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build linux x64 - run: dotnet publish -c Release --self-contained -r linux-x64 -o packages/linux/amd64 -p:PublishSingleFile=true -p:PublishTrimmed=true -p:InvariantGlobalization=true -p:DebugType=None -p:DebugSymbols=false -p:VersionSuffix=pr$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }') - - name: install test dependencies - run: | - sudo pip install impacket pytest - - run: | - sudo cp packages/linux/amd64/SMBeagle /bin/smbeagle - sudo chmod +x /bin/smbeagle - sudo mkdir /empty_dir - - name: run pytest - run: | - cd tests - sudo pytest -v diff --git a/tests/Dockerfile.Linux b/tests/Dockerfile.Linux new file mode 100644 index 0000000..34eace2 --- /dev/null +++ b/tests/Dockerfile.Linux @@ -0,0 +1,9 @@ +FROM punksecurity/smbeagle +RUN apt update && apt install python3 python3-pip -y +RUN pip install impacket pytest +RUN mkdir /empty_dir +WORKDIR /tests/ +COPY tests/* . +ENTRYPOINT [ "" ] +ENV ROOTDIR "/" +CMD pytest -v -k 'not on_windows' diff --git a/tests/Dockerfile.Windows b/tests/Dockerfile.Windows new file mode 100644 index 0000000..6217ac7 --- /dev/null +++ b/tests/Dockerfile.Windows @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/windows:20H2 +ENV PYTHON_VERSION 3.10.5 +ENV PYTHON_GET_PIP_URL https://bootstrap.pypa.io/get-pip.py + +COPY windows_scripts/* / +RUN "powershell -noprofile -executionpolicy bypass -file .\install_python.ps1" + +RUN "pip install impacket pytest" + +ENV ROOTDIR "C:\\" +WORKDIR "C:\\" +RUN mkdir empty_dir tests +COPY tests tests +COPY x64 "C:\\windows\\system32\\." +WORKDIR tests +CMD pytest -v -k "not on_linux" + +# Cant test native auth as windows auth broken in containers... tried this hacky fix but no good +#RUN net user /add test +#RUN net localgroup administrators test /add +#USER test +#ENV NATIVE_AUTH=1 +#RUN net user test goose; pytest -k test_fifty_files_in_the_root diff --git a/tests/tests/helpers.py b/tests/tests/helpers.py index 390965a..0eaae6d 100644 --- a/tests/tests/helpers.py +++ b/tests/tests/helpers.py @@ -9,11 +9,12 @@ def __setupSMB(address, dir, SMB2 = True): - os.chdir("/empty_dir") + os.chdir(f"{os.environ['ROOTDIR']}empty_dir") server = smbserver.SimpleSMBServer(listenAddress=address, listenPort=445) server.addShare("share", dir, "") server.addCredential("test", 1200, "9FD78381EC915F1AAAD3B435B51404EE", "25EDEDFF26CB970623DDA4733227A3F7") server.setSMB2Support(SMB2) + server.setLogFile('') server.start() def setupSMB(address, dir): @@ -25,7 +26,7 @@ class SMB(object): def __init__(self, address = "0.0.0.0", dir_structure = ["fileA", "fileB"]): self.address = address self.dir_structure = dir_structure - self.dir = f"/{uuid.uuid4().hex}" + self.dir = f"{os.environ['ROOTDIR']}{uuid.uuid4().hex}" def __enter__(self): self.smb = setupSMB(self.address, self.dir) os.mkdir(self.dir) @@ -57,8 +58,15 @@ def runSMBeagle(*args, print_out=True): def runSMBeagleToCSV(*args): return runSMBeagle("-c","out.csv",*args) +def runSMBeagleQuick(*args): + return runSMBeagleToCSV("-D",*args) + def runSMBeagleToCSVWithAuth(*args): - return runSMBeagleToCSV("-u","test", "-p", "goose", *args) + try: + os.environ["NATIVE_AUTH"] + return runSMBeagleToCSV(*args) + except: + return runSMBeagleToCSV("-u","test", "-p", "goose", *args) def runSMBeagleToCSVWithAuthAndReturnResults(*args): print(runSMBeagleToCSVWithAuth(*args)) diff --git a/tests/tests/test_010_options.py b/tests/tests/test_010_options.py index 8f90151..8def4c4 100644 --- a/tests/tests/test_010_options.py +++ b/tests/tests/test_010_options.py @@ -2,40 +2,42 @@ username_or_password_missing_error = "ERROR: Username and Password required on none Windows platforms" def test_username_and_password_required_on_linux(): - assert username_or_password_missing_error in runSMBeagleToCSV() + assert username_or_password_missing_error in runSMBeagleQuick() def test_password_required_on_linux(): - assert username_or_password_missing_error in runSMBeagleToCSV("-p","goose") + assert username_or_password_missing_error in runSMBeagleQuick("-p","goose") def test_username_required_on_linux(): - assert username_or_password_missing_error in runSMBeagleToCSV("-u","goose") + assert username_or_password_missing_error in runSMBeagleQuick("-u","goose") +def test_username_and_password_not_required_on_windows(): + assert username_or_password_missing_error not in runSMBeagleQuick() def test_username_and_password_accepted(): - assert username_or_password_missing_error not in runSMBeagleToCSV("-u","goose", "-p", "goose") + assert username_or_password_missing_error not in runSMBeagleQuick("-u","goose", "-p", "goose") def test_long_username_accepted(): - assert username_or_password_missing_error not in runSMBeagleToCSV("--username","goose", "-p", "goose") + assert username_or_password_missing_error not in runSMBeagleQuick("--username","goose", "-p", "goose") def test_long_password_accepted(): - assert username_or_password_missing_error not in runSMBeagleToCSV("-u","goose", "--password", "goose") + assert username_or_password_missing_error not in runSMBeagleQuick("-u","goose", "--password", "goose") output_required_error = "At least one option from group 'output' (c, csv-file, e, elasticsearch-host)" def test_csv_or_elasticsearch_required(): assert output_required_error in runSMBeagle() def test_short_csv_accepted(): - assert output_required_error not in runSMBeagle("-c","out.csv") + assert output_required_error not in runSMBeagleQuick("-c","out.csv") def test_long_csv_accepted(): - assert output_required_error not in runSMBeagle("--csv-file","out.csv") + assert output_required_error not in runSMBeagleQuick("--csv-file","out.csv") def test_short_elasticsearch_accepted(): - assert output_required_error not in runSMBeagle("-e","elasticsearch") + assert output_required_error not in runSMBeagleQuick("-e","elasticsearch") def test_long_elasticsearch_accepted(): - assert output_required_error not in runSMBeagle("--elasticsearch-host","elasticsearch") + assert output_required_error not in runSMBeagleQuick("--elasticsearch-host","elasticsearch") def test_manual_host_accepted(): - assert "127.0.0.2" in runSMBeagleToCSVWithAuth("-h", "127.0.0.2") + assert "127.0.0.2" in runSMBeagleToCSVWithAuth("-D","-h", "127.0.0.2") def test_multiple_manual_host_accepted(): - output = runSMBeagleToCSVWithAuth("-h", "127.0.0.2", "127.0.0.3") + output = runSMBeagleToCSVWithAuth("-D","-h", "127.0.0.2", "127.0.0.3") assert "127.0.0.2" in output and "127.0.0.3" in output def test_manual_network_accepted(): - output = runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24") + output = runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24") assert "127.0.0.0/24" in output def test_multiple_manual_network_accepted(): - output = runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24", "127.0.1.0/24") + output = runSMBeagleToCSVWithAuth("-D","-n", "127.0.0.0/24", "127.0.1.0/24") assert "127.0.0.0/24" in output and "127.0.1.0/24" in output diff --git a/tests/tests/test_020_tcp_scans.py b/tests/tests/test_020_tcp_scans.py index b46cfc9..496f639 100644 --- a/tests/tests/test_020_tcp_scans.py +++ b/tests/tests/test_020_tcp_scans.py @@ -10,51 +10,51 @@ def test_one_manual_host_tcp_success(): with SMB(): - assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-h", "127.0.0.2") + assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-h", "127.0.0.2") def test_one_manual_host_tcp_fail_if_not_listening(): with SMB("127.0.0.2"): - assert no_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-h", "127.0.0.3") + assert no_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-h", "127.0.0.3") def test_two_manual_host_tcp_success(): with SMB("127.0.0.2"): with SMB("127.0.0.3"): - assert two_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-h", "127.0.0.2", "127.0.0.3") + assert two_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-h", "127.0.0.2", "127.0.0.3") def test_one_manual_host_tcp_success_and_not_two_if_second_not_listening(): with SMB("127.0.0.2"): - assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-h", "127.0.0.2", "127.0.0.3") + assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-h", "127.0.0.2", "127.0.0.3") def test_one_discovered_host_tcp_success(): with SMB("127.0.0.2"): - assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24") + assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24") def test_no_discovered_host_when_filtered(): with SMB("127.0.0.2"): - assert no_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24","-H","127.0.0.2" ) + assert no_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24","-H","127.0.0.2" ) def test_one_discovered_host_when_one_filtered(): with SMB("127.0.0.2"): with SMB("127.0.0.3"): - assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24","-H","127.0.0.2" ) + assert one_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24","-H","127.0.0.2" ) def test_two_discovered_host_tcp_success(): with SMB("127.0.0.2"): with SMB("127.0.0.3"): - assert two_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24") + assert two_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24") def test_three_discovered_host_tcp_success(): with SMB("127.0.0.2"): with SMB("127.0.0.3"): with SMB("127.0.0.4"): - assert three_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24") + assert three_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24") def test_four_discovered_host_tcp_success(): with SMB("127.0.0.2"): with SMB("127.0.0.3"): with SMB("127.0.0.4"): with SMB("127.0.0.5"): - assert four_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-n", "127.0.0.0/24") + assert four_smb_service_discovered_message in runSMBeagleToCSVWithAuth("-D", "-n", "127.0.0.0/24") def test_disable_network_discovery(): no_networks_to_scan_message = "there are no networks or hosts to scan" diff --git a/tests/windows_scripts/install_python.ps1 b/tests/windows_scripts/install_python.ps1 new file mode 100644 index 0000000..4ab3236 --- /dev/null +++ b/tests/windows_scripts/install_python.ps1 @@ -0,0 +1,16 @@ +$url = ('https://www.python.org/ftp/python/{0}/python-{0}-amd64.exe' -f $env:PYTHON_VERSION) +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +Invoke-WebRequest -Uri $url -OutFile 'python.exe'; +# https://docs.python.org/3.7/using/windows.html#installing-without-ui +Start-Process python.exe -Wait -ArgumentList @( + '/quiet', + 'InstallAllUsers=1', + 'TargetDir=C:\Python', + 'PrependPath=1', + 'Shortcuts=0', + 'Include_doc=0', + 'Include_pip=1', + 'Include_test=0' + ); +#the installer updated PATH, so we should refresh our local value +Remove-Item python.exe -Force