Post

If You Do It Twice, Automate It

Repetition slows you down, especially during reconnaissance. This post shows how I automated my everyday hacking workflow using `.zshrc`. The goal is simple, stop typing the same commands and start hacking faster.

If You Do It Twice, Automate It

Image-description

Introduction

It all started with my journey as a hacker on Hack The Box. At first, it was about learning — breaking machines, understanding vulnerabilities, and getting comfortable with the process. Over time, I moved into more competitive hacking, where speed actually mattered. Machines were being pwned in record time, and rankings depended on how fast you could get it done.

That phase taught me something important: speed is a skill, not just talent. And more importantly, it exposed a flaw in my workflow.

I realized I was doing the same repetitive tasks over and over again — creating the same directories, running the same scans, setting up the same listeners — every single time I started a new machine. That’s when this quote came back to mind:

“Insanity is doing the same thing over and over again and expecting different results.”

— often attributed to Einstein (attribution disputed)

If I wanted better results, my reconnaissance process needed to change. Not by adding more tools or writing massive scripts, but by removing friction from the basics. Instead of building full scripts, I started experimenting with my .zshrc file — turning repeated actions into simple aliases and functions that matched the way I actually work.

This post is about that mindset shift: optimizing the boring parts so I can focus on what actually matters.

Set up

First, you need a running Linux system… duh.

This works with both .zshrc and .bashrc, since we’ll be using aliases and basic shell functions. Whether you’re on a virtual machine or bare metal, everything here should work the same.

That’s really all you need: a working Linux environment. Simple, right?

The real deal

Here is my workflow. I first need to create some folders, that will help organize my work. Here are the file structure I use:

For simple machines or challenges

1
2
3
4
5
6
7
8
9
10
11
12
~/HTB-Machine                                                                                      
> tree . 
.
├── Evidence
│   ├── creds
│   ├── data
│   └── screens
├── Logs
├── Scans
└── Tools

8 directories, 0 files

That’s 8 directories that I always use when it comes to simple machines. So every time I will have to create them in order for me to store all the necessary files. Now imagine doing this every single time using mkdir…that’s insanity.

For Prolabs, Certifications or Penetration Tests

For Prolabs or Pentest the file structure is also different. As shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
~/HTB-ProLab  
> tree .
.
├── External
│   ├── evidence
│   │   ├── creds
│   │   ├── data
│   │   └── screens
│   ├── logs
│   ├── scans
│   ├── scope
│   └── tools
└── Internal
    ├── evidence
    │   ├── creds
    │   ├── data
    │   └── screens
    ├── logs
    ├── scans
    ├── scope
    └── tools

19 directories, 0 files

Having an automated way to create these folders every time turned out to be a really good practice. It helped me save time on some basic tasks such as directory creation, nmap scans, replacing the IP of the machine you are working on, verifying your HTB IP, even setting up a listener. The list goes on….

This means that every time I need to create directories or run repetitive tasks like nmap scans, I can do it with a single command. No need to remember the file structure or retype commands — if I’ve done it enough times, it gets automated. I am not insane. Now that you catch my drift, let’s go through with weaponization of our Linux system.

Weaponization

Aliases

Let’s start with the aliases I found useful:

OpenVPN stuff

I have a directory where all my vpns are stored ~/Downloads/vpns/

1
2
alias conn="cd ~/Downloads/vpns/"  # gets you in the vpn directory
alias vpn="sudo openvpn"           # Help starts vpn => $ vpn labs.ovpn

SSH stuff

This is for personal preference. I rather have SSH not read or write on my known_hosts file and remove the interactive prompt.

1
2
alias SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "
alias SCP="scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "

Together, these options mean:

StrictHostKeyChecking=no

  • Automatically accepts the host key

  • No interactive prompt

UserKnownHostsFile=/dev/null

  • Doesn’t read or write ~/.ssh/known_hosts

  • Host keys are forgotten immediately

👉 Result: non-interactive, stateless SSH

TL;DR

  • ✅ Correct for HTB / CTF / labs
  • ❌ Unsafe for real infrastructure (that’s why they are capitalized in my .zshrc)

Checksec

I use pwntools and their new output is not my favorite. I liked the old one better so here is the workaround I did.

1
alias checksec="pwn checksec"    # just for better output

Directories

This helps me move quickly in my file system. one command and I am where I wanted to be.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
alias lab="cd $HOME/HTB/Machines"    # Going straight to the machine folder

alias chal="cd $HOME/HTB/Challenges"    # Going straight to the machine folder

alias prolabs="cd $HOME/HTB/ProLabs"    # Going straight to the machine folder

alias ctfs="cd $HOME/CTFs"    # Going straight to the machine folder

alias fort="cd $HOME/HTB/Fortress"    # Going straight to the machine folder

alias www="cd /run/media/$USER/ssd/www/"    # I use my ssd to store the tools I can upload to the victim  

