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
22 changes: 22 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Goal
<!-- Clearly describe the purpose and objective of this pull request. What problem does it solve or what feature does it add? -->

## Changes
<!-- List the key changes made in this PR. Bullet points are preferred. -->

## Testing
<!-- Describe how you tested these changes. Include any relevant test scenarios, steps to reproduce testing, or test results. -->

## Artifacts & Screenshots
<!-- Add any relevant screenshots, videos, or other visual artifacts that demonstrate the changes. -->

## Checklist

- [ ] PR has a clear, descriptive title
- [ ] Documentation has been updated if applicable
- [ ] No secrets or large temporary files are included in the changes

---

<!-- Example commit message for reference: -->
<!-- docs: add PR template -->
Binary file added labs/assets/homepage.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions labs/lab9/falco/rules/custom-rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Custom Falco rule — Lab 9
# Detects writes to /tmp inside any container (not host)

- rule: "Write to /tmp by container"
desc: "Detect any file write to /tmp inside a container"
condition: >
open_write
and container.id != host
and fd.name startswith /tmp/
output: >
"Write to /tmp detected (container=%container.name user=%user.name
file=%fd.name cmdline=%proc.cmdline)"
priority: WARNING
tags: [container, drift]

# Bonus: Detect cryptominer network/process patterns
# Combines 2 indicators: (1) process name matching known miner binaries,
# (2) network tools making connections to mining-pool ports.
# Uses spawned_process (execve-based) since connect tracepoints are not
# available on all kernels (e.g., Linux 7.x with modern eBPF).

- rule: "Possible Cryptominer Activity"
desc: "Detect known miner processes or network tools connecting to mining-pool ports"
condition: >
spawned_process
and container.id != host
and (proc.name in (xmrig, ethminer, cgminer, t-rex, claymore)
or (proc.name in (nc, ncat, netcat, curl, wget)
and (proc.cmdline contains "3333"
or proc.cmdline contains "4444"
or proc.cmdline contains "5555"
or proc.cmdline contains "7777"
or proc.cmdline contains "14444"
or proc.cmdline contains "19999"
or proc.cmdline contains "45700")))
output: >
"Possible cryptominer detected (container=%container.name
proc=%proc.name cmdline=%proc.cmdline
user=%user.name)"
priority: CRITICAL
tags: [container, mitre_execution, mitre_command_and_control]
9 changes: 9 additions & 0 deletions labs/lab9/manifests/compose/juice-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
juice-shop:
image: bkimminich/juice-shop@sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
user: "1000:1000"
read_only: true
cap_drop:
- ALL
ports:
- "3000:3000"
36 changes: 36 additions & 0 deletions labs/lab9/manifests/k8s/juice-hardened.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: juice-shop-hardened
labels:
app: juice-shop
spec:
replicas: 1
selector:
matchLabels:
app: juice-shop
template:
metadata:
labels:
app: juice-shop
spec:
securityContext:
runAsNonRoot: true
containers:
- name: juice-shop
image: bkimminich/juice-shop@sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
ports:
- containerPort: 3000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
21 changes: 21 additions & 0 deletions labs/lab9/manifests/k8s/juice-unhardened.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: juice-shop-unhardened
labels:
app: juice-shop
spec:
replicas: 1
selector:
matchLabels:
app: juice-shop
template:
metadata:
labels:
app: juice-shop
spec:
containers:
- name: juice-shop
image: bkimminich/juice-shop:latest
ports:
- containerPort: 3000
26 changes: 26 additions & 0 deletions labs/lab9/policies/compose-security.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package compose.security

# Starter compose security policy — same deny[msg] pattern, different input shape.
# input is the parsed docker-compose YAML; use input.services to iterate.
# Uses `some name` to get object keys (Rego v1).

deny contains msg if {
some name
svc := input.services[name]
not svc.user
msg := sprintf("service %q must specify a non-root user", [name])
}

deny contains msg if {
some name
svc := input.services[name]
not svc.read_only == true
msg := sprintf("service %q must set read_only: true", [name])
}

deny contains msg if {
some name
svc := input.services[name]
not svc.cap_drop
msg := sprintf("service %q must drop all capabilities (cap_drop: [ALL])", [name])
}
47 changes: 47 additions & 0 deletions labs/lab9/policies/extra/hardening.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

# Lab 9 — Conftest hardening policies for K8s manifests
# Each deny[msg] rule enforces one hardening requirement (Lecture 9 slide 10)
# Rego v1 syntax (deny contains msg if { ... })

