Carlos A. Rojas

A javascript-free experience!

Because I respect your digital privacy...

No JavaScript. No trackers. No nonsense.

You are reading plain HTML styled with a little CSS, rendered on my computer with PHP and sent to you finished. There is no JavaScript on this page. Nothing is watching you scroll, nothing is loading a megabyte of framework to show you a paragraph, and nothing phones home to an ad network while you read. It loads instantly, it works in any browser, it works with scripting disabled, it reflows on your phone, and it respects your privacy by simply not collecting anything.

That is not nostalgia. It is a deliberate engineering stance. Most of the modern web ships surveillance and bloat by default. I build the opposite: the smallest thing that does the job, and nothing more. If a feature genuinely needs scripting, it earns its place and degrades gracefully without it. If it does not, it is not here. (Here is the longer argument for why.)

What this site is: a working record of what I do and what I am good at: enterprise IT and infrastructure, cybersecurity engineering and security operations, Windows and Linux administration, authorized security testing, web application security, home labs, and digital privacy. Below and in the sections above you will find what I am working on now, what I have built before, what I have found while testing, and the occasional thing from this month's security news that I actually did something with.

Woops, I'm Running PHP on an Unmanaged Box (PHP2Shell)!

Find every PHP service you own, then patch it right

PHP2Shell-class bugs turn a single unauthenticated request into code execution. The defense isn't clever — it's knowing what you own and actually restarting after you patch. Five steps, each feeding the next. Tools: free ProjectDiscovery kit plus jq.

The gotcha: installing the fixed package doesn't change a worker already in memory. The old binary serves until you restart. No restart = not patched.

1. Inventory what you own. Every root domain in domains.txt (product, marketing, regional, acquired, agency-registered). Public IP ranges in ranges.txt. This is the step everyone skips and the one that saves you.

2. Find every web service, every port.

subfinder -dL domains.txt -all -silent | cat - domains.txt | dnsx -silent > hosts.txt
cat ranges.txt >> hosts.txt
naabu -l hosts.txt -p 80,443,8000,8080,8443,8888,9443 -silent > open.txt
httpx -l open.txt -silent > web.txt

If port 9000 answers from the internet, it's likely PHP-FPM's FastCGI port. Close it.

3. Flag the PHP services. Keep any response showing an X-Powered-By/Server PHP header or a PHPSESSID cookie.

httpx -l web.txt -silent -td -irh -j -o probe.json \
  -path /,/index.php,/phpmyadmin/,/webmail/,/wp-login.php
jq -r 'select((.tech // [] | tostring | test("PHP")) or (.header | tostring | test("PHP/|PHPSESSID"))) | .url' \
  probe.json | sort -u > php.txt

Anything PHP without a version goes straight to step 5.

4. Check advertised versions (lead, not proof).

# replace with the CONFIRMED CVE id / template path
nuclei -t <CVE-ID>.yaml -l php.txt -o affected.txt

Banner-only checks report what a server claims. Backported patches leave old version strings on fixed code — verify on the host.

5. Confirm, patch, restart, verify.

dpkg -l 'php*' | grep ^ii          # Debian/Ubuntu
rpm -qa 'php*'                     # RHEL/Rocky/Alma
ps -eo pid,args | grep -E 'php-fpm|php-cgi|lsphp|httpd|apache2' | grep -v grep

Patch Apache mod_php hosts first (interpreter runs before app code). Upgrade, then restart PHP-FPM, Apache, LiteSpeed; rebuild containers on a fixed php: base. This should print nothing:

sudo grep -lE '(php|libphp).*\(deleted\)' /proc/[0-9]*/maps 2>/dev/null

Finish with one request through production and confirm with php -v / php-fpm -v on the host.

A version banner is a scanner finding, not a verdict — see Scanner Finding vs. Proven Exploitability.

Using Rsync Between Proxy Hops

Transfer Files via Several Proxies

If you don't care about the robustness of Rsync I'll spare you from this article. Just use this SCP command for transferring your files (note the use of the "jump" flag -J):

#For SCP:
scp -J userB@ProxySystemB userC@SourceSystemC:/PATH/TO/SOURCE/FILE.txt /PATH/TO/DESTINATION

Likewise, if you just need to open a remote shell session via a proxy just use this SSH command (again, note the "jump" flag):

#For SSH:
ssh -J  userB@ProxySystemB userC@TargetSystemC

In either scenario above, you can specify an arbitrary number of proxies to reach your target system. However, it is worth noting that there are probably better solutions if you find yourself using more than three proxies. Here's an example using three proxy hops:

#Using 3 Proxy Hops to Open a Remote Shell:
ssh -J  bluey@192.168.1.2 bingo@192.168.65.4 bandit@192.168.122.8 target@target_system

For those interested in the file integrity benefits of Rsync, read on. Rsync unfortunately doesn't have a native jump option like SCP or SSH, but it can still be instructed to accomplish the same task. We can use the remote shell flag (-e) to forward our copy request to the proxy system. System B, the proxy, will then be responsible for connecting to System C, the source, and spitting the data out to System A, the localhost.

This solution does come with one very important caveat however. Using Rsync in this way assumes that all parties in this transaction have properly configured passwordless key-based SSH authentication enabled. Assuming that you do not have this configured, here's a quick guide on how to do it:

#Generate SSH Keys on Source System "A":
ssh-keygen -t rsa -b 2048

#Send your keys to every proxy in your chain.
ssh-copy-id bluey@192.168.1.2
ssh-copy-id bingo@192.168.65.4

You will have to supply the appropriate user's credentials for every ssh-copy-id operation. If everything went well you should be able to open a remote shell via SSH to each of those hosts without a password (because it will use the keys in your machine to authenticate you, which is arguably way more secure than using a password).

With that out of the way now, we can start our Rsync process. This will have the added benefits of file integrity monitoring, low bandwidth consumption, and permission mirroring. Here's the command:

#Rsync via proxy:
rsync -avhP -e 'ssh bluey@192.168.1.2 ssh' bingo@192.168.65.4:/PATH/TO/SOURCE/ /PATH/TO/DESTINATION/

If you cannot or do not want to configure key-based authentication, Rsync will prompt you for a password for every copy operation it performs. It's not the end of the world, but it can make multi-file directory transfers very cumbersome. It will also prevent you from automating this or making a cronjob with this solution.