Obscure
← Back to the log
September 7, 2026·Johnytiger

Obscure

A TryHackMe box that runs on patience: anonymous FTP hands over a password-recovery binary, Ghidra hands over its hardcoded ID, an Odoo 10 RCE gets a shell in a container, and a ret2libc ROP chain over pwntools finishes it as root.

securityctfwriteuptryhackmebinary exploitationlinux

Kicking things off with an initial Nmap scan we get the following.

Nmap service scan showing anonymous FTP on 21, OpenSSH on 22 and Werkzeug 0.9.6 / Python 2.7.9 on port 80

nmap -sCV -p- -Pn 10.67.172.42 -v -T5 --min-rate 1500 --max-rtt-timeout 500ms --max-retries 3 --open -oN nmap_targets.txt

We have 21 FTP with anonymous, so the first thing we'll do is check it out and see if we can find any loot.

Coming in hot we found 2 things, a notice.txt and a password file. Let's download everything to our computer and check it out.

Anonymous FTP session listing the pub directory and pulling down notice.txt and the password binary with mget

The password file is an application ELF file. Running it shows us it's a password recovery program and we just need to give it an employee ID. Let's move on from here and enumerate the web app we saw on port 80.

Running ./password, which prints "Password Recovery" and asks for the employee id that is in your email

Let's run feroxbuster and a Nikto scan — it's an Odoo application. We find out that Odoo has a few vulnerabilities, we just need to disclose which version this one is currently at.

The Odoo login page served at 10.67.172.42/web/login

So far we have ID'd Werkzeug 0.9.6, Python 2.7.9 and Odoo 10. There's an RCE, but it requires us to be logged in. After trying just about every exploit we found available for these versions we could not get anything to budge, so let's move over to the password binary and throw that into Ghidra to see what's going on there.

Ghidra decompiling the pass() function, with a strcmp against a hardcoded employee id on line 18

After inspecting in Ghidra, we find the pass() function, and upon inspection we see line 18 with a hardcoded id param. Let's throw that into the password app and see if it will work.

The password binary accepting the hardcoded employee id and printing the recovered password

Turns out it's the employee's ID and it works, giving us a password!

Through our earlier tests we had discovered that admin@antisoft.thm is a real account, so let's hit it with our password on the Odoo app.

The authenticated Odoo backend showing the Apps dashboard

We gain access to the dashboard! Now let's try that RCE CVE for Odoo 10 and see if we can gain a shell. Grabbing 44064.md from searchsploit we follow the instructions and modify our code with our listener.

A netcat listener on port 80 catching a shell back as the odoo user

We get a shell! Now let's upgrade this to a Python shell. After enumerating a bit we realize this is a Docker container.

ls -la /.dockerenv

Enumerating the Odoo directory finds us our first flag.

Reading flag.txt out of /var/lib/odoo

Now let's enumerate this Docker container. Inspecting the Odoo conf we find some database creds. We will save these in case for later.

The contents of /etc/odoo/odoo.conf, showing admin_passwd and the Postgres host, user and password

Checking that database doesn't give us much lead, so I'm going to transfer over linPEAS and see what we can get. We actually don't have wget, so let's go back to enumerating the psql and see if we can get a version number.

psql -h 172.17.0.2 -U odoo -d main -c "SHOW server_version;"

It's version "9.4.26" — upon researching this one we find out that this version has its vulnerabilities patched, so we will continue enumerating from here.

Querying pg_user and confirming the odoo role has the superuser flag set

User odoo is a superuser, we can bust back a shell from here to 443.

A shell on the database container running as the postgres user

We get the shell and begin our enumeration process again. We check for the important stuff obviously, and find exim4 is the oddball of the list.

A setuid binary listing on the database container ending in /usr/sbin/exim4

Let's check the version and see if it is vulnerable — find out it is, it's version 4.89. Let's give CVE-2019-10149 ("Return of the WIZard") a shot.

After trying a bunch of things and running around everywhere, I'm realizing this might have been a dead end. Let's take a step back onto the odoo user and search for important files.

find / -perm -4000 on the Odoo container, turning up a setuid binary called /ret

find / -perm -4000 2>/dev/null

Gets us this weird program /ret — wtf is that lol, let's check it out.

Running /ret, which prints "Exploit this binary to get on the box! What do you have for me?"

Looking like another Ghidra.

root.txt on the container reading "Well done, my friend, you rooted a docker container", next to a list of ROP gadgets

Don't waste your time like I did, it's a trap lmao. Enumerating the machine from the root Docker does absolutely nothing we couldn't have done on the odoo account.

Checking the kernel once again we get 4.4.0-210-generic — no exploits available, this is actually the patched version.

uname -a showing kernel 4.4.0-210-generic

Going back on the root account, remember we saw something running on 4444 — turns out it's another one of those ret binaries again. We run the same binary again to port 4444 and bam, we get user on the other machine as zeeshan!

Piping the payload into the service on 4444, landing a shell as zeeshan on hydra and reading user.txt

Enumerating as zeeshan, I found /exploit_me — another setuid+setgid root ELF. This one had no win() and no /bin/sh string, so ret2win was out. A quick cyclic-pattern crash in gdb put the offset at 40, and the plan wrote itself: leak puts@got via puts@plt, return into main for a second overflow, then ROP into execve("/bin/sh", NULL, NULL) at the resolved libc address.

The pwntools exploit leaking the libc base, dropping to an interactive root prompt and reading /root/root.txt

pwntools has an SSH transport that lets you drive a remote process the same way as a local one, so I dropped my key into zeeshan@hydra's authorized_keys, scp'd the target's libc back to Kali, and ran the exploit over that SSH channel. Leak came back clean, second payload landed, and interactive mode dropped me into a # prompt as root. /root/root.txt

End of transmissionAll posts
Drive
Johnytiger
Obscure · Johnytiger