Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target/
.git/
.idea/
*.iml
mise.toml
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/target/
/src/main/webapp/WEB-INF/lib/ojdbc14.jar
/src/main/webapp/WEB-INF/lib/ojdbc6.jar
/src/main/webapp/WEB-INF/lib/db2jcc.jar

# Intellij
.idea/
Expand Down
51 changes: 51 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Stage 1: build the WAR using Java 8 + Maven
FROM maven:3.9-eclipse-temurin-8 AS build

WORKDIR /build
COPY pom.xml .
COPY src ./src

RUN mvn install -Dversion.jdk=1.4 -Dversion.webxml=25 -DskipTests -q

# ---------------------------------------------------------------------------
# Stage 2: runtime — Tomcat 9 + JRE 8
# ---------------------------------------------------------------------------
FROM tomcat:9-jre8-temurin AS runtime

# Remove default webapps
RUN rm -rf "$CATALINA_HOME/webapps/ROOT" \
"$CATALINA_HOME/webapps/docs" \
"$CATALINA_HOME/webapps/examples" \
"$CATALINA_HOME/webapps/host-manager" \
"$CATALINA_HOME/webapps/manager"

# Copy and pre-explode the WAR so the entrypoint can edit conf/ on disk
COPY --from=build /build/target/spiracle.war /tmp/spiracle.war
RUN apt-get update -qq && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/* \
&& mkdir -p "$CATALINA_HOME/webapps/spiracle" \
&& unzip -q /tmp/spiracle.war -d "$CATALINA_HOME/webapps/spiracle" \
&& rm /tmp/spiracle.war

# ---- JDBC drivers (downloaded at image build time from Maven Central) ----

# MySQL Connector/J 5.1.49 — has com.mysql.jdbc.Driver (legacy classname)
RUN curl -fsSL \
"https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar" \
-o "$CATALINA_HOME/lib/mysql-connector-java-5.1.49.jar"

# MSSQL JDBC — jre8 classifier
RUN curl -fsSL \
"https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre8/mssql-jdbc-12.4.2.jre8.jar" \
-o "$CATALINA_HOME/lib/mssql-jdbc-12.4.2.jre8.jar"

# Oracle ojdbc8 — Java 8 compatible
RUN curl -fsSL \
"https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/21.13.0.0/ojdbc8-21.13.0.0.jar" \
-o "$CATALINA_HOME/lib/ojdbc8-21.13.0.0.jar"

# ---- entrypoint ----
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
176 changes: 155 additions & 21 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= Spiracle

Spiracle is an insecure web application used to test system security controls.
Spiracle is an insecure web application used to test system security controls.

It can be used to read/write arbitrary files and open network connections.
The application is also vulnerable to numerous other vulnerabilities such as:
Expand All @@ -12,14 +12,46 @@ The application is also vulnerable to numerous other vulnerabilities such as:
* Deserialization (CWE-502)
* and many more...

CAUTION: Due to its insecure design, this application should NOT be deployed on an unsecured network or system.
CAUTION: Due to its insecure design, this application should NOT be deployed on an unsecured network or system. Run on localhost or throwaway networks only.

This application has been tested on the following application servers:

* Apache Tomcat 5.0.x
* Apache Tomcat 7.x
* IBM WebSphere Liberty Core 8.5.5.3

Your mileage may vary with other application servers.

== Docker (quickstart)

The fastest way to run Spiracle. No local Tomcat or database install needed.

Pick a database and run its compose file from the repo root:

----
# MySQL (smallest image — recommended for first run)
$ docker compose -f docker-compose.mysql.yml up --build

# Microsoft SQL Server (~1.5 GB image)
$ docker compose -f docker-compose.mssql.yml up --build

# Oracle XE (~2–4 GB image; first pull takes several minutes)
$ docker compose -f docker-compose.oracle.yml up --build
----

Once healthy, browse to: http://localhost:8080/spiracle/

Tear down (removes volumes):

----
$ docker compose -f docker-compose.mysql.yml down -v
----

The multi-stage `Dockerfile` builds the WAR with JDK 8 / Maven (using `-Dversion.jdk=1.4 -Dversion.webxml=25`) and deploys it on Tomcat 9. MySQL, MSSQL, and Oracle JDBC drivers are bundled in the image; `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables at startup.

NOTE: The MySQL compose file pins `mysql:5.7`. The `java4` WAR bundles the legacy `mysql-connector 3.1.14`, which cannot negotiate MySQL 8's `utf8mb4` charset (index 255); MySQL 5.7 is required for the old connector to connect.

Full Docker reference: link:docker/README.md[docker/README.md]

== Installation

* Download pre-built `spiracle.war` file from the releases page: https://github.com/waratek/spiracle/releases
Expand All @@ -28,10 +60,51 @@ Your mileage may vary with other application servers.

* Copy the war file to the `$CATALINA_HOME/webapps/` directory.

=== Liberty Core

* Ensure that the application war file is extracted to your server's apps directory:
+
----
$ mkdir ./wlp/usr/servers/defaultServer/apps/spiracle
$ cd ./wlp/usr/servers/defaultServer/apps/spiracle
$ jar xvf /path/to/downloaded/spiracle.war
----

* Modify `server.xml` as follows:
+
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>jsp-2.2</feature>
<feature>Servlet-3.0</feature> <!--1-->
</featureManager>

<webApplication contextRoot="spiracle" location="spiracle"/> <!--2-->
<httpSession idLength="28" /> <!--3-->

<httpEndpoint id="defaultHttpEndpoint"
host="*" <!--4-->
httpPort="9080"
httpsPort="9443"/>
</server>
----
+
<1> Enable `Servlet-3.0` as a feature
<2> Add a `webApplication` tag referencing Spiracle
<3> Change `httpSession` parameter length
<4> Add a `host` attribute

NOTE: WebSphere Liberty has no Servlet 2.5 feature; `Servlet-3.0` is the lowest available and serves this branch's Servlet 2.5 WAR. It honors the older `web-app version="2.5"` descriptor and skips annotation scanning, so servlets register from `web-25.xml` (matching the Tomcat behavior on this branch).

=== Database setup

If you would like to run the SQL injection tests, the database should be populated as follows. Data files are available in the web applications `spiracle/conf/` directory after the `spiracle.war` file has been deployed and exploded.

NOTE: When using Docker, database initialisation is handled automatically by the compose stack. Manual setup is only needed for bare-metal deployments.

==== Oracle

. Ensure that the link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar) is installed in the applications `WEB-INF/lib/` directory after the `spiracle.war` file is exploded on first run.
Expand Down Expand Up @@ -72,8 +145,6 @@ c3p0.username=test
c3p0.password=test
----

NOTE: Spiracle is bundled with MySQL Connector 3.1.14. If this version is not compatible with your MySQL installation replace `mysql-connector-java-5.1.34.jar` under `WEB-INF/lib/` with a compatible version.

== Running

After deployment, the Spiracle application will be available at:
Expand All @@ -82,33 +153,75 @@ After deployment, the Spiracle application will be available at:
http://ip:port/spiracle/
----

Properties file can be overridden when submitting the request by appending the new value to the URL:

----
&connectionType=c3p0.mysql
----

== Testing

Spiracle ships with a link:tests/hurl/README.md[Hurl] test harness under `tests/hurl/`. Tests are endpoint-based and run against any deployment (Docker or bare-metal).

=== Suites

[cols="1,1,3",options="header"]
|===
|Suite |Agent required |What it covers

|`smoke/`
|No
|App root responds 200; benign query returns data; unblocked SQLi widens result set (confirming injections succeed without agent)

|`functional/`
|No
|SendRedirect, SQL queries, reflected XSS via `customTag.jsp`, path traversal, 404/empty-result/no-param negative cases

|`rasp/`
|Yes (Waratek RASP)
|440-case SQL injection matrix (139 MySQL, 301 Oracle); asserts status `550`, which is only emitted when the Waratek agent intercepts the query
|===

=== Quick run (plain Docker stack)

----
# Bring up MySQL stack
$ docker compose -f docker-compose.mysql.yml up -d

# Smoke
$ ./tests/hurl/run.sh smoke localhost 8080

# Functional
$ ./tests/hurl/run.sh functional localhost 8080
----

The `rasp/` suite will fail on a plain deployment — expected. Run it only with the Waratek agent attached to Tomcat.

Full harness reference: link:tests/hurl/README.md[tests/hurl/README.md]

== Building

Prerequisites:

* Java >= 1.4
* Java 8 toolchain (compiles `-source 1.4 -target 1.4`; this branch targets Java 1.4 source level with a Servlet 2.5 descriptor)
* Apache Maven
* link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar)
* link:https://www.oracle.com/technetwork/apps-tech/jdbc-10201-088211.html[Java 4 Oracle Database JDBC Driver] (ojdbc14.jar)
* link:http://www-01.ibm.com/support/docview.wss?uid=swg21363866[Java 4 DB2 JDBC Driver] (db2jcc.jar v3.69.66 is good)

The connection pooling dependencies for Java 4 are not in Maven, and need to be manually downloaded and installed into your local Maven repository.
If you wish to use the database features, ensure that the Oracle database JDBC driver file `ojdbc6.jar` is available under `./src/main/webapp/WEB-INF/lib`

- Download https://master.dl.sourceforge.net/project/c3p0/c3p0-bin/c3p0-0.9.2/jdk14-versions/c3p0-0.9.2-jdk14.bin.zip
- Unzip the downloaded file
- The 2 required jar files are in the lib directory
- To install into a local Maven repository:
=== Build flags

$ mvn install:install-file -DgroupId=com.mchange -DartifactId=c3p0 -Dversion=0.9.2-j14 -Dpackaging=jar -Dfile=c3p0-0.9.2-jdk14.jar
Two flags parameterise the build:

$ mvn install:install-file -DgroupId=com.mchange -DartifactId=mchange-commons-java -Dversion=0.2.3.3-j14 -Dpackaging=jar -Dfile=mchange-commons-java-0.2.3.3-jdk14.jar
`-Dversion.jdk=<level>`:: Sets the Java source and target compiler level. On this branch the only supported value is `1.4`.
`-Dversion.webxml=<25|30>`:: Selects the Servlet descriptor version. On this branch use `25` (Servlet 2.5 — `@WebServlet` annotations are not available in Java 1.4).

If you wish to use the database features, ensure that the appropriate database JDBC driver file is available under `./src/main/webapp/WEB-INF/lib`

To build the Spiracle Test Application WAR file, run:

$ mvn package -Dversion.webxml=24 -Dversion.jdk=1.4
Representative invocation:

----
# Java 1.4, Servlet 2.5 (required for this branch)
$ mvn install -Dversion.jdk=1.4 -Dversion.webxml=25
----

To clean the build infrastructure, run:

Expand All @@ -118,6 +231,28 @@ The WAR file will be output to:

./target/spiracle.war

=== Toolchain

The repo carries a `mise.toml` pinning Temurin 8 and Maven 3.9. With link:https://mise.jdx.dev[mise] installed:

----
$ mise install # installs pinned JDK + Maven
$ mvn install -Dversion.jdk=1.4 -Dversion.webxml=25
----

The Docker image pins its own JDK via its base image — no local JDK is needed when building through Docker.

=== Branch model

`java4` (this branch):: Java 1.4 source-compatible variant. Servlets registered in `web-25.xml` instead of `@WebServlet` annotations. Legacy dependency set: Servlet 2.4 API, Spring 2.5, JSTL 1.0, jTDS for MSSQL, `mysql-connector 3.1.14`. MySQL compose stack pins `mysql:5.7` because the legacy connector cannot negotiate MySQL 8's `utf8mb4`.
`master`:: Modern, parameterised source tree. Java 5–8 source level. Use `-Dversion.jdk` to select. Switch to `master` if you do not need Java 1.4 source compatibility.

== Recent fixes

* *#8* — `Content-Type: text/plain` is now set on the `SendRedirect` fallback response (no-parameter case).
* *#33* — `setupdb_mysql.sql` is idempotent; uses `IF [NOT] EXISTS` guards so re-running does not error.
* *#103* — Oracle connection NPE fixed in `CreateC3p0Connection`; was reading non-existent property keys from `Spiracle.properties`. Java 1.4 adaptation: uses `length() == 0` instead of `String.isEmpty()` (added in Java 6).

== License

----
Expand All @@ -135,4 +270,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----

52 changes: 52 additions & 0 deletions docker-compose.mssql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
services:
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: "Spiracle_SA_2024!"
MSSQL_PID: Developer
healthcheck:
test:
- "CMD-SHELL"
- |
/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'Spiracle_SA_2024!' \
-No -Q 'SELECT 1' > /dev/null 2>&1
interval: 10s
timeout: 5s
retries: 15
start_period: 30s

db-init:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
SA_PASSWORD: "Spiracle_SA_2024!"
volumes:
- ./src/main/webapp/conf/setupdb_mssql.sql:/init/setupdb_mssql.sql:ro
- ./docker/mssql-create-login.sql:/init/mssql-create-login.sql:ro
depends_on:
db:
condition: service_healthy
entrypoint:
- /bin/bash
- -c
- |
/opt/mssql-tools18/bin/sqlcmd -S db -U SA -P "$$SA_PASSWORD" -No \
-i /init/setupdb_mssql.sql && \
/opt/mssql-tools18/bin/sqlcmd -S db -U SA -P "$$SA_PASSWORD" -No \
-d spiracle -i /init/mssql-create-login.sql
restart: "no"

app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
SPIRACLE_DEFAULT_CONNECTION: c3p0.mssql
SPIRACLE_DB_HOST: db
depends_on:
db:
condition: service_healthy
db-init:
condition: service_completed_successfully
35 changes: 35 additions & 0 deletions docker-compose.mysql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
services:
db:
# java4 bundles the legacy mysql-connector 3.1.14, which cannot negotiate
# MySQL 8's utf8mb4 (charset index 255). Pin to 5.7 (also the floor for the
# CREATE USER IF NOT EXISTS seed) so the old connector can connect.
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test
volumes:
# Seed SQL mounted outside initdb.d so the shell script controls execution
- ./src/main/webapp/conf/setupdb_mysql.sql:/init/setupdb_mysql.sql:ro
# Shell script in initdb.d runs seed with --force (bare DROPs on fresh DB)
- ./docker/mysql-seed.sh:/docker-entrypoint-initdb.d/01-seed.sh:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password=rootpassword"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s

app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
SPIRACLE_DEFAULT_CONNECTION: c3p0.mysql
SPIRACLE_DB_HOST: db
depends_on:
db:
condition: service_healthy
Loading