alias bugb="cd $HOME/BugBounty/"    # Going straight to the Bug Bounty folder

# These two aliases are used in some functions 
alias hax="mkdir -p Evidence/{creds,data,screens} Logs Scans Tools && echo -e '\033[1;32mDirectories Created !\033[0m'"    # This helps me creating all the directories I will need to work on a machine

alias pentest="mkdir -p {External,Internal}/evidence/{creds,data,screens} {External,Internal}/logs {External,Internal}/scans {External,Internal}/scope {External,Internal}/tools && echo -e '\033[1;32mDirec
tories created !\033[0m'"    # This helps me creating all the directories I will need to work on a Prolab or real penetration test

HTB IP

This one is just because ip commands or ifconfig are too verbose for me to just copy paste the HTB ip at times. I am that lazy.

For this you have to make sure ip command works on your host, this was crafted specifically for the output of ip -br a

1
alias htbip="ip -br a | grep tun | awk '{print \$3}' | sed 's#/.*##'"

In case you do not have this package installed, you can install per Linux distribution as shown below:

🐧 Debian / Ubuntu / Kali / Parrot

1
2
sudo apt update
sudo apt install iproute2

🎩 RHEL / CentOS / Rocky / Alma / Oracle Linux

1
sudo dnf install iproute

(Older systems may use yum instead of dnf)

🧢 Fedora

1
sudo dnf install iproute

🦎 openSUSE

1
sudo zypper install iproute2

🐧 Arch Linux / Manjaro

1
sudo pacman -S iproute2

🐧 Alpine Linux

1
sudo apk add iproute2

🧪 Gentoo

1
sudo emerge --ask sys-apps/iproute2

Nmap commands

So this is the most insane part. you can run these commands even asleep in your head at this point. Why keep typing it ? How insane one has to be ? You are losing few seconds or minutes typing all of this every time you have to start an engagement.

Here make sure that you have an environment variable with the right value as IP. Be aware and always make sure with echo $IP

1
2
3
4
5
6
7
8
9
10
11
12
alias openPorts='nmap -Pn -p- -vv -T4 --open -oN ./all-open.out $IP'    # Scan for all open ports

alias openFastPorts='nmap -Pn -F -vv -T4 --open -oN ./fast-port-scan.out $IP'    # Scan for ~100 most common ports

alias serviceScanAll='sudo nmap -Pn -sV -sC -p- -vv -T4 -oN ./services-all-scan.out $IP'    # Service scan for all the open ports

alias serviceScanFast='sudo nmap -Pn -sV -sC -vv -T4 -oN ./services-fast-scan.out $IP'    # Service scan for ~100 most common ports

alias UDPScanALL='sudo nmap -Pn -sU -p- -vv -T4 -oN ./all-open-UDP.out $IP'    # Regular UDP all ports scan

alias UDPScan='sudo nmap -Pn -sU --open -vv -T4 -oN ./open-UDP.out $IP'    # Regular UDP open ports scan

P.S: Aliases don’t need parameters and can just be run as any other commands. So please for these nmap make sure the IP is the correct one.

Bash functions

This is the fun part. Instead of having scripts, why not make functions in our .zshrc or .bashrc file instead ? I have free will so I did the following.

Helps create a new folder for Machines

This function here uses an alias we have called hax that will help not only create the new folder but also add all the necessary folder you will need for documentation as mentioned earlier in the aliases.

1
2
3
4
5
6
7
function box() {
    if [ -z "$1" ];then
        echo "\033[1;31mProvide a Machine name !\033[0m"
        return 1
    fi
    lab && mkdir -- "$1" && cd "$1" && hax;
}

This function takes 1 parameter which is the name of the machine/box:

eg:

1
box Testos

Starting a reverse listener

1
2
3
4
5
6
7
function revListener() {
    if [ -z "$1" ];then
        echo "\033[1;31mProvide the listening port !\033[0m"
        return 1
    fi
    rlwrap -car nc -nlvp $1
}

This function takes one argument which is the port to listen to:

eg:

1
revListener 9009

Helps create a new folder for Prolabs/Pentest

This function here uses an alias we have called pentest that will help not only create the new folder but also add all the necessary folder you will need for documentation as mentioned earlier in the aliases.

1
2
3
4
5
6
7
function pro() {
    if [ -z "$1" ];then
        echo "\033[1;31mProvide a Lab name !\033[0m"
        return 1
    fi
    prolab && mkdir -- "$1" && cd "$1" && pentest;
}

This function takes 1 parameter, which is the name of Lab/Pentest.

eg:

1
pro ACME.inc

Function to change the value of $IP

Each engagment has a different IP, so changing the environment variable $IP is important for me since I use that variable for all my nmap scans as shown @TODO

Just make sure your .zshrc or .bashrc contain a line like this

1
export IP=0.0.0.0      # IP variable placeholder

This will help you see if your variable is set correctly. If you run echo $IP and it shows 0.0.0.0, then you know you did set your IP variable yet.

1
2
3
4
5
6
7
8
9
function replaceIP() {
    if [ -z "$1" ]; then
        echo "\033[1;31mProvide a new IP!\033[0m"
        return 1
    fi
    sed -i "s/^export IP=[^ ]*/export IP=$1/" ~/.zshrc
    echo "IP address updated to $1"
    source ~/.zshrc
}

This takes 1 argument which is the IP to replace to our default value of 0.0.0.0

eg:

1
replaceIP 10.10.14.14

Adding an host to the /etc/hosts file

This comes handy when you need to append a new host to your /etc/hosts file. This is how quickly I am doing it now.

1
2
3
4
5
6
7
function hostAdd(){
    if [ $# -ne 2 ];then
        echo "\033[1;31mProvide the I.P and hostname to add !\033[0m"
        return 1
    fi
    echo -e "$1\t\t$2" | sudo tee -a /etc/hosts
}

It takes 2 argumentts which are the IP and the hostname.

eg:

1
hostAdd 10.0.0.12 testos.lolz

Tranfering tools to current working directory

I have a folder with tools that I can transfer such as Rubeus.exe, nc.exe, RunasCs.exe…just to name a few. That file is called www (ippsec way ;) ) So I made this function to just help me tranfering files with one command knowing the file I want to transfer.

1
2
3
4
5
6
7
8
9
10
function wwwToVictim(){
    if [ $# -ne 1 ]; then
        echo "\033[1;31mWhat is the filename you need to add to the current directory ?\033[0m"
        return 1
    fi
    echo "\033[1;32mCopying file $1 to $(pwd)\033[0m"
    cp -v "/run/media/$USER/ssd/www/"$1 $(pwd)
    echo "\033[1;32m[+] Done\033[0m"

}

This function only takes one argument which is the file we want to copy. I made this function because in my workflow, I’ve got a folder named tool where I store all the downloaded or uploaded tools which hacking/pentesting. That’s why it takes one argument and copy it to the current working directory. It can be ran as shown below:

eg:

1
wwwToVictim Rubeus.exe

This will copy the file Rubeus.exe to the current directory.

Spinning Python server dynamically

Contraty to the alias, here we are spinning the webserver dynamically. This came in handy when port 80 was busy. I needed a dynamic way to spin the server with any random port.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
py_serve() {
    local PORT=${1:-8000}

    if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then
        echo "[-] Invalid port: $PORT"
        echo "Usage: py-serve [port]"
        return 1
    fi

    if [ "$PORT" -lt 1024 ]; then
        echo "[*] Starting Python HTTP server on privileged port $PORT (sudo)"
        sudo python3 -m http.server "$PORT"
    else
        echo "[*] Starting Python HTTP server on port $PORT"
        python3 -m http.server "$PORT"
    fi
}

This function takes only 1 or 0 argument, which is the port python would be opening to server. when no argument is set, it defaults to port 8000.

Just a reminder that ports below 1024 require sudo.

Unless you want to grant python permission to bind to low ports (Ports < 1024). (Which I won’t recommend) You can do the following to set it up:

1
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python3

Once done we can verify it with:

1
getcap /usr/bin/python3

If you can do it, you can also undo it:

1
sudo setcap -r /usr/bin/python3

eg:

1
2
py_serve         # default port
py_serve 8081    # starting the server on 8081 

Key Takeaways

TL;DR

  • Speed matters in hacking, especially in competitive environments.
  • Repeating the same setup tasks manually is wasted time and mental energy.
  • If you do something often enough, it should be automated.
  • Shell customization (.zshrc / .bashrc) is a lightweight, flexible alternative to full scripts.
  • Optimizing your workflow lets you focus on what actually matters: hacking.

There is also a tool I recommend for recon with bugbounty as I contributed a little on my friend’s Durgaprasad-123’s project can be found here.

I also made a repository, where you can just go and copy paste all of these details. That file would be updated depending on my needs or suggestions. But the article might not be. Here is the link to the repository.

Conclusion

At the end of the day, this isn’t about being lazy — it’s about being efficient. When you’re hacking, your brain should be focused on understanding the target, chaining vulnerabilities, and thinking creatively — not remembering directory structures or retyping the same commands for the hundredth time.

Customizing your shell is one of those low-effort, high-reward improvements that quietly levels you up. Every second you save on setup is a second you can spend actually hacking. Over time, that adds up.

You don’t need my exact aliases or functions to benefit from this approach. The real takeaway is this: pay attention to what you repeat. If you catch yourself doing the same thing more than a few times, automate it — whether that’s with aliases, functions, or small scripts that fit your workflow.

Tools don’t make a hacker better. Habits do. And good habits start with removing unnecessary friction.

If this post made you rethink even one part of your workflow, then it did its job.

Stay curious, automate the boring stuff and happy hacking !

This post is licensed under CC BY 4.0 by the author.