mirror of
https://github.com/hackerschoice/thc-tips-tricks-hacks-cheat-sheet.git
synced 2026-06-05 21:46:34 +02:00
ghostip
This commit is contained in:
Executable
+359
@@ -0,0 +1,359 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
# Usage:
|
||||
# ======
|
||||
# source <(curl -fsSL https://github.com/hackerschoice/thc-tips-tricks-hacks-cheat-sheet/raw/master/tools/ghostip.sh)
|
||||
#
|
||||
# A Linux tool to use a non existing IP address (aka GHOST-IP).
|
||||
#
|
||||
# A typical use case it to use use nmap from a host but with an
|
||||
# IP address that is not assigned to any other host on the local network.
|
||||
# Your nmap-scans will appear as if originating from 'nowhere'.
|
||||
#
|
||||
# Ghost LAN & WAN taffic by default.
|
||||
#
|
||||
# GHOST_IP_LAN=
|
||||
# The Ghost IP to use for traffic towards the LAN [default=1.0.0.2].
|
||||
# If set to a LAN address then ghost a single LAN interface only.
|
||||
# -1 to disable LAN ghosting.
|
||||
#
|
||||
# GHOST_IP_WAN=
|
||||
# An unused IP address on the WAN facing Interface.
|
||||
# Find an IP Address automatcially if not set [default].
|
||||
# -1 to disable WAN ghosting.
|
||||
#
|
||||
# Complex Examples:
|
||||
# =================
|
||||
# Example 1: Ghost-route traffic towards _all_ LAN,
|
||||
# appearing from 1.0.0.2 [default]
|
||||
# $ GHOST_IP_WAN=-1 source ./ghostip.sh
|
||||
#
|
||||
# Example 2: Ghost-route traffic towards _one_ specific LAN,
|
||||
# appearing from 172.17.0.99 (an unused local LAN IP):
|
||||
# $ GHOST_IP_WAN=-1 GHOST_IP_LAN=172.17.0.99 source ./ghostip.sh
|
||||
#
|
||||
# Example 3: Ghost-route traffic towards the WAN,
|
||||
# appearing from 192.168.0.222 (an unused local LAN IP):
|
||||
# $ GHOST_IP_WAN=192.168.0.222 GHOST_IP_LAN=-1 source ./ghostip.sh
|
||||
#
|
||||
# GHOST_NAME=update
|
||||
# The name of the cgroup. Must not exist.
|
||||
#
|
||||
# GHOST_IPT=
|
||||
# IPtables match of traffic that should be ghost-routed.
|
||||
# This is really only needed if the host is NOT a ROUTER _and_
|
||||
# the application is a proxy (like Wiretap, socks, gsnc, ..):
|
||||
# It allows the application to connect back to the C2 but all other
|
||||
# traffic by the application will be ghost-routed.
|
||||
#
|
||||
# Do not ghost the C2 traffic:
|
||||
# GHOST_IPT="! -d 1.2.3.0/24"
|
||||
# Only ghost TCP:
|
||||
# GHOST_IPT="-p tcp"
|
||||
#
|
||||
# How it work:
|
||||
# ============
|
||||
# It createa a cgroup and iptable SNAT/DNAT rules for the cgroup. It then moves
|
||||
# the current shell into the new cgroup. Any new programm started from that shell
|
||||
# will use the Ghost-IP.
|
||||
#
|
||||
# This script can be sourced/evaled or executed as BASH or ZSH script.
|
||||
# Some ninja to make it work on ZSH & BASH:
|
||||
# - Bash arrays start at index #0, Zsh at index #1.
|
||||
# - Array expansion differs between Bash and Zsh.
|
||||
# Try IFS="/" a=(${=HOME}) && echo "${#a[@]}" to understand.
|
||||
#
|
||||
# Some ideas stolen from novpn:
|
||||
# https://gist.github.com/kriswebdev/a8d291936fe4299fb17d3744497b1170
|
||||
|
||||
if [ -n $ZSH_EVAL_CONTEXT ]; then
|
||||
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
|
||||
else
|
||||
(return 0 2>/dev/null) && sourced=1 || sourced=0
|
||||
fi
|
||||
|
||||
err() {
|
||||
echo -e >&2 "${CDR}ERROR: ${CN}$@"
|
||||
}
|
||||
|
||||
# Find the Internet facing GW
|
||||
ghost_find_gw() {
|
||||
local arr
|
||||
local IFS
|
||||
IFS=" " arr=($(ip route show match "1.1.1.1"))
|
||||
gw_dev="${arr[@]:4:1}"
|
||||
gw_ip="${arr[@]:2:1}"
|
||||
}
|
||||
|
||||
ghost_find_other() {
|
||||
local arr
|
||||
local a
|
||||
local IFS
|
||||
local d
|
||||
local i
|
||||
|
||||
unset ghost_all_dev
|
||||
unset ghost_all_dev_ip
|
||||
|
||||
[ "$GHOST_IP_LAN" == "-1" ] && return
|
||||
|
||||
IFS=$'\n' arr=($(ip addr show))
|
||||
for l in "${arr[@]}"; do
|
||||
[[ "$l" =~ ^[0-9]+: ]] && {
|
||||
unset d
|
||||
unset i
|
||||
[[ "$l" != *"state UP"* ]] && continue
|
||||
[[ "$l" == *" master "* ]] && continue # Bridge master / veth
|
||||
d="${l#*:}"
|
||||
d="${d%%:*}"
|
||||
d="${d// /}"
|
||||
# Main Internet dev
|
||||
[ "$d" == "$gw_dev" ] && unset d
|
||||
[ "$d" == "lo" ] && unset d
|
||||
continue
|
||||
}
|
||||
[ -z "$d" ] && continue
|
||||
[[ "$l" == *"inet "* ]] && {
|
||||
l="${l##*inet }"
|
||||
i="${l%% *}"
|
||||
i="${i%%/*}"
|
||||
[ -z "$i" ] && continue
|
||||
ghost_all_dev+=("$d")
|
||||
ghost_all_dev_ip+=("$i")
|
||||
unset d
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
ghost_find_single() {
|
||||
local IFS
|
||||
local l
|
||||
local arr
|
||||
|
||||
unset single_dev single_dev_ip
|
||||
[ -z "$ghost_ip" ] && return
|
||||
IFS=$'\n' arr=($(ip route show match "${ghost_ip:?}"))
|
||||
|
||||
# Find the DEV for this IP
|
||||
for l in "${arr[@]}"; do
|
||||
[[ "$l" != *" scope link "* ]] && continue
|
||||
single_dev="${l##*dev }"
|
||||
single_dev="${single_dev%% *}"
|
||||
single_dev_ip="${l##*link src }"
|
||||
single_dev_ip="${single_dev_ip%% *}"
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
ghost_init() {
|
||||
local IFS=" "
|
||||
local classid="0xF0110011"
|
||||
|
||||
[ -t 1 ] && {
|
||||
CDR="\e[0;31m" # red
|
||||
CDC="\e[0;36m" # cyan
|
||||
CDY="\e[0;33m" # yellow
|
||||
CY="\e[1;33m" # yellow
|
||||
CDM="\e[0;35m" # magenta
|
||||
CDG="\e[0;32m" # green
|
||||
CF="\e[2m" # faint
|
||||
CN="\e[0m" # none
|
||||
}
|
||||
|
||||
GHOST_NAME="${GHOST_NAME:-update}"
|
||||
|
||||
# Check for cgroup v1
|
||||
cg_root="/sys/fs/cgroup/net_cls"
|
||||
[ ! -f "${cg_root}/cgroup.procs" ] && cg_root="$(mount -t cgroup | grep net_cls | head -n1 | grep -oP '^cgroup on \K\S+')"
|
||||
[ ! -f "${cg_root}/cgroup.procs" ] && unset cg_root
|
||||
ipt_args=("-m" "cgroup" "--cgroup" "$classid")
|
||||
|
||||
# Check for cgroup v2
|
||||
cg_rootv2="/sys/fs/cgroup"
|
||||
[ ! -f "${cg_rootv2}/cgroup.procs" ] && cg_rootv2="/sys/fs/cgroup/unified"
|
||||
[ ! -f "${cg_rootv2}/cgroup.procs" ] && cg_rootv2="$(mount -t cgroup2 | head -n1 | grep -oP '^cgroup2 on \K\S+')"
|
||||
[ ! -f "${cg_rootv2}/cgroup.procs" ] && unset cg_rootv2
|
||||
[ -n "$cg_rootv2" ] && { cg_root="${cg_rootv2}"; ipt_args=("-m" "cgroup" "--path" "${GHOST_NAME:?}"); }
|
||||
|
||||
# ZSH/BASH compat (see notes above)
|
||||
ipt_args=($(echo "$GHOST_IPT") "${ipt_args[@]}")
|
||||
[ -z "$cg_root" ] && { err "No cgroup v1 or v2 found. Not possible to isolate an app to a ghost-IP."; return; }
|
||||
|
||||
mkdir -p "${cg_root}/${GHOST_NAME}" 2>/dev/null
|
||||
[ -z "$cg_rootv2" ] && echo "$classid" >"${cg_root}/${GHOST_NAME}/net_cls.classid"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Add rule if not exist yet
|
||||
iptnat() {
|
||||
local IFS
|
||||
local ins="$1"
|
||||
shift 1
|
||||
|
||||
unset IFS
|
||||
GHOST_UNDO_CMD+=("iptables -t nat -D $*")
|
||||
iptables -t nat -C "$@" 2>/dev/null && return
|
||||
iptables -t nat "$ins" "$@"
|
||||
}
|
||||
|
||||
# Find an unused IP Address on the LAN
|
||||
ghost_find_local() {
|
||||
local arr
|
||||
local IFS
|
||||
local str
|
||||
local cidr
|
||||
local ipp
|
||||
local dev="${1:?}"
|
||||
local mode="${2}"
|
||||
|
||||
IFS=" " arr=($(ip addr show dev "${dev:?}" | grep -m1 -F " inet "))
|
||||
str="${arr[@]:1:1}"
|
||||
cidr=${str##*/}
|
||||
ipp=${str%%/*}
|
||||
ipp=${ipp%.*}
|
||||
[ -z "$cidr" ] && cidr="24"
|
||||
[ "$cidr" -lt 24 ] && cidr="24"
|
||||
[ "$cidr" -gt 24 ] && return # To bad. cant find automatically.
|
||||
for n in {0..10}; do
|
||||
# .0, .1 , .254, .255 should not be tried.
|
||||
d=$((RANDOM % 252 + 2))
|
||||
ping -4 -c2 -i1 -W2 -w2 -A -q "$ipp.$d" &>/dev/null || break
|
||||
done
|
||||
str="$(arp -n "$ipp.$d")"
|
||||
[[ "$str" != *"incomplete"* ]] && { echo "Error. IP $ipp.$d is used: $str"; return; }
|
||||
ghost_ip="$ipp.$d"
|
||||
echo -e "--> Using unused IP ${CDY}${ghost_ip}${CN}. Set ${CDC}GHOST_IP_${mode}=<IP>${CN} otherwise."
|
||||
}
|
||||
|
||||
# orig-ip new-ip device
|
||||
ghost_print() {
|
||||
local dev="$3 "
|
||||
local ip="$2 "
|
||||
echo -e "[${CDG}$4${CN}] ${CDM}Traffic leaving ${CDG}${dev:0:8}${CDM} will now appear as ${CY}${ip:0:16} ${CDY}${CF}[not $1]${CN}"
|
||||
}
|
||||
|
||||
# ghost_single [LAN,WAN]
|
||||
ghost_single() {
|
||||
local mode="$1"
|
||||
[ -z "$single_dev" ] && return 255
|
||||
[ -z "$single_dev_ip" ] && return 255
|
||||
|
||||
[ -n "$ghost_ip" ] && [ -z "$single_dev" ] && { err "${CDC}GHOST_IP_${mode}=${CN} must be a local IP address [not ${ghost_ip}]"; return; }
|
||||
[ -z "$ghost_ip" ] && ghost_find_local "$single_dev" "$mode"
|
||||
[ -z "$ghost_ip" ] && {
|
||||
err "Set ${CDC}export GHOST_IP_${mode}=<IP>${CN} to a local and unused IP Address."
|
||||
return 255
|
||||
}
|
||||
|
||||
iptnat -I POSTROUTING -o "${single_dev:?}" -m state --state NEW,ESTABLISHED "${ipt_args[@]}" -j SNAT --to "${ghost_ip:?}"
|
||||
# NO longer needed because we used -m state for outgoing.
|
||||
# iptnat -I PREROUTING -i "${single_dev:?}" -d "${ghost_ip}" -m state --state ESTABLISHED,RELATED -j DNAT --to "${single_dev_ip:?}"
|
||||
|
||||
# Block anyone connecting to our Ghost-IP:
|
||||
# We dont want to show in the INPUT chain. Instead route all invalid to 0.0.0.0 (/dev/null):
|
||||
iptnat -I PREROUTING -i "${single_dev}" -d "${ghost_ip}" -m state --state NEW -j DNAT --to 0.0.0.0
|
||||
|
||||
# We must respond to ARP request to our Ghost-IP. The simplest is to add
|
||||
# the Ghost-IP to the same network interface. An alternative would be to
|
||||
# use "arp -i eth0 -Ds ${ghost_ip} eth0"
|
||||
ip addr add "${ghost_ip}/32" dev "${single_dev}" 2>/dev/null
|
||||
|
||||
GHOST_UNDO_CMD+=("ip addr del ${ghost_ip}/32 dev ${single_dev}")
|
||||
ghost_print "${single_dev_ip}" "${ghost_ip}" "${single_dev}" "$mode"
|
||||
return 0
|
||||
}
|
||||
|
||||
ghost_lan() {
|
||||
local n
|
||||
local d
|
||||
local ghost_ip
|
||||
local devip
|
||||
|
||||
[ -n "$GHOST_IP_LAN" ] && {
|
||||
ghost_ip="${GHOST_IP_LAN}"
|
||||
ghost_find_single
|
||||
ghost_single "LAN" && return
|
||||
}
|
||||
|
||||
ghost_find_other
|
||||
[ ${#ghost_all_dev[@]} -le 0 ] && return
|
||||
|
||||
# We like to keep the IPT rules to a min. Thus can't use connmark. Instead
|
||||
# pick a Ghost-IP that is not essential.
|
||||
# ghost_ip_default="104.17.25.14" # cdnjs.cloudflare.com
|
||||
ghost_ip="${GHOST_IP_LAN:-1.0.0.2}"
|
||||
|
||||
iptnat -I POSTROUTING ! -o "${gw_dev:?}" -m state --state NEW,ESTABLISHED "${ipt_args[@]}" -j SNAT --to "${ghost_ip:?}"
|
||||
n=0
|
||||
for d in "${ghost_all_dev[@]}"; do
|
||||
devip="${ghost_all_dev_ip[@]:$n:1}"
|
||||
ghost_print "${devip}" "${ghost_ip}" "${d}" "LAN"
|
||||
iptnat -I PREROUTING -i "${d}" -d "${ghost_ip}" -m state --state ESTABLISHED,RELATED -j DNAT --to "${devip}"
|
||||
((n++))
|
||||
done
|
||||
iptnat -I PREROUTING -d "${ghost_ip}" -m state --state NEW -j DNAT --to 0.0.0.0
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
ghost_up2() {
|
||||
local ghost_ip
|
||||
|
||||
ghost_down
|
||||
|
||||
ghost_find_gw || return
|
||||
|
||||
[ "$GHOST_IP_LAN" != "-1" ] && ghost_lan
|
||||
|
||||
[ "$GHOST_IP_WAN" != "-1" ] && {
|
||||
ghost_ip="${GHOST_IP_WAN}"
|
||||
single_dev="$gw_dev"
|
||||
single_dev_ip="$gw_ip"
|
||||
|
||||
ghost_single "WAN"
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
ghost_up() {
|
||||
ghost_up2 || return
|
||||
[ -n "$GHOST_IPT" ] && echo -e "Traffic matching: ${CDG}${GHOST_IPT}${CN}"
|
||||
|
||||
if [ -n $sourced ]; then
|
||||
echo "$$" >"${cg_root:?}/${GHOST_NAME}/cgroup.procs"
|
||||
echo -e "\
|
||||
--> Your current shell (${SHELL##*/}/$$) and any new process started
|
||||
from this shell are now ghost-routed.
|
||||
--> To ghost-route new connections of an already running process:
|
||||
${CDC}"'echo "<PID>" >"'"${cg_root:?}/${GHOST_NAME}/cgroup.procs"'"'"${CN}"
|
||||
echo -e "To UNDO type ${CDC}ghost_down${CN} or:"
|
||||
else
|
||||
echo -e "\
|
||||
--> To ghost-route the current shell and all processes started from
|
||||
this shell:
|
||||
${CDC}"'echo "$$" >"'"${cg_root:?}/${GHOST_NAME}/cgroup.procs"'"'"${CN}
|
||||
--> To ghost-route new connections of an already running process:
|
||||
${CDC}"'echo "<PID>" >"'"${cg_root:?}/${GHOST_NAME}/cgroup.procs"'"'"${CN}"
|
||||
echo -e "To UNDO type:"
|
||||
fi
|
||||
|
||||
echo -en "${CF}"
|
||||
for c in "${GHOST_UNDO_CMD[@]}"; do
|
||||
echo "$c";
|
||||
done
|
||||
[ -n $sourced ] && echo "unset GHOST_UNDO_CMD"
|
||||
echo -en "${CN}"
|
||||
}
|
||||
|
||||
ghost_down() {
|
||||
local c
|
||||
|
||||
for c in "${GHOST_UNDO_CMD[@]}"; do
|
||||
eval "$c"
|
||||
done
|
||||
unset GHOST_UNDO_CMD
|
||||
}
|
||||
|
||||
ghost_init && \
|
||||
ghost_up
|
||||
+2
-1
@@ -148,7 +148,8 @@ get_virt() {
|
||||
local os
|
||||
local os_prefix
|
||||
|
||||
if grep -sqF docker "/proc/1/cgroup" &>/dev/null || grep -sqF " /docker/" "/proc/self/mountinfo" || grep -sqF docker/overlay "/proc/self/mountinfo"; then
|
||||
# old way: grep -sqF " /docker/" "/proc/self/mountinfo"
|
||||
if grep -sqF docker "/proc/1/cgroup" &>/dev/null || grep -F -m1 ' / / r' "/proc/self/mountinfo" | grep -sqF "docker"; then
|
||||
cont="Docker"
|
||||
elif tr '\000' '\n' <"/proc/1/environ" | grep -Eiq '^container=podman' || grep -sqF /libpod- "/proc/self/cgroup"; then
|
||||
cont="Podman"
|
||||
|
||||
Reference in New Issue
Block a user