# 1. runAsNonRoot must be true (pod-level OR container-level securityContext)
deny contains msg if {
container := input.spec.template.spec.containers[_]
not container.securityContext.runAsNonRoot == true
not input.spec.template.spec.securityContext.runAsNonRoot == true
msg := sprintf("container %q must set runAsNonRoot: true in securityContext", [container.name])
}

# 2. allowPrivilegeEscalation must be false for every container
deny contains msg if {
container := input.spec.template.spec.containers[_]
not container.securityContext.allowPrivilegeEscalation == false
msg := sprintf("container %q must set allowPrivilegeEscalation: false in securityContext", [container.name])
}

# 3. capabilities.drop must include "ALL" for every container
deny contains msg if {
container := input.spec.template.spec.containers[_]
not container.securityContext.capabilities.drop
msg := sprintf("container %q must drop all capabilities (capabilities.drop must include ALL)", [container.name])
}

deny contains msg if {
container := input.spec.template.spec.containers[_]
not "ALL" in container.securityContext.capabilities.drop
msg := sprintf("container %q capabilities.drop must include ALL", [container.name])
}

# 4. resources.limits.memory must be set for every container
deny contains msg if {
container := input.spec.template.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("container %q must set resources.limits.memory", [container.name])
}

# 5. image must use sha256: digest, not a :tag (optional hardening)
deny contains msg if {
container := input.spec.template.spec.containers[_]
not contains(container.image, "@sha256:")
msg := sprintf("container %q image must use a sha256 digest, found %q", [container.name, container.image])
}
12 changes: 12 additions & 0 deletions labs/lab9/policies/k8s-security.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package k8s.security

# Starter K8s security policy — deny[msg] pattern (Lecture 9 slide 10)
# This checks the pod-level securityContext for runAsNonRoot.
# Extend this pattern for your own rules in policies/extra/hardening.rego.

deny contains msg if {
container := input.spec.template.spec.containers[_]
not container.securityContext.runAsNonRoot == true
not input.spec.template.spec.securityContext.runAsNonRoot == true
msg := sprintf("container %q must run as non-root (runAsNonRoot: true)", [container.name])
}
52 changes: 52 additions & 0 deletions labs/submission1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Triage Report — OWASP Juice Shop

## Scope & Asset

- Asset: OWASP Juice Shop (local lab instance)
- Image: bkimminich/juice-shop:v19.0.0
- Release link/date: <https://hub.docker.com/layers/bkimminich/juice-shop/v19.0.0/images/sha256-547bd3fef4a6d7e25e131da68f454e6dc4a59d281f8793df6853e6796c9bbf58>
- Image digest (optional): sha256:2765a26de7647609099a338d5b7f61085d95903c8703bb70f03fcc4b12f0818d

## Environment

- Host OS: Arch Linux (Linux kernel version: 6.16.8)
- Docker: Docker API version: 1.51, Docker client version: 28.4.0

## Deployment Details

- Run command used: `docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:v19.0.0`
- Access URL: <http://127.0.0.1:3000>
- Network exposure: 127.0.0.1 only [x] Yes [ ] No (explain if No)

## Health Check

- Page load:
![homepage](assets/homepage.jpg)

- API check: first 5–10 lines from `curl -s http://127.0.0.1:3000/rest/products | head`

```html
<html>
<head>
<meta charset='utf-8'>
<title>Error: Unexpected path: /rest/products</title>
<style>* {
margin: 0;
padding: 0;
outline: 0;
}
```

## Surface Snapshot (Triage)

- Login/Registration visible: [x] Yes [ ] No - notes: Registration requires minimal password length of 5 characters
- Product listing/search present: [x] Yes [ ] No
- Admin or account area discoverable: [x] Yes [ ] No — notes: Admin and account areas can be found by examining frontend source code (file `main.js`).
- Client-side errors in console: [ ] Yes [x] No
- Security headers (quick look — optional): `curl -I http://127.0.0.1:3000` → CSP/HSTS present? notes: No CSP or HSTS are present. The Access-Control-Allow-Origin allows all origins.

## Risks Observed (Top 3)

1) Risk of malicious API calls on behalf of authenticated user from other websites. This can happen because of too broad Access-Control-Allow-Oirigin, which enables other sites to do request from client side to the application.
2) Risk of content/script injection. Absence of CSP headers or meta tags leads to weakened security state, which makes XSS attacks more dangerous.
3) Risk of password attacks. As the password policy is not strict enough, attackers may utilize this knowledge to perform password spray, password bruteforce (as the website lacks bruteforce protection).
Loading