# Upload

目录下两个文件，index.php以及example.php，又要扫描目录。

index.php

```php
<?php
if (!isset($_GET["ctf"])) {
    highlight_file(__FILE__);
    die();
}

if(isset($_GET["ctf"]))
    $ctf = $_GET["ctf"];

if($ctf=="upload") {
    if ($_FILES['postedFile']['size'] > 1024*512) {
        die("这么大个的东西你是想d我吗？");
    }
    $imageinfo = getimagesize($_FILES['postedFile']['tmp_name']);
    if ($imageinfo === FALSE) {
        die("如果不能好好传图片的话就还是不要来打扰我了");
    }
    if ($imageinfo[0] !== 1 && $imageinfo[1] !== 1) {
        die("东西不能方方正正的话就很讨厌");
    }
    $fileName=urldecode($_FILES['postedFile']['name']);
    if(stristr($fileName,"c") || stristr($fileName,"i") || stristr($fileName,"h") || stristr($fileName,"ph")) {
        die("有些东西让你传上去的话那可不得了");
    }
    $imagePath = "image/" . mb_strtolower($fileName);
    if(move_uploaded_file($_FILES["postedFile"]["tmp_name"], $imagePath)) {
        echo "upload success, image at $imagePath";
    } else {
        die("传都没有传上去");
    }
}
```

example.php

```php
<?php
if (!isset($_GET["ctf"])) {
    highlight_file(__FILE__);
    die();
}

if(isset($_GET["ctf"]))
    $ctf = $_GET["ctf"];

if($ctf=="poc") {
    $zip = new \ZipArchive();
    $name_for_zip = "example/" . $_POST["file"];
    if(explode(".",$name_for_zip)[count(explode(".",$name_for_zip))-1]!=="zip") {
        die("要不咱们再看看？");
    }
    if ($zip->open($name_for_zip) !== TRUE) {
        die ("都不能解压呢");
    }

    echo "可以解压，我想想存哪里";
    $pos_for_zip = "/tmp/example/" . md5($_SERVER["REMOTE_ADDR"]);
    $zip->extractTo($pos_for_zip);
    $zip->close();
    unlink($name_for_zip);
    $files = glob("$pos_for_zip/*");
    foreach($files as $file){
        if (is_dir($file)) {
            continue;
        }
        $first = imagecreatefrompng($file);
        $size = min(imagesx($first), imagesy($first));
        $second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);
        if ($second !== FALSE) {
            $final_name = pathinfo($file)["basename"];
            imagepng($second, 'example/'.$final_name);
            imagedestroy($second);
        }
        imagedestroy($first);
        unlink($file);
    }

}
```

index.php有个文件上传的点，但是经过了过滤

```
array getimagesize ( string $filename [, array &$imageinfo ] )
```

> `getimagesize()` 函数用于获取图像大小及相关信息，成功返回一个数组，失败则返回 FALSE 并产生一条 E\_WARNING 级的错误信息。
>
> `getimagesize()` 函数将测定任何 GIF，JPG，PNG，SWF，SWC，PSD，TIFF，BMP，IFF，JP2，JPX，JB2，JPC，XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。

如果获取成功，返回的数组内容如下：

* 索引 0 给出的是图像宽度的像素值
* 索引 1 给出的是图像高度的像素值
* 索引 2 给出的是图像的类型，返回的是数字，其中1 = GIF，2 = JPG，3 = PNG，4 = SWF，5 = PSD，6 = BMP，7 = TIFF(intel byte order)，8 = TIFF(motorola byte order)，9 = JPC，10 = JP2，11 = JPX，12 = JB2，13 = SWC，14 = IFF，15 = WBMP，16 = XBM
* 索引 3 给出的是一个宽度和高度的字符串，可以直接用于 HTML 的 \<image> 标签
* 索引 bits 给出的是图像的每种颜色的位数，二进制格式
* 索引 channels 给出的是图像的通道值，RGB 图像默认是 3
* 索引 mime 给出的是图像的 MIME 信息，此信息可以用来在 HTTP Content-type 头信息中发送正确的信息，如： header("Content-type: image/jpeg");

可以通过伪造文件头绕过，例如以下文件头：

```python
with open('png.php','wb') as f:
    f.write(b'\x89PNG\r\n\x1a\n<?php phpinfo(); ?>')
with open('gif.php','wb') as f:
    f.write(b'GIF89a<?php phpinfo(); ?>')
```

这里通过XBM文件格式绕过长宽设置

```
#define width 1
#define height 1
```

我们可以本地测试一下

<figure><img src="/files/DtuBSYu2dKFNNF013HtK" alt=""><figcaption></figcaption></figure>

根据example.php文件，我们应该要上传一个.zip文件，但是index.php过滤了字母i，通过Unicode绕过了这个mb\_strtolower检测。

看一下这个函数：

```
mb_strtolower （ string $str ， string $encoding= mb_internal_encoding（） ）：字符串
```

将字符串转换为小写

{% hint style="info" %}
和 [strtolower()](https://www.php.net/manual/zh/function.strtolower.php) 不同的是，“字母”字符的检测是根据字符的 Unicode 属性。 因此函数的行为不会受语言设置的影响，能偶转换任意具有“字母”属性的字符，例如元音变音 A（Ä）。
{% endhint %}

它具有的特点是能转换任意具有字母属性的字符，测试一下：

```php
<?php
    var_dump(mb_strtolower('Ⅰ'));
    var_dump(strtolower('Ⅰ'));
    var_dump(mb_strtolower('İ'));
    var_dump(strtolower('İ'));
```

发现在版本5.2、5.4、7.3时，四个dump值都是false，当版本号为5.6以及7.1时，

```php
    var_dump(mb_strtolower('İ'));
```

为真，所以这是一个限制了版本的函数利用，目前唯一一个找到能利用的字符：

<https://unicode-table.com/cn/0130/>

由于有urldecode，所以直接用%c4%b0表示。

最后是绕过图片检查，用这个[脚本](https://github.com/huntergregal/PNG-IDAT-Payload-Generator)来制作一个图片马。将脚本的payload复制到[CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Hex\('Auto'\)Raw_Deflate\('Fixed%20Huffman%20Coding'\)To_Hex\('Space',0\)\&input=YTM5ZjY3NTQ2ZjJjMjQxNTJiMTE2NzEyNTQ2ZjExMmUyOTE1MmIyMTY3MjI2YjZmNWY1MzEw)

<figure><img src="/files/bAIbLv95Tc1DBqPJNUHq" alt=""><figcaption></figcaption></figure>

得到的数据通过010editor修改：

<figure><img src="/files/DI7XhSUuvQu0S8y5pX98" alt=""><figcaption></figcaption></figure>

修改后内容如下：

<figure><img src="/files/qnmRzdDLF29Sbg7mjXxL" alt=""><figcaption></figcaption></figure>

压缩，复制出16进制

<figure><img src="/files/xMnpjErEsbtU34KbxVBQ" alt=""><figcaption></figcaption></figure>

修改payload的内容：

<figure><img src="/files/feNyhpZEJKBhOGWHMxHG" alt=""><figcaption></figcaption></figure>

生成exp：

<figure><img src="/files/5g0doTI1RXtYA8H2csAp" alt=""><figcaption></figcaption></figure>

修改图片后缀为php然后压缩成zip上传

<figure><img src="/files/9BkcCLqamgFU291baHTN" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/s7rnFqc3ithlwiy5b424" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/QFFcF5iwc6YjlLUBBlFH" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook-88.gitbook.io/ctf-writeup/2021/2021-ciscn/upload.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
